fix macro_hooks patch
[nedit-bw.git] / handle_hardlink.patch
blob6920031a98fe4c60ce2e51f6a641cf3b9602bbdb
1 Subject: handle hardlinks
3 Gives the user a choice what happend if he open a file which is currently
4 opened with an other path:
6 * ignore it
7 * ask the user how to handle it
8 * automatic re-save the to-be-open file to a new inode
10 Similar when you save a file that has more than one hardlink.
12 ---
14 doc/help.etx | 19 +++++++++
15 source/file.c | 107 +++++++++++++++++++++++++++++++++++++++++++++------
16 source/menu.c | 51 +++++++++++++++++++++++-
17 source/nedit.h | 6 ++
18 source/preferences.c | 20 +++++++++
19 source/preferences.h | 2
20 source/server.c | 9 ++++
21 source/window.c | 31 ++++++++++++++
22 source/window.h | 1
23 9 files changed, 233 insertions(+), 13 deletions(-)
25 diff --quilt old/source/file.c new/source/file.c
26 --- old/source/file.c
27 +++ new/source/file.c
28 @@ -64,6 +64,7 @@ static const char CVSID[] = "$Id: file.c
29 #else
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 +#include <utime.h>
33 #ifndef __MVS__
34 #include <sys/param.h>
35 #endif
36 @@ -207,6 +208,8 @@ WindowInfo *EditExistingFile(WindowInfo
38 WindowInfo *window;
39 char fullname[MAXPATHLEN];
40 + struct stat statbuf;
41 + int response, doUnlink = 0;
43 /* first look to see if file is already displayed in a window */
44 window = FindWindowWithFile(name, path);
45 @@ -219,7 +222,38 @@ WindowInfo *EditExistingFile(WindowInfo
47 return window;
51 + /* Get the full name of the file */
52 + strcpy(fullname, path);
53 + strcat(fullname, name);
55 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE
56 + && stat(fullname, &statbuf) == 0
57 + && statbuf.st_nlink > 1) {
59 + response = 0;
60 + window = FindWindowWithInode(name, path);
61 + if (window != NULL) {
62 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT) {
63 + response = DialogF(DF_INF, window->shell, 3, "Open File",
64 + "%s has more than one hardlink\n"
65 + "and is already opened in an other window.\n\n"
66 + "Unlink this file after open (make this file uniq)\n"
67 + "or show the other window?",
68 + "Open & Unlink", "Show other", "Cancel", fullname);
69 + }
71 + if (response == 3)
72 + return NULL;
73 + else if (response == 2) {
74 + RaiseShellWindow(window->shell, True);
75 + return window;
76 + } else if (response == 1 || GetPrefHardlinkMode() == HARDLINK_UNLINK) {
77 + doUnlink = 1;
78 + }
79 + }
80 + }
82 /* If an existing window isn't specified; or the window is already
83 in use (not Untitled or Untitled and modified), or is currently
84 busy running a macro; create the window */
85 @@ -276,12 +310,28 @@ WindowInfo *EditExistingFile(WindowInfo
86 UpdateStatsLine(window);
88 /* Add the name to the convenience menu of previously opened files */
89 - strcpy(fullname, path);
90 - strcat(fullname, name);
91 if(GetPrefAlwaysCheckRelTagsSpecs())
92 AddRelTagsFile(GetPrefTagFile(), path, TAG);
93 AddToPrevOpenMenu(fullname);
95 + if (doUnlink) {
96 + if (unlink(fullname) != 0) {
97 + DialogF(DF_ERR, window->shell, 1,
98 + "Error open File",
99 + "Error can't unlink %s.\n"
100 + "Changes are made to multiple opended files.",
101 + "Continue", fullname);
102 + } else {
103 + struct utimbuf utimbuf;
104 + doSave(window);
105 + chmod(fullname, statbuf.st_mode);
106 + chown(fullname, statbuf.st_uid, statbuf.st_gid);
107 + utimbuf.actime = statbuf.st_atime;
108 + utimbuf.modtime = statbuf.st_mtime;
109 + utime(fullname, &utimbuf);
113 PostOpenHook(window);
115 return window;
116 @@ -911,8 +961,14 @@ int CloseFileAndWindow(WindowInfo *windo
118 int SaveWindow(WindowInfo *window)
120 - int stat;
122 + int response;
123 + struct stat statbuf;
124 + char fullname[MAXPATHLEN];
126 + /* Get the full name of the file */
127 + strcpy(fullname, window->path);
128 + strcat(fullname, window->filename);
130 /* Try to ensure our information is up-to-date */
131 CheckForChangesToFile(window);
133 @@ -929,7 +985,7 @@ int SaveWindow(WindowInfo *window)
134 /* Check for external modifications and warn the user */
135 if (GetPrefWarnFileMods() && fileWasModifiedExternally(window))
137 - stat = DialogF(DF_WARN, window->shell, 2, "Save File",
138 + response = DialogF(DF_WARN, window->shell, 2, "Save File",
139 "%s has been modified by another program.\n\n"
140 "Continuing this operation will overwrite any external\n"
141 "modifications to the file since it was opened in NEdit,\n"
142 @@ -938,7 +994,7 @@ int SaveWindow(WindowInfo *window)
143 "use Save As... to save this file under a different name,\n"
144 "or Revert to Saved to revert to the modified version.",
145 "Continue", "Cancel", window->filename);
146 - if (stat == 2)
147 + if (response == 2)
149 /* Cancel and mark file as externally modified */
150 window->lastModTime = 0;
151 @@ -946,18 +1002,45 @@ int SaveWindow(WindowInfo *window)
152 return FALSE;
157 + response = 0;
158 + if (GetPrefHardlinkMode() != HARDLINK_IGNORE
159 + && stat(fullname, &statbuf) == 0
160 + && statbuf.st_nlink > 1) {
162 + if (GetPrefHardlinkMode() == HARDLINK_PROMPT
163 + && window->hardlinkDontPromptAgain == False) {
164 + response = DialogF(DF_QUES, window->shell, 3, "Save File",
165 + "%s has more than one hardlink.\n\n"
166 + "Unlink this file before saving (make this file uniq)?",
167 + "Unlink & Save", "Save", "Cancel", fullname);
170 + if (response == 3) {
171 + return FALSE;
172 + } else if (response == 2) {
173 + window->hardlinkDontPromptAgain = True;
174 + } else if (GetPrefHardlinkMode() == HARDLINK_UNLINK || response == 1) {
175 + if (unlink(fullname) != 0) {
176 + DialogF(DF_ERR, window->shell, 1,
177 + "Error saving File",
178 + "Error can't unlink %s",
179 + "Cancel", fullname);
184 #ifdef VMS
185 RemoveBackupFile(window);
186 - stat = doSave(window);
187 + response = doSave(window);
188 #else
189 if (writeBckVersion(window))
190 return FALSE;
191 - stat = doSave(window);
192 - if (stat)
193 + response = doSave(window);
194 + if (response)
195 RemoveBackupFile(window);
196 #endif /*VMS*/
197 - return stat;
198 + return response;
201 int SaveWindowAs(WindowInfo *window, const char *newName, int addWrap)
202 diff --quilt old/source/menu.c new/source/menu.c
203 --- old/source/menu.c
204 +++ new/source/menu.c
205 @@ -142,6 +142,9 @@ static void autoIndentDefCB(Widget w, Wi
206 static void smartIndentDefCB(Widget w, WindowInfo *window, caddr_t callData);
207 static void autoSaveDefCB(Widget w, WindowInfo *window, caddr_t callData);
208 static void preserveDefCB(Widget w, WindowInfo *window, caddr_t callData);
209 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
210 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
211 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData);
212 static void noWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
213 static void newlineWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
214 static void contWrapDefCB(Widget w, WindowInfo *window, caddr_t callData);
215 @@ -1019,7 +1022,19 @@ Widget CreateMenuBar(Widget parent, Wind
216 "Incremental Backup", 'B', autoSaveDefCB, window, GetPrefAutoSave(),
217 SHORT);
220 + /* Hardlink mode default sub menu */
221 + subSubPane = createMenu(subPane, "hardlinkMode", "Hardlinks", 'H',
222 + NULL, FULL);
223 + window->hardlinkDefItem[HARDLINK_IGNORE] = createMenuRadioToggle(
224 + subSubPane, "ignore", "Ignore", 'I', ignoreHardlinkDefCB, window,
225 + GetPrefHardlinkMode() == HARDLINK_IGNORE, SHORT);
226 + window->hardlinkDefItem[HARDLINK_PROMPT] = createMenuRadioToggle(
227 + subSubPane, "prompt", "Prompt", 'P', promptHardlinkDefCB, window,
228 + GetPrefHardlinkMode() == HARDLINK_PROMPT, SHORT);
229 + window->hardlinkDefItem[HARDLINK_UNLINK] = createMenuRadioToggle(
230 + subSubPane, "unlink", "Unlink", 'U', unlinkHardlinkDefCB, window,
231 + GetPrefHardlinkMode() == HARDLINK_UNLINK, SHORT);
233 /* Show Matching sub menu */
234 subSubPane = createMenu(subPane, "showMatching", "Show Matching (..)", 'M',
235 NULL, FULL);
236 @@ -1863,6 +1878,40 @@ static void preserveDefCB(Widget w, Wind
240 +static void setHardlinkModeMenu(enum hardlinkMode mode)
242 + WindowInfo *win;
243 + int i;
245 + if (mode >= N_HARDLINK_MODES) {
246 + return;
249 + /* Set the preference and make the other windows' menus agree */
250 + SetPrefHardlinkMode(mode);
251 + for (win = WindowList; win != NULL; win = win->next) {
252 + for (i = 0; i < N_HARDLINK_MODES; i++) {
253 + XmToggleButtonSetState(win->hardlinkDefItem[i],
254 + mode == i ? True : False, False);
259 +static void ignoreHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
261 + setHardlinkModeMenu(HARDLINK_IGNORE);
264 +static void promptHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
266 + setHardlinkModeMenu(HARDLINK_PROMPT);
269 +static void unlinkHardlinkDefCB(Widget w, WindowInfo *window, caddr_t callData)
271 + setHardlinkModeMenu(HARDLINK_UNLINK);
274 static void fontDefCB(Widget w, WindowInfo *window, caddr_t callData)
276 HidePointerOnKeyedEvent(WidgetToWindow(MENU_WIDGET(w))->lastFocus,
277 diff --quilt old/source/nedit.h new/source/nedit.h
278 --- old/source/nedit.h
279 +++ new/source/nedit.h
280 @@ -113,6 +113,10 @@ enum showWrapMarginEnums {SHOW_WRAP_MARG
281 in preferences.c */
282 enum truncSubstitution {TRUNCSUBST_SILENT, TRUNCSUBST_FAIL, TRUNCSUBST_WARN, TRUNCSUBST_IGNORE};
284 +/* This enum must be kept in sync with HardlinkModes[] in in preferences.c */
285 +enum hardlinkMode {HARDLINK_IGNORE, HARDLINK_PROMPT, HARDLINK_UNLINK,
286 + N_HARDLINK_MODES};
288 #define NO_FLASH_STRING "off"
289 #define FLASH_DELIMIT_STRING "delimiter"
290 #define FLASH_RANGE_STRING "range"
291 @@ -570,6 +574,8 @@ typedef struct _WindowInfo {
292 "tabbed" documents, while each document
293 has its own background menu. */
294 int inMacroHook; /* to protect GC in MacroApplyHook() */
295 + Widget hardlinkDefItem[N_HARDLINK_MODES];
296 + Boolean hardlinkDontPromptAgain;
297 } WindowInfo;
299 extern WindowInfo *WindowList;
300 diff --quilt old/source/preferences.c new/source/preferences.c
301 --- old/source/preferences.c
302 +++ new/source/preferences.c
303 @@ -167,6 +167,13 @@ static char* TruncSubstitutionModes[] =
304 #define DEFAULT_TAB_DIST -1
305 #define DEFAULT_EM_TAB_DIST -1
307 +static char *HardlinkModes[] = {
308 + "Ignore",
309 + "Prompt",
310 + "Unlink",
311 + NULL
314 /* list of available language modes and language specific preferences */
315 static int NLanguageModes = 0;
316 typedef struct {
317 @@ -337,6 +344,7 @@ static struct prefData {
318 int truncSubstitution;
319 Boolean forceOSConversion;
320 Boolean showScrolltip;
321 + int hardlinkMode;
322 } PrefData;
324 /* Temporary storage for preferences strings which are discarded after being
325 @@ -1167,6 +1175,8 @@ static PrefDescripRec PrefDescrip[] = {
326 &PrefData.showCursorline, NULL, True},
327 {"showScrolltip", "ShowScrolltip", PREF_BOOLEAN, "True",
328 &PrefData.showScrolltip, NULL, False},
329 + {"hardlinkMode", "HardlinkMode", PREF_ENUM, "Ignore",
330 + &PrefData.hardlinkMode, HardlinkModes, True},
333 static XrmOptionDescRec OpTable[] = {
334 @@ -2310,6 +2320,16 @@ Boolean GetPrefShowScrolltip(void)
335 return PrefData.showScrolltip;
338 +void SetPrefHardlinkMode(int mode)
340 + setIntPref(&PrefData.hardlinkMode, mode);
343 +int GetPrefHardlinkMode(void)
345 + return PrefData.hardlinkMode;
349 ** If preferences don't get saved, ask the user on exit whether to save
351 diff --quilt old/source/preferences.h new/source/preferences.h
352 --- old/source/preferences.h
353 +++ new/source/preferences.h
354 @@ -216,5 +216,7 @@ Boolean GetPrefHonorSymlinks(void);
355 Boolean GetPrefForceOSConversion(void);
356 void SetPrefFocusOnRaise(Boolean);
357 Boolean GetPrefShowScrolltip(void);
358 +void SetPrefHardlinkMode(int mode);
359 +int GetPrefHardlinkMode(void);
361 #endif /* NEDIT_PREFERENCES_H_INCLUDED */
362 diff --quilt old/source/server.c new/source/server.c
363 --- old/source/server.c
364 +++ new/source/server.c
365 @@ -338,6 +338,7 @@ static void processServerCommandString(c
366 WindowInfo *window, *lastFile = NULL;
367 long currentDesktop = QueryCurrentDesktop(TheDisplay,
368 RootWindow(TheDisplay, DefaultScreen(TheDisplay)));
369 + WindowInfo *window_ino;
371 /* If the command string is empty, put up an empty, Untitled window
372 (or just pop one up if it already exists) */
373 @@ -467,6 +468,14 @@ static void processServerCommandString(c
374 filename, pathname, editFlags, geometry, iconicFlag,
375 lmLen == 0 ? NULL : langMode,
376 tabbed == -1? GetPrefOpenInTab() : tabbed, True);
377 + window_ino = FindWindowWithInode(filename, pathname);
378 + if (window && window == window_ino) {
379 + deleteFileOpenProperty2(requestname);
380 + /* we opened a window with the same inode but another path
381 + but the client may waits for closing this path,
382 + we do it, but it's not fair */
383 + deleteFileClosedProperty2(requestname);
386 if (window) {
387 CleanUpTabBarExposeQueue(window);
388 diff --quilt old/source/window.c new/source/window.c
389 --- old/source/window.c
390 +++ new/source/window.c
391 @@ -333,6 +333,7 @@ WindowInfo *CreateWindow(const char *nam
392 window->device = 0;
393 window->inode = 0;
394 window->inMacroHook = 0;
395 + window->hardlinkDontPromptAgain = False;
397 /* If window geometry was specified, split it apart into a window position
398 component and a window size component. Create a new geometry string
399 @@ -1199,6 +1200,35 @@ WindowInfo *FindWindowWithFile(const cha
403 +** Check if there is already a window open for a given inode
405 +WindowInfo *FindWindowWithInode(const char *name, const char *path)
407 + WindowInfo *w;
408 + char fullname[MAXPATHLEN];
409 + struct stat statbuf;
410 + ino_t ino;
411 + dev_t dev;
413 + strcpy(fullname, path);
414 + strcat(fullname, name);
416 + if (stat(fullname, &statbuf) < 0) {
417 + return NULL;
420 + ino = statbuf.st_ino;
421 + dev = statbuf.st_dev;
423 + for (w = WindowList; w != NULL; w = w->next) {
424 + if (w->inode == ino && w->device == dev) {
425 + return w;
428 + return NULL;
432 ** Add another independently scrollable pane to the current document,
433 ** splitting the pane which currently has keyboard focus.
435 @@ -4521,6 +4551,7 @@ static void cloneDocument(WindowInfo *wi
436 window->inode = orgWin->inode;
437 window->fileClosedAtom = orgWin->fileClosedAtom;
438 orgWin->fileClosedAtom = None;
439 + window->hardlinkDontPromptAgain = orgWin->hardlinkDontPromptAgain;
441 /* copy the text/split panes settings, cursor pos & selection */
442 cloneTextPanes(window, orgWin);
443 diff --quilt old/source/window.h new/source/window.h
444 --- old/source/window.h
445 +++ new/source/window.h
446 @@ -47,6 +47,7 @@ void SetWindowModified(WindowInfo *windo
447 void MakeSelectionVisible(WindowInfo *window, Widget textPane);
448 int GetSimpleSelection(textBuffer *buf, int *left, int *right);
449 WindowInfo *FindWindowWithFile(const char *name, const char *path);
450 +WindowInfo *FindWindowWithInode(const char *name, const char *path);
451 void SetAutoIndent(WindowInfo *window, int state);
452 void SetShowMatching(WindowInfo *window, int state);
453 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
454 diff --quilt old/doc/help.etx new/doc/help.etx
455 --- old/doc/help.etx
456 +++ new/doc/help.etx
457 @@ -4136,6 +4136,25 @@ Preferences
458 Show file name and path in a tooltip when moving the mouse pointer over a tab.
459 (See Tabbed_Editing_.)
461 +**Hardlinks**
462 + On UNIX systems it is possible to have a physical same file with different
463 + filenames. Because it can be dangerous to change a file which is accessible
464 + from different filenames, NEdit can warn the user either when the user writes
465 + to a file with multiple hardlinks, or if the file is already opened with a
466 + different name.
468 + ~Ignore~
469 + Ignore any hardlink informations.
471 + ~Prompt~
472 + Prompt the user in thes two cases. For the open case the user can choose to
473 + open the other window with the file, or make the file to open a destine copy
474 + of the original file. For the save case the user can save the file under the
475 + name but a new destine file or ignore it.
477 + ~Unlink~
478 + Always make a destine copy of the file.
480 **Show Cursorline**
481 Background the current line with a colored bar. Use the color dialog to
482 change the background color.