sh.dir.c revision 100616
178189Sbrian/* $Header: /src/pub/tcsh/sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $ */
278189Sbrian/*
378189Sbrian * sh.dir.c: Directory manipulation functions
478189Sbrian */
578189Sbrian/*-
66059Samurai * Copyright (c) 1980, 1991 The Regents of the University of California.
778189Sbrian * All rights reserved.
878189Sbrian *
978189Sbrian * Redistribution and use in source and binary forms, with or without
1078189Sbrian * modification, are permitted provided that the following conditions
1178189Sbrian * are met:
1278189Sbrian * 1. Redistributions of source code must retain the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer.
1478189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
156059Samurai *    notice, this list of conditions and the following disclaimer in the
1678189Sbrian *    documentation and/or other materials provided with the distribution.
1778189Sbrian * 3. Neither the name of the University nor the names of its contributors
1878189Sbrian *    may be used to endorse or promote products derived from this software
1978189Sbrian *    without specific prior written permission.
2078189Sbrian *
2178189Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2278189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2378189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2478189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2578189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2678189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
276059Samurai * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2850479Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
296059Samurai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3036285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3143313Sbrian * SUCH DAMAGE.
3230715Sbrian */
3336285Sbrian#include "sh.h"
3436285Sbrian
3581634SbrianRCSID("$Id: sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $")
3636285Sbrian
3730715Sbrian/*
3830715Sbrian * C Shell - directory management
3930715Sbrian */
4030715Sbrian
4146686Sbrianstatic	void			 dstart		__P((const char *));
4238814Sbrianstatic	struct directory	*dfind		__P((Char *));
4330715Sbrianstatic	Char 			*dfollow	__P((Char *));
4430715Sbrianstatic	void 	 	 	 printdirs	__P((int));
4530715Sbrianstatic	Char 			*dgoto		__P((Char *));
4630715Sbrianstatic	void 	 	 	 dnewcwd	__P((struct directory *, int));
476059Samuraistatic	void 	 	 	 dset		__P((Char *));
4836285Sbrianstatic  void 			 dextract	__P((struct directory *));
4936285Sbrianstatic  int 			 skipargs	__P((Char ***, char *, char *));
506059Samuraistatic	void			 dgetstack	__P((void));
5136285Sbrian
5236285Sbrianstatic struct directory dhead INIT_ZERO_STRUCT;		/* "head" of loop */
5381634Sbrianstatic int    printd;			/* force name to be printed */
5436285Sbrian
5536285Sbrianint     bequiet = 0;		/* do not print dir stack -strike */
5636285Sbrian
576059Samuraistatic void
5813389Sphkdstart(from)
5936285Sbrian    const char *from;
6036285Sbrian{
6143313Sbrian    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
6243313Sbrian}
6343313Sbrian
6481634Sbrian/*
6581634Sbrian * dinit - initialize current working directory
6636285Sbrian */
6736285Sbrianvoid
6836285Sbriandinit(hp)
6946686Sbrian    Char   *hp;
706059Samurai{
7130715Sbrian    register char *tcp;
7230715Sbrian    register Char *cp;
7344305Sbrian    register struct directory *dp;
7430715Sbrian    char    path[MAXPATHLEN];
7536285Sbrian
7636285Sbrian    /* Don't believe the login shell home, because it may be a symlink */
7736285Sbrian    tcp = (char *) getcwd(path, sizeof(path));
7836285Sbrian    if (tcp == NULL || *tcp == '\0') {
7936285Sbrian	xprintf("%s: %s\n", progname, strerror(errno));
8036285Sbrian	if (hp && *hp) {
8136285Sbrian	    tcp = short2str(hp);
8236285Sbrian	    dstart(tcp);
8336285Sbrian	    if (chdir(tcp) == -1)
8436285Sbrian		cp = NULL;
8536285Sbrian	    else
8636285Sbrian		cp = Strsave(hp);
8736285Sbrian	}
8836285Sbrian	else
8936285Sbrian	    cp = NULL;
9036285Sbrian	if (cp == NULL) {
9136285Sbrian	    dstart("/");
9236285Sbrian	    if (chdir("/") == -1)
9336285Sbrian		/* I am not even try to print an error message! */
9436285Sbrian		xexit(1);
9536285Sbrian	    cp = SAVE("/");
9636285Sbrian	}
9736285Sbrian    }
9836285Sbrian    else {
9963484Sbrian#ifdef S_IFLNK
10036285Sbrian	struct stat swd, shp;
10146686Sbrian
10236285Sbrian	/*
1036059Samurai	 * See if $HOME is the working directory we got and use that
1046059Samurai	 */
10536285Sbrian	if (hp && *hp &&
10636285Sbrian	    stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
10736285Sbrian	    DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
10836285Sbrian		swd.st_ino == shp.st_ino)
10936285Sbrian	    cp = Strsave(hp);
11036285Sbrian	else {
11136285Sbrian	    char   *cwd;
11236285Sbrian
11336285Sbrian	    /*
11436285Sbrian	     * use PWD if we have it (for subshells)
11536285Sbrian	     */
11655146Sbrian	    if ((cwd = getenv("PWD")) != NULL) {
11736285Sbrian		if (stat(cwd, &shp) != -1 &&
11836285Sbrian			DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
11936285Sbrian		    swd.st_ino == shp.st_ino)
12036285Sbrian		    tcp = cwd;
12136285Sbrian	    }
12236285Sbrian	    cp = dcanon(SAVE(tcp), STRNULL);
12336285Sbrian	}
12436285Sbrian#else /* S_IFLNK */
12536285Sbrian	cp = dcanon(SAVE(tcp), STRNULL);
12628327Sbrian#endif /* S_IFLNK */
12731343Sbrian    }
12828327Sbrian
12931343Sbrian    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
13031343Sbrian    dp->di_name = cp;
13136285Sbrian    dp->di_count = 0;
13232658Sbrian    dhead.di_next = dhead.di_prev = dp;
13336285Sbrian    dp->di_next = dp->di_prev = &dhead;
13436285Sbrian    printd = 0;
13536285Sbrian    dnewcwd(dp, 0);
13632658Sbrian    set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
13737060Sbrian}
13837060Sbrian
13928327Sbrianstatic void
14028327Sbriandset(dp)
1416059SamuraiChar *dp;
14236285Sbrian{
14344305Sbrian    /*
14436285Sbrian     * Don't call set() directly cause if the directory contains ` or
14555146Sbrian     * other junk characters glob will fail.
1466059Samurai     */
14736285Sbrian    set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
14836285Sbrian    set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
14936285Sbrian
15036285Sbrian    tsetenv(STRPWD, dp);
15136285Sbrian}
1526059Samurai
1536059Samurai#define DIR_PRINT	0x01	/* -p */
15446686Sbrian#define DIR_LONG  	0x02	/* -l */
15536285Sbrian#define DIR_VERT  	0x04	/* -v */
15636285Sbrian#define DIR_LINE  	0x08	/* -n */
15736285Sbrian#define DIR_SAVE 	0x10	/* -S */
15836285Sbrian#define DIR_LOAD	0x20	/* -L */
15936285Sbrian#define DIR_CLEAR	0x40	/* -c */
16036285Sbrian#define DIR_OLD	  	0x80	/* - */
16136285Sbrian
16236285Sbrianstatic int
16336285Sbrianskipargs(v, dstr, str)
16436285Sbrian    Char ***v;
16536285Sbrian    char   *dstr;
1666059Samurai    char   *str;
1676059Samurai{
16830715Sbrian    Char  **n = *v, *s;
16946828Sbrian
1706059Samurai    int dflag = 0, loop = 1;
17136285Sbrian    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
17294894Sbrian	if (*(s = &((*n)[1])) == '\0')	/* test for bare "-" argument */
17328461Sbrian	    dflag |= DIR_OLD;
17436285Sbrian	else {
1756059Samurai	    char *p;
17628327Sbrian	    while (loop && *s != '\0')	/* examine flags */
17736285Sbrian	    {
17828461Sbrian		if ((p = strchr(dstr, *s++)) != NULL)
17936285Sbrian		    dflag |= (1 << (p - dstr));
18028461Sbrian	        else {
18128679Sbrian		    stderror(ERR_DIRUS, short2str(**v), dstr, str);
18236285Sbrian		    loop = 0;	/* break from both loops */
18328327Sbrian		    break;
18428327Sbrian	        }
1856059Samurai	    }
1866059Samurai	}
1876059Samurai    if (*n && (dflag & DIR_OLD))
188134789Sbrian	stderror(ERR_DIRUS, short2str(**v), dstr, str);
18947695Sbrian    *v = n;
1906059Samurai    /* make -l, -v, and -n imply -p */
1916059Samurai    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
1926059Samurai	dflag |= DIR_PRINT;
1936059Samurai    return dflag;
1946059Samurai}
19536285Sbrian
19636285Sbrian/*
19736285Sbrian * dodirs - list all directories in directory loop
19836285Sbrian */
19936285Sbrian/*ARGSUSED*/
20036285Sbrianvoid
20136285Sbriandodirs(v, c)
20236285Sbrian    Char  **v;
20394894Sbrian    struct command *c;
20494894Sbrian{
20536285Sbrian    static char flags[] = "plvnSLc";
20636285Sbrian    int dflag = skipargs(&v, flags, "");
20736285Sbrian
20836285Sbrian    USE(c);
20936285Sbrian    if ((dflag & DIR_CLEAR) != 0) {
21028679Sbrian	struct directory *dp, *fdp;
2116059Samurai	for (dp = dcwd->di_next; dp != dcwd; ) {
2126059Samurai	    fdp = dp;
2136059Samurai	    dp = dp->di_next;
21454912Sbrian	    if (fdp != &dhead)
21530715Sbrian		dfree(fdp);
2166059Samurai	}
21730715Sbrian	dhead.di_next = dhead.di_prev = dp;
21836285Sbrian	dp->di_next = dp->di_prev = &dhead;
21950867Sbrian    }
22050867Sbrian    if ((dflag & DIR_LOAD) != 0)
22163484Sbrian	loaddirs(*v);
22263484Sbrian    else if ((dflag & DIR_SAVE) != 0)
22363484Sbrian	recdirs(*v, 1);
2246059Samurai
2256059Samurai    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
22632658Sbrian	v++;
22732658Sbrian
22832658Sbrian    if (*v != NULL || (dflag & DIR_OLD))
22932658Sbrian	stderror(ERR_DIRUS, "dirs", flags, "");
23032658Sbrian    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
23136285Sbrian	printdirs(dflag);
23232658Sbrian}
23337210Sbrian
23437210Sbrianstatic void
23537210Sbrianprintdirs(dflag)
23637210Sbrian    int dflag;
23737210Sbrian{
23837210Sbrian    register struct directory *dp;
23937210Sbrian    Char   *s, *user;
24037210Sbrian    int     idx, len, cur;
24137210Sbrian    extern int T_Cols;
24237210Sbrian
24337210Sbrian    dp = dcwd;
24437210Sbrian    idx = 0;
24537210Sbrian    cur = 0;
24637210Sbrian    do {
24737210Sbrian	if (dp == &dhead)
24844305Sbrian	    continue;
24932658Sbrian	if (dflag & DIR_VERT) {
25032658Sbrian	    xprintf("%d\t", idx++);
25132658Sbrian	    cur = 0;
25232658Sbrian	}
25332658Sbrian	s = dp->di_name;
2546059Samurai	user = NULL;
25546828Sbrian	if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
2566059Samurai	    len = (int) (Strlen(user) + Strlen(s) + 2);
2576059Samurai	else
25836285Sbrian	    len = (int) (Strlen(s) + 1);
2596059Samurai
26036285Sbrian	cur += len;
26136285Sbrian	if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
2626059Samurai	    xputchar('\n');
2636059Samurai	    cur = len;
2646059Samurai	}
26537210Sbrian	if (user)
26632658Sbrian	    xprintf("~%S", user);
26732658Sbrian	xprintf("%S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
26836285Sbrian    } while ((dp = dp->di_prev) != dcwd);
26936285Sbrian    if (!(dflag & DIR_VERT))
27037210Sbrian	xputchar('\n');
27136285Sbrian}
27232658Sbrian
27332658Sbrianvoid
27432658Sbriandtildepr(dir)
27536285Sbrian    Char *dir;
27632658Sbrian{
27732658Sbrian    Char* user;
2786059Samurai    if ((user = getusername(&dir)) != NULL)
27928679Sbrian	xprintf("~%S%S", user, dir);
2806059Samurai    else
2816059Samurai	xprintf("%S", dir);
2826059Samurai}
28328679Sbrian
2846059Samuraivoid
28528679Sbriandtilde()
28628679Sbrian{
2876059Samurai    struct directory *d = dcwd;
2886059Samurai
2896059Samurai    do {
2906059Samurai	if (d == &dhead)
2916059Samurai	    continue;
2926059Samurai	d->di_name = dcanon(d->di_name, STRNULL);
29346828Sbrian    } while ((d = d->di_prev) != dcwd);
2946059Samurai
2956059Samurai    dset(dcwd->di_name);
29637210Sbrian}
29736285Sbrian
29836285Sbrian
2996059Samurai/* dnormalize():
3006059Samurai *	The path will be normalized if it
3016059Samurai *	1) is "..",
30244305Sbrian *	2) or starts with "../",
3036059Samurai *	3) or ends with "/..",
3046059Samurai *	4) or contains the string "/../",
3056059Samurai *	then it will be normalized, unless those strings are quoted.
3066059Samurai *	Otherwise, a copy is made and sent back.
30736285Sbrian */
30836285SbrianChar   *
3096059Samuraidnormalize(cp, exp)
3106059Samurai    Char   *cp;
3116059Samurai    int exp;
3126059Samurai{
3136059Samurai
31436285Sbrian/* return true if dp is of the form "../xxx" or "/../xxx" */
3156059Samurai#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
3166059Samurai#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
31736285Sbrian
3186059Samurai#ifdef S_IFLNK
3196059Samurai    if (exp) {
32036285Sbrian 	int     dotdot = 0;
32144359Sbrian	Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
32236285Sbrian	struct stat sb;
32336285Sbrian# ifdef apollo
32436285Sbrian	bool slashslash;
32536285Sbrian# endif /* apollo */
3266059Samurai
32736285Sbrian	/*
32836285Sbrian	 * count the number of "../xxx" or "xxx/../xxx" in the path
32936285Sbrian	 */
33036285Sbrian	for (dp=start; *dp && *(dp+1); dp++)
3316059Samurai	    if (IS_DOTDOT(start, dp))
3326059Samurai	        dotdot++;
3336059Samurai	/*
3346059Samurai	 * if none, we are done.
3356059Samurai	 */
3366059Samurai        if (dotdot == 0)
3376059Samurai	    return (Strsave(cp));
33836285Sbrian
3396059Samurai	/*
34036285Sbrian	 * If the path doesn't exist, we are done too.
3416059Samurai	 */
3426059Samurai	if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT)
3436059Samurai	    return (Strsave(cp));
3446059Samurai
3456059Samurai
34636285Sbrian	cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
3476059Samurai					   sizeof(Char)));
3486059Samurai	(void) Strcpy(cwd, dcwd->di_name);
34936285Sbrian
35036285Sbrian	/*
3516059Samurai	 * If the path starts with a slash, we are not relative to
35236285Sbrian	 * the current working directory.
3536059Samurai	 */
3546059Samurai	if (ABSOLUTEP(start))
3556059Samurai	    *cwd = '\0';
3566059Samurai# ifdef apollo
3576059Samurai	slashslash = cwd[0] == '/' && cwd[1] == '/';
3586059Samurai# endif /* apollo */
3596059Samurai
3606059Samurai	/*
36136285Sbrian	 * Ignore . and count ..'s
36271658Sbrian	 */
36371658Sbrian	for (;;) {
36471658Sbrian	    dotdot = 0;
36571658Sbrian	    buf[0] = '\0';
36671658Sbrian	    dp = buf;
36771658Sbrian	    while (*cp)
36836285Sbrian	        if (IS_DOT(start, cp)) {
3696059Samurai	            if (*++cp)
3706059Samurai	                cp++;
3716059Samurai	        }
37244305Sbrian	        else if (IS_DOTDOT(start, cp)) {
3736059Samurai		    if (buf[0])
3746059Samurai		        break; /* finish analyzing .././../xxx/[..] */
3756059Samurai		    dotdot++;
3766059Samurai		    cp += 2;
3776059Samurai		    if (*cp)
3786059Samurai		        cp++;
3796059Samurai	        }
3806059Samurai	        else
3816059Samurai			*dp++ = *cp++;
38230715Sbrian
38346828Sbrian	    *dp = '\0';
3846059Samurai	    while (dotdot > 0)
38544305Sbrian	        if ((dp = Strrchr(cwd, '/')) != NULL) {
38636285Sbrian# ifdef apollo
38744305Sbrian		    if (dp == &cwd[1])
3886059Samurai		        slashslash = 1;
38944305Sbrian# endif /* apollo */
39044305Sbrian		        *dp = '\0';
39144305Sbrian		        dotdot--;
39263484Sbrian	        }
39336285Sbrian	        else
3946059Samurai		    break;
3956059Samurai
3966059Samurai	    if (!*cwd) {	/* too many ..'s, starts with "/" */
39730715Sbrian	        cwd[0] = '/';
39836285Sbrian# ifdef apollo
3996059Samurai		cwd[1] = '/';
40047695Sbrian		cwd[2] = '\0';
40136285Sbrian# else /* !apollo */
40236285Sbrian		cwd[1] = '\0';
4036059Samurai# endif /* apollo */
4046059Samurai	    }
4056059Samurai# ifdef apollo
4066059Samurai	    else if (slashslash && cwd[1] == '\0') {
4076059Samurai		cwd[1] = '/';
4086059Samurai		cwd[2] = '\0';
40930715Sbrian	    }
41031343Sbrian# endif /* apollo */
4116059Samurai
41231343Sbrian	    if (buf[0]) {
41331343Sbrian	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
4146059Samurai		    cwd[dotdot++] = '/';
4156059Samurai	        cwd[dotdot] = '\0';
41636285Sbrian	        dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
41736285Sbrian	        xfree((ptr_t) cwd);
4186059Samurai	        cwd = dp;
4196059Samurai	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
4206059Samurai		    cwd[--dotdot] = '\0';
4216059Samurai	    }
4226059Samurai	    /* Reduction of ".." following the stuff we collected in buf
4236059Samurai	     * only makes sense if the directory item in buf really exists.
4246059Samurai	     * Avoid reduction of "-I../.." (typical compiler call) to ""
4256059Samurai	     * or "/usr/nonexistant/../bin" to "/usr/bin":
4266059Samurai	     */
4276059Samurai	    if (cwd[0]) {
4286059Samurai	        struct stat exists;
42936285Sbrian		if (0 != stat(short2str(cwd), &exists)) {
4306059Samurai		    xfree((ptr_t) cwd);
4316059Samurai		    return Strsave(start);
4326059Samurai		}
43336285Sbrian	    }
4346059Samurai	    if (!*cp)
43536285Sbrian	        break;
4366059Samurai	}
4376059Samurai	return cwd;
43836285Sbrian    }
4396059Samurai#endif /* S_IFLNK */
44036285Sbrian    return Strsave(cp);
4416059Samurai}
4426059Samurai
4436059Samurai
4446059Samurai/*
44536285Sbrian * dochngd - implement chdir command.
4466059Samurai */
44736285Sbrian/*ARGSUSED*/
4486059Samuraivoid
4496059Samuraidochngd(v, c)
4506059Samurai    Char  **v;
4516059Samurai    struct command *c;
4526059Samurai{
45330715Sbrian    register Char *cp;
45444305Sbrian    register struct directory *dp;
4556059Samurai    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
45636285Sbrian
4576059Samurai    USE(c);
45844305Sbrian    printd = 0;
45944305Sbrian    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
4606059Samurai
4616059Samurai    if (cp == NULL) {
4626059Samurai	if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
46346828Sbrian	    stderror(ERR_NAME | ERR_NOHOMEDIR);
4646059Samurai	if (chdir(short2str(cp)) < 0)
46530715Sbrian	    stderror(ERR_NAME | ERR_CANTCHANGE);
46636285Sbrian	cp = Strsave(cp);
46728679Sbrian    }
4686059Samurai    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
46936285Sbrian	stderror(ERR_NAME | ERR_TOOMANY);
4706735Samurai	/* NOTREACHED */
4716059Samurai	return;
47294894Sbrian    }
4738857Srgrimes    else if ((dp = dfind(cp)) != 0) {
47494894Sbrian	char   *tmp;
47554912Sbrian
47631962Sbrian	printd = 1;
4776735Samurai	if (chdir(tmp = short2str(dp->di_name)) < 0)
47837019Sbrian	    stderror(ERR_SYSTEM, tmp, strerror(errno));
47994894Sbrian	dcwd->di_prev->di_next = dcwd->di_next;
48054912Sbrian	dcwd->di_next->di_prev = dcwd->di_prev;
4816059Samurai	dfree(dcwd);
4826059Samurai	dnewcwd(dp, dflag);
4836059Samurai	return;
48495872Sbrian    }
48595872Sbrian    else
48695872Sbrian	if ((cp = dfollow(cp)) == NULL)
48795872Sbrian	    return;
48895872Sbrian    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
48995872Sbrian    dp->di_name = cp;
49094894Sbrian    dp->di_count = 0;
49194894Sbrian    dp->di_next = dcwd->di_next;
49294894Sbrian    dp->di_prev = dcwd->di_prev;
49394894Sbrian    dp->di_prev->di_next = dp;
49494894Sbrian    dp->di_next->di_prev = dp;
495134789Sbrian    dfree(dcwd);
49694894Sbrian    dnewcwd(dp, dflag);
49794894Sbrian}
49894894Sbrian
49994894Sbrianstatic Char *
50094894Sbriandgoto(cp)
50146828Sbrian    Char   *cp;
5026059Samurai{
5036059Samurai    Char   *dp;
50437320Sbrian
50537320Sbrian    if (!ABSOLUTEP(cp))
50637320Sbrian    {
50746686Sbrian	register Char *p, *q;
50837320Sbrian	int     cwdlen;
50954912Sbrian
51046686Sbrian	for (p = dcwd->di_name; *p++;)
51154912Sbrian	    continue;
51254912Sbrian	if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1)	/* root */
51354912Sbrian	    cwdlen = 0;
51437320Sbrian	for (p = cp; *p++;)
51537320Sbrian	    continue;
51637320Sbrian	dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
5176059Samurai	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
51836285Sbrian	    continue;
51936285Sbrian	if (cwdlen)
52054912Sbrian	    p[-1] = '/';
5216059Samurai	else
5226059Samurai	    p--;		/* don't add a / after root */
52336285Sbrian	for (q = cp; (*p++ = *q++) != '\0';)
52454912Sbrian	    continue;
5256059Samurai	xfree((ptr_t) cp);
5266059Samurai	cp = dp;
52736285Sbrian	dp += cwdlen;
52836285Sbrian    }
5296059Samurai    else
53054912Sbrian	dp = cp;
5316059Samurai
5326059Samurai#ifdef WINNT_NATIVE
53344305Sbrian    cp = SAVE(getcwd(NULL, 0));
53495872Sbrian#else /* !WINNT_NATIVE */
53537995Sbrian    cp = dcanon(cp, dp);
5366059Samurai#endif /* WINNT_NATIVE */
5376059Samurai    return cp;
5386059Samurai}
5396059Samurai
54036285Sbrian/*
54147695Sbrian * dfollow - change to arg directory; fall back on cdpath if not valid
54247695Sbrian */
54336285Sbrianstatic Char *
54447695Sbriandfollow(cp)
54547695Sbrian    register Char *cp;
5466059Samurai{
54747695Sbrian    register Char *dp;
54847695Sbrian    struct varent *c;
5496059Samurai    char    ebuf[MAXPATHLEN];
5506059Samurai    int serrno;
55144305Sbrian
55244305Sbrian    cp = globone(cp, G_ERROR);
55344305Sbrian#ifdef apollo
55444305Sbrian    if (Strchr(cp, '`')) {
55544305Sbrian	char *dptr, *ptr;
55644305Sbrian	if (chdir(dptr = short2str(cp)) < 0)
55744305Sbrian	    stderror(ERR_SYSTEM, dptr, strerror(errno));
55844305Sbrian	else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
559102413Scharnier		xfree((ptr_t) cp);
56044305Sbrian		cp = Strsave(str2short(ptr));
56136285Sbrian		return dgoto(cp);
5626059Samurai	}
5636059Samurai	else
5646059Samurai	    stderror(ERR_SYSTEM, dptr, ebuf);
5656059Samurai    }
56681634Sbrian#endif /* apollo */
5676059Samurai
5686059Samurai    (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
5696059Samurai    ebuf[MAXPATHLEN-1] = '\0';
5706059Samurai    /*
5716059Samurai     * if we are ignoring symlinks, try to fix relatives now.
5726059Samurai     * if we are expading symlinks, it should be done by now.
5736059Samurai     */
5746059Samurai    dp = dnormalize(cp, symlinks == SYM_IGNORE);
57536285Sbrian    if (chdir(short2str(dp)) >= 0) {
57636285Sbrian        xfree((ptr_t) cp);
57736285Sbrian        return dgoto(dp);
57836285Sbrian    }
57944305Sbrian    else {
58036285Sbrian        xfree((ptr_t) dp);
58136285Sbrian        if (chdir(short2str(cp)) >= 0)
58263484Sbrian	    return dgoto(cp);
58336285Sbrian	else if (errno != ENOENT && errno != ENOTDIR)
5846059Samurai	    stderror(ERR_SYSTEM, ebuf, strerror(errno));
5856059Samurai	serrno = errno;
5866059Samurai    }
5876059Samurai
5886059Samurai    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
5896059Samurai	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
5906059Samurai	Char  **cdp;
59154912Sbrian	register Char *p;
59244305Sbrian	Char    buf[MAXPATHLEN];
59344305Sbrian
59444305Sbrian	for (cdp = c->vec; *cdp; cdp++) {
59544305Sbrian	    for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
59663484Sbrian		continue;
59744305Sbrian	    dp[-1] = '/';
59844305Sbrian	    for (p = cp; (*dp++ = *p++) != '\0';)
59944305Sbrian		continue;
60044305Sbrian	    /*
60144305Sbrian	     * We always want to fix the directory here
60244305Sbrian	     * If we are normalizing symlinks
60363484Sbrian	     */
60444305Sbrian	    dp = dnormalize(buf, symlinks == SYM_IGNORE ||
60544305Sbrian				 symlinks == SYM_EXPAND);
6066059Samurai	    if (chdir(short2str(dp)) >= 0) {
6076059Samurai		printd = 1;
60830715Sbrian		xfree((ptr_t) cp);
60936285Sbrian		return dgoto(dp);
61028679Sbrian	    }
6116059Samurai	    else if (chdir(short2str(cp)) >= 0) {
61294894Sbrian		printd = 1;
61394894Sbrian		xfree((ptr_t) dp);
61494894Sbrian		return dgoto(cp);
61594894Sbrian	    }
61694894Sbrian	}
61794894Sbrian    }
61894894Sbrian    dp = varval(cp);
61994894Sbrian    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
62094894Sbrian	xfree((ptr_t) cp);
62194894Sbrian	cp = Strsave(dp);
62294894Sbrian	printd = 1;
62394894Sbrian	return dgoto(cp);
62494894Sbrian    }
62594894Sbrian    xfree((ptr_t) cp);
62694894Sbrian    /*
62794894Sbrian     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
62894894Sbrian     * directories we could get to.
629134789Sbrian     */
63094894Sbrian    if (!bequiet) {
63194894Sbrian	stderror(ERR_SYSTEM, ebuf, strerror(serrno));
6326059Samurai	return (NULL);
63328679Sbrian    }
63428679Sbrian    else
63536285Sbrian	return (NULL);
6366059Samurai}
6376059Samurai
6386059Samurai
6396059Samurai/*
6406059Samurai * dopushd - push new directory onto directory stack.
64144305Sbrian *	with no arguments exchange top and second.
6426059Samurai *	with numeric argument (+n) bring it to top.
6436059Samurai */
6446059Samurai/*ARGSUSED*/
6456059Samuraivoid
6466059Samuraidopushd(v, c)
6476059Samurai    Char  **v;
6486059Samurai    struct command *c;
64944305Sbrian{
6506059Samurai    register struct directory *dp;
65136285Sbrian    register Char *cp;
65236285Sbrian    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
65336285Sbrian
65436285Sbrian    USE(c);
65544305Sbrian    printd = 1;
65636285Sbrian    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
65736285Sbrian
65863484Sbrian    if (cp == NULL) {
65936285Sbrian	if (adrof(STRpushdtohome)) {
6606059Samurai	    if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
6616059Samurai		stderror(ERR_NAME | ERR_NOHOMEDIR);
66236285Sbrian	    if (chdir(short2str(cp)) < 0)
6636059Samurai		stderror(ERR_NAME | ERR_CANTCHANGE);
6646059Samurai	    cp = Strsave(cp);	/* hmmm... PWP */
66536285Sbrian	    if ((cp = dfollow(cp)) == NULL)
6666059Samurai		return;
6676059Samurai	    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
66854912Sbrian	    dp->di_name = cp;
6696059Samurai	    dp->di_count = 0;
6706059Samurai	    dp->di_prev = dcwd;
67130715Sbrian	    dp->di_next = dcwd->di_next;
67236285Sbrian	    dcwd->di_next = dp;
67328679Sbrian	    dp->di_next->di_prev = dp;
6746059Samurai	}
67536285Sbrian	else {
6766735Samurai	    char   *tmp;
67794894Sbrian
6788857Srgrimes	    if ((dp = dcwd->di_prev) == &dhead)
67954912Sbrian		dp = dhead.di_prev;
68031962Sbrian	    if (dp == dcwd)
6816735Samurai		stderror(ERR_NAME | ERR_NODIR);
68254912Sbrian	    if (chdir(tmp = short2str(dp->di_name)) < 0)
6836059Samurai		stderror(ERR_SYSTEM, tmp, strerror(errno));
6846059Samurai	    dp->di_prev->di_next = dp->di_next;
6856059Samurai	    dp->di_next->di_prev = dp->di_prev;
6866059Samurai	    dp->di_next = dcwd->di_next;
68728679Sbrian	    dp->di_prev = dcwd;
6886059Samurai	    dcwd->di_next->di_prev = dp;
6896059Samurai	    dcwd->di_next = dp;
6906059Samurai	}
6916059Samurai    }
69236285Sbrian    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
69336285Sbrian	stderror(ERR_NAME | ERR_TOOMANY);
69454912Sbrian	/* NOTREACHED */
6956059Samurai	return;
6966059Samurai    }
6976059Samurai    else if ((dp = dfind(cp)) != NULL) {
69836285Sbrian	char   *tmp;
69954912Sbrian
7006059Samurai	if (chdir(tmp = short2str(dp->di_name)) < 0)
7016059Samurai	    stderror(ERR_SYSTEM, tmp, strerror(errno));
7026059Samurai	/*
70354912Sbrian	 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
7046059Samurai	 */
7056059Samurai	if (adrof(STRdextract))
7066059Samurai	    dextract(dp);
70754912Sbrian    }
70836285Sbrian    else {
70936285Sbrian	register Char *ccp;
71036285Sbrian
71194894Sbrian	if ((ccp = dfollow(cp)) == NULL)
71294894Sbrian	    return;
713134789Sbrian	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
71436285Sbrian	dp->di_name = ccp;
7156059Samurai	dp->di_count = 0;
7166059Samurai	dp->di_prev = dcwd;
7176059Samurai	dp->di_next = dcwd->di_next;
7186059Samurai	dcwd->di_next = dp;
71944305Sbrian	dp->di_next->di_prev = dp;
7206059Samurai    }
7216059Samurai    dnewcwd(dp, dflag);
7226059Samurai}
72336285Sbrian
72436285Sbrian/*
72536285Sbrian * dfind - find a directory if specified by numeric (+n) argument
72636285Sbrian */
72736285Sbrianstatic struct directory *
7286059Samuraidfind(cp)
7296059Samurai    register Char *cp;
7306059Samurai{
7316059Samurai    register struct directory *dp;
7326059Samurai    register int i;
7336059Samurai    register Char *ep;
73454912Sbrian
7356059Samurai    if (*cp++ != '+')
7366059Samurai	return (0);
73730715Sbrian    for (ep = cp; Isdigit(*ep); ep++)
73836285Sbrian	continue;
73928679Sbrian    if (*ep)
7406059Samurai	return (0);
7416059Samurai    i = getn(cp);
74236285Sbrian    if (i <= 0)
74336285Sbrian	return (0);
74436285Sbrian    for (dp = dcwd; i != 0; i--) {
74536285Sbrian	if ((dp = dp->di_prev) == &dhead)
7466059Samurai	    dp = dp->di_prev;
7476059Samurai	if (dp == dcwd)
7486059Samurai	    stderror(ERR_NAME | ERR_DEEP);
7496059Samurai    }
7506059Samurai    return (dp);
7516059Samurai}
75236285Sbrian
7536059Samurai/*
7546059Samurai * dopopd - pop a directory out of the directory stack
7556059Samurai *	with a numeric argument just discard it.
75636285Sbrian */
7576059Samurai/*ARGSUSED*/
7586059Samuraivoid
7596059Samuraidopopd(v, c)
76036285Sbrian    Char  **v;
76136285Sbrian    struct command *c;
76244305Sbrian{
76344305Sbrian    Char *cp;
76426353Sbrian    register struct directory *dp, *p = NULL;
76526353Sbrian    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
76636285Sbrian
76744305Sbrian    USE(c);
7686059Samurai    printd = 1;
7696059Samurai    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
77054912Sbrian
7716059Samurai    if (cp == NULL)
7726059Samurai	dp = dcwd;
77330715Sbrian    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
774134789Sbrian	stderror(ERR_NAME | ERR_TOOMANY);
77528679Sbrian	/* NOTREACHED */
7766059Samurai	return;
7776059Samurai    }
77836285Sbrian    else if ((dp = dfind(cp)) == 0)
77936285Sbrian	stderror(ERR_NAME | ERR_BADDIR);
7806059Samurai    if (dp->di_prev == &dhead && dp->di_next == &dhead)
78136285Sbrian	stderror(ERR_NAME | ERR_EMPTY);
7826059Samurai    if (dp == dcwd) {
7836059Samurai	char   *tmp;
78436285Sbrian
7856059Samurai	if ((p = dp->di_prev) == &dhead)
78636285Sbrian	    p = dhead.di_prev;
7876059Samurai	if (chdir(tmp = short2str(p->di_name)) < 0)
7886059Samurai	    stderror(ERR_SYSTEM, tmp, strerror(errno));
7896059Samurai    }
7906059Samurai    dp->di_prev->di_next = dp->di_next;
7916059Samurai    dp->di_next->di_prev = dp->di_prev;
79236285Sbrian    if (dp == dcwd) {
7936059Samurai	dnewcwd(p, dflag);
7946059Samurai    }
79536285Sbrian    else {
7966059Samurai	printdirs(dflag);
7976059Samurai    }
79854912Sbrian    dfree(dp);
7996059Samurai}
8006059Samurai
80130715Sbrian/*
80236285Sbrian * dfree - free the directory (or keep it if it still has ref count)
80328679Sbrian */
8046059Samuraivoid
80536285Sbriandfree(dp)
806134789Sbrian    register struct directory *dp;
807134789Sbrian{
80894894Sbrian
8098857Srgrimes    if (dp->di_count != 0) {
81054912Sbrian	dp->di_next = dp->di_prev = 0;
81131962Sbrian    }
812134789Sbrian    else {
81354912Sbrian	xfree((ptr_t) dp->di_name);
8146059Samurai	xfree((ptr_t) dp);
8156059Samurai    }
8166059Samurai}
81763484Sbrian
81863484Sbrian/*
8196059Samurai * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
82028679Sbrian *	we are of course assuming that the file system is standardly
8216059Samurai *	constructed (always have ..'s, directories have links)
8226059Samurai */
8236059SamuraiChar   *
8246059Samuraidcanon(cp, p)
82536285Sbrian    register Char *cp, *p;
82636285Sbrian{
82754912Sbrian    register Char *sp;
8286059Samurai    register Char *p1, *p2;	/* general purpose */
8296059Samurai    bool    slash;
8306059Samurai#ifdef apollo
83136285Sbrian    bool    slashslash;
83254912Sbrian#endif /* apollo */
8336059Samurai    size_t  clen;
8346059Samurai
8356059Samurai#ifdef S_IFLNK			/* if we have symlinks */
83654912Sbrian    Char    link[MAXPATHLEN];
8376059Samurai    char    tlink[MAXPATHLEN];
8386059Samurai    int     cc;
8396059Samurai    Char   *newcp;
84054912Sbrian#endif /* S_IFLNK */
84136285Sbrian
84236285Sbrian    /*
84336285Sbrian     * if the path given is too long truncate it!
84494894Sbrian     */
84594894Sbrian    if ((clen = Strlen(cp)) >= MAXPATHLEN)
846134789Sbrian	cp[clen = MAXPATHLEN - 1] = '\0';
84736285Sbrian
8486059Samurai    /*
8496059Samurai     * christos: if the path given does not start with a slash prepend cwd. If
8506059Samurai     * cwd does not start with a slash or the result would be too long try to
8516059Samurai     * correct it.
85244305Sbrian     */
8536059Samurai    if (!ABSOLUTEP(cp)) {
8546059Samurai	Char    tmpdir[MAXPATHLEN];
8556059Samurai	size_t	len;
85636285Sbrian
85736285Sbrian	p1 = varval(STRcwd);
85836285Sbrian	if (p1 == STRNULL || !ABSOLUTEP(p1)) {
85936285Sbrian	    char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
86036285Sbrian	    if (tmp == NULL || *tmp == '\0') {
8616059Samurai		xprintf("%s: %s\n", progname, strerror(errno));
8626059Samurai		set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
8636059Samurai	    } else {
8646059Samurai		set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
8656059Samurai	    }
86654912Sbrian	    p1 = varval(STRcwd);
8676059Samurai	}
8686059Samurai	len = Strlen(p1);
86930715Sbrian	if (len + clen + 1 >= MAXPATHLEN)
870134789Sbrian	    cp[MAXPATHLEN - (len + 1)] = '\0';
871134789Sbrian	(void) Strcpy(tmpdir, p1);
8726059Samurai	(void) Strcat(tmpdir, STRslash);
87354912Sbrian	(void) Strcat(tmpdir, cp);
8746059Samurai	xfree((ptr_t) cp);
8756059Samurai	cp = p = Strsave(tmpdir);
87630715Sbrian    }
877134789Sbrian
8786059Samurai#ifdef apollo
87936285Sbrian    slashslash = (cp[0] == '/' && cp[1] == '/');
88046828Sbrian#endif /* apollo */
8816059Samurai
88254912Sbrian    while (*p) {		/* for each component */
88354912Sbrian	sp = p;			/* save slash address */
88446828Sbrian	while (*++p == '/')	/* flush extra slashes */
88546828Sbrian	    continue;
88646828Sbrian	if (p != ++sp)
88746828Sbrian	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
88836285Sbrian		continue;
88936285Sbrian	p = sp;			/* save start of component */
8906059Samurai	slash = 0;
8916059Samurai	if (*p)
8926059Samurai	    while (*++p)	/* find next slash or end of path */
89336285Sbrian		if (*p == '/') {
89436285Sbrian		    slash = 1;
89536285Sbrian		    *p = 0;
89636285Sbrian		    break;
89736285Sbrian		}
8986059Samurai
8996059Samurai#ifdef apollo
90036285Sbrian	if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
90136285Sbrian	    slashslash = 1;
90244359Sbrian#endif /* apollo */
90344359Sbrian	if (*sp == '\0') {	/* if component is null */
90436285Sbrian	    if (--sp == cp)	/* if path is one char (i.e. /) */
90536285Sbrian		break;
90636285Sbrian	    else
90736285Sbrian		*sp = '\0';
90893420Sbrian	}
90936285Sbrian	else if (sp[0] == '.' && sp[1] == 0) {
91036285Sbrian	    if (slash) {
91136285Sbrian		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
91236285Sbrian		    continue;
91344359Sbrian		p = --sp;
91444359Sbrian	    }
9156059Samurai	    else if (--sp != cp)
9166059Samurai		*sp = '\0';
91749599Sbrian	    else
91849599Sbrian		sp[1] = '\0';
91949599Sbrian	}
92049599Sbrian	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
92149599Sbrian	    /*
92249599Sbrian	     * We have something like "yyy/xxx/..", where "yyy" can be null or
92349599Sbrian	     * a path starting at /, and "xxx" is a single component. Before
92481634Sbrian	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
92581634Sbrian	     * symbolic link.
92681897Sbrian	     */
92781634Sbrian	    *--sp = 0;		/* form the pathname for readlink */
92881634Sbrian#ifdef S_IFLNK			/* if we have symlinks */
92981634Sbrian	    if (sp != cp && /* symlinks != SYM_IGNORE && */
93081634Sbrian		(cc = readlink(short2str(cp), tlink,
93181634Sbrian			       sizeof tlink)) >= 0) {
93281634Sbrian		tlink[cc] = '\0';
93336285Sbrian		(void) Strncpy(link, str2short(tlink),
93436285Sbrian		    sizeof(link) / sizeof(Char));
93536285Sbrian		link[sizeof(link) / sizeof(Char) - 1] = '\0';
93636285Sbrian
93736285Sbrian		if (slash)
93836285Sbrian		    *p = '/';
93936285Sbrian		/*
94036285Sbrian		 * Point p to the '/' in "/..", and restore the '/'.
94136285Sbrian		 */
94236285Sbrian		*(p = sp) = '/';
94336285Sbrian		/*
9446059Samurai		 * find length of p
94554912Sbrian		 */
9466059Samurai		for (p1 = p; *p1++;)
9476059Samurai		    continue;
94830715Sbrian		if (*link != '/') {
94936285Sbrian		    /*
9506059Samurai		     * Relative path, expand it between the "yyy/" and the
95136285Sbrian		     * "/..". First, back sp up to the character past "yyy/".
9526059Samurai		     */
95336285Sbrian		    while (*--sp != '/')
9546059Samurai			continue;
95554912Sbrian		    sp++;
95654912Sbrian		    *sp = 0;
95752942Sbrian		    /*
95852942Sbrian		     * New length is "yyy/" + link + "/.." and rest
95936285Sbrian		     */
96038814Sbrian		    p1 = newcp = (Char *) xmalloc((size_t)
96136285Sbrian						(((sp - cp) + cc + (p1 - p)) *
96251970Sbrian						 sizeof(Char)));
96351970Sbrian		    /*
96451970Sbrian		     * Copy new path into newcp
96536285Sbrian		     */
96636285Sbrian		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
96736285Sbrian			continue;
96838814Sbrian		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
96952942Sbrian			continue;
97052942Sbrian		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
97136285Sbrian			continue;
9726059Samurai		    /*
97354912Sbrian		     * Restart canonicalization at expanded "/xxx".
9746059Samurai		     */
9756059Samurai		    p = sp - cp - 1 + newcp;
97630715Sbrian		}
977134789Sbrian		else {
9786059Samurai		    /*
97947169Sbrian		     * New length is link + "/.." and rest
98047169Sbrian		     */
9816059Samurai		    p1 = newcp = (Char *) xmalloc((size_t)
98254912Sbrian					    ((cc + (p1 - p)) * sizeof(Char)));
9836059Samurai		    /*
9846059Samurai		     * Copy new path into newcp
98530715Sbrian		     */
986134789Sbrian		    for (p2 = link; (*p1++ = *p2++) != '\0';)
987134789Sbrian			continue;
9886059Samurai		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
98954912Sbrian			continue;
9906059Samurai		    /*
9916059Samurai		     * Restart canonicalization at beginning
99230715Sbrian		     */
99346828Sbrian		    p = newcp;
9946059Samurai		}
99563484Sbrian		xfree((ptr_t) cp);
99663484Sbrian		cp = newcp;
99763484Sbrian#ifdef apollo
99863484Sbrian                slashslash = (cp[0] == '/' && cp[1] == '/');
99963484Sbrian#endif /* apollo */
100063484Sbrian		continue;	/* canonicalize the link */
100163484Sbrian	    }
100263484Sbrian#endif /* S_IFLNK */
100363484Sbrian	    *sp = '/';
100463484Sbrian	    if (sp != cp)
100563484Sbrian		while (*--sp != '/')
100663484Sbrian		    continue;
100763484Sbrian	    if (slash) {
100863484Sbrian		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
100963484Sbrian		    continue;
101063484Sbrian		p = sp;
101154912Sbrian	    }
10126059Samurai	    else if (cp == sp)
10136059Samurai		*++sp = '\0';
101430715Sbrian	    else
1015134789Sbrian		*sp = '\0';
1016134789Sbrian	}
10176059Samurai	else {			/* normal dir name (not . or .. or nothing) */
101854912Sbrian
10196059Samurai#ifdef S_IFLNK			/* if we have symlinks */
10206059Samurai	    if (sp != cp && symlinks == SYM_CHASE &&
102130715Sbrian		(cc = readlink(short2str(cp), tlink,
102236285Sbrian			       sizeof tlink)) >= 0) {
10236059Samurai		tlink[cc] = '\0';
102478411Sbrian		(void) Strncpy(link, str2short(tlink),
102578411Sbrian		    sizeof(link) / sizeof(Char));
102678411Sbrian		link[sizeof(link) / sizeof(Char) - 1] = '\0';
102778411Sbrian
102878411Sbrian		/*
102978411Sbrian		 * restore the '/'.
103078411Sbrian		 */
103178411Sbrian		if (slash)
103278411Sbrian		    *p = '/';
103354912Sbrian
10346059Samurai		/*
10356059Samurai		 * point sp to p (rather than backing up).
103630715Sbrian		 */
103736285Sbrian		sp = p;
10386059Samurai
103936285Sbrian		/*
104054912Sbrian		 * find length of p
10416059Samurai		 */
10426059Samurai		for (p1 = p; *p1++;)
10436059Samurai		    continue;
104436285Sbrian		if (*link != '/') {
10456059Samurai		    /*
1046134789Sbrian		     * Relative path, expand it between the "yyy/" and the
104746686Sbrian		     * remainder. First, back sp up to the character past
104831514Sbrian		     * "yyy/".
10496059Samurai		     */
105054912Sbrian		    while (*--sp != '/')
10516059Samurai			continue;
105254912Sbrian		    sp++;
10536059Samurai		    *sp = 0;
10546059Samurai		    /*
105546686Sbrian		     * New length is "yyy/" + link + "/.." and rest
105652942Sbrian		     */
105763484Sbrian		    p1 = newcp = (Char *) xmalloc((size_t)
1058134833Smarcel						  (((sp - cp) + cc + (p1 - p))
105963484Sbrian						   * sizeof(Char)));
106063484Sbrian		    /*
106163484Sbrian		     * Copy new path into newcp
106260957Sbrian		     */
106352942Sbrian		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
106446686Sbrian			continue;
106546686Sbrian		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
106636285Sbrian			continue;
106736285Sbrian		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
106836285Sbrian			continue;
106936285Sbrian		    /*
107036285Sbrian		     * Restart canonicalization at expanded "/xxx".
107137010Sbrian		     */
107254912Sbrian		    p = sp - cp - 1 + newcp;
107354912Sbrian		}
107454912Sbrian		else {
107554912Sbrian		    /*
10766059Samurai		     * New length is link + the rest
10776059Samurai		     */
10786059Samurai		    p1 = newcp = (Char *) xmalloc((size_t)
107946686Sbrian					    ((cc + (p1 - p)) * sizeof(Char)));
108046686Sbrian		    /*
108136285Sbrian		     * Copy new path into newcp
108236285Sbrian		     */
108394894Sbrian		    for (p2 = link; (*p1++ = *p2++) != '\0';)
108436285Sbrian			continue;
108536285Sbrian		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
108636285Sbrian			continue;
108736285Sbrian		    /*
108894894Sbrian		     * Restart canonicalization at beginning
108936285Sbrian		     */
109046686Sbrian		    p = newcp;
109136285Sbrian		}
109236285Sbrian		xfree((ptr_t) cp);
109336285Sbrian		cp = newcp;
109446686Sbrian#ifdef apollo
10956059Samurai                slashslash = (cp[0] == '/' && cp[1] == '/');
109636285Sbrian#endif /* apollo */
109778411Sbrian		continue;	/* canonicalize the link */
109836285Sbrian	    }
109936285Sbrian#endif /* S_IFLNK */
110036285Sbrian	    if (slash)
110136285Sbrian		*p = '/';
110278411Sbrian	}
110336285Sbrian    }
110436285Sbrian
110536285Sbrian    /*
1106134789Sbrian     * fix home...
110736285Sbrian     */
110836285Sbrian#ifdef S_IFLNK
110936285Sbrian    p1 = varval(STRhome);
111036285Sbrian    cc = (int) Strlen(p1);
111137060Sbrian    /*
111237060Sbrian     * See if we're not in a subdir of STRhome
111337160Sbrian     */
111437160Sbrian    if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
111537160Sbrian	(cp[cc] != '/' && cp[cc] != '\0'))) {
111637160Sbrian	static ino_t home_ino = (ino_t) -1;
111744305Sbrian	static dev_t home_dev = (dev_t) -1;
111837160Sbrian	static Char *home_ptr = NULL;
111937160Sbrian	struct stat statbuf;
112037160Sbrian	int found;
112137160Sbrian
112237160Sbrian	/*
112337160Sbrian	 * Get dev and ino of STRhome
112437160Sbrian	 */
112537060Sbrian	if (home_ptr != p1 &&
112637060Sbrian	    stat(short2str(p1), &statbuf) != -1) {
112744305Sbrian	    home_dev = statbuf.st_dev;
112844305Sbrian	    home_ino = statbuf.st_ino;
112944305Sbrian	    home_ptr = p1;
113037060Sbrian	}
113137060Sbrian	/*
113237060Sbrian	 * Start comparing dev & ino backwards
113337060Sbrian	 */
113437060Sbrian	p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char));
113537060Sbrian	link[sizeof(link) / sizeof(Char) - 1] = '\0';
113637060Sbrian	found = 0;
113794894Sbrian	while (*p2 && stat(short2str(p2), &statbuf) != -1) {
113894894Sbrian	    if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
113994894Sbrian			statbuf.st_ino == home_ino) {
114094894Sbrian			found = 1;
114194894Sbrian			break;
114294894Sbrian	    }
114394894Sbrian	    if ((sp = Strrchr(p2, '/')) != NULL)
114494894Sbrian		*sp = '\0';
114594894Sbrian	}
114694894Sbrian	/*
114794894Sbrian	 * See if we found it
114894894Sbrian	 */
114994894Sbrian	if (*p2 && found) {
115094894Sbrian	    /*
115198244Sbrian	     * Use STRhome to make '~' work
115298244Sbrian	     */
115394894Sbrian	    newcp = Strspl(p1, cp + Strlen(p2));
115494894Sbrian	    xfree((ptr_t) cp);
115594894Sbrian	    cp = newcp;
115694894Sbrian	}
115794894Sbrian    }
115894894Sbrian#endif /* S_IFLNK */
115994894Sbrian
116094894Sbrian#ifdef apollo
116194894Sbrian    if (slashslash) {
1162134789Sbrian	if (cp[1] != '/') {
116394894Sbrian	    p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1164134789Sbrian	    *p = '/';
116594894Sbrian	    (void) Strcpy(&p[1], cp);
116694894Sbrian	    xfree((ptr_t) cp);
1167134789Sbrian	    cp = p;
116894894Sbrian	}
116994894Sbrian    }
117094894Sbrian    if (cp[1] == '/' && cp[2] == '/')
117194894Sbrian	(void) Strcpy(&cp[1], &cp[2]);
117294894Sbrian#endif /* apollo */
117394894Sbrian    return cp;
117494894Sbrian}
117594894Sbrian
117694894Sbrian
117794894Sbrian/*
117894894Sbrian * dnewcwd - make a new directory in the loop the current one
117994894Sbrian */
118094894Sbrianstatic void
118194894Sbriandnewcwd(dp, dflag)
118294894Sbrian    register struct directory *dp;
118394894Sbrian    int dflag;
118494894Sbrian{
118594894Sbrian    int print;
118694894Sbrian
118794894Sbrian    if (adrof(STRdunique)) {
118894894Sbrian	struct directory *dn;
118994894Sbrian
119094894Sbrian	for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
119194894Sbrian	    if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
119294894Sbrian		dn->di_next->di_prev = dn->di_prev;
119394894Sbrian		dn->di_prev->di_next = dn->di_next;
119494894Sbrian		dfree(dn);
119594894Sbrian		break;
119694894Sbrian	    }
119794894Sbrian    }
119894894Sbrian    dcwd = dp;
119994894Sbrian    dset(dcwd->di_name);
120094894Sbrian    dgetstack();
120194894Sbrian    print = printd;		/* if printd is set, print dirstack... */
120294894Sbrian    if (adrof(STRpushdsilent))	/* but pushdsilent overrides printd... */
120394894Sbrian	print = 0;
120494894Sbrian    if (dflag & DIR_PRINT)	/* but DIR_PRINT overrides pushdsilent... */
120594894Sbrian	print = 1;
120694894Sbrian    if (bequiet)		/* and bequiet overrides everything */
120794894Sbrian	print = 0;
120894894Sbrian    if (print)
120994894Sbrian	printdirs(dflag);
121094894Sbrian    cwd_cmd();			/* PWP: run the defined cwd command */
121194894Sbrian}
121294894Sbrian
121394894Sbrianvoid
1214dsetstack()
1215{
1216    Char **cp;
1217    struct varent *vp;
1218    struct directory *dn, *dp;
1219
1220    if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
1221	return;
1222
1223    /* Free the whole stack */
1224    while ((dn = dhead.di_prev) != &dhead) {
1225	dn->di_next->di_prev = dn->di_prev;
1226	dn->di_prev->di_next = dn->di_next;
1227	if (dn != dcwd)
1228	    dfree(dn);
1229    }
1230
1231    /* thread the current working directory */
1232    dhead.di_prev = dhead.di_next = dcwd;
1233    dcwd->di_next = dcwd->di_prev = &dhead;
1234
1235    /* put back the stack */
1236    for (cp = vp->vec; cp && *cp && **cp; cp++) {
1237	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1238	dp->di_name = Strsave(*cp);
1239	dp->di_count = 0;
1240	dp->di_prev = dcwd;
1241	dp->di_next = dcwd->di_next;
1242	dcwd->di_next = dp;
1243	dp->di_next->di_prev = dp;
1244    }
1245    dgetstack();	/* Make $dirstack reflect the current state */
1246}
1247
1248static void
1249dgetstack()
1250{
1251    int i = 0;
1252    Char **dblk, **dbp;
1253    struct directory *dn;
1254
1255    if (adrof(STRdirstack) == NULL)
1256    	return;
1257
1258    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
1259	continue;
1260    dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1261    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
1262	 *dbp = Strsave(dn->di_name);
1263    *dbp = NULL;
1264    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1265}
1266
1267/*
1268 * getstakd - added by kfk 17 Jan 1984
1269 * Support routine for the stack hack.  Finds nth directory in
1270 * the directory stack, or finds last directory in stack.
1271 */
1272int
1273getstakd(s, cnt)
1274    Char   *s;
1275    int     cnt;
1276{
1277    struct directory *dp;
1278
1279    dp = dcwd;
1280    if (cnt < 0) {		/* < 0 ==> last dir requested. */
1281	dp = dp->di_next;
1282	if (dp == &dhead)
1283	    dp = dp->di_next;
1284    }
1285    else {
1286	while (cnt-- > 0) {
1287	    dp = dp->di_prev;
1288	    if (dp == &dhead)
1289		dp = dp->di_prev;
1290	    if (dp == dcwd)
1291		return (0);
1292	}
1293    }
1294    (void) Strncpy(s, dp->di_name, BUFSIZE);
1295    s[BUFSIZE - 1] = '\0';
1296    return (1);
1297}
1298
1299/*
1300 * Karl Kleinpaste - 10 Feb 1984
1301 * Added dextract(), which is used in pushd +n.
1302 * Instead of just rotating the entire stack around, dextract()
1303 * lets the user have the nth dir extracted from its current
1304 * position, and pushes it onto the top.
1305 */
1306static void
1307dextract(dp)
1308    struct directory *dp;
1309{
1310    if (dp == dcwd)
1311	return;
1312    dp->di_next->di_prev = dp->di_prev;
1313    dp->di_prev->di_next = dp->di_next;
1314    dp->di_next = dcwd->di_next;
1315    dp->di_prev = dcwd;
1316    dp->di_next->di_prev = dp;
1317    dcwd->di_next = dp;
1318}
1319
1320void
1321loaddirs(fname)
1322    Char *fname;
1323{
1324    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1325
1326    bequiet = 1;
1327    if (fname)
1328	loaddirs_cmd[1] = fname;
1329    else if ((fname = varval(STRdirsfile)) != STRNULL)
1330	loaddirs_cmd[1] = fname;
1331    else
1332	loaddirs_cmd[1] = STRtildotdirs;
1333    dosource(loaddirs_cmd, (struct command *)0);
1334    bequiet = 0;
1335}
1336
1337/*
1338 * create a file called ~/.cshdirs which has a sequence
1339 * of pushd commands which will restore the dir stack to
1340 * its state before exit/logout. remember that the order
1341 * is reversed in the file because we are pushing.
1342 * -strike
1343 */
1344void
1345recdirs(fname, def)
1346    Char *fname;
1347    int def;
1348{
1349    int     fp, ftmp, oldidfds;
1350    int     cdflag = 0;
1351    extern struct directory *dcwd;
1352    struct directory *dp;
1353    unsigned int    num;
1354    Char   *snum;
1355    Char    qname[MAXPATHLEN*2];
1356
1357    if (fname == NULL && !def)
1358	return;
1359
1360    if (fname == NULL) {
1361	if ((fname = varval(STRdirsfile)) == STRNULL)
1362	    fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1363	else
1364	    fname = Strsave(fname);
1365    }
1366    else
1367	fname = globone(fname, G_ERROR);
1368
1369    if ((fp = creat(short2str(fname), 0600)) == -1) {
1370	xfree((ptr_t) fname);
1371	return;
1372    }
1373
1374    if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
1375	num = (unsigned int) ~0;
1376    else
1377	num = (unsigned int) atoi(short2str(snum));
1378
1379    oldidfds = didfds;
1380    didfds = 0;
1381    ftmp = SHOUT;
1382    SHOUT = fp;
1383
1384    dp = dcwd->di_next;
1385    do {
1386	if (dp == &dhead)
1387	    continue;
1388
1389	if (cdflag == 0) {
1390	    cdflag = 1;
1391	    xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1392	}
1393	else
1394	    xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1395
1396	if (num-- == 0)
1397	    break;
1398
1399    } while ((dp = dp->di_next) != dcwd->di_next);
1400
1401    (void) close(fp);
1402    SHOUT = ftmp;
1403    didfds = oldidfds;
1404    xfree((ptr_t) fname);
1405}
1406