diff options
Diffstat (limited to 'filenames.c')
-rw-r--r-- | filenames.c | 581 |
1 files changed, 255 insertions, 326 deletions
diff --git a/filenames.c b/filenames.c index 20c0c23..ad6a23d 100644 --- a/filenames.c +++ b/filenames.c @@ -20,7 +20,6 @@ #include <unistd.h> #include <string.h> #include <stdio.h> -#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> @@ -28,116 +27,100 @@ #include "strxtra.h" #include "filenames.h" #include "misc.h" +#include "error.h" + +#ifdef S_IFLNK +static char const *unsymlink __P((char *n)); +#endif +static void canonical_name __P((char *n)); +static char const *lex_name __P((void)); +static int same_link __P((struct stat *x, struct stat *y)); + +FILE *popen (); /* relative_file_name takes two arguments: - * 1) an absolute path name for a directory. - * (This name MUST have a trailing /). - * 2) an absolute path name for a file. - * - * It looks for common components at the front of the file and - * directory names and generates a relative path name for the file - * (relative to the specified directory). - * - * This may result in a huge number of ../s if the names - * have no components in common. - * - * The output from this concatenated with the input directory name - * and run through span_file_name should result in the original input - * absolute path name of the file. - * - * Examples: - * dir arg return value - * /x/y/z/ /x/y/q/file -> ../q/file - * /x/y/z/ /q/t/p/file -> ../../../q/t/p/file - * /x/y/z/ /x/y/z/file -> file - */ + 1) an absolute path name for a directory. (*must* have a trailing "/"). + 2) an absolute path name for a file. + + It looks for a common directory prefix and generates a name for the + given file that is relative to the given directory. The result + might begin with a long sequence of "../"s, if the given names are + long but have a short common prefix. + + (Note: If the the result of relative_file_name is appended to its + directory argument and passed to span_file_name, span_file_name's + result should match relative_file_name's file name argument.) + + Examples: + dir arg return value + /x/y/z/ /x/y/q/file ../q/file + /x/y/z/ /q/t/p/file ../../../q/t/p/file + /x/y/z/ /x/y/z/file file */ + char const * -relative_file_name (char const *dir, char const *arg) +relative_file_name (char const *dir_name, char const *file_name) { - char const *a; - char const *d; - char const *lasta; - char const *lastd; - static char file_name_buffer[BUFSIZ]; - char *buf = file_name_buffer; - - lasta = a = arg; - lastd = d = dir; - while (*a == *d) + static char file_name_buffer[MAXPATHLEN]; + char *bp = file_name_buffer; + + while (*file_name && *file_name++ == *dir_name++) + ; + while (*--dir_name != '/') + ; + dir_name++; + while (*--file_name != '/') + ; + file_name++; + /* file_name and dir_name now point past their common directory prefix */ + + /* copy "../" into the buffer for each component of the directory + that remains. */ + + while (*dir_name) { - if (*a == '/') + if (*dir_name++ == '/') { - lasta = a; - lastd = d; + strcpy (bp, "../"); + bp += 3; } - ++a; - ++d; } - /* lasta and lastd now point to the last / in each - * file name where the leading file components were - * identical. - */ - ++lasta; - ++lastd; - /* copy a ../ into the buffer for each component of - * the directory that remains. - */ - - while (*lastd != '\0') - { - if (*lastd == '/') - { - strcpy (buf, "../"); - buf += 3; - } - ++lastd; - } - /* now tack on remainder of arg */ - strcpy (buf, lasta); + + strcpy (bp, file_name); return file_name_buffer; } -/* span_file_name accepts a directory name and a file name and returns - a cannonical form of the full file name within that directory. It - gets rid of ./ and things like that. If the file is an absolute - name then the directory is ignored. */ +/* span_file_name accepts a canonical directory name and a file name + and returns a canonical path to the file name relative to the + directory. If the file name is absolute, then the directory is + ignored. */ + char const * -span_file_name (char const *dir, char const *arg) +span_file_name (char const *dir_name, char const *file_name) { - char *argptr; - static char file_name_buffer[BUFSIZ]; - - /* reduce directory to cannonical form */ - strcpy (file_name_buffer, dir); - cannoname (file_name_buffer); - /* tack the obilgatory / on the end */ - strcat (file_name_buffer, "/"); - /* stick file name in buffer after directory */ - argptr = file_name_buffer + strlen (file_name_buffer); - strcpy (argptr, arg); - /* and reduce it to cannonical form also */ - cannoname (argptr); + char *fnp; + static char file_name_buffer[MAXPATHLEN]; + + strcpy (file_name_buffer, dir_name); + fnp = file_name_buffer + strlen (file_name_buffer); + *fnp++ = '/'; + strcpy (fnp, file_name); + canonical_name (fnp); /* If it is an absolute name, just return it */ - if (*argptr == '/') - return argptr; - /* otherwise, combine the names to cannonical form */ - cannoname (file_name_buffer); + if (*fnp == '/') + return fnp; + /* otherwise, combine the names to canonical form */ + canonical_name (file_name_buffer); return file_name_buffer; } -/* root_name returns the base name of the file with any leading - * directory information or trailing suffix stripped off. Examples: - * - * /usr/include/stdio.h -> stdio - * fred -> fred - * barney.c -> barney - * bill/bob -> bob - * / -> < null string > - */ +/* root_name strips off the directory prefix and one suffix. If there + is neither prefix nor suffix, (i.e., "/"), it returns the empty + string. */ + char const * root_name (char const *path) { - static char file_name_buffer[BUFSIZ]; + static char file_name_buffer[MAXPATHLEN]; char const *root; char const *dot; @@ -158,15 +141,9 @@ root_name (char const *path) return file_name_buffer; } -/* suff_name returns the suffix (including the dot) or a null string - * if there is no suffix. Examples: - * - * /usr/include/stdio.h -> .h - * fred -> < null string > - * barney.c -> .c - * bill/bob -> < null string > - * / -> < null string > - */ +/* suff_name returns the suffix (including the dot), or the + empty-string if there is none. */ + char const * suff_name (char const *path) { @@ -178,66 +155,57 @@ suff_name (char const *path) return dot; } -int -can_crunch (char const *path1, char const *path2) +/* Return non-zero if the two stat bufs refer to the same file or + directory */ + +static int +same_link (struct stat *x, struct stat *y) { - char const *slash1; - char const *slash2; - - slash1 = strrchr (path1, '/'); - slash2 = strrchr (path2, '/'); - - if (slash1 == NULL && slash2 == NULL) - return strequ (suff_name (path1), suff_name (path2)); - if ((slash1 - path1) != (slash2 - path2)) - return 0; - if (!strnequ (path1, path2, slash1 - path1)) - return 0; - return strequ (suff_name (slash1), suff_name (slash2)); + return ((x->st_ino == y->st_ino) && (x->st_dev == y->st_dev)); } -/* look_up adds ../s to the beginning of a file name until it finds - * the one that really exists. Returns NULL if it gets all the way - * to / and never finds it. - * - * If the file name starts with /, just return it as is. - * - * This routine is used to locate the ID database file. - */ +/* find_id_file adds "../"s to the beginning of a file name until it + finds the one that really exists. If the file name starts with + "/", just return it as is. If we fail for any reason, report the + error and exit. */ + char const * -look_up (char const *arg) +find_id_file (char const *arg) { - static char file_name_buffer[BUFSIZ]; - char *buf = file_name_buffer; - struct stat rootb; - struct stat statb; + static char file_name_buffer[MAXPATHLEN]; + char *name; + char *dir_end; + struct stat root_buf; + struct stat stat_buf; - /* if we got absolute name, just use it. */ if (arg[0] == '/') return arg; - /* if the name we were give exists, don't bother searching */ - if (stat (arg, &statb) == 0) + if (stat (arg, &stat_buf) == 0) return arg; - /* search up the tree until we find a directory where this - * relative file name is visible. - * (or we run out of tree to search by hitting root). - */ - if (stat ("/", &rootb) != 0) - return NULL; + name = &file_name_buffer[sizeof (file_name_buffer) - strlen (arg) - 1]; + strcpy (name, arg); + dir_end = name - 1; + + if (stat ("/", &root_buf) < 0) + { + error (1, errno, "Can't stat `/'"); + return NULL; + } do { - strcpy (buf, "../"); - buf += 3; - strcpy (buf, arg); - if (stat (file_name_buffer, &statb) == 0) - return file_name_buffer; - *buf = '\0'; - if (stat (file_name_buffer, &statb) != 0) + *--name = '/'; + *--name = '.'; + *--name = '.'; + if (stat (name, &stat_buf) == 0) + return name; + *dir_end = '\0'; + if (stat (name, &stat_buf) < 0) return NULL; + *dir_end = '/'; } - while (!((statb.st_ino == rootb.st_ino) || - (statb.st_dev == rootb.st_dev))); + while (name >= &file_name_buffer[3] && !same_link(&stat_buf, &root_buf)); + error (1, errno, "Can't stat `%s' anywhere between here and `/'", arg); return NULL; } @@ -255,123 +223,101 @@ static char dotdot[] = ".."; static char const *nextc = NULL; static char *namep; -/* lexname - Return next name component. Uses global variables initialized - * by cannoname to figure out what it is scanning. +/* lex_name - Return next name component. Uses global variables initialized + * by canonical_name to figure out what it is scanning. */ static char const * -lexname (void) +lex_name (void) { char c; char const *d; - if (nextc) + if (nextc == NULL) + return NULL; + + c = *nextc++; + if (c == '\0') { - c = *nextc++; - if (c == '\0') - { - nextc = NULL; - return NULL; - } - if (c == '/') - { - return &slash[0]; - } - if (c == '.') - { - if ((*nextc == '/') || (*nextc == '\0')) - return &dot[0]; - if (*nextc == '.' && (*(nextc + 1) == '/' || *(nextc + 1) == '\0')) - { - ++nextc; - return &dotdot[0]; - } - } - d = namep; - *namep++ = c; - while ((c = *nextc) != '/') + nextc = NULL; + return NULL; + } + if (c == '/') + return slash; + if (c == '.') + { + if ((*nextc == '/') || (*nextc == '\0')) + return dot; + if (*nextc == '.' && (*(nextc + 1) == '/' || *(nextc + 1) == '\0')) { - *namep++ = c; - if (c == '\0') - { - nextc = NULL; - return d; - } ++nextc; + return dotdot; } - *namep++ = '\0'; - return d; } - else + d = namep; + *namep++ = c; + while ((c = *nextc) != '/') { - return NULL; + *namep++ = c; + if (c == '\0') + { + nextc = NULL; + return d; + } + ++nextc; } + *namep++ = '\0'; + return d; } -/* cannoname - Put a file name in cannonical form. Looks for all the - * whacky wonderful things a demented *ni* programmer might put - * in a file name and reduces the name to cannonical form. - */ -void -cannoname (char *n) +/* canonical_name puts a file name in canonical form. It looks for all + the whacky wonderful things a demented *ni* programmer might put in + a file name and reduces the name to canonical form. */ + +static void +canonical_name (char *file_name) { char const *components[1024]; - char const **cap = &components[0]; + char const **cap = components; char const **cad; char const *cp; - char namebuf[2048]; + char name_buf[2048]; char const *s; /* initialize scanner */ - nextc = n; - namep = &namebuf[0]; - - /* break the file name into individual components */ - while ((cp = lexname ())) - { - *cap++ = cp; - } + nextc = file_name; + namep = name_buf; - /* If name is empty, leave it that way */ - if (cap == &components[0]) + while ((cp = lex_name ())) + *cap++ = cp; + if (cap == components) return; - - /* flag end of component list */ *cap = NULL; /* remove all trailing slashes and dots */ - while ((--cap != &components[0]) && - ((*cap == &slash[0]) || (*cap == &dot[0]))) + while ((--cap != components) && + ((*cap == slash) || (*cap == dot))) *cap = NULL; - /* squeeze out all . / component sequences */ - cap = &components[0]; - cad = cap; + /* squeeze out all "./" sequences */ + cad = cap = components; while (*cap) { - if ((*cap == &dot[0]) && (*(cap + 1) == &slash[0])) - { - cap += 2; - } + if ((*cap == dot) && (*(cap + 1) == slash)) + cap += 2; else - { - *cad++ = *cap++; - } + *cad++ = *cap++; } *cad++ = NULL; /* find multiple // and use last slash as root, except on apollo which - * apparently actually uses // in real file names (don't ask me why). - */ + apparently actually uses // in real file names (don't ask me why). */ #ifndef apollo s = NULL; - cap = &components[0]; - cad = cap; + cad = cap = components; while (*cap) { - if ((s == &slash[0]) && (*cap == &slash[0])) - { - cad = &components[0]; - } + if ((s == slash) && (*cap == slash)) + cad = components; s = *cap++; *cad++ = s; } @@ -379,16 +325,15 @@ cannoname (char *n) #endif /* if this is absolute name get rid of any /.. at beginning */ - if ((components[0] == &slash[0]) && (components[1] == &dotdot[0])) + if ((components[0] == slash) && (components[1] == dotdot)) { - cap = &components[1]; - cad = cap; - while (*cap == &dotdot[0]) + cad = cap = &components[1]; + while (*cap == dotdot) { ++cap; if (*cap == NULL) break; - if (*cap == &slash[0]) + if (*cap == slash) ++cap; } while (*cap) @@ -397,13 +342,11 @@ cannoname (char *n) } /* squeeze out any name/.. sequences (but leave leading ../..) */ - cap = &components[0]; + cap = components; cad = cap; while (*cap) { - if ((*cap == &dotdot[0]) && - ((cad - 2) >= &components[0]) && - ((*(cad - 2)) != &dotdot[0])) + if ((*cap == dotdot) && ((cad - 2) >= components) && (*(cad - 2) != dotdot)) { cad -= 2; ++cap; @@ -411,117 +354,104 @@ cannoname (char *n) ++cap; } else - { - *cad++ = *cap++; - } + *cad++ = *cap++; } /* squeezing out a trailing /.. can leave unsightly trailing /s */ - if ((cad >= &components[2]) && ((*(cad - 1)) == &slash[0])) + if ((cad >= &components[2]) && ((*(cad - 1)) == slash)) --cad; *cad = NULL; /* if it was just name/.. it now becomes . */ if (components[0] == NULL) { - components[0] = &dot[0]; + components[0] = dot; components[1] = NULL; } /* re-assemble components */ - cap = &components[0]; + cap = components; while ((s = *cap++)) { while (*s) - *n++ = *s++; + *file_name++ = *s++; } - *n++ = '\0'; + *file_name++ = '\0'; } -/* kshgetwd is a routine that acts just like getwd, but is optimized - * for ksh users, taking advantage of the fact that ksh maintains - * an environment variable named PWD holding path name of the - * current working directory. - * - * The primary motivation for this is not really that it is algorithmically - * simpler, but that it is much less likely to bother NFS if we can just - * guess the name of the current working directory using the hint that - * ksh maintains. Anything that avoids NFS gettar failed messages is - * worth doing. - */ +/* get_PWD is an optimized getwd(3) or getcwd(3) that takes advantage + of the shell's $PWD environment-variable, if present. This is + particularly worth doing on NFS mounted filesystems. */ + char const * -kshgetwd (char *pathname) +get_PWD (char *pwd_buf) { - struct stat kshstat, dotstat; - char kshname[MAXPATHLEN]; - char const *kshp; + struct stat pwd_stat; + struct stat dot_stat; + char *pwd = getenv ("PWD"); - kshp = getenv ("PWD"); - if (kshp) + if (pwd) { - /* OK, there was a PWD environment variable */ - strcpy (kshname, kshp); - if (unsymlink (kshname) - /* And we could resolve the symbolic links through it */ - && kshname[0] == '/' - /* And the name we have is an absolute path name */ - && stat (kshname, &kshstat) == 0 - /* And we can stat the name */ - && stat (".", &dotstat) == 0 - /* And we can stat "." */ - && (kshstat.st_dev == dotstat.st_dev) - && (kshstat.st_ino == dotstat.st_ino)) - /* By golly, that name is the same file as "." ! */ - return strcpy (pathname, kshname); + pwd = strcpy (pwd_buf, pwd); + if (pwd[0] != '/' + || stat (".", &dot_stat) < 0 + || stat (pwd, &pwd_stat) < 0 + || !same_link(&pwd_stat, &dot_stat) +#ifdef S_IFLNK + || !unsymlink (pwd) + || pwd[0] != '/' + || stat (pwd, &pwd_stat) < 0 + || !same_link(&pwd_stat, &dot_stat) +#endif + ) + pwd = 0; } - /* Oh well, something did not work out right, do it the hard way */ + if (pwd == 0) + { + /* Oh well, something did not work out right, so do it the hard way... */ #if HAVE_GETCWD - return getcwd (pathname, BUFSIZ); + pwd = getcwd (pwd_buf, MAXPATHLEN); #else #if HAVE_GETWD - return getwd (pathname); + pwd = getwd (pwd_buf); #endif #endif + } + if (pwd) + strcat (pwd, "/"); + else + error (1, errno, "Can't determine current working directory!"); + + return pwd; } -/* unsymlink is a routine that resolves all symbolic links in - * a file name, transforming a name to the "actual" file name - * instead of the name in terms of symbolic links. - * - * If it can resolve all links and discover an actual file - * it returns a pointer to its argument string and transforms - * the argument in place to the actual name. - * - * If no such actual file exists, or for some reason the links - * cannot be resolved, it returns a NULL pointer and leaves the - * name alone. - */ -char const * -unsymlink (char *n) +#ifdef S_IFLNK + +/* unsymlink resolves all symbolic links in a file name into hard + links. If successful, it returns its argument and transforms + the file name in situ. If unsuccessful, it returns NULL, and leaves + the argument untouched. */ + +static char const * +unsymlink (char *file_name_buf) { - char newname[MAXPATHLEN]; - char partname[MAXPATHLEN]; - char linkname[MAXPATHLEN]; + char new_buf[MAXPATHLEN]; + char part_buf[MAXPATHLEN]; + char link_buf[MAXPATHLEN]; char const *s; char *d; char *lastcomp; - int linksize; - struct stat statb; - - /* Just stat the file to automagically do all the symbolic - * link verification checks and make sure we have access to - * directories, etc. - */ - if (stat (n, &statb) != 0) - return NULL; - strcpy (newname, n); + struct stat stat_buf; + + strcpy (new_buf, file_name_buf); + /* Now loop, lstating each component to see if it is a symbolic - * link. For symbolic link components, use readlink() to get - * the real name, put the read link name in place of the - * last component, and start again. - */ - cannoname (newname); - s = &newname[0]; - d = &partname[0]; + link. For symbolic link components, use readlink() to get the + real name, put the read link name in place of the last component, + and start again. */ + + canonical_name (new_buf); + s = new_buf; + d = part_buf; if (*s == '/') *d++ = *s++; lastcomp = d; @@ -531,28 +461,25 @@ unsymlink (char *n) { /* we have a complete component name in partname, check it out */ *d = '\0'; - if (lstat (partname, &statb) != 0) + if (lstat (part_buf, &stat_buf) < 0) return NULL; - if ((statb.st_mode & S_IFMT) == S_IFLNK) + if ((stat_buf.st_mode & S_IFMT) == S_IFLNK) { /* This much of name is a symbolic link, do a readlink - * and tack the bits and pieces together - */ - linksize = readlink (partname, linkname, MAXPATHLEN); - if (linksize < 0) + and tack the bits and pieces together */ + int link_size = readlink (part_buf, link_buf, MAXPATHLEN); + if (link_size < 0) return NULL; - linkname[linksize] = '\0'; - strcpy (lastcomp, linkname); - lastcomp += linksize; + link_buf[link_size] = '\0'; + strcpy (lastcomp, link_buf); + lastcomp += link_size; strcpy (lastcomp, s); - strcpy (newname, partname); - cannoname (newname); - s = &newname[0]; - d = &partname[0]; + strcpy (new_buf, part_buf); + canonical_name (new_buf); + s = new_buf; + d = part_buf; if (*s == '/') - { - *d++ = *s++; - } + *d++ = *s++; lastcomp = d; } else @@ -569,10 +496,12 @@ unsymlink (char *n) *d++ = *s++; } } - strcpy (n, newname); - return n; + strcpy (file_name_buf, new_buf); + return file_name_buf; } +#endif + FILE * open_source_FILE (char *file_name, char const *filter) { |