📄 file.c
字号:
static const char CVSID[] = "$Id: file.c,v 1.66.2.4 2003/09/28 14:18:12 edg Exp $";/******************************************************************************** ** file.c -- Nirvana Editor file i/o ** ** Copyright (C) 1999 Mark Edel ** ** This is free software; you can redistribute it and/or modify it under the ** terms of the GNU General Public License as published by the Free Software ** Foundation; either version 2 of the License, or (at your option) any later ** version. ** ** This software is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ** for more details. ** ** You should have received a copy of the GNU General Public License along with ** software; if not, write to the Free Software Foundation, Inc., 59 Temple ** Place, Suite 330, Boston, MA 02111-1307 USA ** ** Nirvana Text Editor ** May 10, 1991 ** ** Written by Mark Edel ** ********************************************************************************/#ifdef HAVE_CONFIG_H#include "../config.h"#endif#include "file.h"#include "textBuf.h"#include "text.h"#include "window.h"#include "preferences.h"#include "undo.h"#include "menu.h"#include "tags.h"#include "server.h"#include "../util/misc.h"#include "../util/DialogF.h"#include "../util/fileUtils.h"#include "../util/getfiles.h"#include "../util/printUtils.h"#include "../util/utils.h"#include <stdio.h>#include <errno.h>#include <limits.h>#include <string.h>#include <stdlib.h>#ifdef VMS#include "../util/VMSparam.h"#include <types.h>#include <stat.h>#include <unixio.h>#else#include <sys/types.h>#include <sys/stat.h>#ifndef __MVS__#include <sys/param.h>#endif#include <unistd.h>#include <fcntl.h>#endif /*VMS*/#include <Xm/Xm.h>#include <Xm/ToggleB.h>#include <Xm/FileSB.h>#include <Xm/RowColumn.h>#include <Xm/Form.h>#include <Xm/Label.h>#ifdef HAVE_DEBUG_H#include "../debug.h"#endif/* Maximum frequency in miliseconds of checking for external modifications. The periodic check is only performed on buffer modification, and the check interval is only to prevent checking on every keystroke in case of a file system which is slow to process stat requests (which I'm not sure exists) */#define MOD_CHECK_INTERVAL 3000static int doSave(WindowInfo *window);static void safeClose(WindowInfo *window);static int doOpen(WindowInfo *window, const char *name, const char *path, int flags);static void backupFileName(WindowInfo *window, char *name, int len);static int writeBckVersion(WindowInfo *window);static int bckError(WindowInfo *window, const char *errString, const char *file);static int fileWasModifiedExternally(WindowInfo *window);static const char *errorString(void);static void addWrapNewlines(WindowInfo *window);static void setFormatCB(Widget w, XtPointer clientData, XtPointer callData);static void addWrapCB(Widget w, XtPointer clientData, XtPointer callData);static int cmpWinAgainstFile(WindowInfo *window, const char *fileName);static int min(int i1, int i2);#ifdef VMSvoid removeVersionNumber(char *fileName);#endif /*VMS*/void EditNewFile(char *geometry, int iconic, const char *languageMode, const char *defaultPath){ char name[MAXPATHLEN]; WindowInfo *window; /*... test for creatability? */ /* Find a (relatively) unique name for the new file */ UniqueUntitledName(name); /* create the window */ window = CreateWindow(name, geometry, iconic); strcpy(window->filename, name); strcpy(window->path, defaultPath ? defaultPath : ""); SetWindowModified(window, FALSE); CLEAR_ALL_LOCKS(window->lockReasons); UpdateWindowReadOnly(window); UpdateStatsLine(window); UpdateWindowTitle(window); if (languageMode == NULL) DetermineLanguageMode(window, True); else SetLanguageMode(window, FindLanguageMode(languageMode), True);}/*** Open an existing file specified by name and path. Use the window inWindow** unless inWindow is NULL or points to a window which is already in use** (displays a file other than Untitled, or is Untitled but modified). Flags** can be any of:**** CREATE: If file is not found, (optionally) prompt the** user whether to create** SUPPRESS_CREATE_WARN When creating a file, don't ask the user** PREF_READ_ONLY Make the file read-only regardless**** If languageMode is passed as NULL, it will be determined automatically** from the file extension or file contents.*/WindowInfo *EditExistingFile(WindowInfo *inWindow, const char *name, const char *path, int flags, char *geometry, int iconic, const char *languageMode){ WindowInfo *window; char fullname[MAXPATHLEN]; /* first look to see if file is already displayed in a window */ window = FindWindowWithFile(name, path); if (window != NULL) { RaiseShellWindow(window->shell); return window; } /* If an existing window isn't specified; or the window is already in use (not Untitled or Untitled and modified), or is currently busy running a macro; create the window */ if (inWindow == NULL || inWindow->filenameSet || inWindow->fileChanged || inWindow->macroCmdData != NULL) window = CreateWindow(name, geometry, iconic); else { window = inWindow; if (!iconic) { RaiseShellWindow(window->shell); } } /* Open the file */ if (!doOpen(window, name, path, flags)) { /* The user may have destroyed the window instead of closing the warning dialog; don't close it twice */ safeClose(window); return NULL; } /* Bring the title bar and statistics line up to date, doOpen does not necessarily set the window title or read-only status */ UpdateWindowTitle(window); UpdateWindowReadOnly(window); UpdateStatsLine(window); /* Add the name to the convenience menu of previously opened files */ strcpy(fullname, path); strcat(fullname, name); if(GetPrefAlwaysCheckRelTagsSpecs()) AddRelTagsFile(GetPrefTagFile(), path, TAG); AddToPrevOpenMenu(fullname); /* Decide what language mode to use, trigger language specific actions */ if (languageMode == NULL) DetermineLanguageMode(window, True); else SetLanguageMode(window, FindLanguageMode(languageMode), True); return window;}void RevertToSaved(WindowInfo *window){ char name[MAXPATHLEN], path[MAXPATHLEN]; int i; int insertPositions[MAX_PANES], topLines[MAX_PANES]; int horizOffsets[MAX_PANES]; int openFlags = 0; Widget text; /* Can't revert untitled windows */ if (!window->filenameSet) { DialogF(DF_WARN, window->shell, 1, "Error", "Window was never saved, can't re-read", "Dismiss"); return; } /* save insert & scroll positions of all of the panes to restore later */ for (i=0; i<=window->nPanes; i++) { text = i==0 ? window->textArea : window->textPanes[i-1]; insertPositions[i] = TextGetCursorPos(text); TextGetScroll(text, &topLines[i], &horizOffsets[i]); } /* re-read the file, update the window title if new file is different */ strcpy(name, window->filename); strcpy(path, window->path); RemoveBackupFile(window); ClearUndoList(window); openFlags |= IS_USER_LOCKED(window->lockReasons) ? PREF_READ_ONLY : 0; if (!doOpen(window, name, path, openFlags)) { /* This is a bit sketchy. The only error in doOpen that irreperably damages the window is "too much binary data". It should be pretty rare to be reverting something that was fine only to find that now it has too much binary data. */ if (!window->fileMissing) safeClose(window); else { /* Treat it like an externally modified file */ window->lastModTime=0; window->fileMissing=FALSE; } return; } UpdateWindowTitle(window); UpdateWindowReadOnly(window); /* restore the insert and scroll positions of each pane */ for (i=0; i<=window->nPanes; i++) { text = i==0 ? window->textArea : window->textPanes[i-1]; TextSetCursorPos(text, insertPositions[i]); TextSetScroll(text, topLines[i], horizOffsets[i]); }}/*** Checks whether a window is still alive, and closes it only if so.** Intended to be used when the file could not be opened for some reason.** Normally the window is still alive, but the user may have closed the ** window instead of the error dialog. In that case, we shouldn't close the ** window a second time.*/static void safeClose(WindowInfo *window){ WindowInfo* p = WindowList; while(p) { if (p == window) { CloseWindow(window); return; } p = p->next; }}static int doOpen(WindowInfo *window, const char *name, const char *path, int flags){ char fullname[MAXPATHLEN]; struct stat statbuf; int fileLen, readLen; char *fileString, *c; FILE *fp = NULL; int fd; int resp; /* initialize lock reasons */ CLEAR_ALL_LOCKS(window->lockReasons); /* Update the window data structure */ strcpy(window->filename, name); strcpy(window->path, path); window->filenameSet = TRUE; window->fileMissing = TRUE; /* Get the full name of the file */ strcpy(fullname, path); strcat(fullname, name); /* Open the file */#ifndef DONT_USE_ACCESS /* The only advantage of this is if you use clearcase, which messes up the mtime of files opened with r+, even if they're never actually written. To avoid requiring special builds for clearcase users, this is now the default. */ { if ((fp = fopen(fullname, "r")) != NULL) { if(access(fullname, W_OK) != 0) SET_PERM_LOCKED(window->lockReasons, TRUE);#else fp = fopen(fullname, "rb+"); if (fp == NULL) { /* Error opening file or file is not writeable */ fp = fopen(fullname, "rb"); if (fp != NULL) { /* File is read only */ SET_PERM_LOCKED(window->lockReasons, TRUE);#endif } else if (flags & CREATE && errno == ENOENT) { /* Give option to create (or to exit if this is the only window) */ if (!(flags & SUPPRESS_CREATE_WARN)) { if (WindowList == window && window->next == NULL) { resp = DialogF(DF_WARN, window->shell, 3, "New File", "Can't open %s:\n%s", "New File", "Cancel", "Exit NEdit", fullname, errorString()); } else { resp = DialogF(DF_WARN, window->shell, 2, "New File", "Can't open %s:\n%s", "New File", "Cancel", fullname, errorString()); } if (resp == 2) { return FALSE; } else if (resp == 3) { exit(EXIT_SUCCESS); } } /* Test if new file can be created */ if ((fd = creat(fullname, 0666)) == -1) { DialogF(DF_ERR, window->shell, 1, "Error creating File", "Can't create %s:\n%s", "Dismiss", fullname, errorString()); return FALSE; } else {#ifdef VMS /* get correct version number and close before removing */ getname(fd, fullname);#endif close(fd); remove(fullname); } SetWindowModified(window, FALSE); if ((flags & PREF_READ_ONLY) != 0) { SET_USER_LOCKED(window->lockReasons, TRUE); } UpdateWindowReadOnly(window); return TRUE; } else { /* A true error */ DialogF(DF_ERR, window->shell, 1, "Error opening File", "Could not open %s%s:\n%s", "Dismiss", path, name, errorString()); return FALSE; } } /* Get the length of the file, the protection mode, and the time of the last modification to the file */ if (fstat(fileno(fp), &statbuf) != 0) { fclose(fp); window->filenameSet = FALSE; /* Temp. prevent check for changes. */ DialogF(DF_ERR, window->shell, 1, "Error opening File", "Error opening %s", "Dismiss", name); window->filenameSet = TRUE; return FALSE; } if (S_ISDIR(statbuf.st_mode)) { fclose(fp); window->filenameSet = FALSE; /* Temp. prevent check for changes. */ DialogF(DF_ERR, window->shell, 1, "Error opening File", "Can't open directory %s", "Dismiss", name); window->filenameSet = TRUE; return FALSE; }#ifdef S_ISBLK if (S_ISBLK(statbuf.st_mode)) { fclose(fp); window->filenameSet = FALSE; /* Temp. prevent check for changes. */ DialogF(DF_ERR, window->shell, 1, "Error opening File", "Can't open block device %s", "Dismiss", name); window->filenameSet = TRUE; return FALSE; }#endif fileLen = statbuf.st_size; /* Allocate space for the whole contents of the file (unfortunately) */ fileString = (char *)malloc(fileLen+1); /* +1 = space for null */ if (fileString == NULL) { fclose(fp); window->filenameSet = FALSE; /* Temp. prevent check for changes. */ DialogF(DF_ERR, window->shell, 1, "Error while opening File", "File is too large to edit", "Dismiss"); window->filenameSet = TRUE; return FALSE; } /* Read the file into fileString and terminate with a null */ readLen = fread(fileString, sizeof(char), fileLen, fp); if (ferror(fp)) { fclose(fp); window->filenameSet = FALSE; /* Temp. prevent check for changes. */ DialogF(DF_ERR, window->shell, 1, "Error while opening File", "Error reading %s:\n%s", "Dismiss", name, errorString()); window->filenameSet = TRUE; free(fileString); return FALSE; } fileString[readLen] = 0; /* Close the file */ if (fclose(fp) != 0) { /* unlikely error */ DialogF(DF_WARN, window->shell, 1, "Error while opening File", "Unable to close file", "Dismiss"); /* we read it successfully, so continue */ } /* Any errors that happen after this point leave the window in a "broken" state, and thus RevertToSaved will abandon the window if window->fileMissing is FALSE and doOpen fails. */ window->fileMode = statbuf.st_mode; window->lastModTime = statbuf.st_mtime; window->fileMissing = FALSE; /* Detect and convert DOS and Macintosh format files */ window->fileFormat = FormatOfFile(fileString); if (window->fileFormat == DOS_FILE_FORMAT) ConvertFromDosFileString(fileString, &readLen, NULL); else if (window->fileFormat == MAC_FILE_FORMAT) ConvertFromMacFileString(fileString, readLen); /* Display the file contents in the text widget */ window->ignoreModify = True; BufSetAll(window->buffer, fileString); window->ignoreModify = False;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -