sh.dir.c revision 131962
1131962Smp/* $Header: /src/pub/tcsh/sh.dir.c,v 3.63 2004/05/10 19:12:37 christos Exp $ */
259243Sobrien/*
359243Sobrien * sh.dir.c: Directory manipulation functions
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35131962SmpRCSID("$Id: sh.dir.c,v 3.63 2004/05/10 19:12:37 christos Exp $")
3659243Sobrien
3759243Sobrien/*
3859243Sobrien * C Shell - directory management
3959243Sobrien */
4059243Sobrien
4159243Sobrienstatic	void			 dstart		__P((const char *));
4259243Sobrienstatic	struct directory	*dfind		__P((Char *));
4359243Sobrienstatic	Char 			*dfollow	__P((Char *));
4459243Sobrienstatic	void 	 	 	 printdirs	__P((int));
4559243Sobrienstatic	Char 			*dgoto		__P((Char *));
4659243Sobrienstatic	void 	 	 	 dnewcwd	__P((struct directory *, int));
4759243Sobrienstatic	void 	 	 	 dset		__P((Char *));
4859243Sobrienstatic  void 			 dextract	__P((struct directory *));
4959243Sobrienstatic  int 			 skipargs	__P((Char ***, char *, char *));
5059243Sobrienstatic	void			 dgetstack	__P((void));
5159243Sobrien
5259243Sobrienstatic struct directory dhead INIT_ZERO_STRUCT;		/* "head" of loop */
5359243Sobrienstatic int    printd;			/* force name to be printed */
5459243Sobrien
5559243Sobrienint     bequiet = 0;		/* do not print dir stack -strike */
5659243Sobrien
5759243Sobrienstatic void
5859243Sobriendstart(from)
5959243Sobrien    const char *from;
6059243Sobrien{
6159243Sobrien    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
6259243Sobrien}
6359243Sobrien
6459243Sobrien/*
6559243Sobrien * dinit - initialize current working directory
6659243Sobrien */
6759243Sobrienvoid
6859243Sobriendinit(hp)
6959243Sobrien    Char   *hp;
7059243Sobrien{
7159243Sobrien    register char *tcp;
7259243Sobrien    register Char *cp;
7359243Sobrien    register struct directory *dp;
7459243Sobrien    char    path[MAXPATHLEN];
7559243Sobrien
7659243Sobrien    /* Don't believe the login shell home, because it may be a symlink */
7759243Sobrien    tcp = (char *) getcwd(path, sizeof(path));
7859243Sobrien    if (tcp == NULL || *tcp == '\0') {
7959243Sobrien	xprintf("%s: %s\n", progname, strerror(errno));
8059243Sobrien	if (hp && *hp) {
8159243Sobrien	    tcp = short2str(hp);
8259243Sobrien	    dstart(tcp);
8359243Sobrien	    if (chdir(tcp) == -1)
8459243Sobrien		cp = NULL;
8559243Sobrien	    else
8659243Sobrien		cp = Strsave(hp);
8759243Sobrien	}
8859243Sobrien	else
8959243Sobrien	    cp = NULL;
9059243Sobrien	if (cp == NULL) {
9159243Sobrien	    dstart("/");
9259243Sobrien	    if (chdir("/") == -1)
9359243Sobrien		/* I am not even try to print an error message! */
9459243Sobrien		xexit(1);
9559243Sobrien	    cp = SAVE("/");
9659243Sobrien	}
9759243Sobrien    }
9859243Sobrien    else {
9959243Sobrien#ifdef S_IFLNK
10059243Sobrien	struct stat swd, shp;
10159243Sobrien
10259243Sobrien	/*
10359243Sobrien	 * See if $HOME is the working directory we got and use that
10459243Sobrien	 */
10559243Sobrien	if (hp && *hp &&
10659243Sobrien	    stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
10759243Sobrien	    DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
10859243Sobrien		swd.st_ino == shp.st_ino)
10959243Sobrien	    cp = Strsave(hp);
11059243Sobrien	else {
11159243Sobrien	    char   *cwd;
11259243Sobrien
11359243Sobrien	    /*
11459243Sobrien	     * use PWD if we have it (for subshells)
11559243Sobrien	     */
11659243Sobrien	    if ((cwd = getenv("PWD")) != NULL) {
11759243Sobrien		if (stat(cwd, &shp) != -1 &&
11859243Sobrien			DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
11959243Sobrien		    swd.st_ino == shp.st_ino)
12059243Sobrien		    tcp = cwd;
12159243Sobrien	    }
12259243Sobrien	    cp = dcanon(SAVE(tcp), STRNULL);
12359243Sobrien	}
12459243Sobrien#else /* S_IFLNK */
12559243Sobrien	cp = dcanon(SAVE(tcp), STRNULL);
12659243Sobrien#endif /* S_IFLNK */
12759243Sobrien    }
12859243Sobrien
12959243Sobrien    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
13059243Sobrien    dp->di_name = cp;
13159243Sobrien    dp->di_count = 0;
13259243Sobrien    dhead.di_next = dhead.di_prev = dp;
13359243Sobrien    dp->di_next = dp->di_prev = &dhead;
13459243Sobrien    printd = 0;
13559243Sobrien    dnewcwd(dp, 0);
13659243Sobrien    set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
13759243Sobrien}
13859243Sobrien
13959243Sobrienstatic void
14059243Sobriendset(dp)
14159243SobrienChar *dp;
14259243Sobrien{
14359243Sobrien    /*
14459243Sobrien     * Don't call set() directly cause if the directory contains ` or
14559243Sobrien     * other junk characters glob will fail.
14659243Sobrien     */
14759243Sobrien    set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
14859243Sobrien    set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
14959243Sobrien
15059243Sobrien    tsetenv(STRPWD, dp);
15159243Sobrien}
15259243Sobrien
15359243Sobrien#define DIR_PRINT	0x01	/* -p */
15459243Sobrien#define DIR_LONG  	0x02	/* -l */
15559243Sobrien#define DIR_VERT  	0x04	/* -v */
15659243Sobrien#define DIR_LINE  	0x08	/* -n */
15759243Sobrien#define DIR_SAVE 	0x10	/* -S */
15859243Sobrien#define DIR_LOAD	0x20	/* -L */
15959243Sobrien#define DIR_CLEAR	0x40	/* -c */
16059243Sobrien#define DIR_OLD	  	0x80	/* - */
16159243Sobrien
16259243Sobrienstatic int
16359243Sobrienskipargs(v, dstr, str)
16459243Sobrien    Char ***v;
16559243Sobrien    char   *dstr;
16659243Sobrien    char   *str;
16759243Sobrien{
16859243Sobrien    Char  **n = *v, *s;
16959243Sobrien
17059243Sobrien    int dflag = 0, loop = 1;
17159243Sobrien    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
17259243Sobrien	if (*(s = &((*n)[1])) == '\0')	/* test for bare "-" argument */
17359243Sobrien	    dflag |= DIR_OLD;
17459243Sobrien	else {
17559243Sobrien	    char *p;
17659243Sobrien	    while (loop && *s != '\0')	/* examine flags */
17759243Sobrien	    {
17859243Sobrien		if ((p = strchr(dstr, *s++)) != NULL)
17959243Sobrien		    dflag |= (1 << (p - dstr));
18059243Sobrien	        else {
18159243Sobrien		    stderror(ERR_DIRUS, short2str(**v), dstr, str);
18259243Sobrien		    loop = 0;	/* break from both loops */
18359243Sobrien		    break;
18459243Sobrien	        }
18559243Sobrien	    }
18659243Sobrien	}
18759243Sobrien    if (*n && (dflag & DIR_OLD))
18859243Sobrien	stderror(ERR_DIRUS, short2str(**v), dstr, str);
18959243Sobrien    *v = n;
19059243Sobrien    /* make -l, -v, and -n imply -p */
19159243Sobrien    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
19259243Sobrien	dflag |= DIR_PRINT;
19359243Sobrien    return dflag;
19459243Sobrien}
19559243Sobrien
19659243Sobrien/*
19759243Sobrien * dodirs - list all directories in directory loop
19859243Sobrien */
19959243Sobrien/*ARGSUSED*/
20059243Sobrienvoid
20159243Sobriendodirs(v, c)
20259243Sobrien    Char  **v;
20359243Sobrien    struct command *c;
20459243Sobrien{
20559243Sobrien    static char flags[] = "plvnSLc";
20659243Sobrien    int dflag = skipargs(&v, flags, "");
20759243Sobrien
20859243Sobrien    USE(c);
20959243Sobrien    if ((dflag & DIR_CLEAR) != 0) {
21059243Sobrien	struct directory *dp, *fdp;
21159243Sobrien	for (dp = dcwd->di_next; dp != dcwd; ) {
21259243Sobrien	    fdp = dp;
21359243Sobrien	    dp = dp->di_next;
21459243Sobrien	    if (fdp != &dhead)
21559243Sobrien		dfree(fdp);
21659243Sobrien	}
21759243Sobrien	dhead.di_next = dhead.di_prev = dp;
21859243Sobrien	dp->di_next = dp->di_prev = &dhead;
21959243Sobrien    }
22059243Sobrien    if ((dflag & DIR_LOAD) != 0)
22159243Sobrien	loaddirs(*v);
22259243Sobrien    else if ((dflag & DIR_SAVE) != 0)
22359243Sobrien	recdirs(*v, 1);
22459243Sobrien
22559243Sobrien    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
22659243Sobrien	v++;
22759243Sobrien
22859243Sobrien    if (*v != NULL || (dflag & DIR_OLD))
22959243Sobrien	stderror(ERR_DIRUS, "dirs", flags, "");
23059243Sobrien    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
23159243Sobrien	printdirs(dflag);
23259243Sobrien}
23359243Sobrien
23459243Sobrienstatic void
23559243Sobrienprintdirs(dflag)
23659243Sobrien    int dflag;
23759243Sobrien{
23859243Sobrien    register struct directory *dp;
23959243Sobrien    Char   *s, *user;
24059243Sobrien    int     idx, len, cur;
24159243Sobrien    extern int T_Cols;
24259243Sobrien
24359243Sobrien    dp = dcwd;
24459243Sobrien    idx = 0;
24559243Sobrien    cur = 0;
24659243Sobrien    do {
24759243Sobrien	if (dp == &dhead)
24859243Sobrien	    continue;
24959243Sobrien	if (dflag & DIR_VERT) {
25059243Sobrien	    xprintf("%d\t", idx++);
25159243Sobrien	    cur = 0;
25259243Sobrien	}
25359243Sobrien	s = dp->di_name;
25459243Sobrien	user = NULL;
25559243Sobrien	if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
25659243Sobrien	    len = (int) (Strlen(user) + Strlen(s) + 2);
25759243Sobrien	else
25859243Sobrien	    len = (int) (Strlen(s) + 1);
25959243Sobrien
26059243Sobrien	cur += len;
26159243Sobrien	if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
26259243Sobrien	    xputchar('\n');
26359243Sobrien	    cur = len;
26459243Sobrien	}
26559243Sobrien	if (user)
26659243Sobrien	    xprintf("~%S", user);
267131962Smp	xprintf("\045S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
26859243Sobrien    } while ((dp = dp->di_prev) != dcwd);
26959243Sobrien    if (!(dflag & DIR_VERT))
27059243Sobrien	xputchar('\n');
27159243Sobrien}
27259243Sobrien
27359243Sobrienvoid
27459243Sobriendtildepr(dir)
27559243Sobrien    Char *dir;
27659243Sobrien{
27759243Sobrien    Char* user;
27859243Sobrien    if ((user = getusername(&dir)) != NULL)
279131962Smp	xprintf("~\045S%S", user, dir);
28059243Sobrien    else
28159243Sobrien	xprintf("%S", dir);
28259243Sobrien}
28359243Sobrien
28459243Sobrienvoid
28559243Sobriendtilde()
28659243Sobrien{
28759243Sobrien    struct directory *d = dcwd;
28859243Sobrien
28959243Sobrien    do {
29059243Sobrien	if (d == &dhead)
29159243Sobrien	    continue;
29259243Sobrien	d->di_name = dcanon(d->di_name, STRNULL);
29359243Sobrien    } while ((d = d->di_prev) != dcwd);
29459243Sobrien
29559243Sobrien    dset(dcwd->di_name);
29659243Sobrien}
29759243Sobrien
29859243Sobrien
29959243Sobrien/* dnormalize():
30059243Sobrien *	The path will be normalized if it
30159243Sobrien *	1) is "..",
30259243Sobrien *	2) or starts with "../",
30359243Sobrien *	3) or ends with "/..",
30459243Sobrien *	4) or contains the string "/../",
30559243Sobrien *	then it will be normalized, unless those strings are quoted.
30659243Sobrien *	Otherwise, a copy is made and sent back.
30759243Sobrien */
30859243SobrienChar   *
30959243Sobriendnormalize(cp, exp)
31059243Sobrien    Char   *cp;
31159243Sobrien    int exp;
31259243Sobrien{
31359243Sobrien
31459243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */
31559243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
31659243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
31759243Sobrien
31859243Sobrien#ifdef S_IFLNK
31959243Sobrien    if (exp) {
32059243Sobrien 	int     dotdot = 0;
32159243Sobrien	Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
322100616Smp	struct stat sb;
32359243Sobrien# ifdef apollo
32459243Sobrien	bool slashslash;
32559243Sobrien# endif /* apollo */
32659243Sobrien
32759243Sobrien	/*
32859243Sobrien	 * count the number of "../xxx" or "xxx/../xxx" in the path
32959243Sobrien	 */
33059243Sobrien	for (dp=start; *dp && *(dp+1); dp++)
33159243Sobrien	    if (IS_DOTDOT(start, dp))
33259243Sobrien	        dotdot++;
33359243Sobrien	/*
33459243Sobrien	 * if none, we are done.
33559243Sobrien	 */
33659243Sobrien        if (dotdot == 0)
33759243Sobrien	    return (Strsave(cp));
33859243Sobrien
339100616Smp	/*
340100616Smp	 * If the path doesn't exist, we are done too.
341100616Smp	 */
342100616Smp	if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT)
343100616Smp	    return (Strsave(cp));
344100616Smp
345100616Smp
34659243Sobrien	cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
34759243Sobrien					   sizeof(Char)));
34859243Sobrien	(void) Strcpy(cwd, dcwd->di_name);
34959243Sobrien
35059243Sobrien	/*
35159243Sobrien	 * If the path starts with a slash, we are not relative to
35259243Sobrien	 * the current working directory.
35359243Sobrien	 */
35459243Sobrien	if (ABSOLUTEP(start))
35559243Sobrien	    *cwd = '\0';
35659243Sobrien# ifdef apollo
35759243Sobrien	slashslash = cwd[0] == '/' && cwd[1] == '/';
35859243Sobrien# endif /* apollo */
35959243Sobrien
36059243Sobrien	/*
36159243Sobrien	 * Ignore . and count ..'s
36259243Sobrien	 */
36359243Sobrien	for (;;) {
36459243Sobrien	    dotdot = 0;
36559243Sobrien	    buf[0] = '\0';
36659243Sobrien	    dp = buf;
36759243Sobrien	    while (*cp)
36859243Sobrien	        if (IS_DOT(start, cp)) {
36959243Sobrien	            if (*++cp)
37059243Sobrien	                cp++;
37159243Sobrien	        }
37259243Sobrien	        else if (IS_DOTDOT(start, cp)) {
37359243Sobrien		    if (buf[0])
37459243Sobrien		        break; /* finish analyzing .././../xxx/[..] */
37559243Sobrien		    dotdot++;
37659243Sobrien		    cp += 2;
37759243Sobrien		    if (*cp)
37859243Sobrien		        cp++;
37959243Sobrien	        }
38059243Sobrien	        else
38159243Sobrien			*dp++ = *cp++;
38259243Sobrien
38359243Sobrien	    *dp = '\0';
38459243Sobrien	    while (dotdot > 0)
38559243Sobrien	        if ((dp = Strrchr(cwd, '/')) != NULL) {
38659243Sobrien# ifdef apollo
38759243Sobrien		    if (dp == &cwd[1])
38859243Sobrien		        slashslash = 1;
38959243Sobrien# endif /* apollo */
39059243Sobrien		        *dp = '\0';
39159243Sobrien		        dotdot--;
39259243Sobrien	        }
39359243Sobrien	        else
39459243Sobrien		    break;
39559243Sobrien
39659243Sobrien	    if (!*cwd) {	/* too many ..'s, starts with "/" */
39759243Sobrien	        cwd[0] = '/';
39859243Sobrien# ifdef apollo
39959243Sobrien		cwd[1] = '/';
40059243Sobrien		cwd[2] = '\0';
40159243Sobrien# else /* !apollo */
40259243Sobrien		cwd[1] = '\0';
40359243Sobrien# endif /* apollo */
40459243Sobrien	    }
40559243Sobrien# ifdef apollo
40659243Sobrien	    else if (slashslash && cwd[1] == '\0') {
40759243Sobrien		cwd[1] = '/';
40859243Sobrien		cwd[2] = '\0';
40959243Sobrien	    }
41059243Sobrien# endif /* apollo */
41159243Sobrien
41259243Sobrien	    if (buf[0]) {
41359243Sobrien	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
41459243Sobrien		    cwd[dotdot++] = '/';
41559243Sobrien	        cwd[dotdot] = '\0';
41659243Sobrien	        dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
41759243Sobrien	        xfree((ptr_t) cwd);
41859243Sobrien	        cwd = dp;
41959243Sobrien	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
42059243Sobrien		    cwd[--dotdot] = '\0';
42159243Sobrien	    }
422100616Smp	    /* Reduction of ".." following the stuff we collected in buf
423100616Smp	     * only makes sense if the directory item in buf really exists.
424100616Smp	     * Avoid reduction of "-I../.." (typical compiler call) to ""
425100616Smp	     * or "/usr/nonexistant/../bin" to "/usr/bin":
426100616Smp	     */
427100616Smp	    if (cwd[0]) {
428100616Smp	        struct stat exists;
429100616Smp		if (0 != stat(short2str(cwd), &exists)) {
430100616Smp		    xfree((ptr_t) cwd);
431100616Smp		    return Strsave(start);
432100616Smp		}
433100616Smp	    }
43459243Sobrien	    if (!*cp)
43559243Sobrien	        break;
43659243Sobrien	}
43759243Sobrien	return cwd;
43859243Sobrien    }
43959243Sobrien#endif /* S_IFLNK */
44059243Sobrien    return Strsave(cp);
44159243Sobrien}
44259243Sobrien
44359243Sobrien
44459243Sobrien/*
44559243Sobrien * dochngd - implement chdir command.
44659243Sobrien */
44759243Sobrien/*ARGSUSED*/
44859243Sobrienvoid
44959243Sobriendochngd(v, c)
45059243Sobrien    Char  **v;
45159243Sobrien    struct command *c;
45259243Sobrien{
45359243Sobrien    register Char *cp;
45459243Sobrien    register struct directory *dp;
45559243Sobrien    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
45659243Sobrien
45759243Sobrien    USE(c);
45859243Sobrien    printd = 0;
45959243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
46059243Sobrien
46159243Sobrien    if (cp == NULL) {
46259243Sobrien	if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
46359243Sobrien	    stderror(ERR_NAME | ERR_NOHOMEDIR);
46459243Sobrien	if (chdir(short2str(cp)) < 0)
46559243Sobrien	    stderror(ERR_NAME | ERR_CANTCHANGE);
46659243Sobrien	cp = Strsave(cp);
46759243Sobrien    }
46859243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
46959243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
47059243Sobrien	/* NOTREACHED */
47159243Sobrien	return;
47259243Sobrien    }
47359243Sobrien    else if ((dp = dfind(cp)) != 0) {
47459243Sobrien	char   *tmp;
47559243Sobrien
47659243Sobrien	printd = 1;
47759243Sobrien	if (chdir(tmp = short2str(dp->di_name)) < 0)
47859243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
47959243Sobrien	dcwd->di_prev->di_next = dcwd->di_next;
48059243Sobrien	dcwd->di_next->di_prev = dcwd->di_prev;
48159243Sobrien	dfree(dcwd);
48259243Sobrien	dnewcwd(dp, dflag);
48359243Sobrien	return;
48459243Sobrien    }
48559243Sobrien    else
48659243Sobrien	if ((cp = dfollow(cp)) == NULL)
48759243Sobrien	    return;
48859243Sobrien    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
48959243Sobrien    dp->di_name = cp;
49059243Sobrien    dp->di_count = 0;
49159243Sobrien    dp->di_next = dcwd->di_next;
49259243Sobrien    dp->di_prev = dcwd->di_prev;
49359243Sobrien    dp->di_prev->di_next = dp;
49459243Sobrien    dp->di_next->di_prev = dp;
49559243Sobrien    dfree(dcwd);
49659243Sobrien    dnewcwd(dp, dflag);
49759243Sobrien}
49859243Sobrien
49959243Sobrienstatic Char *
50059243Sobriendgoto(cp)
50159243Sobrien    Char   *cp;
50259243Sobrien{
50359243Sobrien    Char   *dp;
50459243Sobrien
50559243Sobrien    if (!ABSOLUTEP(cp))
50659243Sobrien    {
50759243Sobrien	register Char *p, *q;
50859243Sobrien	int     cwdlen;
50959243Sobrien
51059243Sobrien	for (p = dcwd->di_name; *p++;)
51159243Sobrien	    continue;
51259243Sobrien	if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1)	/* root */
51359243Sobrien	    cwdlen = 0;
51459243Sobrien	for (p = cp; *p++;)
51559243Sobrien	    continue;
51659243Sobrien	dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
51759243Sobrien	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
51859243Sobrien	    continue;
51959243Sobrien	if (cwdlen)
52059243Sobrien	    p[-1] = '/';
52159243Sobrien	else
52259243Sobrien	    p--;		/* don't add a / after root */
52359243Sobrien	for (q = cp; (*p++ = *q++) != '\0';)
52459243Sobrien	    continue;
52559243Sobrien	xfree((ptr_t) cp);
52659243Sobrien	cp = dp;
52759243Sobrien	dp += cwdlen;
52859243Sobrien    }
52959243Sobrien    else
53059243Sobrien	dp = cp;
53159243Sobrien
532131962Smp#if defined(WINNT_NATIVE)
53359243Sobrien    cp = SAVE(getcwd(NULL, 0));
534131962Smp#elif defined(__CYGWIN__)
535131962Smp    if (ABSOLUTEP(cp) && cp[1] == ':') /* Only DOS paths are treated that way */
536131962Smp    	cp = SAVE(getcwd(NULL, 0));
537131962Smp    else
538131962Smp    	cp = dcanon(cp, dp);
53969408Sache#else /* !WINNT_NATIVE */
54059243Sobrien    cp = dcanon(cp, dp);
54169408Sache#endif /* WINNT_NATIVE */
54259243Sobrien    return cp;
54359243Sobrien}
54459243Sobrien
54559243Sobrien/*
54659243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid
54759243Sobrien */
54859243Sobrienstatic Char *
54959243Sobriendfollow(cp)
55059243Sobrien    register Char *cp;
55159243Sobrien{
55259243Sobrien    register Char *dp;
55359243Sobrien    struct varent *c;
55459243Sobrien    char    ebuf[MAXPATHLEN];
55559243Sobrien    int serrno;
55659243Sobrien
55759243Sobrien    cp = globone(cp, G_ERROR);
55859243Sobrien#ifdef apollo
55959243Sobrien    if (Strchr(cp, '`')) {
56059243Sobrien	char *dptr, *ptr;
56159243Sobrien	if (chdir(dptr = short2str(cp)) < 0)
56259243Sobrien	    stderror(ERR_SYSTEM, dptr, strerror(errno));
56359243Sobrien	else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
56459243Sobrien		xfree((ptr_t) cp);
56559243Sobrien		cp = Strsave(str2short(ptr));
56659243Sobrien		return dgoto(cp);
56759243Sobrien	}
56859243Sobrien	else
56959243Sobrien	    stderror(ERR_SYSTEM, dptr, ebuf);
57059243Sobrien    }
57159243Sobrien#endif /* apollo */
57259243Sobrien
57359243Sobrien    (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
57459243Sobrien    ebuf[MAXPATHLEN-1] = '\0';
57559243Sobrien    /*
57659243Sobrien     * if we are ignoring symlinks, try to fix relatives now.
57759243Sobrien     * if we are expading symlinks, it should be done by now.
57859243Sobrien     */
57959243Sobrien    dp = dnormalize(cp, symlinks == SYM_IGNORE);
58059243Sobrien    if (chdir(short2str(dp)) >= 0) {
58159243Sobrien        xfree((ptr_t) cp);
58259243Sobrien        return dgoto(dp);
58359243Sobrien    }
58459243Sobrien    else {
58559243Sobrien        xfree((ptr_t) dp);
58659243Sobrien        if (chdir(short2str(cp)) >= 0)
58759243Sobrien	    return dgoto(cp);
58859243Sobrien	else if (errno != ENOENT && errno != ENOTDIR)
58959243Sobrien	    stderror(ERR_SYSTEM, ebuf, strerror(errno));
59059243Sobrien	serrno = errno;
59159243Sobrien    }
59259243Sobrien
59359243Sobrien    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
594100616Smp	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
59559243Sobrien	Char  **cdp;
59659243Sobrien	register Char *p;
59759243Sobrien	Char    buf[MAXPATHLEN];
59859243Sobrien
59959243Sobrien	for (cdp = c->vec; *cdp; cdp++) {
60059243Sobrien	    for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
60159243Sobrien		continue;
60259243Sobrien	    dp[-1] = '/';
60359243Sobrien	    for (p = cp; (*dp++ = *p++) != '\0';)
60459243Sobrien		continue;
60559243Sobrien	    /*
60659243Sobrien	     * We always want to fix the directory here
60759243Sobrien	     * If we are normalizing symlinks
60859243Sobrien	     */
60959243Sobrien	    dp = dnormalize(buf, symlinks == SYM_IGNORE ||
61059243Sobrien				 symlinks == SYM_EXPAND);
61159243Sobrien	    if (chdir(short2str(dp)) >= 0) {
61259243Sobrien		printd = 1;
61359243Sobrien		xfree((ptr_t) cp);
61459243Sobrien		return dgoto(dp);
61559243Sobrien	    }
61659243Sobrien	    else if (chdir(short2str(cp)) >= 0) {
61759243Sobrien		printd = 1;
61859243Sobrien		xfree((ptr_t) dp);
61959243Sobrien		return dgoto(cp);
62059243Sobrien	    }
62159243Sobrien	}
62259243Sobrien    }
62359243Sobrien    dp = varval(cp);
62459243Sobrien    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
62559243Sobrien	xfree((ptr_t) cp);
62659243Sobrien	cp = Strsave(dp);
62759243Sobrien	printd = 1;
62859243Sobrien	return dgoto(cp);
62959243Sobrien    }
63059243Sobrien    xfree((ptr_t) cp);
63159243Sobrien    /*
63259243Sobrien     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
63359243Sobrien     * directories we could get to.
63459243Sobrien     */
63559243Sobrien    if (!bequiet) {
63659243Sobrien	stderror(ERR_SYSTEM, ebuf, strerror(serrno));
63759243Sobrien	return (NULL);
63859243Sobrien    }
63959243Sobrien    else
64059243Sobrien	return (NULL);
64159243Sobrien}
64259243Sobrien
64359243Sobrien
64459243Sobrien/*
64559243Sobrien * dopushd - push new directory onto directory stack.
64659243Sobrien *	with no arguments exchange top and second.
64759243Sobrien *	with numeric argument (+n) bring it to top.
64859243Sobrien */
64959243Sobrien/*ARGSUSED*/
65059243Sobrienvoid
65159243Sobriendopushd(v, c)
65259243Sobrien    Char  **v;
65359243Sobrien    struct command *c;
65459243Sobrien{
65559243Sobrien    register struct directory *dp;
65659243Sobrien    register Char *cp;
65759243Sobrien    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
65859243Sobrien
65959243Sobrien    USE(c);
66059243Sobrien    printd = 1;
66159243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
66259243Sobrien
66359243Sobrien    if (cp == NULL) {
66459243Sobrien	if (adrof(STRpushdtohome)) {
66559243Sobrien	    if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
66659243Sobrien		stderror(ERR_NAME | ERR_NOHOMEDIR);
66759243Sobrien	    if (chdir(short2str(cp)) < 0)
66859243Sobrien		stderror(ERR_NAME | ERR_CANTCHANGE);
66959243Sobrien	    cp = Strsave(cp);	/* hmmm... PWP */
67059243Sobrien	    if ((cp = dfollow(cp)) == NULL)
67159243Sobrien		return;
67259243Sobrien	    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
67359243Sobrien	    dp->di_name = cp;
67459243Sobrien	    dp->di_count = 0;
67559243Sobrien	    dp->di_prev = dcwd;
67659243Sobrien	    dp->di_next = dcwd->di_next;
67759243Sobrien	    dcwd->di_next = dp;
67859243Sobrien	    dp->di_next->di_prev = dp;
67959243Sobrien	}
68059243Sobrien	else {
68159243Sobrien	    char   *tmp;
68259243Sobrien
68359243Sobrien	    if ((dp = dcwd->di_prev) == &dhead)
68459243Sobrien		dp = dhead.di_prev;
68559243Sobrien	    if (dp == dcwd)
68659243Sobrien		stderror(ERR_NAME | ERR_NODIR);
68759243Sobrien	    if (chdir(tmp = short2str(dp->di_name)) < 0)
68859243Sobrien		stderror(ERR_SYSTEM, tmp, strerror(errno));
68959243Sobrien	    dp->di_prev->di_next = dp->di_next;
69059243Sobrien	    dp->di_next->di_prev = dp->di_prev;
69159243Sobrien	    dp->di_next = dcwd->di_next;
69259243Sobrien	    dp->di_prev = dcwd;
69359243Sobrien	    dcwd->di_next->di_prev = dp;
69459243Sobrien	    dcwd->di_next = dp;
69559243Sobrien	}
69659243Sobrien    }
69759243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
69859243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
69959243Sobrien	/* NOTREACHED */
70059243Sobrien	return;
70159243Sobrien    }
70259243Sobrien    else if ((dp = dfind(cp)) != NULL) {
70359243Sobrien	char   *tmp;
70459243Sobrien
70559243Sobrien	if (chdir(tmp = short2str(dp->di_name)) < 0)
70659243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
70759243Sobrien	/*
70859243Sobrien	 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
70959243Sobrien	 */
71059243Sobrien	if (adrof(STRdextract))
71159243Sobrien	    dextract(dp);
71259243Sobrien    }
71359243Sobrien    else {
71459243Sobrien	register Char *ccp;
71559243Sobrien
71659243Sobrien	if ((ccp = dfollow(cp)) == NULL)
71759243Sobrien	    return;
71859243Sobrien	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
71959243Sobrien	dp->di_name = ccp;
72059243Sobrien	dp->di_count = 0;
72159243Sobrien	dp->di_prev = dcwd;
72259243Sobrien	dp->di_next = dcwd->di_next;
72359243Sobrien	dcwd->di_next = dp;
72459243Sobrien	dp->di_next->di_prev = dp;
72559243Sobrien    }
72659243Sobrien    dnewcwd(dp, dflag);
72759243Sobrien}
72859243Sobrien
72959243Sobrien/*
73059243Sobrien * dfind - find a directory if specified by numeric (+n) argument
73159243Sobrien */
73259243Sobrienstatic struct directory *
73359243Sobriendfind(cp)
73459243Sobrien    register Char *cp;
73559243Sobrien{
73659243Sobrien    register struct directory *dp;
73759243Sobrien    register int i;
73859243Sobrien    register Char *ep;
73959243Sobrien
74059243Sobrien    if (*cp++ != '+')
74159243Sobrien	return (0);
74259243Sobrien    for (ep = cp; Isdigit(*ep); ep++)
74359243Sobrien	continue;
74459243Sobrien    if (*ep)
74559243Sobrien	return (0);
74659243Sobrien    i = getn(cp);
74759243Sobrien    if (i <= 0)
74859243Sobrien	return (0);
74959243Sobrien    for (dp = dcwd; i != 0; i--) {
75059243Sobrien	if ((dp = dp->di_prev) == &dhead)
75159243Sobrien	    dp = dp->di_prev;
75259243Sobrien	if (dp == dcwd)
75359243Sobrien	    stderror(ERR_NAME | ERR_DEEP);
75459243Sobrien    }
75559243Sobrien    return (dp);
75659243Sobrien}
75759243Sobrien
75859243Sobrien/*
75959243Sobrien * dopopd - pop a directory out of the directory stack
76059243Sobrien *	with a numeric argument just discard it.
76159243Sobrien */
76259243Sobrien/*ARGSUSED*/
76359243Sobrienvoid
76459243Sobriendopopd(v, c)
76559243Sobrien    Char  **v;
76659243Sobrien    struct command *c;
76759243Sobrien{
76859243Sobrien    Char *cp;
76959243Sobrien    register struct directory *dp, *p = NULL;
77059243Sobrien    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
77159243Sobrien
77259243Sobrien    USE(c);
77359243Sobrien    printd = 1;
77459243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
77559243Sobrien
77659243Sobrien    if (cp == NULL)
77759243Sobrien	dp = dcwd;
77859243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
77959243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
78059243Sobrien	/* NOTREACHED */
78159243Sobrien	return;
78259243Sobrien    }
78359243Sobrien    else if ((dp = dfind(cp)) == 0)
78459243Sobrien	stderror(ERR_NAME | ERR_BADDIR);
78559243Sobrien    if (dp->di_prev == &dhead && dp->di_next == &dhead)
78659243Sobrien	stderror(ERR_NAME | ERR_EMPTY);
78759243Sobrien    if (dp == dcwd) {
78859243Sobrien	char   *tmp;
78959243Sobrien
79059243Sobrien	if ((p = dp->di_prev) == &dhead)
79159243Sobrien	    p = dhead.di_prev;
79259243Sobrien	if (chdir(tmp = short2str(p->di_name)) < 0)
79359243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
79459243Sobrien    }
79559243Sobrien    dp->di_prev->di_next = dp->di_next;
79659243Sobrien    dp->di_next->di_prev = dp->di_prev;
79759243Sobrien    if (dp == dcwd) {
79859243Sobrien	dnewcwd(p, dflag);
79959243Sobrien    }
80059243Sobrien    else {
80159243Sobrien	printdirs(dflag);
80259243Sobrien    }
80359243Sobrien    dfree(dp);
80459243Sobrien}
80559243Sobrien
80659243Sobrien/*
80759243Sobrien * dfree - free the directory (or keep it if it still has ref count)
80859243Sobrien */
80959243Sobrienvoid
81059243Sobriendfree(dp)
81159243Sobrien    register struct directory *dp;
81259243Sobrien{
81359243Sobrien
81459243Sobrien    if (dp->di_count != 0) {
81559243Sobrien	dp->di_next = dp->di_prev = 0;
81659243Sobrien    }
81759243Sobrien    else {
81859243Sobrien	xfree((ptr_t) dp->di_name);
81959243Sobrien	xfree((ptr_t) dp);
82059243Sobrien    }
82159243Sobrien}
82259243Sobrien
82359243Sobrien/*
82459243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
82559243Sobrien *	we are of course assuming that the file system is standardly
82659243Sobrien *	constructed (always have ..'s, directories have links)
82759243Sobrien */
82859243SobrienChar   *
82959243Sobriendcanon(cp, p)
83059243Sobrien    register Char *cp, *p;
83159243Sobrien{
83259243Sobrien    register Char *sp;
83359243Sobrien    register Char *p1, *p2;	/* general purpose */
83459243Sobrien    bool    slash;
83559243Sobrien#ifdef apollo
83659243Sobrien    bool    slashslash;
83759243Sobrien#endif /* apollo */
83873393Skris    size_t  clen;
83959243Sobrien
84059243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
84159243Sobrien    Char    link[MAXPATHLEN];
84259243Sobrien    char    tlink[MAXPATHLEN];
84359243Sobrien    int     cc;
84459243Sobrien    Char   *newcp;
84559243Sobrien#endif /* S_IFLNK */
84659243Sobrien
84759243Sobrien    /*
84873393Skris     * if the path given is too long truncate it!
84959243Sobrien     */
85073393Skris    if ((clen = Strlen(cp)) >= MAXPATHLEN)
85173393Skris	cp[clen = MAXPATHLEN - 1] = '\0';
85259243Sobrien
85359243Sobrien    /*
85459243Sobrien     * christos: if the path given does not start with a slash prepend cwd. If
85573393Skris     * cwd does not start with a slash or the result would be too long try to
85673393Skris     * correct it.
85759243Sobrien     */
85859243Sobrien    if (!ABSOLUTEP(cp)) {
85959243Sobrien	Char    tmpdir[MAXPATHLEN];
86073393Skris	size_t	len;
86159243Sobrien
86259243Sobrien	p1 = varval(STRcwd);
86373393Skris	if (p1 == STRNULL || !ABSOLUTEP(p1)) {
86473393Skris	    char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
86573393Skris	    if (tmp == NULL || *tmp == '\0') {
86673393Skris		xprintf("%s: %s\n", progname, strerror(errno));
86773393Skris		set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
86873393Skris	    } else {
86973393Skris		set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
87073393Skris	    }
87173393Skris	    p1 = varval(STRcwd);
87273393Skris	}
87373393Skris	len = Strlen(p1);
87473393Skris	if (len + clen + 1 >= MAXPATHLEN)
87573393Skris	    cp[MAXPATHLEN - (len + 1)] = '\0';
87659243Sobrien	(void) Strcpy(tmpdir, p1);
87759243Sobrien	(void) Strcat(tmpdir, STRslash);
87859243Sobrien	(void) Strcat(tmpdir, cp);
87959243Sobrien	xfree((ptr_t) cp);
88059243Sobrien	cp = p = Strsave(tmpdir);
88159243Sobrien    }
88259243Sobrien
88359243Sobrien#ifdef apollo
88459243Sobrien    slashslash = (cp[0] == '/' && cp[1] == '/');
88559243Sobrien#endif /* apollo */
88659243Sobrien
88759243Sobrien    while (*p) {		/* for each component */
88859243Sobrien	sp = p;			/* save slash address */
88959243Sobrien	while (*++p == '/')	/* flush extra slashes */
89059243Sobrien	    continue;
89159243Sobrien	if (p != ++sp)
89259243Sobrien	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
89359243Sobrien		continue;
89459243Sobrien	p = sp;			/* save start of component */
89559243Sobrien	slash = 0;
89659243Sobrien	if (*p)
89759243Sobrien	    while (*++p)	/* find next slash or end of path */
89859243Sobrien		if (*p == '/') {
89959243Sobrien		    slash = 1;
90059243Sobrien		    *p = 0;
90159243Sobrien		    break;
90259243Sobrien		}
90359243Sobrien
90459243Sobrien#ifdef apollo
90559243Sobrien	if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
90659243Sobrien	    slashslash = 1;
90759243Sobrien#endif /* apollo */
90859243Sobrien	if (*sp == '\0') {	/* if component is null */
90959243Sobrien	    if (--sp == cp)	/* if path is one char (i.e. /) */
91059243Sobrien		break;
91159243Sobrien	    else
91259243Sobrien		*sp = '\0';
91359243Sobrien	}
91459243Sobrien	else if (sp[0] == '.' && sp[1] == 0) {
91559243Sobrien	    if (slash) {
91659243Sobrien		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
91759243Sobrien		    continue;
91859243Sobrien		p = --sp;
91959243Sobrien	    }
92059243Sobrien	    else if (--sp != cp)
92159243Sobrien		*sp = '\0';
92259243Sobrien	    else
92359243Sobrien		sp[1] = '\0';
92459243Sobrien	}
92559243Sobrien	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
92659243Sobrien	    /*
92759243Sobrien	     * We have something like "yyy/xxx/..", where "yyy" can be null or
92859243Sobrien	     * a path starting at /, and "xxx" is a single component. Before
92959243Sobrien	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
93059243Sobrien	     * symbolic link.
93159243Sobrien	     */
93259243Sobrien	    *--sp = 0;		/* form the pathname for readlink */
93359243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
93459243Sobrien	    if (sp != cp && /* symlinks != SYM_IGNORE && */
93559243Sobrien		(cc = readlink(short2str(cp), tlink,
936131962Smp			       sizeof(tlink) - 1)) >= 0) {
93759243Sobrien		tlink[cc] = '\0';
93859243Sobrien		(void) Strncpy(link, str2short(tlink),
93959243Sobrien		    sizeof(link) / sizeof(Char));
94059243Sobrien		link[sizeof(link) / sizeof(Char) - 1] = '\0';
94159243Sobrien
94259243Sobrien		if (slash)
94359243Sobrien		    *p = '/';
94459243Sobrien		/*
94559243Sobrien		 * Point p to the '/' in "/..", and restore the '/'.
94659243Sobrien		 */
94759243Sobrien		*(p = sp) = '/';
94859243Sobrien		/*
94959243Sobrien		 * find length of p
95059243Sobrien		 */
95159243Sobrien		for (p1 = p; *p1++;)
95259243Sobrien		    continue;
95359243Sobrien		if (*link != '/') {
95459243Sobrien		    /*
95559243Sobrien		     * Relative path, expand it between the "yyy/" and the
95659243Sobrien		     * "/..". First, back sp up to the character past "yyy/".
95759243Sobrien		     */
95859243Sobrien		    while (*--sp != '/')
95959243Sobrien			continue;
96059243Sobrien		    sp++;
96159243Sobrien		    *sp = 0;
96259243Sobrien		    /*
96359243Sobrien		     * New length is "yyy/" + link + "/.." and rest
96459243Sobrien		     */
96559243Sobrien		    p1 = newcp = (Char *) xmalloc((size_t)
96659243Sobrien						(((sp - cp) + cc + (p1 - p)) *
96759243Sobrien						 sizeof(Char)));
96859243Sobrien		    /*
96959243Sobrien		     * Copy new path into newcp
97059243Sobrien		     */
97159243Sobrien		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
97259243Sobrien			continue;
97359243Sobrien		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
97459243Sobrien			continue;
97559243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
97659243Sobrien			continue;
97759243Sobrien		    /*
97859243Sobrien		     * Restart canonicalization at expanded "/xxx".
97959243Sobrien		     */
98059243Sobrien		    p = sp - cp - 1 + newcp;
98159243Sobrien		}
98259243Sobrien		else {
98359243Sobrien		    /*
98459243Sobrien		     * New length is link + "/.." and rest
98559243Sobrien		     */
98659243Sobrien		    p1 = newcp = (Char *) xmalloc((size_t)
98759243Sobrien					    ((cc + (p1 - p)) * sizeof(Char)));
98859243Sobrien		    /*
98959243Sobrien		     * Copy new path into newcp
99059243Sobrien		     */
99159243Sobrien		    for (p2 = link; (*p1++ = *p2++) != '\0';)
99259243Sobrien			continue;
99359243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
99459243Sobrien			continue;
99559243Sobrien		    /*
99659243Sobrien		     * Restart canonicalization at beginning
99759243Sobrien		     */
99859243Sobrien		    p = newcp;
99959243Sobrien		}
100059243Sobrien		xfree((ptr_t) cp);
100159243Sobrien		cp = newcp;
100259243Sobrien#ifdef apollo
100359243Sobrien                slashslash = (cp[0] == '/' && cp[1] == '/');
100459243Sobrien#endif /* apollo */
100559243Sobrien		continue;	/* canonicalize the link */
100659243Sobrien	    }
100759243Sobrien#endif /* S_IFLNK */
100859243Sobrien	    *sp = '/';
100959243Sobrien	    if (sp != cp)
101059243Sobrien		while (*--sp != '/')
101159243Sobrien		    continue;
101259243Sobrien	    if (slash) {
101359243Sobrien		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
101459243Sobrien		    continue;
101559243Sobrien		p = sp;
101659243Sobrien	    }
101759243Sobrien	    else if (cp == sp)
101859243Sobrien		*++sp = '\0';
101959243Sobrien	    else
102059243Sobrien		*sp = '\0';
102159243Sobrien	}
102259243Sobrien	else {			/* normal dir name (not . or .. or nothing) */
102359243Sobrien
102459243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
102559243Sobrien	    if (sp != cp && symlinks == SYM_CHASE &&
102659243Sobrien		(cc = readlink(short2str(cp), tlink,
1027131962Smp			       sizeof(tlink) - 1)) >= 0) {
102859243Sobrien		tlink[cc] = '\0';
102959243Sobrien		(void) Strncpy(link, str2short(tlink),
103059243Sobrien		    sizeof(link) / sizeof(Char));
103159243Sobrien		link[sizeof(link) / sizeof(Char) - 1] = '\0';
103259243Sobrien
103359243Sobrien		/*
103459243Sobrien		 * restore the '/'.
103559243Sobrien		 */
103659243Sobrien		if (slash)
103759243Sobrien		    *p = '/';
103859243Sobrien
103959243Sobrien		/*
104059243Sobrien		 * point sp to p (rather than backing up).
104159243Sobrien		 */
104259243Sobrien		sp = p;
104359243Sobrien
104459243Sobrien		/*
104559243Sobrien		 * find length of p
104659243Sobrien		 */
104759243Sobrien		for (p1 = p; *p1++;)
104859243Sobrien		    continue;
104959243Sobrien		if (*link != '/') {
105059243Sobrien		    /*
105159243Sobrien		     * Relative path, expand it between the "yyy/" and the
105259243Sobrien		     * remainder. First, back sp up to the character past
105359243Sobrien		     * "yyy/".
105459243Sobrien		     */
105559243Sobrien		    while (*--sp != '/')
105659243Sobrien			continue;
105759243Sobrien		    sp++;
105859243Sobrien		    *sp = 0;
105959243Sobrien		    /*
106059243Sobrien		     * New length is "yyy/" + link + "/.." and rest
106159243Sobrien		     */
106259243Sobrien		    p1 = newcp = (Char *) xmalloc((size_t)
106359243Sobrien						  (((sp - cp) + cc + (p1 - p))
106459243Sobrien						   * sizeof(Char)));
106559243Sobrien		    /*
106659243Sobrien		     * Copy new path into newcp
106759243Sobrien		     */
106859243Sobrien		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
106959243Sobrien			continue;
107059243Sobrien		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
107159243Sobrien			continue;
107259243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
107359243Sobrien			continue;
107459243Sobrien		    /*
107559243Sobrien		     * Restart canonicalization at expanded "/xxx".
107659243Sobrien		     */
107759243Sobrien		    p = sp - cp - 1 + newcp;
107859243Sobrien		}
107959243Sobrien		else {
108059243Sobrien		    /*
108159243Sobrien		     * New length is link + the rest
108259243Sobrien		     */
108359243Sobrien		    p1 = newcp = (Char *) xmalloc((size_t)
108459243Sobrien					    ((cc + (p1 - p)) * sizeof(Char)));
108559243Sobrien		    /*
108659243Sobrien		     * Copy new path into newcp
108759243Sobrien		     */
108859243Sobrien		    for (p2 = link; (*p1++ = *p2++) != '\0';)
108959243Sobrien			continue;
109059243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
109159243Sobrien			continue;
109259243Sobrien		    /*
109359243Sobrien		     * Restart canonicalization at beginning
109459243Sobrien		     */
109559243Sobrien		    p = newcp;
109659243Sobrien		}
109759243Sobrien		xfree((ptr_t) cp);
109859243Sobrien		cp = newcp;
109959243Sobrien#ifdef apollo
110059243Sobrien                slashslash = (cp[0] == '/' && cp[1] == '/');
110159243Sobrien#endif /* apollo */
110259243Sobrien		continue;	/* canonicalize the link */
110359243Sobrien	    }
110459243Sobrien#endif /* S_IFLNK */
110559243Sobrien	    if (slash)
110659243Sobrien		*p = '/';
110759243Sobrien	}
110859243Sobrien    }
110959243Sobrien
111059243Sobrien    /*
111159243Sobrien     * fix home...
111259243Sobrien     */
111359243Sobrien#ifdef S_IFLNK
111459243Sobrien    p1 = varval(STRhome);
111559243Sobrien    cc = (int) Strlen(p1);
111659243Sobrien    /*
111759243Sobrien     * See if we're not in a subdir of STRhome
111859243Sobrien     */
111959243Sobrien    if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
112059243Sobrien	(cp[cc] != '/' && cp[cc] != '\0'))) {
112159243Sobrien	static ino_t home_ino = (ino_t) -1;
112259243Sobrien	static dev_t home_dev = (dev_t) -1;
112359243Sobrien	static Char *home_ptr = NULL;
112459243Sobrien	struct stat statbuf;
112559243Sobrien	int found;
112659243Sobrien
112759243Sobrien	/*
112859243Sobrien	 * Get dev and ino of STRhome
112959243Sobrien	 */
113059243Sobrien	if (home_ptr != p1 &&
113159243Sobrien	    stat(short2str(p1), &statbuf) != -1) {
113259243Sobrien	    home_dev = statbuf.st_dev;
113359243Sobrien	    home_ino = statbuf.st_ino;
113459243Sobrien	    home_ptr = p1;
113559243Sobrien	}
113659243Sobrien	/*
113759243Sobrien	 * Start comparing dev & ino backwards
113859243Sobrien	 */
113959243Sobrien	p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char));
114059243Sobrien	link[sizeof(link) / sizeof(Char) - 1] = '\0';
114159243Sobrien	found = 0;
114259243Sobrien	while (*p2 && stat(short2str(p2), &statbuf) != -1) {
114359243Sobrien	    if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
114459243Sobrien			statbuf.st_ino == home_ino) {
114559243Sobrien			found = 1;
114659243Sobrien			break;
114759243Sobrien	    }
114859243Sobrien	    if ((sp = Strrchr(p2, '/')) != NULL)
114959243Sobrien		*sp = '\0';
115059243Sobrien	}
115159243Sobrien	/*
115259243Sobrien	 * See if we found it
115359243Sobrien	 */
115459243Sobrien	if (*p2 && found) {
115559243Sobrien	    /*
115659243Sobrien	     * Use STRhome to make '~' work
115759243Sobrien	     */
115859243Sobrien	    newcp = Strspl(p1, cp + Strlen(p2));
115959243Sobrien	    xfree((ptr_t) cp);
116059243Sobrien	    cp = newcp;
116159243Sobrien	}
116259243Sobrien    }
116359243Sobrien#endif /* S_IFLNK */
116459243Sobrien
116559243Sobrien#ifdef apollo
116659243Sobrien    if (slashslash) {
116759243Sobrien	if (cp[1] != '/') {
116859243Sobrien	    p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
116959243Sobrien	    *p = '/';
117059243Sobrien	    (void) Strcpy(&p[1], cp);
117159243Sobrien	    xfree((ptr_t) cp);
117259243Sobrien	    cp = p;
117359243Sobrien	}
117459243Sobrien    }
117559243Sobrien    if (cp[1] == '/' && cp[2] == '/')
117659243Sobrien	(void) Strcpy(&cp[1], &cp[2]);
117759243Sobrien#endif /* apollo */
117859243Sobrien    return cp;
117959243Sobrien}
118059243Sobrien
118159243Sobrien
118259243Sobrien/*
118359243Sobrien * dnewcwd - make a new directory in the loop the current one
118459243Sobrien */
118559243Sobrienstatic void
118659243Sobriendnewcwd(dp, dflag)
118759243Sobrien    register struct directory *dp;
118859243Sobrien    int dflag;
118959243Sobrien{
119059243Sobrien    int print;
119159243Sobrien
119259243Sobrien    if (adrof(STRdunique)) {
119359243Sobrien	struct directory *dn;
119459243Sobrien
119559243Sobrien	for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
119659243Sobrien	    if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
119759243Sobrien		dn->di_next->di_prev = dn->di_prev;
119859243Sobrien		dn->di_prev->di_next = dn->di_next;
119959243Sobrien		dfree(dn);
120059243Sobrien		break;
120159243Sobrien	    }
120259243Sobrien    }
120359243Sobrien    dcwd = dp;
120459243Sobrien    dset(dcwd->di_name);
120559243Sobrien    dgetstack();
120659243Sobrien    print = printd;		/* if printd is set, print dirstack... */
120759243Sobrien    if (adrof(STRpushdsilent))	/* but pushdsilent overrides printd... */
120859243Sobrien	print = 0;
120959243Sobrien    if (dflag & DIR_PRINT)	/* but DIR_PRINT overrides pushdsilent... */
121059243Sobrien	print = 1;
121159243Sobrien    if (bequiet)		/* and bequiet overrides everything */
121259243Sobrien	print = 0;
121359243Sobrien    if (print)
121459243Sobrien	printdirs(dflag);
121559243Sobrien    cwd_cmd();			/* PWP: run the defined cwd command */
121659243Sobrien}
121759243Sobrien
121859243Sobrienvoid
121959243Sobriendsetstack()
122059243Sobrien{
122159243Sobrien    Char **cp;
122259243Sobrien    struct varent *vp;
122359243Sobrien    struct directory *dn, *dp;
122459243Sobrien
1225100616Smp    if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
122659243Sobrien	return;
122759243Sobrien
122859243Sobrien    /* Free the whole stack */
122959243Sobrien    while ((dn = dhead.di_prev) != &dhead) {
123059243Sobrien	dn->di_next->di_prev = dn->di_prev;
123159243Sobrien	dn->di_prev->di_next = dn->di_next;
123259243Sobrien	if (dn != dcwd)
123359243Sobrien	    dfree(dn);
123459243Sobrien    }
123559243Sobrien
123659243Sobrien    /* thread the current working directory */
123759243Sobrien    dhead.di_prev = dhead.di_next = dcwd;
123859243Sobrien    dcwd->di_next = dcwd->di_prev = &dhead;
123959243Sobrien
124059243Sobrien    /* put back the stack */
124159243Sobrien    for (cp = vp->vec; cp && *cp && **cp; cp++) {
124259243Sobrien	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
124359243Sobrien	dp->di_name = Strsave(*cp);
124459243Sobrien	dp->di_count = 0;
124559243Sobrien	dp->di_prev = dcwd;
124659243Sobrien	dp->di_next = dcwd->di_next;
124759243Sobrien	dcwd->di_next = dp;
124859243Sobrien	dp->di_next->di_prev = dp;
124959243Sobrien    }
125059243Sobrien    dgetstack();	/* Make $dirstack reflect the current state */
125159243Sobrien}
125259243Sobrien
125359243Sobrienstatic void
125459243Sobriendgetstack()
125559243Sobrien{
125659243Sobrien    int i = 0;
125759243Sobrien    Char **dblk, **dbp;
125859243Sobrien    struct directory *dn;
125959243Sobrien
126059243Sobrien    if (adrof(STRdirstack) == NULL)
126159243Sobrien    	return;
126259243Sobrien
126359243Sobrien    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
126459243Sobrien	continue;
126559243Sobrien    dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
126659243Sobrien    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
126759243Sobrien	 *dbp = Strsave(dn->di_name);
126859243Sobrien    *dbp = NULL;
126959243Sobrien    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
127059243Sobrien}
127159243Sobrien
127259243Sobrien/*
127359243Sobrien * getstakd - added by kfk 17 Jan 1984
127459243Sobrien * Support routine for the stack hack.  Finds nth directory in
127559243Sobrien * the directory stack, or finds last directory in stack.
127659243Sobrien */
127759243Sobrienint
127859243Sobriengetstakd(s, cnt)
127959243Sobrien    Char   *s;
128059243Sobrien    int     cnt;
128159243Sobrien{
128259243Sobrien    struct directory *dp;
128359243Sobrien
128459243Sobrien    dp = dcwd;
128559243Sobrien    if (cnt < 0) {		/* < 0 ==> last dir requested. */
128659243Sobrien	dp = dp->di_next;
128759243Sobrien	if (dp == &dhead)
128859243Sobrien	    dp = dp->di_next;
128959243Sobrien    }
129059243Sobrien    else {
129159243Sobrien	while (cnt-- > 0) {
129259243Sobrien	    dp = dp->di_prev;
129359243Sobrien	    if (dp == &dhead)
129459243Sobrien		dp = dp->di_prev;
129559243Sobrien	    if (dp == dcwd)
129659243Sobrien		return (0);
129759243Sobrien	}
129859243Sobrien    }
129969408Sache    (void) Strncpy(s, dp->di_name, BUFSIZE);
130069408Sache    s[BUFSIZE - 1] = '\0';
130159243Sobrien    return (1);
130259243Sobrien}
130359243Sobrien
130459243Sobrien/*
130559243Sobrien * Karl Kleinpaste - 10 Feb 1984
130659243Sobrien * Added dextract(), which is used in pushd +n.
130759243Sobrien * Instead of just rotating the entire stack around, dextract()
130859243Sobrien * lets the user have the nth dir extracted from its current
130959243Sobrien * position, and pushes it onto the top.
131059243Sobrien */
131159243Sobrienstatic void
131259243Sobriendextract(dp)
131359243Sobrien    struct directory *dp;
131459243Sobrien{
131559243Sobrien    if (dp == dcwd)
131659243Sobrien	return;
131759243Sobrien    dp->di_next->di_prev = dp->di_prev;
131859243Sobrien    dp->di_prev->di_next = dp->di_next;
131959243Sobrien    dp->di_next = dcwd->di_next;
132059243Sobrien    dp->di_prev = dcwd;
132159243Sobrien    dp->di_next->di_prev = dp;
132259243Sobrien    dcwd->di_next = dp;
132359243Sobrien}
132459243Sobrien
132559243Sobrienvoid
132659243Sobrienloaddirs(fname)
132759243Sobrien    Char *fname;
132859243Sobrien{
132959243Sobrien    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
133059243Sobrien
133159243Sobrien    bequiet = 1;
133259243Sobrien    if (fname)
133359243Sobrien	loaddirs_cmd[1] = fname;
133459243Sobrien    else if ((fname = varval(STRdirsfile)) != STRNULL)
133559243Sobrien	loaddirs_cmd[1] = fname;
133659243Sobrien    else
133759243Sobrien	loaddirs_cmd[1] = STRtildotdirs;
133859243Sobrien    dosource(loaddirs_cmd, (struct command *)0);
133959243Sobrien    bequiet = 0;
134059243Sobrien}
134159243Sobrien
134259243Sobrien/*
134359243Sobrien * create a file called ~/.cshdirs which has a sequence
134459243Sobrien * of pushd commands which will restore the dir stack to
134559243Sobrien * its state before exit/logout. remember that the order
134659243Sobrien * is reversed in the file because we are pushing.
134759243Sobrien * -strike
134859243Sobrien */
134959243Sobrienvoid
135059243Sobrienrecdirs(fname, def)
135159243Sobrien    Char *fname;
135259243Sobrien    int def;
135359243Sobrien{
135459243Sobrien    int     fp, ftmp, oldidfds;
135559243Sobrien    int     cdflag = 0;
135659243Sobrien    extern struct directory *dcwd;
135759243Sobrien    struct directory *dp;
135859243Sobrien    unsigned int    num;
135959243Sobrien    Char   *snum;
136059243Sobrien    Char    qname[MAXPATHLEN*2];
136159243Sobrien
136259243Sobrien    if (fname == NULL && !def)
136359243Sobrien	return;
136459243Sobrien
136559243Sobrien    if (fname == NULL) {
136659243Sobrien	if ((fname = varval(STRdirsfile)) == STRNULL)
136759243Sobrien	    fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
136859243Sobrien	else
136959243Sobrien	    fname = Strsave(fname);
137059243Sobrien    }
137159243Sobrien    else
137259243Sobrien	fname = globone(fname, G_ERROR);
137359243Sobrien
137459243Sobrien    if ((fp = creat(short2str(fname), 0600)) == -1) {
137559243Sobrien	xfree((ptr_t) fname);
137659243Sobrien	return;
137759243Sobrien    }
137859243Sobrien
137983098Smp    if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
138059243Sobrien	num = (unsigned int) ~0;
138159243Sobrien    else
138259243Sobrien	num = (unsigned int) atoi(short2str(snum));
138359243Sobrien
138459243Sobrien    oldidfds = didfds;
138559243Sobrien    didfds = 0;
138659243Sobrien    ftmp = SHOUT;
138759243Sobrien    SHOUT = fp;
138859243Sobrien
138959243Sobrien    dp = dcwd->di_next;
139059243Sobrien    do {
139159243Sobrien	if (dp == &dhead)
139259243Sobrien	    continue;
139359243Sobrien
139459243Sobrien	if (cdflag == 0) {
139559243Sobrien	    cdflag = 1;
139659243Sobrien	    xprintf("cd %S\n", quote_meta(qname, dp->di_name));
139759243Sobrien	}
139859243Sobrien	else
139959243Sobrien	    xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
140059243Sobrien
140159243Sobrien	if (num-- == 0)
140259243Sobrien	    break;
140359243Sobrien
140459243Sobrien    } while ((dp = dp->di_next) != dcwd->di_next);
140559243Sobrien
140659243Sobrien    (void) close(fp);
140759243Sobrien    SHOUT = ftmp;
140859243Sobrien    didfds = oldidfds;
140959243Sobrien    xfree((ptr_t) fname);
141059243Sobrien}
1411