dupuy@westend.columbia.edu (Alexander Dupuy) (03/10/88)
Submitted-By: "Alexander Dupuy" <dupuy@westend.columbia.edu> Archive-Name: name Comp.sources.misc: Volume 2, Issue 73 Submitted-By: "Alexander Dupuy" <dupuy@westend.columbia.edu> Archive-Name: name Have your filesystems become such a mess of symbolic links that you don't know where a given path really leads? If so, this program will help. It takes a pathname and gives you back the true name of the file. It has an option [-a] to give you an absolute name (leading '/'), an option [-n] to give you a normalized name (all "//", "/./", and "/../" removed, if possible), and even an option [-i] which ignores symbolic links (this is pretty useless if you don't also specify the normalize option). The most useful option is the verbose [-v] option, which prints out all symbolic links encountered while looking up the name. This helps in figuring out how it got from here to there. Sorry, no man page. If you hadn't guessed, this is BSD-only. However, I was able to compile it under HP-UX with "-lBSD" for the getwd() call. How many System V's have symlinks anyhow? @alex -- inet: dupuy@columbia.edu uucp: ...!rutgers!columbia!dupuy : This is a shar archive. Extract with sh, not csh. : The rest of this file will extract: : : name.c : echo x - name.c sed 's/^X//' > name.c << '//go.sysin dd *' X/* X * name - find a file's real name X */ X X#include <stdio.h> X#include <errno.h> X#include <strings.h> X#include <sys/param.h> X#include <sys/stat.h> X#include <sys/dir.h> X Xchar usage[] = "Usage: name [-ainv] pathname...\n"; X Xint absolute; /* print absolute pathnames */ Xint normalize; /* normalize pathnames */ Xint ignore; /* ignore symbolic links */ Xint verbose; /* describe each symbolic link */ X Xmain (argc, argv) Xchar **argv; X{ X int option; X extern int optind; X int errors = 0; X char realname[MAXPATHLEN + MAXNAMLEN]; X extern char *name(); X X while ((option = getopt (argc, argv, "ainv")) != EOF) X switch (option) X { X case 'a': X absolute += 1; X break; X X case 'i': X ignore += 1; X break; X X case 'n': X normalize += 1; X break; X X case 'v': X verbose += 1; X break; X X default: X fputs (usage, stderr); X exit (1); X } X X argc -= optind; X argv += optind; X X if (argc == 0) /* have to have a path... */ X { X fputs (usage, stderr); X exit (1); X } X X while (argc-- > 0) X { X if (name (*argv++, realname) == 0) X { /* print error returned by name() */ X fputs (realname, stderr); X (void) putc ('\n', stderr); X errors++; X } X else /* print the real name itself */ X puts (realname); X } X X exit (errors); X} X Xstatic int links; /* how many symbolic links in path */ X Xchar *name (path, truename) Xchar *path; Xchar *truename; X{ X static char prefix[MAXPATHLEN + MAXNAMLEN + 1]; X extern char *getwd(); X X if (*path != '/' && absolute) /* get absolute pathname of relative */ X { X if (getwd (prefix) == NULL) /* system five doesn't have this... */ X { X strcpy (truename, prefix); /* contains error message from getwd */ X return (NULL); X } X strcat (prefix, "/"); /* add trailing '/' */ X } X else X prefix[0] = '\0'; X X links = 0; X X if (name1 (prefix, path) == 0) /* an error occurred */ X { X strcpy (truename, prefix); /* copy back the error message */ X return (NULL); X } X else X { X strcpy (truename, prefix); /* copy back the real name */ X return (truename); X } X} X X X#define rootdir(name) ((name)[0]=='/' && (name)[1]=='\0') X X#define dotdir(name) \ X((name)[0]=='.' && ((name)[1]=='\0' || ((name)[1]=='.' && (name)[2]=='\0'))) X X#define dotdotdir(name) \ X((name)[0]=='.' && (name)[1]=='.' && (name)[2]=='\0') X X X/* X * Recursively add suffix to prefix, canonicalizing as we go... X */ X Xstatic name1 (prefix, suffix) Xchar *prefix; Xregister char *suffix; X{ X extern char *sys_errlist[]; X char link[MAXPATHLEN]; X struct stat status; X register char *splice; X register char *cut; X register int cc; X int result; X X splice = prefix + strlen (prefix); X X do X { X if (!normalize) X { X if (*suffix == '/') /* skip one leading "/" */ X { X *splice++ = '/'; X *splice = '\0'; X suffix++; X } X } X else X { X if (*suffix == '/') X { X while (*suffix == '/') X suffix++; /* treat "//" as "/" */ X X if (splice == prefix || X (*suffix != '\0' && splice[-1] != '/')) X { X *splice++ = '/'; X *splice = '\0'; X } X } X X if (*suffix == '.') X { X if (suffix[1] == '\0') X { /* treat "." as "." (not "") */ X if (splice == prefix) X { X *splice++ = '.'; X *splice = '\0'; X } /* treat "x/." as "x" */ X else if (!rootdir (prefix)) X *--splice = '\0'; X return (1); /* treat "/." as "/" */ X } X X if (suffix[1] == '/') X { X suffix += 2; /* treat "/./" as "/" */ X continue; X } X X if (suffix[1] == '.') X { X if (suffix[2] == '\0') X { /* treat ".." as ".." */ X if (prefix == splice) X strcpy (prefix, ".."); X X else if (!rootdir (prefix)) X { X *--splice = '\0'; X X if ((splice = rindex (prefix, '/')) == 0) X { X if (dotdotdir (prefix)) X { X strcat (prefix, "/.."); X return (1); X } X else X splice = prefix; X } X else if (dotdotdir (splice + 1)) X { X strcat (splice, "/.."); X return (1); X } X X *splice= '\0'; X } X X return (1); X } X X if (suffix[2] == '/') X { X if (splice == prefix) X { X strcpy (prefix, ".."); X splice += 2; X suffix += 2; X } X X else if (!rootdir (prefix)) X { /* don't back up "/" prefix */ X *--splice = '\0'; X X if ((splice = rindex (prefix, '/')) == 0) X { X if (dotdotdir (prefix)) X { X strcat (prefix, "/.."); X splice = prefix + strlen (prefix); X suffix += 2; X } X else X { X splice = prefix; X *splice = '\0'; X suffix += 3; X } X } X else X { X if (dotdotdir (splice)) X { X strcat (splice, ".."); X splice += strlen (splice); X suffix += 2; X } X else X { X *splice = '\0'; X suffix += 2; X } X } X } X else X suffix += 3; X X continue; X } X } X } X } X X if (!*suffix) X break; /* empty suffix string */ X X if ((cut = index (suffix, '/')) == 0) X { X cc = strlen (suffix); X cut = suffix + cc; X } X else X cc = cut - suffix; X X if (cc >= MAXNAMLEN) X { X (void) sprintf (prefix, "%s: %s", X suffix, sys_errlist[ENAMETOOLONG]); X return (0); X } X X if (cc == 0) /* suffix has leading '/' */ X { X cut++; X cc = 1; /* so force it to be copied */ X } X X strncpy (splice, suffix, cc); X splice[cc] = '\0'; X X if (!ignore) X { X if ((result = lstat (prefix, &status)) == -1) X { X (void) sprintf (splice + cc, ": %s", sys_errlist[errno]); X return (0); X } X X if ((status.st_mode & S_IFMT) == S_IFLNK) X { X if ((result = readlink (prefix, link, MAXPATHLEN)) == -1) X { X (void) sprintf (splice + cc, ": %s", sys_errlist[errno]); X return (0); X } X link[result] = '\0'; X X if (links++ == MAXSYMLINKS && X (result = stat (prefix, &status)) == -1) X { X (void) sprintf (splice + cc, ": %s", sys_errlist[errno]); X return (0); X } X X if (verbose) X { X (void) printf ("%s -> %s\n", prefix, link); X } X X if (*link == '/') X *prefix = '\0'; /* chop prefix if link is absolute */ X else X *splice = '\0'; /* chop just the link name */ X X if (name1 (prefix, link) == 0) X return (0); /* recurse */ X X splice = prefix + strlen (prefix); X } X } X else if ((result = stat (prefix, &status)) == -1) X { X (void) sprintf (splice + cc, ": %s", sys_errlist[errno]); X return (0); X } X X suffix = cut; /* advance suffix past cut */ X X splice += strlen (splice); /* advance splice to end of prefix */ X } X while (*suffix); X X return (1); X} //go.sysin dd * exit inet: dupuy@columbia.edu uucp: ...!rutgers!columbia!dupuy