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