CVS rebase
[nedit-bw.git] / handle_hardlink.patch
bloba6c945e6776495f700a5104589f163d8a6f74d02
1 ---
3 doc/help.etx | 19 +++++++++
4 source/file.c | 107 +++++++++++++++++++++++++++++++++++++++++++++------
5 source/menu.c | 51 +++++++++++++++++++++++-
6 source/nedit.h | 6 ++
7 source/preferences.c | 20 +++++++++
8 source/preferences.h | 2
9 source/server.c | 6 ++
10 source/window.c | 33 +++++++++++++++
11 source/window.h | 1
12 9 files changed, 232 insertions(+), 13 deletions(-)
14 diff --quilt old/source/file.c new/source/file.c
15 --- old/source/file.c
16 +++ new/source/file.c
17 @@ -64,6 +64,7 @@ static const char CVSID[] = "$Id: file.c
18 #else
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 +#include <utime.h>
22 #ifndef __MVS__
23 #include <sys/param.h>
24 #endif
25 @@ -207,6 +208,8 @@ WindowInfo *EditExistingFile(WindowInfo
27 WindowInfo *window;
28 char fullname[MAXPATHLEN];
29 + struct stat statbuf;
30 + int response, doUnlink = 0;
32 /* first look to see if file is already displayed in a window */
33 window = FindWindowWithFile(name, path);
34 @@ -219,7 +222,38 @@ WindowInfo *EditExistingFile(WindowInfo
36 return window;
40 + /* Get the full name of the file */
41 + strcpy(fullname, path);
42 + strcat(fullname, name);
44 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE
45 + && stat(fullname, &statbuf) == 0
46 + && statbuf.st_nlink > 1) {
48 + response = 0;
49 + window = FindWindowWithInode(name, path);
50 + if (window != NULL) {
51 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT) {
52 + response = DialogF(DF_INF, window->shell, 3, "Open File",
53 + "%s has more than one hardlink\n"
54 + "and is already opened in an other window.\n\n"
55 + "Unlink this file after open (make this file uniq)\n"
56 + "or show the other window?",
57 + "Open & Unlink", "Show other", "Cancel", fullname);
58 + }
60 + if (response == 3)
61 + return NULL;
62 + else if (response == 2) {
63 + RaiseShellWindow(window->shell, True);
64 + return window;
65 + } else if (response == 1 || GetPrefHardlinkMode() == HARDLINK_UNLINK) {
66 + doUnlink = 1;
67 + }
68 + }
69 + }
71 /* If an existing window isn't specified; or the window is already
72 in use (not Untitled or Untitled and modified), or is currently
73 busy running a macro; create the window */
74 @@ -276,12 +310,28 @@ WindowInfo *EditExistingFile(WindowInfo
75 UpdateStatsLine(window);
77 /* Add the name to the convenience menu of previously opened files */
78 - strcpy(fullname, path);
79 - strcat(fullname, name);
80 if(GetPrefAlwaysCheckRelTagsSpecs())
81 AddRelTagsFile(GetPrefTagFile(), path, TAG);
82 AddToPrevOpenMenu(fullname);
84 + if (doUnlink) {
85 + if (unlink(fullname) != 0) {
86 + DialogF(DF_ERR, window->shell, 1,
87 + "Error open File",
88 + "Error can't unlink %s.\n"
89 + "Changes are made to multiple opended files.",
90 + "Continue", fullname);
91 + } else {
92 + struct utimbuf utimbuf;
93 + doSave(window);
94 + chmod(fullname, statbuf.st_mode);
95 + chown(fullname, statbuf.st_uid, statbuf.st_gid);
96 + utimbuf.actime = statbuf.st_atime;
97 + utimbuf.modtime = statbuf.st_mtime;
98 + utime(fullname, &utimbuf);
99 + }
102 MacroApplyHook(window, "post_open_hook", 0, NULL, NULL);
104 return window;
105 @@ -911,8 +961,14 @@ int CloseFileAndWindow(WindowInfo *windo
107 int SaveWindow(WindowInfo *window)
109 - int stat;
111 + int response;
112 + struct stat statbuf;
113 + char fullname[MAXPATHLEN];
115 + /* Get the full name of the file */
116 + strcpy(fullname, window->path);
117 + strcat(fullname, window->filename);
119 /* Try to ensure our information is up-to-date */
120 CheckForChangesToFile(window);
122 @@ -929,7 +985,7 @@ int SaveWindow(WindowInfo *window)
123 /* Check for external modifications and warn the user */
124 if (GetPrefWarnFileMods() && fileWasModifiedExternally(window))
126 - stat = DialogF(DF_WARN, window->shell, 2, "Save File",
127 + response = DialogF(DF_WARN, window->shell, 2, "Save File",
128 "%s has been modified by another program.\n\n"
129 "Continuing this operation will overwrite any external\n"
130 "modifications to the file since it was opened in NEdit,\n"
131 @@ -938,7 +994,7 @@ int SaveWindow(WindowInfo *window)
132 "use Save As... to save this file under a different name,\n"
133 "or Revert to Saved to revert to the modified version.",
134 "Continue", "Cancel", window->filename);
135 - if (stat == 2)
136 + if (response == 2)
138 /* Cancel and mark file as externally modified */
139 window->lastModTime = 0;
140 @@ -946,18 +1002,45 @@ int SaveWindow(WindowInfo *window)
141 return FALSE;
146 + response = 0;
147 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE
148 + && stat(fullname, &statbuf) == 0
149 + && statbuf.st_nlink > 1) {
151 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT
152 + && window->hardlinkDontPromptAgain == False) {
153 + response = DialogF(DF_QUES, window->shell, 3, "Save File",
154 + "%s has more than one hardlink.\n\n"
155 + "Unlink this file before saving (make this file uniq)?",
156 + "Unlink & Save", "Save", "Cancel", fullname);
159 + if (response == 3) {
160 + return FALSE;
161 + } else if (response == 2) {
162 + window->hardlinkDontPromptAgain = True;
163 + } else if (GetPrefHardlinkMode() == HARDLINK_UNLINK || response == 1) {
164 + if (unlink(fullname) != 0) {
165 + DialogF(DF_ERR, window->shell, 1,
166 + "Error saving File",
167 + "Error can't unlink %s",
168 + "Cancel", fullname);
173 #ifdef VMS
174 RemoveBackupFile(window);
175 - stat = doSave(window);
176 + response = doSave(window);
177 #else
178 if (writeBckVersion(window))
179 return FALSE;
180 - stat = doSave(window);
181 - if (stat)
182 + response = doSave(window);
183 + if (response)
184 RemoveBackupFile(window);
185 #endif /*VMS*/
186 - return stat;
187 + return response;
190 int SaveWindowAs(WindowInfo *window, const char *newName, int addWrap)
191 diff --quilt old/source/menu.c new/source/menu.c
192 --- old/source/menu.c
193 +++ new/source/menu.c
194 @@ -141,6 +141,9 @@ static void autoIndentDefCB(Widget w, Wi
195 static void smartIndentDefCB(Widget w, WindowInfo *window, caddr_t callData);
196 static void autoSaveDefCB(Widget w, WindowInfo *window, caddr_t callData);
197 static void preserveDefCB(Widget w, WindowInfo *window, caddr_t callData);
198 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
199 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
200 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
201 static void noWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
202 static void newlineWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
203 static void contWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
204 @@ -1018,7 +1021,19 @@ Widget CreateMenuBar(Widget parent, Wind
205 "Incremental Backup", 'B', autoSaveDefCB, window, GetPrefAutoSave(),
206 SHORT);
209 + /* Hardlink mode default sub menu */
210 + subSubPane = createMenu(subPane, "hardlinkMode", "Hardlinks", 'H',
211 + NULL, FULL);
212 + window->hardlinkDefItem[HARDLINK_IGNORE] = createMenuRadioToggle(
213 + subSubPane, "ignore", "Ignore", 'I', ignoreHardlinkDefCB, window,
214 + GetPrefHardlinkMode() == HARDLINK_IGNORE, SHORT);
215 + window->hardlinkDefItem[HARDLINK_PROMPT] = createMenuRadioToggle(
216 + subSubPane, "prompt", "Prompt", 'P', promptHardlinkDefCB, window,
217 + GetPrefHardlinkMode() == HARDLINK_PROMPT, SHORT);
218 + window->hardlinkDefItem[HARDLINK_UNLINK] = createMenuRadioToggle(
219 + subSubPane, "unlink", "Unlink", 'U', unlinkHardlinkDefCB, window,
220 + GetPrefHardlinkMode() == HARDLINK_UNLINK, SHORT);
222 /* Show Matching sub menu */
223 subSubPane = createMenu(subPane, "showMatching", "Show Matching (..)", 'M',
224 NULL, FULL);
225 @@ -1862,6 +1877,40 @@ static void preserveDefCB(Widget w, Wind
229 +static void setHardlinkModeMenu(enum hardlinkMode mode)
231 + WindowInfo *win;
232 + int i;
234 + if (mode >= N_HARDLINK_MODES) {
235 + return;
238 + /* Set the preference and make the other windows' menus agree */
239 + SetPrefHardlinkMode(mode);
240 + for (win = WindowList; win != NULL; win = win->next) {
241 + for (i = 0; i < N_HARDLINK_MODES; i++) {
242 + XmToggleButtonSetState(win->hardlinkDefItem[i],
243 + mode == i ? True : False, False);
248 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
250 + setHardlinkModeMenu(HARDLINK_IGNORE);
253 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
255 + setHardlinkModeMenu(HARDLINK_PROMPT);
258 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
260 + setHardlinkModeMenu(HARDLINK_UNLINK);
263 static void fontDefCB(Widget w, WindowInfo *window, caddr_t callData)
265 HidePointerOnKeyedEvent(WidgetToWindow(MENU_WIDGET(w))->lastFocus,
266 diff --quilt old/source/nedit.h new/source/nedit.h
267 --- old/source/nedit.h
268 +++ new/source/nedit.h
269 @@ -113,6 +113,10 @@ enum showWrapMarginEnums {SHOW_WRAP_MARG
270 in preferences.c */
271 enum truncSubstitution {TRUNCSUBST_SILENT, TRUNCSUBST_FAIL, TRUNCSUBST_WARN, TRUNCSUBST_IGNORE};
273 +/* This enum must be kept in sync with HardlinkModes[] in in preferences.c */
274 +enum hardlinkMode {HARDLINK_IGNORE, HARDLINK_PROMPT, HARDLINK_UNLINK,
275 + N_HARDLINK_MODES};
277 #define NO_FLASH_STRING "off"
278 #define FLASH_DELIMIT_STRING "delimiter"
279 #define FLASH_RANGE_STRING "range"
280 @@ -570,6 +574,8 @@ typedef struct _WindowInfo {
281 "tabbed" documents, while each document
282 has its own background menu. */
283 int inMacroHook; /* to protect GC in MacroApplyHook() */
284 + Widget hardlinkDefItem[N_HARDLINK_MODES];
285 + Boolean hardlinkDontPromptAgain;
286 } WindowInfo;
288 extern WindowInfo *WindowList;
289 diff --quilt old/source/preferences.c new/source/preferences.c
290 --- old/source/preferences.c
291 +++ new/source/preferences.c
292 @@ -169,6 +169,13 @@ static char* TruncSubstitutionModes[] =
293 #define DEFAULT_TAB_DIST -1
294 #define DEFAULT_EM_TAB_DIST -1
296 +static char *HardlinkModes[] = {
297 + "Ignore",
298 + "Prompt",
299 + "Unlink",
300 + NULL
303 /* list of available language modes and language specific preferences */
304 static int NLanguageModes = 0;
305 typedef struct {
306 @@ -339,6 +346,7 @@ static struct prefData {
307 int truncSubstitution;
308 Boolean forceOSConversion;
309 Boolean showScrolltip;
310 + int hardlinkMode;
311 } PrefData;
313 /* Temporary storage for preferences strings which are discarded after being
314 @@ -1169,6 +1177,8 @@ static PrefDescripRec PrefDescrip[] = {
315 &PrefData.showCursorline, NULL, True},
316 // {"showScrolltip", "ShowScrolltip", PREF_BOOLEAN, "True",
317 // &PrefData.showScrolltip, NULL, False},
318 + {"hardlinkMode", "HardlinkMode", PREF_ENUM, "Ignore",
319 + &PrefData.hardlinkMode, HardlinkModes, True},
322 static XrmOptionDescRec OpTable[] = {
323 @@ -2315,6 +2325,16 @@ Boolean GetPrefShowScrolltip(void)
324 return True;
327 +void SetPrefHardlinkMode(int mode)
329 + setIntPref(&PrefData.hardlinkMode, mode);
332 +int GetPrefHardlinkMode(void)
334 + return PrefData.hardlinkMode;
338 ** If preferences don't get saved, ask the user on exit whether to save
340 diff --quilt old/source/preferences.h new/source/preferences.h
341 --- old/source/preferences.h
342 +++ new/source/preferences.h
343 @@ -215,5 +215,7 @@ Boolean GetPrefHonorSymlinks(void);
344 Boolean GetPrefForceOSConversion(void);
345 void SetPrefFocusOnRaise(Boolean);
346 Boolean GetPrefShowScrolltip(void);
347 +void SetPrefHardlinkMode(int mode);
348 +int GetPrefHardlinkMode(void);
350 #endif /* NEDIT_PREFERENCES_H_INCLUDED */
351 diff --quilt old/source/server.c new/source/server.c
352 --- old/source/server.c
353 +++ new/source/server.c
354 @@ -346,6 +346,7 @@ static void processServerCommandString(c
355 WindowInfo *window, *lastFile = NULL;
356 long currentDesktop = QueryCurrentDesktop(TheDisplay,
357 RootWindow(TheDisplay, DefaultScreen(TheDisplay)));
358 + WindowInfo *window_ino;
360 /* If the command string is empty, put up an empty, Untitled window
361 (or just pop one up if it already exists) */
362 @@ -473,6 +474,11 @@ static void processServerCommandString(c
363 filename, pathname, editFlags, geometry, iconicFlag,
364 lmLen == 0 ? NULL : langMode,
365 tabbed == -1? GetPrefOpenInTab() : tabbed, True);
366 + window_ino = FindWindowWithInode(filename, pathname);
367 + if (window != NULL && window == window_ino) {
368 + deleteFileOpenProperty2(filename, pathname);
369 + /* deleteFileClosedProperty2(filename, pathname); */
372 if (window) {
373 CleanUpTabBarExposeQueue(window);
374 diff --quilt old/source/window.c new/source/window.c
375 --- old/source/window.c
376 +++ new/source/window.c
377 @@ -333,6 +333,7 @@ WindowInfo *CreateWindow(const char *nam
378 window->device = 0;
379 window->inode = 0;
380 window->inMacroHook = 0;
381 + window->hardlinkDontPromptAgain = False;
383 /* If window geometry was specified, split it apart into a window position
384 component and a window size component. Create a new geometry string
385 @@ -1202,6 +1203,37 @@ WindowInfo *FindWindowWithFile(const cha
389 +** Check if there is already a window open for a given inode
391 +WindowInfo *FindWindowWithInode(const char *name, const char *path)
393 + WindowInfo *w;
394 + char fullname[MAXPATHLEN];
395 + struct stat statbuf;
396 + ino_t ino;
397 + dev_t dev;
399 + strcpy(fullname, path);
400 + strcat(fullname, name);
402 + if (stat(fullname, &statbuf) < 0) {
403 + return NULL;
406 + ino = statbuf.st_ino;
407 + dev = statbuf.st_dev;
409 + for (w = WindowList; w != NULL; w = w->next) {
410 + strcpy(fullname, w->path);
411 + strcat(fullname, w->filename);
412 + if (w->inode == ino && w->device == dev) {
413 + return w;
416 + return NULL;
420 ** Add another independently scrollable pane to the current document,
421 ** splitting the pane which currently has keyboard focus.
423 @@ -4532,6 +4564,7 @@ static void cloneDocument(WindowInfo *wi
424 window->inode = orgWin->inode;
425 window->fileClosedAtom = orgWin->fileClosedAtom;
426 orgWin->fileClosedAtom = None;
427 + window->hardlinkDontPromptAgain = orgWin->hardlinkDontPromptAgain;
429 /* copy the text/split panes settings, cursor pos & selection */
430 cloneTextPanes(window, orgWin);
431 diff --quilt old/source/window.h new/source/window.h
432 --- old/source/window.h
433 +++ new/source/window.h
434 @@ -47,6 +47,7 @@ void SetWindowModified(WindowInfo *windo
435 void MakeSelectionVisible(WindowInfo *window, Widget textPane);
436 int GetSimpleSelection(textBuffer *buf, int *left, int *right);
437 WindowInfo *FindWindowWithFile(const char *name, const char *path);
438 +WindowInfo *FindWindowWithInode(const char *name, const char *path);
439 void SetAutoIndent(WindowInfo *window, int state);
440 void SetShowMatching(WindowInfo *window, int state);
441 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
442 diff --quilt old/doc/help.etx new/doc/help.etx
443 --- old/doc/help.etx
444 +++ new/doc/help.etx
445 @@ -4111,6 +4111,25 @@ Preferences
446 Show file name and path in a tooltip when moving the mouse pointer over a tab.
447 (See Tabbed_Editing_.)
449 +**Hardlinks**
450 + On UNIX systems it is possible to have a physical same file with different
451 + filenames. Because it can be dangerous to change a file which is accessible
452 + from different filenames, NEdit can warn the user either when the user writes
453 + to a file with multiple hardlinks, or if the file is already opened with a
454 + different name.
456 + ~Ignore~
457 + Ignore any hardlink informations.
459 + ~Prompt~
460 + Prompt the user in thes two cases. For the open case the user can choose to
461 + open the other window with the file, or make the file to open a destine copy
462 + of the original file. For the save case the user can save the file under the
463 + name but a new destine file or ignore it.
465 + ~Unlink~
466 + Always make a destine copy of the file.
468 **Show Cursorline**
469 Background the current line with a colored bar. Use the color dialog to
470 change the background color.