/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: hardpath.c,v 1.3.2.1 92/06/24 14:10:03 marker Exp $"
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>


/*
 * PURPOSE: This routine takes two pointers, a pointer to a NULL
 *	terminated pathname and a pointer to pre-allocated space.  The
 *	pre-allocated space should be at least MAXPATHLEN.  Returned in
 *	'hardpath' is the directory which would be printed out if you were
 *	to 'cd <softpath>' and type 'pwd'.  In other words, there should be
 *	no symlink names anywhere in the entire pathname.
 */
int
hardpath(softpath, hardpath)
char *softpath;		/* path w/ possible symlinks */
char *hardpath;		/* path w/o any symlinks */
{
    char spath[2*MAXPATHLEN];	/* source buffer */
    char tpath[2*MAXPATHLEN];	/* target buffer */
    struct stat sbuf;		/* stat buffer */
    char rbuf[MAXPATHLEN];	/* readlink buffer */
    register char *ps = spath;	/* ptr to source */
    register char *pt = tpath;	/* ptr to target */
    register int beginning = 1;	/* beginning of parse */
    int len;			/* num of chars returned */
    extern int errno;
	    
    if (strcpy_len(spath, softpath, MAXPATHLEN) >= MAXPATHLEN) {
	errno = ENAMETOOLONG;
	return(-1);
    }
    if (softpath == NULL || hardpath == NULL) {
	errno = EINVAL;
	return(-1);
    }

    while (*ps) {
	if (*ps == '/') {			/* handle '/' */
	    *pt++ = *ps++;
	    while (*ps == '/') ps++;
	    beginning = 0;
	}

	/* Deal with "./" and "../" */
	if (*ps == '.') {			/* '.*' */
	    *pt++ = *ps++;
	    if (beginning) {
		if (*ps == '.') {		/* '..*' */
		    *pt++ = *ps++;
		    if (*ps == '/')		/* '../*' */
			continue;
		} else if (*ps == '/') {	/* './*' */
		    *pt++ = *ps++;
		    if (*ps != '\0') {
			if (*ps == '/')		/* './/*': erase '/' */
			    pt--;
			else			/* './*': back up over './' */
			    pt = tpath;
		    }				/* './': leave alone */
		}
	    } else {				/* '*.*' */
		if (*ps == '.') {		/* '*..*' */
		    *pt++ = *ps++;
		    if (*ps == '/' || *ps == '\0') {	/* '*../*' */
			while (pt != (tpath+1) && *--pt != '/')/* skip .. */
			    ;
			while (pt != (tpath+1) && *--pt != '/')/* skip 1 comp */
			    ;
		    }
		} else if (*ps == '/') {	/* '*./*' */
		    pt--; ps++;
		}
	    }
	}

	while (*ps != 0 && *ps != '/') {	/* copy non-dot component */
	    *pt++ = *ps++;
	}
	beginning = 0;
	if (pt-tpath > MAXPATHLEN) {
	    errno = ENAMETOOLONG;
	    return(-1);
	}

	/* re-map symlinks */
	*pt = '\0';
	if (lstat(tpath, &sbuf))
	    return(-1);
	if (sbuf.st_mode & 0120000) {		/* symlink */
	    if ((len = readlink(tpath, rbuf, MAXPATHLEN)) == -1)
		return(-1);
	    rbuf[len] = '\0';

	    if (len+strlen(ps)+1 > MAXPATHLEN) {/* +1 for '/' */
		errno = ENAMETOOLONG;		/* too much yet to process */
		return(-1);
	    }
	    if (*rbuf == '/') {			/* root replacement */
		strcpy(tpath, rbuf);
		tpath[len] = '/';
		tpath[len+1] = '\0';
		strcat(tpath, ps);
		strcpy(spath, tpath);
		ps = spath; pt = tpath;
	    } else {				/* mid-path replacement */
		while (*pt != '/' && pt != tpath)
		    pt--;
		if (*pt == '/')
		    *(++pt) = '\0';
		else
		    *tpath = '\0';		/* pt == tpath */
		strcat(tpath, rbuf);
		strcat(tpath, "/");
		strcat(tpath, ps);
		strcpy(spath, tpath);
		ps = spath + (pt - tpath);
	    }
	}
    }
    while (pt != (tpath+1) && *(pt-1) == '/')	/* remove trailing '/'s */
	*(--pt) = '\0';
    strcpy(hardpath, tpath);			/* copyout */
    return(0);
}	/* end of hardpath */


static int strcpy_len(s1, s2, n)
register char *s1, *s2;
register int n;
{
    register int on = n;

    while (n && (*s1++ = *s2++)) n--;
    return(on-n);
}
