1 From: Tony Balinski <ajbj@free.fr>
2 Subject: Allow NormalizePathname() and ParseFilename() to use windows path
4 Various operations require the extension of an incomplete file specification
5 to a full, absolute path. Before this patch, this completion uses the
6 current directory of the NEdit process as the base for relative file lookup.
7 However, this works against NEdit's file dialogs and shell processing which
8 start off in the directory of the current document, which may well not be
9 the same as the process' directory. Using "File > Open Selected" may not, in
10 this situation, find the file you would expect.
12 This patch alters NormalizePathname() and ParseFilename(), the utility
13 functions that perform the expansion so that the base for relative file
14 specifications can be passed, then changes all relevant calls to supply the
15 current window's file path value. It includes the nmReadWriteRelToWin.diff
16 adjustments to macro file functions read_file(), write_file() and
17 append_file() for this same behaviour, and adds a new one, full_file_name(),
18 which provides the macro writer with a way of obtaining the relative file
19 name from a relative file path.
24 source/macro.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++--
28 source/selection.c | 10 ++--
30 source/tags.c | 22 ++++-----
31 util/fileUtils.c | 26 ++++++++---
32 util/fileUtils.h | 5 +-
34 11 files changed, 164 insertions(+), 42 deletions(-)
36 diff --quilt old/source/file.c new/source/file.c
39 @@ -1091,7 +1091,7 @@ int SaveWindowAs(WindowInfo *window, con
40 strcpy(fullname, newName);
43 - if (1 == NormalizePathname(fullname))
44 + if (1 == NormalizePathname(fullname, window->path))
48 @@ -1100,7 +1100,7 @@ int SaveWindowAs(WindowInfo *window, con
50 addWrapNewlines(window);
52 - if (ParseFilename(fullname, filename, pathname) != 0) {
53 + if (ParseFilename(fullname, filename, pathname, window->path) != 0) {
57 @@ -1185,7 +1185,7 @@ static int doSave(WindowInfo *window)
58 success = MacroApplyHook(window, "pre_save_hook", 0, NULL, &hookResult);
59 if (success && hookResult.tag == STRING_TAG) {
60 if (ParseFilename(hookResult.val.str.rep,
61 - window->filename, window->path)) {
62 + window->filename, window->path, window->path)) {
66 @@ -1305,7 +1305,7 @@ static int doSave(WindowInfo *window)
69 /* reflect the fact that NEdit is now editing a new version of the file */
70 - ParseFilename(fullname, window->filename, window->path);
71 + ParseFilename(fullname, window->filename, window->path, NULL);
74 /* success, file was written */
75 diff --quilt old/source/macro.c new/source/macro.c
76 --- old/source/macro.c
77 +++ new/source/macro.c
78 @@ -198,6 +198,8 @@ static int replaceInStringMS(WindowInfo
79 DataValue *result, char **errMsg);
80 static int replaceSubstringMS(WindowInfo *window, DataValue *argList, int nArgs,
81 DataValue *result, char **errMsg);
82 +static int fullFileNameMS(WindowInfo *window, DataValue *argList, int nArgs,
83 + DataValue *result, char **errMsg);
84 static int readFileMS(WindowInfo *window, DataValue *argList, int nArgs,
85 DataValue *result, char **errMsg);
86 static int writeFileMS(WindowInfo *window, DataValue *argList, int nArgs,
87 @@ -545,6 +547,7 @@ static const BuiltInSubrName MacroSubrs[
88 { "timer_add", timerAddMS },
89 { "timer_remove", timerRemoveMS },
90 { "escape_literal", escapeLiteralMS },
91 + { "full_file_name", fullFileNameMS },
92 { NULL, NULL } /* sentinel */
95 @@ -2043,7 +2046,7 @@ static int focusWindowMS(WindowInfo *win
97 strncpy(normalizedString, string, MAXPATHLEN);
98 normalizedString[MAXPATHLEN-1] = '\0';
99 - if (1 == NormalizePathname(normalizedString)) {
100 + if (1 == NormalizePathname(normalizedString, window->path)) {
101 /* Something is broken with the input pathname. */
102 *errMsg = "Pathname too long in focus_window()";
104 @@ -2479,6 +2482,108 @@ static int clipboardToStringMS(WindowInf
109 +** Resolve the partial file path in name with respect to path.
110 +** (We don't use NormalizePathname() since this modifies its path in place.)
112 +static char *convFilePathToAbsolute(const char *path, char *name)
114 + static char *nameText = NULL, *ptr;
115 + static size_t nameTextLen = 0;
116 + size_t namelen, pathlen, len;
117 + Bool isRelative = False;
118 + size_t needSlash = 0;
120 + if (!path || !path[0] || strcmp(path, ".") == 0)
121 + path = GetCurrentDir();
124 + /* If path name is relative, make it refer to current window's directory;
125 + absolute paths start with a logical name ([\w$]+) followed by a colon,
126 + or with a non-relative path in brackets (not starting with . or -)
128 + isRelative = ((strchr(name, ':') == NULL) && (strlen(name) > 1) &&
129 + !((name[0] == '[') && (name[1] != '-') &&
130 + (name[1] != '.')));
133 + Any path starting without a "/" is considered relative; if the first
134 + character is "~", we really need the user's home directory */
135 + if (name[0] == '~' && name[1] == '/') {
136 + path = GetHomeDir();
137 + for (++name; *name == '/'; ++name)
138 + continue; /* skip slash(es) following initial tilde */
142 + isRelative = (name[0] != '/');
148 + namelen = strlen(name);
149 + pathlen = strlen(path);
152 + needSlash = (path && pathlen && path[pathlen - 1] != '/') ? 1 : 0;
155 + /* make sure the buffer's big enough */
156 + len = namelen + pathlen + needSlash;
158 + if (nameTextLen < len) {
159 + ptr = realloc(nameText, len + 1);
166 + /* copy in pieces */
167 + strcpy(nameText, path);
169 + nameText[pathlen] = '/';
170 + strcpy(nameText + pathlen + needSlash, name);
172 + CompressPathname(nameText);
178 +** Built-in macro subroutine for expanding a possibly partial file specification
179 +** to a full one, using the current window's directory as a base for relative
180 +** path specifications. It does not check for file/path validity.
182 +static int fullFileNameMS(WindowInfo *window, DataValue *argList, int nArgs,
183 + DataValue *result, char **errMsg)
185 + char stringStorage[TYPE_INT_STR_SIZE(int)], *name;
188 + /* Validate arguments and convert to int */
190 + return wrongNArgsErr(errMsg);
191 + if (!readStringArg(argList[0], &name, stringStorage, errMsg))
194 + name = convFilePathToAbsolute(window->path, name);
195 + len = strlen(name);
198 + result->tag = STRING_TAG;
199 + AllocNString(&result->val.str, len + 1);
200 + strcpy(result->val.str.rep, name);
203 + result->tag = STRING_TAG;
204 + result->val.str.rep = PERM_ALLOC_STR("");
205 + result->val.str.len = 0;
212 ** Built-in macro subroutine for reading the contents of a text file into
213 @@ -2499,7 +2604,9 @@ static int readFileMS(WindowInfo *window
214 return wrongNArgsErr(errMsg);
215 if (!readStringArg(argList[0], &name, stringStorage, errMsg))
219 + name = convFilePathToAbsolute(window->path, name);
221 /* Read the whole file into an allocated string */
222 if ((fp = fopen(name, "r")) == NULL)
224 @@ -2578,7 +2685,9 @@ static int writeOrAppendFile(int append,
226 if (!readStringArg(argList[1], &name, stringStorage[0], errMsg))
230 + name = convFilePathToAbsolute(window->path, name);
233 if ((fp = fopen(name, append ? "a" : "w")) == NULL) {
234 result->tag = INT_TAG;
235 @@ -5254,7 +5363,7 @@ static int neditHomeMV(WindowInfo *windo
236 const char *neditRCName = GetRCFileName(NEDIT_RC);
237 char neditHome[MAXPATHLEN];
239 - if (0 != ParseFilename(neditRCName, NULL, neditHome)) {
240 + if (0 != ParseFilename(neditRCName, NULL, neditHome, NULL)) {
241 M_FAILURE("Unable to parse path of nedit.rc in %s");
244 diff --quilt old/source/menu.c new/source/menu.c
245 --- old/source/menu.c
246 +++ new/source/menu.c
247 @@ -2976,7 +2976,7 @@ static void openAP(Widget w, XEvent *eve
248 fileNameToOpen = args[0];
251 - if (0 != ParseFilename(fileNameToOpen, filename, pathname)
252 + if (0 != ParseFilename(fileNameToOpen, filename, pathname, window->path)
253 || strlen(filename) + strlen(pathname) > MAXPATHLEN - 1) {
254 fprintf(stderr, "nedit: invalid file name for open action: %s\n",
256 @@ -5027,7 +5027,7 @@ static void updatePrevOpenMenu(WindowInf
257 XtUnmanageChild(items[n]);
258 XtDestroyWidget(items[n]);
260 - ParseFilename(prevOpenSorted[index], filename, pathname);
261 + ParseFilename(prevOpenSorted[index], filename, pathname, NULL);
262 fileIsOpen = !!FindWindowWithFile(filename, pathname);
263 XtVaSetValues(items[n],
264 XmNlabelString, st1=XmStringCreateSimple(prevOpenSorted[index]),
265 @@ -5043,7 +5043,7 @@ static void updatePrevOpenMenu(WindowInf
267 /* Add new items for the remaining file names to the menu */
268 for (; index<NPrevOpen; index++) {
269 - ParseFilename(prevOpenSorted[index], filename, pathname);
270 + ParseFilename(prevOpenSorted[index], filename, pathname, NULL);
271 fileIsOpen = !!FindWindowWithFile(filename, pathname);
272 btn = XtVaCreateManagedWidget("win", xmToggleButtonWidgetClass,
273 window->prevOpenMenuPane,
274 diff --quilt old/source/nc.c new/source/nc.c
277 @@ -714,7 +714,7 @@ static void parseCommandLine(int argc, c
278 XtFree(commandString);
279 commandString = newCommandString;
280 outPtr = newCommandString + oldLength;
281 - if (ParseFilename(nameList[j], name, path) != 0) {
282 + if (ParseFilename(nameList[j], name, path, NULL) != 0) {
283 /* An Error, most likely too long paths/strings given */
284 commandLine->serverRequest = NULL;
286 @@ -750,7 +750,7 @@ static void parseCommandLine(int argc, c
287 if (nameList != NULL)
290 - if (ParseFilename(argv[i], name, path) != 0) {
291 + if (ParseFilename(argv[i], name, path, NULL) != 0) {
292 /* An Error, most likely too long paths/strings given */
293 commandLine->serverRequest = NULL;
295 diff --quilt old/source/nedit.c new/source/nedit.c
296 --- old/source/nedit.c
297 +++ new/source/nedit.c
298 @@ -660,7 +660,7 @@ int main(int argc, char **argv)
299 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
300 /* for each expanded file name do: */
301 for (j = 0; j < numFiles; ++j) {
302 - if (ParseFilename(nameList[j], filename, pathname) == 0) {
303 + if (ParseFilename(nameList[j], filename, pathname, NULL) == 0) {
304 /* determine if file is to be openned in new tab, by
305 factoring the options -group, -tabbed & -untabbed */
307 @@ -715,7 +715,7 @@ int main(int argc, char **argv)
308 if (nameList != NULL)
311 - if (ParseFilename(argv[i], filename, pathname) == 0 ) {
312 + if (ParseFilename(argv[i], filename, pathname, NULL) == 0 ) {
313 /* determine if file is to be openned in new tab, by
314 factoring the options -group, -tabbed & -untabbed */
316 diff --quilt old/source/selection.c new/source/selection.c
317 --- old/source/selection.c
318 +++ new/source/selection.c
319 @@ -328,7 +328,7 @@ static void fileCB(Widget widget, Window
320 guranteed to be available, but in practice is there and does work. */
321 #if defined(DONT_HAVE_GLOB) || defined(VMS)
323 - if (ParseFilename(nameText, filename, pathname) != 0) {
324 + if (ParseFilename(nameText, filename, pathname, window->path) != 0) {
325 XBell(TheDisplay, 0);
328 @@ -338,7 +338,7 @@ static void fileCB(Widget widget, Window
329 { char **nameList = NULL;
330 int i, nFiles = 0, maxFiles = 30;
332 - if (ParseFilename(nameText, filename, pathname) != 0) {
333 + if (ParseFilename(nameText, filename, pathname, window->path) != 0) {
334 XBell(TheDisplay, 0);
337 @@ -362,7 +362,8 @@ static void fileCB(Widget widget, Window
338 fileNameToOpen = nameList[i];
341 - if (ParseFilename(fileNameToOpen, filename, pathname) != 0) {
342 + if (ParseFilename(fileNameToOpen, filename, pathname,
343 + window->path) != 0) {
344 XBell(TheDisplay, 0);
346 EditExistingFile(GetPrefOpenInTab() ? window : NULL, filename,
347 @@ -399,7 +400,8 @@ static void fileCB(Widget widget, Window
348 fileNameToOpen = globbuf.gl_pathv[i];
351 - if (ParseFilename(fileNameToOpen, filename, pathname) != 0) {
352 + if (ParseFilename(fileNameToOpen, filename, pathname,
353 + window->path) != 0) {
354 XBell(TheDisplay, 0);
356 EditExistingFile(GetPrefOpenInTab() ? window : NULL, filename,
357 diff --quilt old/source/server.c new/source/server.c
358 --- old/source/server.c
359 +++ new/source/server.c
360 @@ -456,7 +456,7 @@ static void processServerCommandString(c
361 existing window, or opening if they don't exist */
362 editFlags = (readFlag ? PREF_READ_ONLY : 0) | CREATE |
363 (createFlag ? SUPPRESS_CREATE_WARN : 0);
364 - if (ParseFilename(fullname, filename, pathname) != 0) {
365 + if (ParseFilename(fullname, filename, pathname, NULL) != 0) {
366 fprintf(stderr, "NEdit: invalid file name\n");
367 deleteFileClosedProperty2(filename, pathname);
369 diff --quilt old/source/tags.c new/source/tags.c
370 --- old/source/tags.c
371 +++ new/source/tags.c
372 @@ -254,7 +254,7 @@ static int addTag(const char *name, cons
374 sprintf(newfile,"%s%s", path, file);
376 - NormalizePathname(newfile);
377 + NormalizePathname(newfile, NULL);
379 for (t = table[addr]; t; t = t->next) {
380 if (strcmp(name,t->name)) continue;
381 @@ -265,7 +265,7 @@ static int addTag(const char *name, cons
382 if (*t->file != '/') {
383 char tmpfile[MAXPATHLEN];
384 sprintf(tmpfile, "%s%s", t->path, t->file);
385 - NormalizePathname(tmpfile);
386 + NormalizePathname(tmpfile, NULL);
387 if (strcmp(newfile, tmpfile)) continue;
390 @@ -365,7 +365,7 @@ int AddRelTagsFile(const char *tagSpec,
392 strcat(pathName, "/");
393 strcat(pathName, filename);
394 - NormalizePathname(pathName);
395 + NormalizePathname(pathName, NULL);
397 for (t = FileList; t && strcmp(t->filename, pathName); t = t->next);
399 @@ -433,7 +433,7 @@ int AddTagsFile(const char *tagSpec, int
401 strcpy(pathName,filename);
403 - NormalizePathname(pathName);
404 + NormalizePathname(pathName, NULL);
406 for (t = FileList; t && strcmp(t->filename,pathName); t = t->next);
408 @@ -504,7 +504,7 @@ int DeleteTagsFile(const char *tagSpec,
410 strcpy(pathName,filename);
412 - NormalizePathname(pathName);
413 + NormalizePathname(pathName, NULL);
415 for (last=NULL,t = FileList; t; last = t,t = t->next) {
416 if (strcmp(t->filename, pathName))
417 @@ -734,7 +734,7 @@ static int loadTagsFile(const char *tags
421 - ParseFilename(resolvedTagsFile, NULL, tagPath);
422 + ParseFilename(resolvedTagsFile, NULL, tagPath, NULL);
424 /* Read the file and store its contents */
425 while (fgets(line, MAXLINE, fp)) {
426 @@ -1147,7 +1147,7 @@ static int findAllMatches(WindowInfo *wi
427 sprintf(tagFiles[nMatches],"%s%s",tagPath,fileToSearch);
428 strcpy(tagSearch[nMatches],searchString);
429 tagPosInf[nMatches]=startPos;
430 - ParseFilename(tagFiles[nMatches], filename, pathname);
431 + ParseFilename(tagFiles[nMatches], filename, pathname, NULL);
432 /* Is this match in the current file? If so, use it! */
433 if (GetPrefSmartTags() && !strcmp(window->filename,filename)
434 && !strcmp(window->path,pathname) ) {
435 @@ -1204,7 +1204,7 @@ static int findAllMatches(WindowInfo *wi
438 for (i=0; i<nMatches; i++) {
439 - ParseFilename(tagFiles[i], filename, pathname);
440 + ParseFilename(tagFiles[i], filename, pathname, NULL);
441 if ((i<nMatches-1 && !strcmp(tagFiles[i],tagFiles[i+1])) ||
442 (i>0 && !strcmp(tagFiles[i],tagFiles[i-1]))) {
443 if(*(tagSearch[i]) && (tagPosInf[i] != -1)) { /* etags */
444 @@ -1326,7 +1326,7 @@ static void showMatchingCalltip( Widget
447 /* 1. Open the target file */
448 - NormalizePathname(tagFiles[i]);
449 + NormalizePathname(tagFiles[i], NULL);
450 fp = fopen(tagFiles[i], "r");
452 DialogF(DF_ERR, parent, 1, "Error opening File", "Error opening %s",
453 @@ -1445,7 +1445,7 @@ static void editTaggedLocation( Widget p
454 WindowInfo *windowToSearch;
455 WindowInfo *parentWindow = WidgetToWindow(parent);
457 - ParseFilename(tagFiles[i],filename,pathname);
458 + ParseFilename(tagFiles[i], filename, pathname, NULL);
459 /* open the file containing the definition */
460 EditExistingFile(parentWindow, filename, pathname, 0, NULL, False,
461 NULL, GetPrefOpenInTab(), False);
462 @@ -1992,7 +1992,7 @@ static int loadTipsFile(const char *tips
465 /* Get the path to the tips file */
466 - ParseFilename(resolvedTipsFile, NULL, tipPath);
467 + ParseFilename(resolvedTipsFile, NULL, tipPath, NULL);
470 if ((fp = fopen(resolvedTipsFile, "r")) == NULL)
471 diff --quilt old/util/fileUtils.c new/util/fileUtils.c
472 --- old/util/fileUtils.c
473 +++ new/util/fileUtils.c
474 @@ -88,8 +88,8 @@ static void copyThruSlash(char **toStrin
475 ** least MAXPATHLEN chars long.
476 ** To skip setting filename or pathname pass NULL for that argument.
479 -ParseFilename(const char *fullname, char *filename, char *pathname)
480 +int ParseFilename(const char *fullname, char *filename, char *pathname,
481 + const char *relpath)
483 int fullLen = strlen(fullname);
484 int i, pathLen, fileLen;
485 @@ -138,7 +138,7 @@ ParseFilename(const char *fullname, char
487 #ifndef VMS /* UNIX specific... Modify at a later date for VMS */
489 - if (NormalizePathname(pathname)) {
490 + if (NormalizePathname(pathname, relpath)) {
491 return 1; /* pathname too long */
493 pathLen = strlen(pathname);
494 @@ -271,7 +271,7 @@ ResolvePath(const char * pathIn, char *
496 strcpy(pathBuf, resolveBuf);
498 - NormalizePathname(pathBuf);
499 + NormalizePathname(pathBuf, NULL);
503 @@ -287,9 +287,10 @@ ResolvePath(const char * pathIn, char *
504 ** FIXME: Documentation
505 ** FIXME: Change return value to False and True.
507 -int NormalizePathname(char *pathname)
508 +int NormalizePathname(char *pathname, const char *relpath)
510 - /* if this is a relative pathname, prepend current directory */
511 + /* if this is a relative pathname, prepend relpath */
512 + /* if relpath is a relative path, prepend the current directory */
514 /* OS/2, ...: welcome to the world of drive letters ... */
515 if (!_fnisabs(pathname)) {
516 @@ -298,12 +299,13 @@ int NormalizePathname(char *pathname)
520 + int useRelpath = (relpath && *relpath);
522 /* make a copy of pathname to work from */
523 oldPathname=(char *)malloc(strlen(pathname)+1);
524 strcpy(oldPathname, pathname);
525 /* get the working directory and prepend to the path */
526 - strcpy(pathname, GetCurrentDir());
527 + strcpy(pathname, useRelpath ? relpath : GetCurrentDir());
529 /* check for trailing slash, or pathname being root dir "/":
530 don't add a second '/' character as this may break things
531 @@ -320,6 +322,14 @@ int NormalizePathname(char *pathname)
533 strcat(pathname, oldPathname);
537 + /* make sure our relative path wasn't relative to start with */
538 + if (CompressPathname(pathname) == 0)
539 + return NormalizePathname(pathname, NULL);
541 + return 1; /* some non-zero value */
545 /* compress out .. and . */
546 @@ -472,7 +482,7 @@ copyThruSlash(char **toString, char **fr
548 ** Return 0 if everything's fine, 1 else.
550 -int NormalizePathname(char *pathname)
551 +int NormalizePathname(char *pathname, const char *relpath)
555 diff --quilt old/util/fileUtils.h new/util/fileUtils.h
556 --- old/util/fileUtils.h
557 +++ new/util/fileUtils.h
560 enum fileFormats {UNIX_FILE_FORMAT, DOS_FILE_FORMAT, MAC_FILE_FORMAT};
562 -int ParseFilename(const char *fullname, char *filename, char *pathname);
563 +int ParseFilename(const char *fullname, char *filename, char *pathname,
564 + const char *relpath);
565 int ExpandTilde(char *pathname);
566 const char* GetTrailingPathComponents(const char* path,
568 -int NormalizePathname(char *pathname);
569 +int NormalizePathname(char *pathname, const char *relpath);
570 int CompressPathname(char *pathname);
571 int ResolvePath(const char * pathIn, char * pathResolved);
573 diff --quilt old/util/getfiles.c new/util/getfiles.c
574 --- old/util/getfiles.c
575 +++ new/util/getfiles.c
576 @@ -1024,7 +1024,7 @@ static void listCharEH(Widget w, XtPoint
578 for (i=0; i<nItems; i++) {
579 XmStringGetLtoR(items[i], XmSTRING_DEFAULT_CHARSET, &itemString);
580 - if (ParseFilename(itemString, name, path) != 0) {
581 + if (ParseFilename(itemString, name, path, NULL) != 0) {