[comp.sources.misc] v02i073: A filename canonicalizer for BSD

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