sh.dir.c revision 316957
1316957Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.85 2016/04/08 16:10:52 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"
34145479Smp#include "ed.h"
3559243Sobrien
36316957SdchaginRCSID("$tcsh: sh.dir.c,v 3.85 2016/04/08 16:10:52 christos Exp $")
3759243Sobrien
3859243Sobrien/*
3959243Sobrien * C Shell - directory management
4059243Sobrien */
4159243Sobrien
42167465Smpstatic	Char			*agetcwd	(void);
43167465Smpstatic	void			 dstart		(const char *);
44167465Smpstatic	struct directory	*dfind		(Char *);
45195609Smpstatic	Char 			*dfollow	(Char *, int);
46167465Smpstatic	void 	 	 	 printdirs	(int);
47167465Smpstatic	Char 			*dgoto		(Char *);
48167465Smpstatic	void 	 	 	 dnewcwd	(struct directory *, int);
49167465Smpstatic	void 	 	 	 dset		(Char *);
50167465Smpstatic  void 			 dextract	(struct directory *);
51167465Smpstatic  int 			 skipargs	(Char ***, const char *,
52167465Smp						 const char *);
53167465Smpstatic	void			 dgetstack	(void);
5459243Sobrien
5559243Sobrienstatic struct directory dhead INIT_ZERO_STRUCT;		/* "head" of loop */
5659243Sobrienstatic int    printd;			/* force name to be printed */
5759243Sobrien
5859243Sobrienint     bequiet = 0;		/* do not print dir stack -strike */
5959243Sobrien
60167465Smpstatic Char *
61167465Smpagetcwd(void)
62167465Smp{
63167465Smp    char *buf;
64167465Smp    Char *cwd;
65167465Smp    size_t len;
66167465Smp
67167465Smp    len = MAXPATHLEN;
68167465Smp    buf = xmalloc(len);
69167465Smp    while (getcwd(buf, len) == NULL) {
70167465Smp	int err;
71167465Smp
72167465Smp	err = errno;
73167465Smp	if (err != ERANGE) {
74167465Smp	    xfree(buf);
75167465Smp	    errno = err;
76167465Smp	    return NULL;
77167465Smp	}
78167465Smp	len *= 2;
79167465Smp	buf = xrealloc(buf, len);
80167465Smp    }
81167465Smp    if (*buf == '\0') {
82167465Smp	xfree(buf);
83167465Smp	return NULL;
84167465Smp    }
85167465Smp    cwd = SAVE(buf);
86167465Smp    xfree(buf);
87167465Smp    return cwd;
88167465Smp}
89167465Smp
9059243Sobrienstatic void
91167465Smpdstart(const char *from)
9259243Sobrien{
9359243Sobrien    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
9459243Sobrien}
9559243Sobrien
9659243Sobrien/*
9759243Sobrien * dinit - initialize current working directory
9859243Sobrien */
9959243Sobrienvoid
100167465Smpdinit(Char *hp)
10159243Sobrien{
102167465Smp    Char *cp, *tcp;
103145479Smp    struct directory *dp;
10459243Sobrien
10559243Sobrien    /* Don't believe the login shell home, because it may be a symlink */
106167465Smp    tcp = agetcwd();
107167465Smp    if (tcp == NULL) {
10859243Sobrien	xprintf("%s: %s\n", progname, strerror(errno));
10959243Sobrien	if (hp && *hp) {
110167465Smp	    char *xcp = short2str(hp);
111167465Smp	    dstart(xcp);
112167465Smp	    if (chdir(xcp) == -1)
11359243Sobrien		cp = NULL;
11459243Sobrien	    else
11559243Sobrien		cp = Strsave(hp);
11659243Sobrien	}
11759243Sobrien	else
11859243Sobrien	    cp = NULL;
11959243Sobrien	if (cp == NULL) {
12059243Sobrien	    dstart("/");
12159243Sobrien	    if (chdir("/") == -1)
12259243Sobrien		/* I am not even try to print an error message! */
12359243Sobrien		xexit(1);
12459243Sobrien	    cp = SAVE("/");
12559243Sobrien	}
12659243Sobrien    }
12759243Sobrien    else {
12859243Sobrien#ifdef S_IFLNK
12959243Sobrien	struct stat swd, shp;
130167465Smp	int swd_ok;
13159243Sobrien
132167465Smp	swd_ok = stat(short2str(tcp), &swd) == 0;
13359243Sobrien	/*
13459243Sobrien	 * See if $HOME is the working directory we got and use that
13559243Sobrien	 */
136167465Smp	if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 &&
13759243Sobrien	    DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
13859243Sobrien		swd.st_ino == shp.st_ino)
13959243Sobrien	    cp = Strsave(hp);
14059243Sobrien	else {
14159243Sobrien	    char   *cwd;
14259243Sobrien
14359243Sobrien	    /*
14459243Sobrien	     * use PWD if we have it (for subshells)
14559243Sobrien	     */
146167465Smp	    if (swd_ok && (cwd = getenv("PWD")) != NULL) {
147167465Smp		if (stat(cwd, &shp) != -1 &&
14859243Sobrien			DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
149167465Smp		        swd.st_ino == shp.st_ino) {
150167465Smp		    tcp = SAVE(cwd);
151167465Smp		    cleanup_push(tcp, xfree);
152167465Smp		}
15359243Sobrien	    }
154167465Smp	    cleanup_push(tcp, xfree);
155167465Smp	    cp = dcanon(tcp, STRNULL);
156167465Smp	    cleanup_ignore(tcp);
157167465Smp	    cleanup_until(tcp);
15859243Sobrien	}
15959243Sobrien#else /* S_IFLNK */
160167465Smp	cleanup_push(tcp, xfree);
161167465Smp	cp = dcanon(tcp, STRNULL);
162167465Smp	cleanup_ignore(tcp);
163167465Smp	cleanup_until(tcp);
16459243Sobrien#endif /* S_IFLNK */
16559243Sobrien    }
16659243Sobrien
167167465Smp    dp = xcalloc(sizeof(struct directory), 1);
16859243Sobrien    dp->di_name = cp;
16959243Sobrien    dp->di_count = 0;
17059243Sobrien    dhead.di_next = dhead.di_prev = dp;
17159243Sobrien    dp->di_next = dp->di_prev = &dhead;
17259243Sobrien    printd = 0;
17359243Sobrien    dnewcwd(dp, 0);
174167465Smp    setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB);
17559243Sobrien}
17659243Sobrien
17759243Sobrienstatic void
178167465Smpdset(Char *dp)
17959243Sobrien{
18059243Sobrien    /*
18159243Sobrien     * Don't call set() directly cause if the directory contains ` or
18259243Sobrien     * other junk characters glob will fail.
18359243Sobrien     */
184167465Smp    setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB);
185167465Smp    setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB);
18659243Sobrien    tsetenv(STRPWD, dp);
18759243Sobrien}
18859243Sobrien
18959243Sobrien#define DIR_PRINT	0x01	/* -p */
19059243Sobrien#define DIR_LONG  	0x02	/* -l */
19159243Sobrien#define DIR_VERT  	0x04	/* -v */
19259243Sobrien#define DIR_LINE  	0x08	/* -n */
19359243Sobrien#define DIR_SAVE 	0x10	/* -S */
19459243Sobrien#define DIR_LOAD	0x20	/* -L */
19559243Sobrien#define DIR_CLEAR	0x40	/* -c */
19659243Sobrien#define DIR_OLD	  	0x80	/* - */
19759243Sobrien
19859243Sobrienstatic int
199167465Smpskipargs(Char ***v, const char *dstr, const char *str)
20059243Sobrien{
20159243Sobrien    Char  **n = *v, *s;
20259243Sobrien
20359243Sobrien    int dflag = 0, loop = 1;
20459243Sobrien    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
20559243Sobrien	if (*(s = &((*n)[1])) == '\0')	/* test for bare "-" argument */
20659243Sobrien	    dflag |= DIR_OLD;
207231990Smp	else if ((*n)[1] == '-' && (*n)[2] == '\0') {   /* test for -- */
208231990Smp	    n++;
209231990Smp	    break;
210231990Smp	} else {
21159243Sobrien	    char *p;
212231990Smp	    while (*s != '\0')	/* examine flags */ {
21359243Sobrien		if ((p = strchr(dstr, *s++)) != NULL)
21459243Sobrien		    dflag |= (1 << (p - dstr));
215167465Smp	        else
21659243Sobrien		    stderror(ERR_DIRUS, short2str(**v), dstr, str);
21759243Sobrien	    }
21859243Sobrien	}
21959243Sobrien    if (*n && (dflag & DIR_OLD))
22059243Sobrien	stderror(ERR_DIRUS, short2str(**v), dstr, str);
22159243Sobrien    *v = n;
22259243Sobrien    /* make -l, -v, and -n imply -p */
22359243Sobrien    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
22459243Sobrien	dflag |= DIR_PRINT;
22559243Sobrien    return dflag;
22659243Sobrien}
22759243Sobrien
22859243Sobrien/*
22959243Sobrien * dodirs - list all directories in directory loop
23059243Sobrien */
23159243Sobrien/*ARGSUSED*/
23259243Sobrienvoid
233167465Smpdodirs(Char **v, struct command *c)
23459243Sobrien{
235167465Smp    static const char flags[] = "plvnSLc";
23659243Sobrien    int dflag = skipargs(&v, flags, "");
23759243Sobrien
23859243Sobrien    USE(c);
23959243Sobrien    if ((dflag & DIR_CLEAR) != 0) {
24059243Sobrien	struct directory *dp, *fdp;
24159243Sobrien	for (dp = dcwd->di_next; dp != dcwd; ) {
24259243Sobrien	    fdp = dp;
24359243Sobrien	    dp = dp->di_next;
24459243Sobrien	    if (fdp != &dhead)
24559243Sobrien		dfree(fdp);
24659243Sobrien	}
24759243Sobrien	dhead.di_next = dhead.di_prev = dp;
24859243Sobrien	dp->di_next = dp->di_prev = &dhead;
24959243Sobrien    }
25059243Sobrien    if ((dflag & DIR_LOAD) != 0)
25159243Sobrien	loaddirs(*v);
25259243Sobrien    else if ((dflag & DIR_SAVE) != 0)
25359243Sobrien	recdirs(*v, 1);
25459243Sobrien
25559243Sobrien    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
25659243Sobrien	v++;
25759243Sobrien
25859243Sobrien    if (*v != NULL || (dflag & DIR_OLD))
25959243Sobrien	stderror(ERR_DIRUS, "dirs", flags, "");
26059243Sobrien    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
26159243Sobrien	printdirs(dflag);
26259243Sobrien}
26359243Sobrien
26459243Sobrienstatic void
265167465Smpprintdirs(int dflag)
26659243Sobrien{
267145479Smp    struct directory *dp;
26859243Sobrien    Char   *s, *user;
26959243Sobrien    int     idx, len, cur;
27059243Sobrien
27159243Sobrien    dp = dcwd;
27259243Sobrien    idx = 0;
27359243Sobrien    cur = 0;
27459243Sobrien    do {
27559243Sobrien	if (dp == &dhead)
27659243Sobrien	    continue;
27759243Sobrien	if (dflag & DIR_VERT) {
27859243Sobrien	    xprintf("%d\t", idx++);
27959243Sobrien	    cur = 0;
28059243Sobrien	}
28159243Sobrien	s = dp->di_name;
28259243Sobrien	user = NULL;
28359243Sobrien	if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
28459243Sobrien	    len = (int) (Strlen(user) + Strlen(s) + 2);
28559243Sobrien	else
28659243Sobrien	    len = (int) (Strlen(s) + 1);
28759243Sobrien
28859243Sobrien	cur += len;
289167465Smp	if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) {
29059243Sobrien	    xputchar('\n');
29159243Sobrien	    cur = len;
29259243Sobrien	}
29359243Sobrien	if (user)
29459243Sobrien	    xprintf("~%S", user);
295145479Smp	xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
29659243Sobrien    } while ((dp = dp->di_prev) != dcwd);
29759243Sobrien    if (!(dflag & DIR_VERT))
29859243Sobrien	xputchar('\n');
29959243Sobrien}
30059243Sobrien
30159243Sobrienvoid
302167465Smpdtildepr(Char *dir)
30359243Sobrien{
30459243Sobrien    Char* user;
30559243Sobrien    if ((user = getusername(&dir)) != NULL)
306145479Smp	xprintf("~%-S%S", user, dir);
30759243Sobrien    else
30859243Sobrien	xprintf("%S", dir);
30959243Sobrien}
31059243Sobrien
31159243Sobrienvoid
312167465Smpdtilde(void)
31359243Sobrien{
31459243Sobrien    struct directory *d = dcwd;
31559243Sobrien
31659243Sobrien    do {
31759243Sobrien	if (d == &dhead)
31859243Sobrien	    continue;
31959243Sobrien	d->di_name = dcanon(d->di_name, STRNULL);
32059243Sobrien    } while ((d = d->di_prev) != dcwd);
32159243Sobrien
32259243Sobrien    dset(dcwd->di_name);
32359243Sobrien}
32459243Sobrien
32559243Sobrien
32659243Sobrien/* dnormalize():
32759243Sobrien *	The path will be normalized if it
32859243Sobrien *	1) is "..",
32959243Sobrien *	2) or starts with "../",
33059243Sobrien *	3) or ends with "/..",
33159243Sobrien *	4) or contains the string "/../",
33259243Sobrien *	then it will be normalized, unless those strings are quoted.
33359243Sobrien *	Otherwise, a copy is made and sent back.
33459243Sobrien */
33559243SobrienChar   *
336167465Smpdnormalize(const Char *cp, int expnd)
33759243Sobrien{
33859243Sobrien
33959243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */
34059243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
34159243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
34259243Sobrien
34359243Sobrien#ifdef S_IFLNK
344145479Smp    if (expnd) {
345167465Smp	struct Strbuf buf = Strbuf_INIT;
34659243Sobrien 	int     dotdot = 0;
347167465Smp	Char   *dp, *cwd;
348167465Smp	const Char *start = cp;
349167465Smp# ifdef HAVE_SLASHSLASH
350145479Smp	int slashslash;
351167465Smp# endif /* HAVE_SLASHSLASH */
35259243Sobrien
35359243Sobrien	/*
35459243Sobrien	 * count the number of "../xxx" or "xxx/../xxx" in the path
35559243Sobrien	 */
356167465Smp	for ( ; *cp && *(cp + 1); cp++)
357167465Smp	    if (IS_DOTDOT(start, cp))
35859243Sobrien	        dotdot++;
359167465Smp
36059243Sobrien	/*
36159243Sobrien	 * if none, we are done.
36259243Sobrien	 */
36359243Sobrien        if (dotdot == 0)
364167465Smp	    return (Strsave(start));
365167465Smp
366167465Smp# ifdef notdef
367167465Smp	struct stat sb;
368100616Smp	/*
369167465Smp	 * We disable this test because:
370167465Smp	 * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1;
371167465Smp	 * echo ../../dir1 does not expand. We had enabled this before
372167465Smp	 * because it was bothering people with expansions in compilation
373167465Smp	 * lines like -I../../foo. Maybe we need some kind of finer grain
374167465Smp	 * control?
375167465Smp	 *
376100616Smp	 * If the path doesn't exist, we are done too.
377100616Smp	 */
378167465Smp	if (lstat(short2str(start), &sb) != 0 && errno == ENOENT)
379167465Smp	    return (Strsave(start));
380167465Smp# endif
381100616Smp
382167465Smp	cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char));
38359243Sobrien	(void) Strcpy(cwd, dcwd->di_name);
38459243Sobrien
38559243Sobrien	/*
38659243Sobrien	 * If the path starts with a slash, we are not relative to
38759243Sobrien	 * the current working directory.
38859243Sobrien	 */
38959243Sobrien	if (ABSOLUTEP(start))
39059243Sobrien	    *cwd = '\0';
391167465Smp# ifdef HAVE_SLASHSLASH
39259243Sobrien	slashslash = cwd[0] == '/' && cwd[1] == '/';
393167465Smp# endif /* HAVE_SLASHSLASH */
39459243Sobrien
39559243Sobrien	/*
39659243Sobrien	 * Ignore . and count ..'s
39759243Sobrien	 */
398167465Smp	cp = start;
399167465Smp	do {
40059243Sobrien	    dotdot = 0;
401167465Smp	    buf.len = 0;
40259243Sobrien	    while (*cp)
40359243Sobrien	        if (IS_DOT(start, cp)) {
40459243Sobrien	            if (*++cp)
40559243Sobrien	                cp++;
40659243Sobrien	        }
40759243Sobrien	        else if (IS_DOTDOT(start, cp)) {
408167465Smp		    if (buf.len != 0)
40959243Sobrien		        break; /* finish analyzing .././../xxx/[..] */
41059243Sobrien		    dotdot++;
41159243Sobrien		    cp += 2;
41259243Sobrien		    if (*cp)
41359243Sobrien		        cp++;
41459243Sobrien	        }
41559243Sobrien	        else
416167465Smp		    Strbuf_append1(&buf, *cp++);
41759243Sobrien
418167465Smp	    Strbuf_terminate(&buf);
41959243Sobrien	    while (dotdot > 0)
42059243Sobrien	        if ((dp = Strrchr(cwd, '/')) != NULL) {
421167465Smp# ifdef HAVE_SLASHSLASH
42259243Sobrien		    if (dp == &cwd[1])
42359243Sobrien		        slashslash = 1;
424167465Smp# endif /* HAVE_SLASHSLASH */
42559243Sobrien		        *dp = '\0';
42659243Sobrien		        dotdot--;
42759243Sobrien	        }
42859243Sobrien	        else
42959243Sobrien		    break;
43059243Sobrien
43159243Sobrien	    if (!*cwd) {	/* too many ..'s, starts with "/" */
43259243Sobrien	        cwd[0] = '/';
433167465Smp# ifdef HAVE_SLASHSLASH
434167465Smp		/*
435167465Smp		 * Only append another slash, if already the former cwd
436167465Smp		 * was in a double-slash path.
437167465Smp		 */
438167465Smp		cwd[1] = slashslash ? '/' : '\0';
43959243Sobrien		cwd[2] = '\0';
440167465Smp# else /* !HAVE_SLASHSLASH */
44159243Sobrien		cwd[1] = '\0';
442167465Smp# endif /* HAVE_SLASHSLASH */
44359243Sobrien	    }
444167465Smp# ifdef HAVE_SLASHSLASH
44559243Sobrien	    else if (slashslash && cwd[1] == '\0') {
44659243Sobrien		cwd[1] = '/';
44759243Sobrien		cwd[2] = '\0';
44859243Sobrien	    }
449167465Smp# endif /* HAVE_SLASHSLASH */
45059243Sobrien
451167465Smp	    if (buf.len != 0) {
452167465Smp		size_t i;
453167465Smp
454167465Smp		i = Strlen(cwd);
455167465Smp		if (TRM(cwd[i - 1]) != '/') {
456167465Smp		    cwd[i++] = '/';
457167465Smp		    cwd[i] = '\0';
458167465Smp		}
459167465Smp	        dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s);
460167465Smp	        xfree(cwd);
46159243Sobrien	        cwd = dp;
462167465Smp		i = Strlen(cwd) - 1;
463167465Smp	        if (TRM(cwd[i]) == '/')
464167465Smp		    cwd[i] = '\0';
46559243Sobrien	    }
466100616Smp	    /* Reduction of ".." following the stuff we collected in buf
467100616Smp	     * only makes sense if the directory item in buf really exists.
468100616Smp	     * Avoid reduction of "-I../.." (typical compiler call) to ""
469100616Smp	     * or "/usr/nonexistant/../bin" to "/usr/bin":
470100616Smp	     */
471100616Smp	    if (cwd[0]) {
472100616Smp	        struct stat exists;
473100616Smp		if (0 != stat(short2str(cwd), &exists)) {
474167465Smp		    xfree(buf.s);
475167465Smp		    xfree(cwd);
476100616Smp		    return Strsave(start);
477100616Smp		}
478100616Smp	    }
479167465Smp	} while (*cp != '\0');
480167465Smp	xfree(buf.s);
48159243Sobrien	return cwd;
48259243Sobrien    }
48359243Sobrien#endif /* S_IFLNK */
48459243Sobrien    return Strsave(cp);
48559243Sobrien}
48659243Sobrien
48759243Sobrien
48859243Sobrien/*
48959243Sobrien * dochngd - implement chdir command.
49059243Sobrien */
49159243Sobrien/*ARGSUSED*/
49259243Sobrienvoid
493167465Smpdochngd(Char **v, struct command *c)
49459243Sobrien{
495145479Smp    Char *cp;
496145479Smp    struct directory *dp;
49759243Sobrien    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
49859243Sobrien
49959243Sobrien    USE(c);
50059243Sobrien    printd = 0;
50159243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
50259243Sobrien
50359243Sobrien    if (cp == NULL) {
504316957Sdchagin	if (!cdtohome)
505316957Sdchagin	    stderror(ERR_NAME | ERR_TOOFEW);
506316957Sdchagin	else if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
50759243Sobrien	    stderror(ERR_NAME | ERR_NOHOMEDIR);
50859243Sobrien	if (chdir(short2str(cp)) < 0)
50959243Sobrien	    stderror(ERR_NAME | ERR_CANTCHANGE);
51059243Sobrien	cp = Strsave(cp);
51159243Sobrien    }
51259243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
51359243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
51459243Sobrien	/* NOTREACHED */
51559243Sobrien	return;
51659243Sobrien    }
51759243Sobrien    else if ((dp = dfind(cp)) != 0) {
51859243Sobrien	char   *tmp;
51959243Sobrien
52059243Sobrien	printd = 1;
52159243Sobrien	if (chdir(tmp = short2str(dp->di_name)) < 0)
52259243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
52359243Sobrien	dcwd->di_prev->di_next = dcwd->di_next;
52459243Sobrien	dcwd->di_next->di_prev = dcwd->di_prev;
52559243Sobrien	dfree(dcwd);
52659243Sobrien	dnewcwd(dp, dflag);
52759243Sobrien	return;
52859243Sobrien    }
52959243Sobrien    else
530195609Smp	if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL)
53159243Sobrien	    return;
532167465Smp    dp = xcalloc(sizeof(struct directory), 1);
53359243Sobrien    dp->di_name = cp;
53459243Sobrien    dp->di_count = 0;
53559243Sobrien    dp->di_next = dcwd->di_next;
53659243Sobrien    dp->di_prev = dcwd->di_prev;
53759243Sobrien    dp->di_prev->di_next = dp;
53859243Sobrien    dp->di_next->di_prev = dp;
53959243Sobrien    dfree(dcwd);
54059243Sobrien    dnewcwd(dp, dflag);
54159243Sobrien}
54259243Sobrien
54359243Sobrienstatic Char *
544167465Smpdgoto(Char *cp)
54559243Sobrien{
546167465Smp    Char *dp, *ret;
54759243Sobrien
54859243Sobrien    if (!ABSOLUTEP(cp))
54959243Sobrien    {
550145479Smp	Char *p, *q;
551167465Smp	size_t cwdlen;
55259243Sobrien
553167465Smp	cwdlen = Strlen(dcwd->di_name);
554167465Smp	if (cwdlen == 1)	/* root */
55559243Sobrien	    cwdlen = 0;
556167465Smp	dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char));
55759243Sobrien	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
55859243Sobrien	    continue;
55959243Sobrien	if (cwdlen)
56059243Sobrien	    p[-1] = '/';
56159243Sobrien	else
56259243Sobrien	    p--;		/* don't add a / after root */
563167465Smp	Strcpy(p, cp);
564167465Smp	xfree(cp);
56559243Sobrien	cp = dp;
56659243Sobrien	dp += cwdlen;
56759243Sobrien    }
56859243Sobrien    else
56959243Sobrien	dp = cp;
57059243Sobrien
571131962Smp#if defined(WINNT_NATIVE)
572167465Smp    return agetcwd();
573131962Smp#elif defined(__CYGWIN__)
574167465Smp    if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */
575167465Smp	return agetcwd();
576167465Smp    } else {
577167465Smp	cleanup_push(cp, xfree);
578167465Smp    	ret = dcanon(cp, dp);
579167465Smp	cleanup_ignore(cp);
580167465Smp	cleanup_until(cp);
581167465Smp    }
58269408Sache#else /* !WINNT_NATIVE */
583167465Smp    cleanup_push(cp, xfree);
584167465Smp    ret = dcanon(cp, dp);
585167465Smp    cleanup_ignore(cp);
586167465Smp    cleanup_until(cp);
58769408Sache#endif /* WINNT_NATIVE */
588167465Smp    return ret;
58959243Sobrien}
59059243Sobrien
59159243Sobrien/*
59259243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid
59359243Sobrien */
59459243Sobrienstatic Char *
595195609Smpdfollow(Char *cp, int old)
59659243Sobrien{
597145479Smp    Char *dp;
59859243Sobrien    struct varent *c;
59959243Sobrien    int serrno;
60059243Sobrien
601195609Smp    cp = old ? Strsave(cp) : globone(cp, G_ERROR);
602167465Smp    cleanup_push(cp, xfree);
60359243Sobrien#ifdef apollo
60459243Sobrien    if (Strchr(cp, '`')) {
605167465Smp	char *dptr;
60659243Sobrien	if (chdir(dptr = short2str(cp)) < 0)
60759243Sobrien	    stderror(ERR_SYSTEM, dptr, strerror(errno));
608167465Smp	dp = agetcwd();
609167465Smp	cleanup_push(dp, xfree);
610167465Smp	if (dp != NULL) {
611167465Smp	    cleanup_until(cp);
612167465Smp	    return dgoto(dp);
61359243Sobrien	}
614167465Smp	else
615167465Smp	    stderror(ERR_SYSTEM, dptr, strerror(errno));
61659243Sobrien    }
61759243Sobrien#endif /* apollo */
618167465Smp
61959243Sobrien    /*
62059243Sobrien     * if we are ignoring symlinks, try to fix relatives now.
62159243Sobrien     * if we are expading symlinks, it should be done by now.
62259243Sobrien     */
62359243Sobrien    dp = dnormalize(cp, symlinks == SYM_IGNORE);
62459243Sobrien    if (chdir(short2str(dp)) >= 0) {
625167465Smp        cleanup_until(cp);
62659243Sobrien        return dgoto(dp);
62759243Sobrien    }
62859243Sobrien    else {
629167465Smp        xfree(dp);
630167465Smp        if (chdir(short2str(cp)) >= 0) {
631167465Smp	    cleanup_ignore(cp);
632167465Smp	    cleanup_until(cp);
63359243Sobrien	    return dgoto(cp);
634167465Smp	}
635167465Smp	else if (errno != ENOENT && errno != ENOTDIR) {
636167465Smp	    int err;
637167465Smp
638167465Smp	    err = errno;
639167465Smp	    stderror(ERR_SYSTEM, short2str(cp), strerror(err));
640167465Smp	}
64159243Sobrien	serrno = errno;
64259243Sobrien    }
64359243Sobrien
64459243Sobrien    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
645100616Smp	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
646167465Smp	struct Strbuf buf = Strbuf_INIT;
64759243Sobrien	Char  **cdp;
64859243Sobrien
64959243Sobrien	for (cdp = c->vec; *cdp; cdp++) {
650231990Smp	    size_t len = Strlen(*cdp);
651167465Smp	    buf.len = 0;
652231990Smp	    if (len > 0) {
653231990Smp		Strbuf_append(&buf, *cdp);
654231990Smp		if ((*cdp)[len - 1] != '/')
655231990Smp		    Strbuf_append1(&buf, '/');
656231990Smp	    }
657167465Smp	    Strbuf_append(&buf, cp);
658167465Smp	    Strbuf_terminate(&buf);
65959243Sobrien	    /*
66059243Sobrien	     * We always want to fix the directory here
66159243Sobrien	     * If we are normalizing symlinks
66259243Sobrien	     */
663167465Smp	    dp = dnormalize(buf.s, symlinks == SYM_IGNORE ||
664167465Smp				   symlinks == SYM_EXPAND);
66559243Sobrien	    if (chdir(short2str(dp)) >= 0) {
66659243Sobrien		printd = 1;
667167465Smp		xfree(buf.s);
668167465Smp		cleanup_until(cp);
66959243Sobrien		return dgoto(dp);
67059243Sobrien	    }
67159243Sobrien	    else if (chdir(short2str(cp)) >= 0) {
67259243Sobrien		printd = 1;
673167465Smp		xfree(dp);
674167465Smp		xfree(buf.s);
675167465Smp		cleanup_ignore(cp);
676167465Smp		cleanup_until(cp);
67759243Sobrien		return dgoto(cp);
67859243Sobrien	    }
679316957Sdchagin	    xfree(dp);
68059243Sobrien	}
681167465Smp	xfree(buf.s);
68259243Sobrien    }
68359243Sobrien    dp = varval(cp);
68459243Sobrien    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
685167465Smp	cleanup_until(cp);
68659243Sobrien	cp = Strsave(dp);
68759243Sobrien	printd = 1;
68859243Sobrien	return dgoto(cp);
68959243Sobrien    }
69059243Sobrien    /*
69159243Sobrien     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
69259243Sobrien     * directories we could get to.
69359243Sobrien     */
694167465Smp    if (!bequiet)
695167465Smp	stderror(ERR_SYSTEM, short2str(cp), strerror(serrno));
696167465Smp    cleanup_until(cp);
697167465Smp    return (NULL);
69859243Sobrien}
69959243Sobrien
70059243Sobrien
70159243Sobrien/*
70259243Sobrien * dopushd - push new directory onto directory stack.
70359243Sobrien *	with no arguments exchange top and second.
70459243Sobrien *	with numeric argument (+n) bring it to top.
70559243Sobrien */
70659243Sobrien/*ARGSUSED*/
70759243Sobrienvoid
708167465Smpdopushd(Char **v, struct command *c)
70959243Sobrien{
710145479Smp    struct directory *dp;
711145479Smp    Char *cp;
71259243Sobrien    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
71359243Sobrien
71459243Sobrien    USE(c);
71559243Sobrien    printd = 1;
71659243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
71759243Sobrien
71859243Sobrien    if (cp == NULL) {
71959243Sobrien	if (adrof(STRpushdtohome)) {
72059243Sobrien	    if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
72159243Sobrien		stderror(ERR_NAME | ERR_NOHOMEDIR);
72259243Sobrien	    if (chdir(short2str(cp)) < 0)
72359243Sobrien		stderror(ERR_NAME | ERR_CANTCHANGE);
724195609Smp	    if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL)
72559243Sobrien		return;
726167465Smp	    dp = xcalloc(sizeof(struct directory), 1);
72759243Sobrien	    dp->di_name = cp;
72859243Sobrien	    dp->di_count = 0;
72959243Sobrien	    dp->di_prev = dcwd;
73059243Sobrien	    dp->di_next = dcwd->di_next;
73159243Sobrien	    dcwd->di_next = dp;
73259243Sobrien	    dp->di_next->di_prev = dp;
73359243Sobrien	}
73459243Sobrien	else {
73559243Sobrien	    char   *tmp;
73659243Sobrien
73759243Sobrien	    if ((dp = dcwd->di_prev) == &dhead)
73859243Sobrien		dp = dhead.di_prev;
73959243Sobrien	    if (dp == dcwd)
74059243Sobrien		stderror(ERR_NAME | ERR_NODIR);
74159243Sobrien	    if (chdir(tmp = short2str(dp->di_name)) < 0)
74259243Sobrien		stderror(ERR_SYSTEM, tmp, strerror(errno));
74359243Sobrien	    dp->di_prev->di_next = dp->di_next;
74459243Sobrien	    dp->di_next->di_prev = dp->di_prev;
74559243Sobrien	    dp->di_next = dcwd->di_next;
74659243Sobrien	    dp->di_prev = dcwd;
74759243Sobrien	    dcwd->di_next->di_prev = dp;
74859243Sobrien	    dcwd->di_next = dp;
74959243Sobrien	}
75059243Sobrien    }
75159243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
75259243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
75359243Sobrien	/* NOTREACHED */
75459243Sobrien	return;
75559243Sobrien    }
75659243Sobrien    else if ((dp = dfind(cp)) != NULL) {
75759243Sobrien	char   *tmp;
75859243Sobrien
75959243Sobrien	if (chdir(tmp = short2str(dp->di_name)) < 0)
76059243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
76159243Sobrien	/*
76259243Sobrien	 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
76359243Sobrien	 */
76459243Sobrien	if (adrof(STRdextract))
76559243Sobrien	    dextract(dp);
76659243Sobrien    }
76759243Sobrien    else {
768145479Smp	Char *ccp;
76959243Sobrien
770195609Smp	if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL)
77159243Sobrien	    return;
772167465Smp	dp = xcalloc(sizeof(struct directory), 1);
77359243Sobrien	dp->di_name = ccp;
77459243Sobrien	dp->di_count = 0;
77559243Sobrien	dp->di_prev = dcwd;
77659243Sobrien	dp->di_next = dcwd->di_next;
77759243Sobrien	dcwd->di_next = dp;
77859243Sobrien	dp->di_next->di_prev = dp;
77959243Sobrien    }
78059243Sobrien    dnewcwd(dp, dflag);
78159243Sobrien}
78259243Sobrien
78359243Sobrien/*
78459243Sobrien * dfind - find a directory if specified by numeric (+n) argument
78559243Sobrien */
78659243Sobrienstatic struct directory *
787167465Smpdfind(Char *cp)
78859243Sobrien{
789145479Smp    struct directory *dp;
790145479Smp    int i;
791145479Smp    Char *ep;
79259243Sobrien
79359243Sobrien    if (*cp++ != '+')
79459243Sobrien	return (0);
79559243Sobrien    for (ep = cp; Isdigit(*ep); ep++)
79659243Sobrien	continue;
79759243Sobrien    if (*ep)
79859243Sobrien	return (0);
79959243Sobrien    i = getn(cp);
80059243Sobrien    if (i <= 0)
80159243Sobrien	return (0);
80259243Sobrien    for (dp = dcwd; i != 0; i--) {
80359243Sobrien	if ((dp = dp->di_prev) == &dhead)
80459243Sobrien	    dp = dp->di_prev;
80559243Sobrien	if (dp == dcwd)
80659243Sobrien	    stderror(ERR_NAME | ERR_DEEP);
80759243Sobrien    }
80859243Sobrien    return (dp);
80959243Sobrien}
81059243Sobrien
81159243Sobrien/*
81259243Sobrien * dopopd - pop a directory out of the directory stack
81359243Sobrien *	with a numeric argument just discard it.
81459243Sobrien */
81559243Sobrien/*ARGSUSED*/
81659243Sobrienvoid
817167465Smpdopopd(Char **v, struct command *c)
81859243Sobrien{
81959243Sobrien    Char *cp;
820145479Smp    struct directory *dp, *p = NULL;
82159243Sobrien    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
82259243Sobrien
82359243Sobrien    USE(c);
82459243Sobrien    printd = 1;
82559243Sobrien    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
82659243Sobrien
82759243Sobrien    if (cp == NULL)
82859243Sobrien	dp = dcwd;
82959243Sobrien    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
83059243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
83159243Sobrien	/* NOTREACHED */
83259243Sobrien	return;
83359243Sobrien    }
83459243Sobrien    else if ((dp = dfind(cp)) == 0)
83559243Sobrien	stderror(ERR_NAME | ERR_BADDIR);
83659243Sobrien    if (dp->di_prev == &dhead && dp->di_next == &dhead)
83759243Sobrien	stderror(ERR_NAME | ERR_EMPTY);
83859243Sobrien    if (dp == dcwd) {
83959243Sobrien	char   *tmp;
84059243Sobrien
84159243Sobrien	if ((p = dp->di_prev) == &dhead)
84259243Sobrien	    p = dhead.di_prev;
84359243Sobrien	if (chdir(tmp = short2str(p->di_name)) < 0)
84459243Sobrien	    stderror(ERR_SYSTEM, tmp, strerror(errno));
84559243Sobrien    }
84659243Sobrien    dp->di_prev->di_next = dp->di_next;
84759243Sobrien    dp->di_next->di_prev = dp->di_prev;
848167465Smp    dfree(dp);
84959243Sobrien    if (dp == dcwd) {
850167465Smp        dnewcwd(p, dflag);
85159243Sobrien    }
85259243Sobrien    else {
85359243Sobrien	printdirs(dflag);
85459243Sobrien    }
85559243Sobrien}
85659243Sobrien
85759243Sobrien/*
85859243Sobrien * dfree - free the directory (or keep it if it still has ref count)
85959243Sobrien */
86059243Sobrienvoid
861167465Smpdfree(struct directory *dp)
86259243Sobrien{
86359243Sobrien
86459243Sobrien    if (dp->di_count != 0) {
86559243Sobrien	dp->di_next = dp->di_prev = 0;
86659243Sobrien    }
86759243Sobrien    else {
868167465Smp	xfree(dp->di_name);
869167465Smp	xfree(dp);
87059243Sobrien    }
87159243Sobrien}
87259243Sobrien
87359243Sobrien/*
87459243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
87559243Sobrien *	we are of course assuming that the file system is standardly
87659243Sobrien *	constructed (always have ..'s, directories have links)
87759243Sobrien */
87859243SobrienChar   *
879167465Smpdcanon(Char *cp, Char *p)
88059243Sobrien{
881145479Smp    Char *sp;
882145479Smp    Char *p1, *p2;	/* general purpose */
883145479Smp    int    slash;
884167465Smp#ifdef HAVE_SLASHSLASH
885145479Smp    int    slashslash;
886167465Smp#endif /* HAVE_SLASHSLASH */
88773393Skris    size_t  clen;
88859243Sobrien
88959243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
890167465Smp    Char *mlink, *newcp;
891167465Smp    char *tlink;
892167465Smp    size_t cc;
89359243Sobrien#endif /* S_IFLNK */
89459243Sobrien
895167465Smp    clen = Strlen(cp);
89659243Sobrien
89759243Sobrien    /*
89859243Sobrien     * christos: if the path given does not start with a slash prepend cwd. If
89973393Skris     * cwd does not start with a slash or the result would be too long try to
90073393Skris     * correct it.
90159243Sobrien     */
90259243Sobrien    if (!ABSOLUTEP(cp)) {
903167465Smp	Char *tmpdir;
90473393Skris	size_t	len;
90559243Sobrien
90659243Sobrien	p1 = varval(STRcwd);
90773393Skris	if (p1 == STRNULL || !ABSOLUTEP(p1)) {
908167465Smp	    Char *new_cwd = agetcwd();
909167465Smp
910167465Smp	    if (new_cwd == NULL) {
91173393Skris		xprintf("%s: %s\n", progname, strerror(errno));
912167465Smp		setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB);
91373393Skris	    }
914167465Smp	    else
915167465Smp		setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB);
91673393Skris	    p1 = varval(STRcwd);
91773393Skris	}
91873393Skris	len = Strlen(p1);
919167465Smp	tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir));
92059243Sobrien	(void) Strcpy(tmpdir, p1);
92159243Sobrien	(void) Strcat(tmpdir, STRslash);
92259243Sobrien	(void) Strcat(tmpdir, cp);
923167465Smp	xfree(cp);
924167465Smp	cp = p = tmpdir;
92559243Sobrien    }
92659243Sobrien
927167465Smp#ifdef HAVE_SLASHSLASH
92859243Sobrien    slashslash = (cp[0] == '/' && cp[1] == '/');
929167465Smp#endif /* HAVE_SLASHSLASH */
93059243Sobrien
93159243Sobrien    while (*p) {		/* for each component */
93259243Sobrien	sp = p;			/* save slash address */
93359243Sobrien	while (*++p == '/')	/* flush extra slashes */
93459243Sobrien	    continue;
93559243Sobrien	if (p != ++sp)
93659243Sobrien	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
93759243Sobrien		continue;
93859243Sobrien	p = sp;			/* save start of component */
93959243Sobrien	slash = 0;
94059243Sobrien	if (*p)
94159243Sobrien	    while (*++p)	/* find next slash or end of path */
94259243Sobrien		if (*p == '/') {
94359243Sobrien		    slash = 1;
94459243Sobrien		    *p = 0;
94559243Sobrien		    break;
94659243Sobrien		}
94759243Sobrien
948167465Smp#ifdef HAVE_SLASHSLASH
94959243Sobrien	if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
95059243Sobrien	    slashslash = 1;
951167465Smp#endif /* HAVE_SLASHSLASH */
95259243Sobrien	if (*sp == '\0') {	/* if component is null */
95359243Sobrien	    if (--sp == cp)	/* if path is one char (i.e. /) */
95459243Sobrien		break;
95559243Sobrien	    else
95659243Sobrien		*sp = '\0';
95759243Sobrien	}
95859243Sobrien	else if (sp[0] == '.' && sp[1] == 0) {
95959243Sobrien	    if (slash) {
96059243Sobrien		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
96159243Sobrien		    continue;
96259243Sobrien		p = --sp;
96359243Sobrien	    }
96459243Sobrien	    else if (--sp != cp)
96559243Sobrien		*sp = '\0';
96659243Sobrien	    else
96759243Sobrien		sp[1] = '\0';
96859243Sobrien	}
96959243Sobrien	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
97059243Sobrien	    /*
97159243Sobrien	     * We have something like "yyy/xxx/..", where "yyy" can be null or
97259243Sobrien	     * a path starting at /, and "xxx" is a single component. Before
97359243Sobrien	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
97459243Sobrien	     * symbolic link.
97559243Sobrien	     */
97659243Sobrien	    *--sp = 0;		/* form the pathname for readlink */
97759243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
97859243Sobrien	    if (sp != cp && /* symlinks != SYM_IGNORE && */
979167465Smp		(tlink = areadlink(short2str(cp))) != NULL) {
980167465Smp		mlink = str2short(tlink);
981167465Smp		xfree(tlink);
98259243Sobrien
98359243Sobrien		if (slash)
98459243Sobrien		    *p = '/';
98559243Sobrien		/*
98659243Sobrien		 * Point p to the '/' in "/..", and restore the '/'.
98759243Sobrien		 */
98859243Sobrien		*(p = sp) = '/';
989145479Smp		if (*mlink != '/') {
99059243Sobrien		    /*
99159243Sobrien		     * Relative path, expand it between the "yyy/" and the
99259243Sobrien		     * "/..". First, back sp up to the character past "yyy/".
99359243Sobrien		     */
99459243Sobrien		    while (*--sp != '/')
99559243Sobrien			continue;
99659243Sobrien		    sp++;
99759243Sobrien		    *sp = 0;
99859243Sobrien		    /*
999145479Smp		     * New length is "yyy/" + mlink + "/.." and rest
100059243Sobrien		     */
1001167465Smp		    p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) +
1002167465Smp					  Strlen(p) + 1) * sizeof(Char));
100359243Sobrien		    /*
100459243Sobrien		     * Copy new path into newcp
100559243Sobrien		     */
100659243Sobrien		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
100759243Sobrien			continue;
1008145479Smp		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
100959243Sobrien			continue;
101059243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
101159243Sobrien			continue;
101259243Sobrien		    /*
101359243Sobrien		     * Restart canonicalization at expanded "/xxx".
101459243Sobrien		     */
101559243Sobrien		    p = sp - cp - 1 + newcp;
101659243Sobrien		}
101759243Sobrien		else {
1018167465Smp		    newcp = Strspl(mlink, p);
101959243Sobrien		    /*
102059243Sobrien		     * Restart canonicalization at beginning
102159243Sobrien		     */
102259243Sobrien		    p = newcp;
102359243Sobrien		}
1024167465Smp		xfree(cp);
102559243Sobrien		cp = newcp;
1026167465Smp#ifdef HAVE_SLASHSLASH
102759243Sobrien                slashslash = (cp[0] == '/' && cp[1] == '/');
1028167465Smp#endif /* HAVE_SLASHSLASH */
102959243Sobrien		continue;	/* canonicalize the link */
103059243Sobrien	    }
103159243Sobrien#endif /* S_IFLNK */
103259243Sobrien	    *sp = '/';
103359243Sobrien	    if (sp != cp)
103459243Sobrien		while (*--sp != '/')
103559243Sobrien		    continue;
103659243Sobrien	    if (slash) {
103759243Sobrien		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
103859243Sobrien		    continue;
103959243Sobrien		p = sp;
104059243Sobrien	    }
104159243Sobrien	    else if (cp == sp)
104259243Sobrien		*++sp = '\0';
104359243Sobrien	    else
104459243Sobrien		*sp = '\0';
104559243Sobrien	}
104659243Sobrien	else {			/* normal dir name (not . or .. or nothing) */
104759243Sobrien
104859243Sobrien#ifdef S_IFLNK			/* if we have symlinks */
104959243Sobrien	    if (sp != cp && symlinks == SYM_CHASE &&
1050167465Smp		(tlink = areadlink(short2str(cp))) != NULL) {
1051167465Smp		mlink = str2short(tlink);
1052167465Smp		xfree(tlink);
105359243Sobrien
105459243Sobrien		/*
105559243Sobrien		 * restore the '/'.
105659243Sobrien		 */
105759243Sobrien		if (slash)
105859243Sobrien		    *p = '/';
105959243Sobrien
106059243Sobrien		/*
106159243Sobrien		 * point sp to p (rather than backing up).
106259243Sobrien		 */
106359243Sobrien		sp = p;
106459243Sobrien
1065145479Smp		if (*mlink != '/') {
106659243Sobrien		    /*
106759243Sobrien		     * Relative path, expand it between the "yyy/" and the
106859243Sobrien		     * remainder. First, back sp up to the character past
106959243Sobrien		     * "yyy/".
107059243Sobrien		     */
107159243Sobrien		    while (*--sp != '/')
107259243Sobrien			continue;
107359243Sobrien		    sp++;
107459243Sobrien		    *sp = 0;
107559243Sobrien		    /*
1076145479Smp		     * New length is "yyy/" + mlink + "/.." and rest
107759243Sobrien		     */
1078167465Smp		    p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) +
1079167465Smp					  Strlen(p) + 1) * sizeof(Char));
108059243Sobrien		    /*
108159243Sobrien		     * Copy new path into newcp
108259243Sobrien		     */
108359243Sobrien		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
108459243Sobrien			continue;
1085145479Smp		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
108659243Sobrien			continue;
108759243Sobrien		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
108859243Sobrien			continue;
108959243Sobrien		    /*
109059243Sobrien		     * Restart canonicalization at expanded "/xxx".
109159243Sobrien		     */
109259243Sobrien		    p = sp - cp - 1 + newcp;
109359243Sobrien		}
109459243Sobrien		else {
1095167465Smp		    newcp = Strspl(mlink, p);
109659243Sobrien		    /*
109759243Sobrien		     * Restart canonicalization at beginning
109859243Sobrien		     */
109959243Sobrien		    p = newcp;
110059243Sobrien		}
1101167465Smp		xfree(cp);
110259243Sobrien		cp = newcp;
1103167465Smp#ifdef HAVE_SLASHSLASH
110459243Sobrien                slashslash = (cp[0] == '/' && cp[1] == '/');
1105167465Smp#endif /* HAVE_SLASHSLASH */
1106145479Smp		continue;	/* canonicalize the mlink */
110759243Sobrien	    }
110859243Sobrien#endif /* S_IFLNK */
110959243Sobrien	    if (slash)
111059243Sobrien		*p = '/';
111159243Sobrien	}
111259243Sobrien    }
111359243Sobrien
111459243Sobrien    /*
111559243Sobrien     * fix home...
111659243Sobrien     */
111759243Sobrien#ifdef S_IFLNK
111859243Sobrien    p1 = varval(STRhome);
1119167465Smp    cc = Strlen(p1);
112059243Sobrien    /*
112159243Sobrien     * See if we're not in a subdir of STRhome
112259243Sobrien     */
1123167465Smp    if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 ||
112459243Sobrien	(cp[cc] != '/' && cp[cc] != '\0'))) {
112559243Sobrien	static ino_t home_ino = (ino_t) -1;
112659243Sobrien	static dev_t home_dev = (dev_t) -1;
112759243Sobrien	static Char *home_ptr = NULL;
112859243Sobrien	struct stat statbuf;
112959243Sobrien	int found;
1130167465Smp	Char *copy;
113159243Sobrien
113259243Sobrien	/*
113359243Sobrien	 * Get dev and ino of STRhome
113459243Sobrien	 */
113559243Sobrien	if (home_ptr != p1 &&
113659243Sobrien	    stat(short2str(p1), &statbuf) != -1) {
113759243Sobrien	    home_dev = statbuf.st_dev;
113859243Sobrien	    home_ino = statbuf.st_ino;
113959243Sobrien	    home_ptr = p1;
114059243Sobrien	}
114159243Sobrien	/*
114259243Sobrien	 * Start comparing dev & ino backwards
114359243Sobrien	 */
1144167465Smp	p2 = copy = Strsave(cp);
114559243Sobrien	found = 0;
114659243Sobrien	while (*p2 && stat(short2str(p2), &statbuf) != -1) {
114759243Sobrien	    if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
114859243Sobrien			statbuf.st_ino == home_ino) {
114959243Sobrien			found = 1;
115059243Sobrien			break;
115159243Sobrien	    }
115259243Sobrien	    if ((sp = Strrchr(p2, '/')) != NULL)
115359243Sobrien		*sp = '\0';
115459243Sobrien	}
115559243Sobrien	/*
115659243Sobrien	 * See if we found it
115759243Sobrien	 */
115859243Sobrien	if (*p2 && found) {
115959243Sobrien	    /*
116059243Sobrien	     * Use STRhome to make '~' work
116159243Sobrien	     */
116259243Sobrien	    newcp = Strspl(p1, cp + Strlen(p2));
1163167465Smp	    xfree(cp);
116459243Sobrien	    cp = newcp;
116559243Sobrien	}
1166167465Smp	xfree(copy);
116759243Sobrien    }
116859243Sobrien#endif /* S_IFLNK */
116959243Sobrien
1170167465Smp#ifdef HAVE_SLASHSLASH
117159243Sobrien    if (slashslash) {
117259243Sobrien	if (cp[1] != '/') {
1173167465Smp	    p = xmalloc((Strlen(cp) + 2) * sizeof(Char));
117459243Sobrien	    *p = '/';
117559243Sobrien	    (void) Strcpy(&p[1], cp);
1176167465Smp	    xfree(cp);
117759243Sobrien	    cp = p;
117859243Sobrien	}
117959243Sobrien    }
1180167465Smp    if (cp[1] == '/' && cp[2] == '/') {
1181167465Smp	for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';)
1182167465Smp	    continue;
1183167465Smp    }
1184167465Smp#endif /* HAVE_SLASHSLASH */
118559243Sobrien    return cp;
118659243Sobrien}
118759243Sobrien
118859243Sobrien
118959243Sobrien/*
119059243Sobrien * dnewcwd - make a new directory in the loop the current one
119159243Sobrien */
119259243Sobrienstatic void
1193167465Smpdnewcwd(struct directory *dp, int dflag)
119459243Sobrien{
119559243Sobrien    int print;
119659243Sobrien
119759243Sobrien    if (adrof(STRdunique)) {
119859243Sobrien	struct directory *dn;
119959243Sobrien
120059243Sobrien	for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
120159243Sobrien	    if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
120259243Sobrien		dn->di_next->di_prev = dn->di_prev;
120359243Sobrien		dn->di_prev->di_next = dn->di_next;
120459243Sobrien		dfree(dn);
120559243Sobrien		break;
120659243Sobrien	    }
120759243Sobrien    }
120859243Sobrien    dcwd = dp;
120959243Sobrien    dset(dcwd->di_name);
121059243Sobrien    dgetstack();
121159243Sobrien    print = printd;		/* if printd is set, print dirstack... */
121259243Sobrien    if (adrof(STRpushdsilent))	/* but pushdsilent overrides printd... */
121359243Sobrien	print = 0;
121459243Sobrien    if (dflag & DIR_PRINT)	/* but DIR_PRINT overrides pushdsilent... */
121559243Sobrien	print = 1;
121659243Sobrien    if (bequiet)		/* and bequiet overrides everything */
121759243Sobrien	print = 0;
121859243Sobrien    if (print)
121959243Sobrien	printdirs(dflag);
122059243Sobrien    cwd_cmd();			/* PWP: run the defined cwd command */
122159243Sobrien}
122259243Sobrien
122359243Sobrienvoid
1224167465Smpdsetstack(void)
122559243Sobrien{
122659243Sobrien    Char **cp;
122759243Sobrien    struct varent *vp;
122859243Sobrien    struct directory *dn, *dp;
122959243Sobrien
1230100616Smp    if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
123159243Sobrien	return;
123259243Sobrien
123359243Sobrien    /* Free the whole stack */
123459243Sobrien    while ((dn = dhead.di_prev) != &dhead) {
123559243Sobrien	dn->di_next->di_prev = dn->di_prev;
123659243Sobrien	dn->di_prev->di_next = dn->di_next;
123759243Sobrien	if (dn != dcwd)
123859243Sobrien	    dfree(dn);
123959243Sobrien    }
124059243Sobrien
124159243Sobrien    /* thread the current working directory */
124259243Sobrien    dhead.di_prev = dhead.di_next = dcwd;
124359243Sobrien    dcwd->di_next = dcwd->di_prev = &dhead;
124459243Sobrien
124559243Sobrien    /* put back the stack */
124659243Sobrien    for (cp = vp->vec; cp && *cp && **cp; cp++) {
1247167465Smp	dp = xcalloc(sizeof(struct directory), 1);
124859243Sobrien	dp->di_name = Strsave(*cp);
124959243Sobrien	dp->di_count = 0;
125059243Sobrien	dp->di_prev = dcwd;
125159243Sobrien	dp->di_next = dcwd->di_next;
125259243Sobrien	dcwd->di_next = dp;
125359243Sobrien	dp->di_next->di_prev = dp;
125459243Sobrien    }
125559243Sobrien    dgetstack();	/* Make $dirstack reflect the current state */
125659243Sobrien}
125759243Sobrien
125859243Sobrienstatic void
1259167465Smpdgetstack(void)
126059243Sobrien{
126159243Sobrien    int i = 0;
126259243Sobrien    Char **dblk, **dbp;
126359243Sobrien    struct directory *dn;
126459243Sobrien
126559243Sobrien    if (adrof(STRdirstack) == NULL)
126659243Sobrien    	return;
126759243Sobrien
1268167465Smp    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
126959243Sobrien	continue;
1270167465Smp    dbp = dblk = xmalloc((i + 1) * sizeof(Char *));
1271167465Smp    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
127259243Sobrien	 *dbp = Strsave(dn->di_name);
127359243Sobrien    *dbp = NULL;
1274167465Smp    cleanup_push(dblk, blk_cleanup);
127559243Sobrien    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1276167465Smp    cleanup_ignore(dblk);
1277167465Smp    cleanup_until(dblk);
127859243Sobrien}
127959243Sobrien
128059243Sobrien/*
128159243Sobrien * getstakd - added by kfk 17 Jan 1984
128259243Sobrien * Support routine for the stack hack.  Finds nth directory in
128359243Sobrien * the directory stack, or finds last directory in stack.
128459243Sobrien */
1285167465Smpconst Char *
1286167465Smpgetstakd(int cnt)
128759243Sobrien{
128859243Sobrien    struct directory *dp;
128959243Sobrien
129059243Sobrien    dp = dcwd;
129159243Sobrien    if (cnt < 0) {		/* < 0 ==> last dir requested. */
129259243Sobrien	dp = dp->di_next;
129359243Sobrien	if (dp == &dhead)
129459243Sobrien	    dp = dp->di_next;
129559243Sobrien    }
129659243Sobrien    else {
129759243Sobrien	while (cnt-- > 0) {
129859243Sobrien	    dp = dp->di_prev;
129959243Sobrien	    if (dp == &dhead)
130059243Sobrien		dp = dp->di_prev;
130159243Sobrien	    if (dp == dcwd)
1302167465Smp		return NULL;
130359243Sobrien	}
130459243Sobrien    }
1305167465Smp    return dp->di_name;
130659243Sobrien}
130759243Sobrien
130859243Sobrien/*
130959243Sobrien * Karl Kleinpaste - 10 Feb 1984
131059243Sobrien * Added dextract(), which is used in pushd +n.
131159243Sobrien * Instead of just rotating the entire stack around, dextract()
131259243Sobrien * lets the user have the nth dir extracted from its current
131359243Sobrien * position, and pushes it onto the top.
131459243Sobrien */
131559243Sobrienstatic void
1316167465Smpdextract(struct directory *dp)
131759243Sobrien{
131859243Sobrien    if (dp == dcwd)
131959243Sobrien	return;
132059243Sobrien    dp->di_next->di_prev = dp->di_prev;
132159243Sobrien    dp->di_prev->di_next = dp->di_next;
132259243Sobrien    dp->di_next = dcwd->di_next;
132359243Sobrien    dp->di_prev = dcwd;
132459243Sobrien    dp->di_next->di_prev = dp;
132559243Sobrien    dcwd->di_next = dp;
132659243Sobrien}
132759243Sobrien
1328167465Smpstatic void
1329167465Smpbequiet_cleanup(void *dummy)
1330167465Smp{
1331167465Smp    USE(dummy);
1332167465Smp    bequiet = 0;
1333167465Smp}
1334167465Smp
133559243Sobrienvoid
1336167465Smploaddirs(Char *fname)
133759243Sobrien{
133859243Sobrien    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
133959243Sobrien
134059243Sobrien    bequiet = 1;
1341167465Smp    cleanup_push(&bequiet, bequiet_cleanup);
1342167465Smp    if (fname)
134359243Sobrien	loaddirs_cmd[1] = fname;
134459243Sobrien    else if ((fname = varval(STRdirsfile)) != STRNULL)
134559243Sobrien	loaddirs_cmd[1] = fname;
134659243Sobrien    else
134759243Sobrien	loaddirs_cmd[1] = STRtildotdirs;
1348167465Smp    dosource(loaddirs_cmd, NULL);
1349167465Smp    cleanup_until(&bequiet);
135059243Sobrien}
135159243Sobrien
135259243Sobrien/*
135359243Sobrien * create a file called ~/.cshdirs which has a sequence
135459243Sobrien * of pushd commands which will restore the dir stack to
135559243Sobrien * its state before exit/logout. remember that the order
135659243Sobrien * is reversed in the file because we are pushing.
135759243Sobrien * -strike
135859243Sobrien */
135959243Sobrienvoid
1360167465Smprecdirs(Char *fname, int def)
136159243Sobrien{
136259243Sobrien    int     fp, ftmp, oldidfds;
136359243Sobrien    int     cdflag = 0;
136459243Sobrien    struct directory *dp;
136559243Sobrien    unsigned int    num;
136659243Sobrien    Char   *snum;
1367167465Smp    struct Strbuf qname = Strbuf_INIT;
136859243Sobrien
136959243Sobrien    if (fname == NULL && !def)
137059243Sobrien	return;
137159243Sobrien
137259243Sobrien    if (fname == NULL) {
137359243Sobrien	if ((fname = varval(STRdirsfile)) == STRNULL)
137459243Sobrien	    fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
137559243Sobrien	else
137659243Sobrien	    fname = Strsave(fname);
137759243Sobrien    }
137859243Sobrien    else
137959243Sobrien	fname = globone(fname, G_ERROR);
1380167465Smp    cleanup_push(fname, xfree);
1381167465Smp
1382167465Smp    if ((fp = xcreat(short2str(fname), 0600)) == -1) {
1383167465Smp	cleanup_until(fname);
138459243Sobrien	return;
138559243Sobrien    }
138659243Sobrien
138783098Smp    if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
138859243Sobrien	num = (unsigned int) ~0;
138959243Sobrien    else
139059243Sobrien	num = (unsigned int) atoi(short2str(snum));
139159243Sobrien
139259243Sobrien    oldidfds = didfds;
139359243Sobrien    didfds = 0;
139459243Sobrien    ftmp = SHOUT;
139559243Sobrien    SHOUT = fp;
139659243Sobrien
1397167465Smp    cleanup_push(&qname, Strbuf_cleanup);
139859243Sobrien    dp = dcwd->di_next;
139959243Sobrien    do {
140059243Sobrien	if (dp == &dhead)
140159243Sobrien	    continue;
140259243Sobrien
140359243Sobrien	if (cdflag == 0) {
140459243Sobrien	    cdflag = 1;
1405167465Smp	    xprintf("cd %S\n", quote_meta(&qname, dp->di_name));
140659243Sobrien	}
140759243Sobrien	else
1408167465Smp	    xprintf("pushd %S\n", quote_meta(&qname, dp->di_name));
140959243Sobrien
141059243Sobrien	if (num-- == 0)
141159243Sobrien	    break;
141259243Sobrien
141359243Sobrien    } while ((dp = dp->di_next) != dcwd->di_next);
141459243Sobrien
1415167465Smp    xclose(fp);
141659243Sobrien    SHOUT = ftmp;
141759243Sobrien    didfds = oldidfds;
1418167465Smp    cleanup_until(fname);
141959243Sobrien}
1420