push up read_from_pipes.patch
[nedit-bw.git] / handle_hardlink.patch
blob05642b7ec7e169a3ca42e542aef608b2c339311e
1 ---
3 doc/help.etx | 19 ++++++++
4 source/file.c | 111 ++++++++++++++++++++++++++++++++++++++++++++-------
5 source/menu.c | 49 ++++++++++++++++++++++
6 source/nedit.h | 5 ++
7 source/preferences.c | 22 +++++++++-
8 source/preferences.h | 2
9 source/server.c | 6 ++
10 source/window.c | 34 +++++++++++++++
11 source/window.h | 1
12 9 files changed, 233 insertions(+), 16 deletions(-)
14 diff --quilt old/source/file.c new/source/file.c
15 --- old/source/file.c
16 +++ new/source/file.c
17 @@ -64,10 +64,11 @@ static const char CVSID[] = "$Id: file.c
18 #include <stat.h>
19 #include <unixio.h>
20 #else
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 +#include <utime.h>
24 #ifndef __MVS__
25 #include <sys/param.h>
26 #endif
27 #include <fcntl.h>
28 #endif /*VMS*/
29 @@ -190,10 +191,12 @@ WindowInfo *EditExistingFile(WindowInfo
30 const char *path, int flags, char *geometry, int iconic,
31 const char *languageMode, int tabbed, int bgOpen)
33 WindowInfo *window;
34 char fullname[MAXPATHLEN];
35 + struct stat statbuf;
36 + int response, do_unlink = 0;
38 /* first look to see if file is already displayed in a window */
39 window = FindWindowWithFile(name, path);
40 if (window != NULL) {
41 if (!bgOpen) {
42 @@ -202,11 +205,42 @@ WindowInfo *EditExistingFile(WindowInfo
43 else
44 RaiseDocumentWindow(window);
46 return window;
50 + /* Get the full name of the file */
51 + strcpy(fullname, path);
52 + strcat(fullname, name);
54 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE
55 + && stat(fullname, &statbuf) == 0
56 + && statbuf.st_nlink > 1) {
58 + response = 0;
59 + window = FindWindowWithInode(name, path);
60 + if (window != NULL) {
61 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT) {
62 + response = DialogF(DF_INF, window->shell, 3, "Open File",
63 + "%s has more than one hardlink\n"
64 + "and is already opened in an other window.\n\n"
65 + "Unlink this file after open (make this file uniq)\n"
66 + "or show the other window?",
67 + "Open & Unlink", "Show other", "Cancel", fullname);
68 + }
70 + if (response == 3)
71 + return NULL;
72 + else if (response == 2) {
73 + RaiseShellWindow(window->shell, True);
74 + return window;
75 + } else if (response == 1 || GetPrefHardlinkMode() == HARDLINK_UNLINK) {
76 + do_unlink = 1;
77 + }
78 + }
79 + }
81 /* If an existing window isn't specified; or the window is already
82 in use (not Untitled or Untitled and modified), or is currently
83 busy running a macro; create the window */
84 if (inWindow == NULL) {
85 window = CreateWindow(name, geometry, iconic);
86 @@ -259,16 +293,33 @@ WindowInfo *EditExistingFile(WindowInfo
87 UpdateWindowTitle(window);
88 UpdateWindowReadOnly(window);
89 UpdateStatsLine(window);
91 /* Add the name to the convenience menu of previously opened files */
92 - strcpy(fullname, path);
93 - strcat(fullname, name);
94 - if(GetPrefAlwaysCheckRelTagsSpecs())
95 - AddRelTagsFile(GetPrefTagFile(), path, TAG);
96 + if (GetPrefAlwaysCheckRelTagsSpecs()) {
97 + AddRelTagsFile(GetPrefTagFile(), path, TAG);
98 + }
99 AddToPrevOpenMenu(fullname);
101 + if (do_unlink) {
102 + if (unlink(fullname) != 0) {
103 + DialogF(DF_ERR, window->shell, 1,
104 + "Error open File",
105 + "Error can't unlink %s.\n"
106 + "Changes are made to multiple opended files.",
107 + "Continue", fullname);
108 + } else {
109 + struct utimbuf utimbuf;
110 + doSave(window);
111 + chmod(fullname, statbuf.st_mode);
112 + chown(fullname, statbuf.st_uid, statbuf.st_gid);
113 + utimbuf.actime = statbuf.st_atime;
114 + utimbuf.modtime = statbuf.st_mtime;
115 + utime(fullname, &utimbuf);
119 /* file_open_hook() */
120 MacroApplyHook(window, "post_open_hook", 0, NULL, NULL);
122 return window;
124 @@ -896,12 +947,18 @@ int CloseFileAndWindow(WindowInfo *windo
125 return TRUE;
128 int SaveWindow(WindowInfo *window)
130 - int stat;
132 + int response;
133 + struct stat statbuf;
134 + char fullname[MAXPATHLEN];
136 + /* Get the full name of the file */
137 + strcpy(fullname, window->path);
138 + strcat(fullname, window->filename);
140 /* Try to ensure our information is up-to-date */
141 CheckForChangesToFile(window);
143 /* Return success if the file is normal & unchanged or is a
144 read-only file. */
145 @@ -914,39 +971,65 @@ int SaveWindow(WindowInfo *window)
146 return SaveWindowAs(window, NULL, False);
148 /* Check for external modifications and warn the user */
149 if (GetPrefWarnFileMods() && fileWasModifiedExternally(window))
151 - stat = DialogF(DF_WARN, window->shell, 2, "Save File",
152 + response = DialogF(DF_WARN, window->shell, 2, "Save File",
153 "%s has been modified by another program.\n\n"
154 "Continuing this operation will overwrite any external\n"
155 "modifications to the file since it was opened in NEdit,\n"
156 "and your work or someone else's may potentially be lost.\n\n"
157 "To preserve the modified file, cancel this operation and\n"
158 "use Save As... to save this file under a different name,\n"
159 "or Revert to Saved to revert to the modified version.",
160 "Continue", "Cancel", window->filename);
161 - if (stat == 2)
162 + if (response == 2)
164 /* Cancel and mark file as externally modified */
165 window->lastModTime = 0;
166 window->fileMissing = FALSE;
167 return FALSE;
172 + response = 0;
173 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE &&
174 + stat(fullname, &statbuf) == 0 &&
175 + statbuf.st_nlink > 1) {
177 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT &&
178 + window->HLdontpromptagain == False)
179 + response = DialogF(DF_QUES, window->shell, 3, "Save File",
180 + "%s has more than one hardlink.\n\n"
181 + "Unlink this file before saving (make this file uniq)?",
182 + "Unlink & Save", "Save", "Cancel", fullname);
184 + if (response == 3) {
185 + return FALSE;
186 + } else if (response == 2) {
187 + window->HLdontpromptagain = True;
188 + } else if (GetPrefHardlinkMode() == HARDLINK_UNLINK || response == 1) {
189 + if (unlink(fullname) != 0) {
190 + DialogF(DF_ERR, window->shell, 1,
191 + "Error saving File",
192 + "Error can't unlink %s",
193 + "Cancel", fullname);
198 #ifdef VMS
199 RemoveBackupFile(window);
200 - stat = doSave(window);
201 + response = doSave(window);
202 #else
203 if (writeBckVersion(window))
204 return FALSE;
205 - stat = doSave(window);
206 - if (stat)
207 + response = doSave(window);
208 + if (response)
209 RemoveBackupFile(window);
210 #endif /*VMS*/
211 - return stat;
212 + return response;
215 int SaveWindowAs(WindowInfo *window, const char *newName, int addWrap)
217 int response, retVal, fileFormat;
218 diff --quilt old/source/menu.c new/source/menu.c
219 --- old/source/menu.c
220 +++ new/source/menu.c
221 @@ -139,10 +139,13 @@ static void statsCB(Widget w, WindowInfo
222 static void autoIndentOffDefCB(Widget w, WindowInfo *window, caddr_t callData);
223 static void autoIndentDefCB(Widget w, WindowInfo *window, caddr_t callData);
224 static void smartIndentDefCB(Widget w, WindowInfo *window, caddr_t callData);
225 static void autoSaveDefCB(Widget w, WindowInfo *window, caddr_t callData);
226 static void preserveDefCB(Widget w, WindowInfo *window, caddr_t callData);
227 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
228 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
229 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
230 static void noWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
231 static void newlineWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
232 static void contWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
233 static void wrapMarginDefCB(Widget w, WindowInfo *window, caddr_t callData);
234 static void shellSelDefCB(Widget widget, WindowInfo* window, caddr_t callData);
235 @@ -1019,11 +1022,23 @@ Widget CreateMenuBar(Widget parent, Wind
236 GetPrefSaveOldVersion(), SHORT);
237 window->autoSaveDefItem = createMenuToggle(subPane, "incrementalBackup",
238 "Incremental Backup", 'B', autoSaveDefCB, window, GetPrefAutoSave(),
239 SHORT);
242 + /* Hardlink mode default sub menu */
243 + subSubPane = createMenu(subPane, "hardlinkMode", "Hardlinks", 'H',
244 + NULL, FULL);
245 + window->hardlinkDefItem[HARDLINK_IGNORE] = createMenuRadioToggle(
246 + subSubPane, "ignore", "Ignore", 'I', ignoreHardlinkDefCB, window,
247 + GetPrefHardlinkMode() == HARDLINK_IGNORE, SHORT);
248 + window->hardlinkDefItem[HARDLINK_PROMPT] = createMenuRadioToggle(
249 + subSubPane, "prompt", "Prompt", 'P', promptHardlinkDefCB, window,
250 + GetPrefHardlinkMode() == HARDLINK_PROMPT, SHORT);
251 + window->hardlinkDefItem[HARDLINK_UNLINK] = createMenuRadioToggle(
252 + subSubPane, "unlink", "Unlink", 'U', unlinkHardlinkDefCB, window,
253 + GetPrefHardlinkMode() == HARDLINK_UNLINK, SHORT);
255 /* Show Matching sub menu */
256 subSubPane = createMenu(subPane, "showMatching", "Show Matching (..)", 'M',
257 NULL, FULL);
258 window->showMatchingOffDefItem = createMenuRadioToggle(subSubPane, "off",
259 "Off", 'O', showMatchingOffDefCB, window,
260 @@ -1868,10 +1883,42 @@ static void preserveDefCB(Widget w, Wind
261 if (IsTopDocument(win))
262 XmToggleButtonSetState(win->saveLastDefItem, state, False);
266 +static void setHardlinkModeMenu(enum hardlinkMode mode)
268 + WindowInfo *win;
269 + int i;
271 + if (mode >= N_HARDLINK_MODES)
272 + return;
274 + /* Set the preference and make the other windows' menus agree */
275 + SetPrefHardlinkMode(mode);
276 + for (win = WindowList; win != NULL; win = win->next) {
277 + for (i = 0; i < N_HARDLINK_MODES; i++)
278 + XmToggleButtonSetState(win->hardlinkDefItem[i],
279 + mode == i ? True : False, False);
283 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
285 + setHardlinkModeMenu(HARDLINK_IGNORE);
288 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
290 + setHardlinkModeMenu(HARDLINK_PROMPT);
293 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
295 + setHardlinkModeMenu(HARDLINK_UNLINK);
298 static void fontDefCB(Widget w, WindowInfo *window, caddr_t callData)
300 HidePointerOnKeyedEvent(WidgetToWindow(MENU_WIDGET(w))->lastFocus,
301 ((XmAnyCallbackStruct *)callData)->event);
302 ChooseFonts(WidgetToWindow(MENU_WIDGET(w)), False);
303 diff --quilt old/source/nedit.h new/source/nedit.h
304 --- old/source/nedit.h
305 +++ new/source/nedit.h
306 @@ -111,10 +111,13 @@ enum showWrapMarginEnums {SHOW_WRAP_MARG
308 /* This enum must be kept in parallel to the array TruncSubstitutionModes[]
309 in preferences.c */
310 enum truncSubstitution {TRUNCSUBST_SILENT, TRUNCSUBST_FAIL, TRUNCSUBST_WARN, TRUNCSUBST_IGNORE};
312 +/* This enum must be kept in sync with HardlinkModes[] in in preferences.c */
313 +enum hardlinkMode {HARDLINK_IGNORE, HARDLINK_PROMPT, HARDLINK_UNLINK, N_HARDLINK_MODES};
315 #define NO_FLASH_STRING "off"
316 #define FLASH_DELIMIT_STRING "delimiter"
317 #define FLASH_RANGE_STRING "range"
319 #define CHARSET (XmStringCharSet)XmSTRING_DEFAULT_CHARSET
320 @@ -568,10 +571,12 @@ typedef struct _WindowInfo {
321 UserMenuCache *userMenuCache; /* cache user menus: */
322 UserBGMenuCache userBGMenuCache; /* shell & macro menu are shared over all
323 "tabbed" documents, while each document
324 has its own background menu. */
325 Boolean transient;
326 + Widget hardlinkDefItem[N_HARDLINK_MODES];
327 + Bool HLdontpromptagain;
328 } WindowInfo;
330 extern WindowInfo *WindowList;
331 extern Display *TheDisplay;
332 extern Widget TheAppShell;
333 diff --quilt old/source/preferences.c new/source/preferences.c
334 --- old/source/preferences.c
335 +++ new/source/preferences.c
336 @@ -166,10 +166,17 @@ static char* TruncSubstitutionModes[] =
337 #define DEFAULT_WRAP -1
338 #define DEFAULT_INDENT -1
339 #define DEFAULT_TAB_DIST -1
340 #define DEFAULT_EM_TAB_DIST -1
342 +static char *HardlinkModes[] = {
343 + "Ignore",
344 + "Prompt",
345 + "Unlink",
346 + NULL
349 /* list of available language modes and language specific preferences */
350 static int NLanguageModes = 0;
351 typedef struct {
352 char *name;
353 int nExtensions;
354 @@ -337,10 +344,11 @@ static struct prefData {
355 int focusOnRaise;
356 Boolean showScrolltip;
357 Boolean honorSymlinks;
358 int truncSubstitution;
359 Boolean forceOSConversion;
360 + int hardlinkMode;
361 } PrefData;
363 /* Temporary storage for preferences strings which are discarded after being
364 read */
365 static struct {
366 @@ -1168,11 +1176,13 @@ static PrefDescripRec PrefDescrip[] = {
367 {"showCursorline", "ShowCursorline", PREF_BOOLEAN, "True",
368 &PrefData.showCursorline, NULL, True},
369 {"showScrolltip", "ShowScrolltip", PREF_BOOLEAN, "True",
370 &PrefData.showScrolltip, NULL, False},
371 {"honorSymlinks", "HonorSymlinks", PREF_BOOLEAN, "True",
372 - &PrefData.honorSymlinks, NULL, False}
373 + &PrefData.honorSymlinks, NULL, False},
374 + {"hardlinkMode", "HardlinkMode", PREF_ENUM, "Ignore",
375 + &PrefData.hardlinkMode, HardlinkModes, True}
378 static XrmOptionDescRec OpTable[] = {
379 {"-wrap", ".autoWrap", XrmoptionNoArg, (caddr_t)"Continuous"},
380 {"-nowrap", ".autoWrap", XrmoptionNoArg, (caddr_t)"None"},
381 @@ -2328,10 +2338,20 @@ Boolean GetPrefShowCursorline(void)
382 Boolean GetPrefShowScrolltip(void)
384 return (Boolean) PrefData.showScrolltip;
387 +int GetPrefHardlinkMode(void)
389 + return PrefData.hardlinkMode;
392 +void SetPrefHardlinkMode(int mode)
394 + setIntPref(&PrefData.hardlinkMode, mode);
398 ** If preferences don't get saved, ask the user on exit whether to save
400 void MarkPrefsChanged(void)
402 diff --quilt old/source/preferences.h new/source/preferences.h
403 --- old/source/preferences.h
404 +++ new/source/preferences.h
405 @@ -215,7 +215,9 @@ Boolean GetPrefHonorSymlinks(void);
406 Boolean GetPrefForceOSConversion(void);
407 void SetPrefFocusOnRaise(Boolean);
408 void SetPrefShowCursorline(Boolean value);
409 Boolean GetPrefShowCursorline(void);
410 Boolean GetPrefShowScrolltip(void);
411 +int GetPrefHardlinkMode(void);
412 +void SetPrefHardlinkMode(int mode);
414 #endif /* NEDIT_PREFERENCES_H_INCLUDED */
415 diff --quilt old/source/server.c new/source/server.c
416 --- old/source/server.c
417 +++ new/source/server.c
418 @@ -342,10 +342,11 @@ static void processServerCommandString(c
419 int lineNum, createFlag, readFlag, iconicFlag, lastIconic = 0, tabbed = -1;
420 int fileLen, doLen, lmLen, geomLen, charsRead, itemsRead;
421 WindowInfo *window, *lastFile = NULL;
422 long currentDesktop = QueryCurrentDesktop(TheDisplay,
423 RootWindow(TheDisplay, DefaultScreen(TheDisplay)));
424 + WindowInfo *window_ino;
426 /* If the command string is empty, put up an empty, Untitled window
427 (or just pop one up if it already exists) */
428 if (string[0] == '\0') {
429 for (window=WindowList; window!=NULL; window=window->next)
430 @@ -469,10 +470,15 @@ static void processServerCommandString(c
431 macros to execute on. */
432 window = EditExistingFile(findWindowOnDesktop(tabbed, currentDesktop),
433 filename, pathname, editFlags, geometry, iconicFlag,
434 lmLen == 0 ? NULL : langMode,
435 tabbed == -1? GetPrefOpenInTab() : tabbed, True);
436 + window_ino = FindWindowWithInode(filename, pathname);
437 + if (window != NULL && window == window_ino) {
438 + deleteFileOpenProperty2(filename, pathname);
439 + //deleteFileClosedProperty2(filename, pathname);
442 if (window) {
443 CleanUpTabBarExposeQueue(window);
444 if (lastFile && window->shell != lastFile->shell) {
445 CleanUpTabBarExposeQueue(lastFile);
446 diff --quilt old/source/window.c new/source/window.c
447 --- old/source/window.c
448 +++ new/source/window.c
449 @@ -789,10 +789,12 @@ WindowInfo *CreateWindow(const char *nam
450 XtSetSensitive(win->moveDocumentItem, state);
451 XtSetSensitive(win->contextMoveDocumentItem, state);
455 + window->HLdontpromptagain = False;
457 return window;
461 ** ButtonPress event handler for tabs.
462 @@ -1173,10 +1175,41 @@ WindowInfo *FindWindowWithFile(const cha
464 return NULL;
468 +** Check if there is already a window open for a given inode
470 +WindowInfo *FindWindowWithInode(const char *name, const char *path)
472 + WindowInfo *w;
473 + char fullname[MAXPATHLEN];
474 + struct stat statbuf;
475 + ino_t ino;
476 + dev_t dev;
478 + strcpy(fullname, path);
479 + strcat(fullname, name);
481 + if (stat(fullname, &statbuf) < 0) {
482 + return NULL;
485 + ino = statbuf.st_ino;
486 + dev = statbuf.st_dev;
488 + for (w = WindowList; w != NULL; w = w->next) {
489 + strcpy(fullname, w->path);
490 + strcat(fullname, w->filename);
491 + if (w->inode == ino && w->device == dev) {
492 + return w;
495 + return NULL;
499 ** Add another independently scrollable pane to the current document,
500 ** splitting the pane which currently has keyboard focus.
502 void SplitPane(WindowInfo *window)
504 @@ -4503,10 +4536,11 @@ static void cloneDocument(WindowInfo *wi
505 window->device = orgWin->device;
506 window->inode = orgWin->inode;
507 window->fileClosedAtom = orgWin->fileClosedAtom;
508 orgWin->fileClosedAtom = None;
509 window->transient = orgWin->transient;
510 + window->HLdontpromptagain = orgWin->HLdontpromptagain;
512 /* copy the text/split panes settings, cursor pos & selection */
513 cloneTextPanes(window, orgWin);
515 /* copy undo & redo list */
516 diff --quilt old/source/window.h new/source/window.h
517 --- old/source/window.h
518 +++ new/source/window.h
519 @@ -45,10 +45,11 @@ void UpdateMinPaneHeights(WindowInfo *wi
520 void UpdateNewOppositeMenu(WindowInfo *window, int openInTab);
521 void SetWindowModified(WindowInfo *window, int modified);
522 void MakeSelectionVisible(WindowInfo *window, Widget textPane);
523 int GetSimpleSelection(textBuffer *buf, int *left, int *right);
524 WindowInfo *FindWindowWithFile(const char *name, const char *path);
525 +WindowInfo *FindWindowWithInode(const char *name, const char *path);
526 void SetAutoIndent(WindowInfo *window, int state);
527 void SetShowMatching(WindowInfo *window, int state);
528 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
529 const char *boldName, const char *boldItalicName);
530 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
531 diff --quilt old/doc/help.etx new/doc/help.etx
532 --- old/doc/help.etx
533 +++ new/doc/help.etx
534 @@ -4118,10 +4118,29 @@ Preferences
536 **Terminate with Line Break on Save**
537 Some UNIX tools expect that files end with a line feed. If this option is
538 activated, NEdit will append one if required.
540 +**Hardlinks**
541 + On UNIX systems it is possible to have a physical same file with different
542 + filenames. Because it can be dangerous to change a file which is accessible
543 + from different filenames, NEdit can warn the user either when the user writes
544 + to a file with multiple hardlinks, or if the file is already opened with a
545 + different name.
547 + ~Ignore~
548 + Ignore any hardlink informations.
550 + ~Prompt~
551 + Prompt the user in thes two cases. For the open case the user can choose to
552 + open the other window with the file, or make the file to open a destine copy
553 + of the original file. For the save case the user can save the file under the
554 + name but a new destine file or ignore it.
556 + ~Unlink~
557 + Always make a destine copy of the file.
559 **Show Hidden Files**
560 Show hidden files by default in file selection boxes.
562 **Show Cursorline**
563 Background the current line with a colored bar.