sh.dir.c revision 145479
1205194Sdelphij/* $Header: /src/pub/tcsh/sh.dir.c,v 3.66 2005/03/03 16:40:53 kim Exp $ */
2250261Sdelphij/*
3205194Sdelphij * sh.dir.c: Directory manipulation functions
4205194Sdelphij */
5205194Sdelphij/*-
6206002Sdelphij * Copyright (c) 1980, 1991 The Regents of the University of California.
7206002Sdelphij * All rights reserved.
8205194Sdelphij *
9206002Sdelphij * Redistribution and use in source and binary forms, with or without
10205194Sdelphij * modification, are permitted provided that the following conditions
11237410Sdelphij * are met:
12237410Sdelphij * 1. Redistributions of source code must retain the above copyright
13237410Sdelphij *    notice, this list of conditions and the following disclaimer.
14206708Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
15205194Sdelphij *    notice, this list of conditions and the following disclaimer in the
16205194Sdelphij *    documentation and/or other materials provided with the distribution.
17205194Sdelphij * 3. Neither the name of the University nor the names of its contributors
18205194Sdelphij *    may be used to endorse or promote products derived from this software
19237410Sdelphij *    without specific prior written permission.
20205194Sdelphij *
21205194Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22205194Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23237410Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24205194Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25206002Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26205194Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27205194Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28205194Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29205194Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30205194Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31205194Sdelphij * SUCH DAMAGE.
32205194Sdelphij */
33205194Sdelphij#include "sh.h"
34205194Sdelphij#include "ed.h"
35205194Sdelphij
36206924SdelphijRCSID("$Id: sh.dir.c,v 3.66 2005/03/03 16:40:53 kim Exp $")
37205194Sdelphij
38205194Sdelphij/*
39205194Sdelphij * C Shell - directory management
40205194Sdelphij */
41205194Sdelphij
42205194Sdelphijstatic	void			 dstart		__P((const char *));
43205194Sdelphijstatic	struct directory	*dfind		__P((Char *));
44205194Sdelphijstatic	Char 			*dfollow	__P((Char *));
45205194Sdelphijstatic	void 	 	 	 printdirs	__P((int));
46205194Sdelphijstatic	Char 			*dgoto		__P((Char *));
47205194Sdelphijstatic	void 	 	 	 dnewcwd	__P((struct directory *, int));
48205194Sdelphijstatic	void 	 	 	 dset		__P((Char *));
49205194Sdelphijstatic  void 			 dextract	__P((struct directory *));
50205194Sdelphijstatic  int 			 skipargs	__P((Char ***, const char *,
51205194Sdelphij						     const char *));
52205194Sdelphijstatic	void			 dgetstack	__P((void));
53205194Sdelphij
54205194Sdelphijstatic struct directory dhead INIT_ZERO_STRUCT;		/* "head" of loop */
55205194Sdelphijstatic int    printd;			/* force name to be printed */
56205194Sdelphij
57205194Sdelphijint     bequiet = 0;		/* do not print dir stack -strike */
58205194Sdelphij
59205194Sdelphijstatic void
60205194Sdelphijdstart(from)
61205194Sdelphij    const char *from;
62205194Sdelphij{
63205194Sdelphij    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
64205194Sdelphij}
65205194Sdelphij
66205194Sdelphij/*
67205194Sdelphij * dinit - initialize current working directory
68205194Sdelphij */
69205194Sdelphijvoid
70205194Sdelphijdinit(hp)
71205194Sdelphij    Char   *hp;
72205194Sdelphij{
73205194Sdelphij    char *tcp;
74205194Sdelphij    Char *cp;
75206002Sdelphij    struct directory *dp;
76205194Sdelphij    char    path[MAXPATHLEN];
77205194Sdelphij
78205194Sdelphij    /* Don't believe the login shell home, because it may be a symlink */
79205194Sdelphij    tcp = (char *) getcwd(path, sizeof(path));
80205194Sdelphij    if (tcp == NULL || *tcp == '\0') {
81237410Sdelphij	xprintf("%s: %s\n", progname, strerror(errno));
82205194Sdelphij	if (hp && *hp) {
83205194Sdelphij	    tcp = short2str(hp);
84237410Sdelphij	    dstart(tcp);
85205194Sdelphij	    if (chdir(tcp) == -1)
86205194Sdelphij		cp = NULL;
87205194Sdelphij	    else
88205194Sdelphij		cp = Strsave(hp);
89237410Sdelphij	}
90205194Sdelphij	else
91205194Sdelphij	    cp = NULL;
92205194Sdelphij	if (cp == NULL) {
93205194Sdelphij	    dstart("/");
94205194Sdelphij	    if (chdir("/") == -1)
95237410Sdelphij		/* I am not even try to print an error message! */
96205194Sdelphij		xexit(1);
97205194Sdelphij	    cp = SAVE("/");
98205194Sdelphij	}
99205194Sdelphij    }
100237410Sdelphij    else {
101237410Sdelphij#ifdef S_IFLNK
102237410Sdelphij	struct stat swd, shp;
103237410Sdelphij
104237410Sdelphij	/*
105237410Sdelphij	 * See if $HOME is the working directory we got and use that
106237410Sdelphij	 */
107237410Sdelphij	if (hp && *hp &&
108205194Sdelphij	    stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
109237410Sdelphij	    DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
110237410Sdelphij		swd.st_ino == shp.st_ino)
111237410Sdelphij	    cp = Strsave(hp);
112237410Sdelphij	else {
113205194Sdelphij	    char   *cwd;
114250261Sdelphij
115205194Sdelphij	    /*
116205194Sdelphij	     * use PWD if we have it (for subshells)
117205194Sdelphij	     */
118205194Sdelphij	    if ((cwd = getenv("PWD")) != NULL) {
119205194Sdelphij		if (stat(cwd, &shp) != -1 &&
120205194Sdelphij			DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
121205194Sdelphij		    swd.st_ino == shp.st_ino)
122205194Sdelphij		    tcp = cwd;
123205194Sdelphij	    }
124205194Sdelphij	    cp = dcanon(SAVE(tcp), STRNULL);
125237410Sdelphij	}
126205194Sdelphij#else /* S_IFLNK */
127205194Sdelphij	cp = dcanon(SAVE(tcp), STRNULL);
128205194Sdelphij#endif /* S_IFLNK */
129205194Sdelphij    }
130205194Sdelphij
131205194Sdelphij    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
132205194Sdelphij    dp->di_name = cp;
133205194Sdelphij    dp->di_count = 0;
134205194Sdelphij    dhead.di_next = dhead.di_prev = dp;
135205194Sdelphij    dp->di_next = dp->di_prev = &dhead;
136205194Sdelphij    printd = 0;
137205194Sdelphij    dnewcwd(dp, 0);
138205194Sdelphij    set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
139205194Sdelphij}
140205194Sdelphij
141205194Sdelphijstatic void
142205194Sdelphijdset(dp)
143205194SdelphijChar *dp;
144205194Sdelphij{
145205194Sdelphij    /*
146205194Sdelphij     * Don't call set() directly cause if the directory contains ` or
147237410Sdelphij     * other junk characters glob will fail.
148237410Sdelphij     */
149237410Sdelphij    set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
150237410Sdelphij    set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
151237410Sdelphij
152237410Sdelphij    tsetenv(STRPWD, dp);
153237410Sdelphij}
154237410Sdelphij
155237410Sdelphij#define DIR_PRINT	0x01	/* -p */
156237410Sdelphij#define DIR_LONG  	0x02	/* -l */
157205194Sdelphij#define DIR_VERT  	0x04	/* -v */
158205194Sdelphij#define DIR_LINE  	0x08	/* -n */
159205194Sdelphij#define DIR_SAVE 	0x10	/* -S */
160205194Sdelphij#define DIR_LOAD	0x20	/* -L */
161205194Sdelphij#define DIR_CLEAR	0x40	/* -c */
162205194Sdelphij#define DIR_OLD	  	0x80	/* - */
163205194Sdelphij
164205194Sdelphijstatic int
165205194Sdelphijskipargs(v, dstr, str)
166205194Sdelphij    Char ***v;
167205194Sdelphij    const char   *dstr;
168250261Sdelphij    const char   *str;
169237410Sdelphij{
170237410Sdelphij    Char  **n = *v, *s;
171250261Sdelphij
172205194Sdelphij    int dflag = 0, loop = 1;
173205194Sdelphij    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
174205194Sdelphij	if (*(s = &((*n)[1])) == '\0')	/* test for bare "-" argument */
175205194Sdelphij	    dflag |= DIR_OLD;
176205194Sdelphij	else {
177205194Sdelphij	    char *p;
178205194Sdelphij	    while (loop && *s != '\0')	/* examine flags */
179205194Sdelphij	    {
180205194Sdelphij		if ((p = strchr(dstr, *s++)) != NULL)
181205194Sdelphij		    dflag |= (1 << (p - dstr));
182205194Sdelphij	        else {
183205194Sdelphij		    stderror(ERR_DIRUS, short2str(**v), dstr, str);
184237410Sdelphij		    loop = 0;	/* break from both loops */
185237410Sdelphij		    break;
186237410Sdelphij	        }
187237410Sdelphij	    }
188237410Sdelphij	}
189237410Sdelphij    if (*n && (dflag & DIR_OLD))
190237410Sdelphij	stderror(ERR_DIRUS, short2str(**v), dstr, str);
191237410Sdelphij    *v = n;
192237410Sdelphij    /* make -l, -v, and -n imply -p */
193205194Sdelphij    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
194237410Sdelphij	dflag |= DIR_PRINT;
195237410Sdelphij    return dflag;
196237410Sdelphij}
197237410Sdelphij
198237410Sdelphij/*
199237410Sdelphij * dodirs - list all directories in directory loop
200237410Sdelphij */
201237410Sdelphij/*ARGSUSED*/
202250261Sdelphijvoid
203250261Sdelphijdodirs(v, c)
204205194Sdelphij    Char  **v;
205205194Sdelphij    struct command *c;
206205194Sdelphij{
207205194Sdelphij    static char flags[] = "plvnSLc";
208237410Sdelphij    int dflag = skipargs(&v, flags, "");
209237410Sdelphij
210237410Sdelphij    USE(c);
211237410Sdelphij    if ((dflag & DIR_CLEAR) != 0) {
212237410Sdelphij	struct directory *dp, *fdp;
213237410Sdelphij	for (dp = dcwd->di_next; dp != dcwd; ) {
214237410Sdelphij	    fdp = dp;
215237410Sdelphij	    dp = dp->di_next;
216250261Sdelphij	    if (fdp != &dhead)
217250261Sdelphij		dfree(fdp);
218250261Sdelphij	}
219237410Sdelphij	dhead.di_next = dhead.di_prev = dp;
220250261Sdelphij	dp->di_next = dp->di_prev = &dhead;
221205194Sdelphij    }
222237410Sdelphij    if ((dflag & DIR_LOAD) != 0)
223237410Sdelphij	loaddirs(*v);
224205194Sdelphij    else if ((dflag & DIR_SAVE) != 0)
225237410Sdelphij	recdirs(*v, 1);
226205194Sdelphij
227205194Sdelphij    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
228237410Sdelphij	v++;
229205194Sdelphij
230237410Sdelphij    if (*v != NULL || (dflag & DIR_OLD))
231237410Sdelphij	stderror(ERR_DIRUS, "dirs", flags, "");
232237410Sdelphij    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
233237410Sdelphij	printdirs(dflag);
234237410Sdelphij}
235237410Sdelphij
236237410Sdelphijstatic void
237237410Sdelphijprintdirs(dflag)
238237410Sdelphij    int dflag;
239237410Sdelphij{
240237410Sdelphij    struct directory *dp;
241237410Sdelphij    Char   *s, *user;
242237410Sdelphij    int     idx, len, cur;
243237410Sdelphij
244237410Sdelphij    dp = dcwd;
245237410Sdelphij    idx = 0;
246237410Sdelphij    cur = 0;
247237410Sdelphij    do {
248250261Sdelphij	if (dp == &dhead)
249205194Sdelphij	    continue;
250206708Sdelphij	if (dflag & DIR_VERT) {
251205194Sdelphij	    xprintf("%d\t", idx++);
252205194Sdelphij	    cur = 0;
253205194Sdelphij	}
254205194Sdelphij	s = dp->di_name;
255205194Sdelphij	user = NULL;
256205194Sdelphij	if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
257205194Sdelphij	    len = (int) (Strlen(user) + Strlen(s) + 2);
258205194Sdelphij	else
259205194Sdelphij	    len = (int) (Strlen(s) + 1);
260205194Sdelphij
261205194Sdelphij	cur += len;
262205194Sdelphij	if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
263205194Sdelphij	    xputchar('\n');
264205194Sdelphij	    cur = len;
265205194Sdelphij	}
266205194Sdelphij	if (user)
267205194Sdelphij	    xprintf("~%S", user);
268205194Sdelphij	xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
269205194Sdelphij    } while ((dp = dp->di_prev) != dcwd);
270205194Sdelphij    if (!(dflag & DIR_VERT))
271205194Sdelphij	xputchar('\n');
272205194Sdelphij}
273205194Sdelphij
274205194Sdelphijvoid
275205194Sdelphijdtildepr(dir)
276205194Sdelphij    Char *dir;
277205194Sdelphij{
278205194Sdelphij    Char* user;
279205194Sdelphij    if ((user = getusername(&dir)) != NULL)
280205194Sdelphij	xprintf("~%-S%S", user, dir);
281205194Sdelphij    else
282205194Sdelphij	xprintf("%S", dir);
283205194Sdelphij}
284205194Sdelphij
285205194Sdelphijvoid
286205194Sdelphijdtilde()
287205194Sdelphij{
288205194Sdelphij    struct directory *d = dcwd;
289205194Sdelphij
290205194Sdelphij    do {
291205194Sdelphij	if (d == &dhead)
292205194Sdelphij	    continue;
293205194Sdelphij	d->di_name = dcanon(d->di_name, STRNULL);
294250261Sdelphij    } while ((d = d->di_prev) != dcwd);
295205194Sdelphij
296250261Sdelphij    dset(dcwd->di_name);
297250261Sdelphij}
298250261Sdelphij
299206002Sdelphij
300250261Sdelphij/* dnormalize():
301205194Sdelphij *	The path will be normalized if it
302205194Sdelphij *	1) is "..",
303205194Sdelphij *	2) or starts with "../",
304205194Sdelphij *	3) or ends with "/..",
305205194Sdelphij *	4) or contains the string "/../",
306205194Sdelphij *	then it will be normalized, unless those strings are quoted.
307237410Sdelphij *	Otherwise, a copy is made and sent back.
308237410Sdelphij */
309237410SdelphijChar   *
310237410Sdelphijdnormalize(cp, expnd)
311237410Sdelphij    Char   *cp;
312237410Sdelphij    int expnd;
313237410Sdelphij{
314237410Sdelphij
315237410Sdelphij/* return true if dp is of the form "../xxx" or "/../xxx" */
316237410Sdelphij#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
317205194Sdelphij#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
318205194Sdelphij
319205194Sdelphij#ifdef S_IFLNK
320205194Sdelphij    if (expnd) {
321205194Sdelphij 	int     dotdot = 0;
322205194Sdelphij	Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
323205194Sdelphij	struct stat sb;
324205194Sdelphij# ifdef apollo
325205194Sdelphij	int slashslash;
326205194Sdelphij# endif /* apollo */
327205194Sdelphij
328205194Sdelphij	/*
329205194Sdelphij	 * count the number of "../xxx" or "xxx/../xxx" in the path
330205194Sdelphij	 */
331205194Sdelphij	for (dp=start; *dp && *(dp+1); dp++)
332205194Sdelphij	    if (IS_DOTDOT(start, dp))
333205194Sdelphij	        dotdot++;
334205194Sdelphij	/*
335237410Sdelphij	 * if none, we are done.
336237410Sdelphij	 */
337205194Sdelphij        if (dotdot == 0)
338205194Sdelphij	    return (Strsave(cp));
339205194Sdelphij
340205194Sdelphij	/*
341205194Sdelphij	 * If the path doesn't exist, we are done too.
342205194Sdelphij	 */
343205194Sdelphij	if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT)
344205194Sdelphij	    return (Strsave(cp));
345205194Sdelphij
346205194Sdelphij
347205194Sdelphij	cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
348205194Sdelphij					   sizeof(Char)));
349205194Sdelphij	(void) Strcpy(cwd, dcwd->di_name);
350205194Sdelphij
351205194Sdelphij	/*
352205194Sdelphij	 * If the path starts with a slash, we are not relative to
353237410Sdelphij	 * the current working directory.
354237410Sdelphij	 */
355205194Sdelphij	if (ABSOLUTEP(start))
356205194Sdelphij	    *cwd = '\0';
357205194Sdelphij# ifdef apollo
358205194Sdelphij	slashslash = cwd[0] == '/' && cwd[1] == '/';
359205194Sdelphij# endif /* apollo */
360205194Sdelphij
361205194Sdelphij	/*
362205194Sdelphij	 * Ignore . and count ..'s
363205194Sdelphij	 */
364205194Sdelphij	for (;;) {
365205194Sdelphij	    dotdot = 0;
366205194Sdelphij	    buf[0] = '\0';
367205194Sdelphij	    dp = buf;
368205194Sdelphij	    while (*cp)
369205194Sdelphij	        if (IS_DOT(start, cp)) {
370205194Sdelphij	            if (*++cp)
371205194Sdelphij	                cp++;
372205194Sdelphij	        }
373205194Sdelphij	        else if (IS_DOTDOT(start, cp)) {
374205194Sdelphij		    if (buf[0])
375205194Sdelphij		        break; /* finish analyzing .././../xxx/[..] */
376205194Sdelphij		    dotdot++;
377205194Sdelphij		    cp += 2;
378205194Sdelphij		    if (*cp)
379205194Sdelphij		        cp++;
380205194Sdelphij	        }
381205194Sdelphij	        else
382237410Sdelphij			*dp++ = *cp++;
383205194Sdelphij
384205194Sdelphij	    *dp = '\0';
385205194Sdelphij	    while (dotdot > 0)
386205194Sdelphij	        if ((dp = Strrchr(cwd, '/')) != NULL) {
387205194Sdelphij# ifdef apollo
388205194Sdelphij		    if (dp == &cwd[1])
389205194Sdelphij		        slashslash = 1;
390205194Sdelphij# endif /* apollo */
391237410Sdelphij		        *dp = '\0';
392205194Sdelphij		        dotdot--;
393205194Sdelphij	        }
394205194Sdelphij	        else
395205194Sdelphij		    break;
396205194Sdelphij
397205194Sdelphij	    if (!*cwd) {	/* too many ..'s, starts with "/" */
398237410Sdelphij	        cwd[0] = '/';
399237410Sdelphij# ifdef apollo
400205194Sdelphij		cwd[1] = '/';
401205194Sdelphij		cwd[2] = '\0';
402237410Sdelphij# else /* !apollo */
403205194Sdelphij		cwd[1] = '\0';
404237410Sdelphij# endif /* apollo */
405205194Sdelphij	    }
406205194Sdelphij# ifdef apollo
407205194Sdelphij	    else if (slashslash && cwd[1] == '\0') {
408237410Sdelphij		cwd[1] = '/';
409237410Sdelphij		cwd[2] = '\0';
410205194Sdelphij	    }
411205194Sdelphij# endif /* apollo */
412205194Sdelphij
413205194Sdelphij	    if (buf[0]) {
414205194Sdelphij	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
415205194Sdelphij		    cwd[dotdot++] = '/';
416237410Sdelphij	        cwd[dotdot] = '\0';
417205194Sdelphij	        dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
418205194Sdelphij	        xfree((ptr_t) cwd);
419205194Sdelphij	        cwd = dp;
420205194Sdelphij	        if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
421205194Sdelphij		    cwd[--dotdot] = '\0';
422205194Sdelphij	    }
423205194Sdelphij	    /* Reduction of ".." following the stuff we collected in buf
424205194Sdelphij	     * only makes sense if the directory item in buf really exists.
425237410Sdelphij	     * Avoid reduction of "-I../.." (typical compiler call) to ""
426237410Sdelphij	     * or "/usr/nonexistant/../bin" to "/usr/bin":
427237410Sdelphij	     */
428237410Sdelphij	    if (cwd[0]) {
429237410Sdelphij	        struct stat exists;
430205194Sdelphij		if (0 != stat(short2str(cwd), &exists)) {
431205194Sdelphij		    xfree((ptr_t) cwd);
432205194Sdelphij		    return Strsave(start);
433205194Sdelphij		}
434205194Sdelphij	    }
435205194Sdelphij	    if (!*cp)
436205194Sdelphij	        break;
437205194Sdelphij	}
438237410Sdelphij	return cwd;
439205194Sdelphij    }
440205194Sdelphij#endif /* S_IFLNK */
441205194Sdelphij    return Strsave(cp);
442205194Sdelphij}
443205194Sdelphij
444205194Sdelphij
445205194Sdelphij/*
446205194Sdelphij * dochngd - implement chdir command.
447205194Sdelphij */
448205194Sdelphij/*ARGSUSED*/
449205194Sdelphijvoid
450205194Sdelphijdochngd(v, c)
451205194Sdelphij    Char  **v;
452205194Sdelphij    struct command *c;
453205194Sdelphij{
454205194Sdelphij    Char *cp;
455205194Sdelphij    struct directory *dp;
456205194Sdelphij    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
457205194Sdelphij
458205194Sdelphij    USE(c);
459205194Sdelphij    printd = 0;
460205194Sdelphij    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
461205194Sdelphij
462205194Sdelphij    if (cp == NULL) {
463205194Sdelphij	if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
464205194Sdelphij	    stderror(ERR_NAME | ERR_NOHOMEDIR);
465205194Sdelphij	if (chdir(short2str(cp)) < 0)
466205194Sdelphij	    stderror(ERR_NAME | ERR_CANTCHANGE);
467237410Sdelphij	cp = Strsave(cp);
468205194Sdelphij    }
469205194Sdelphij    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
470205194Sdelphij	stderror(ERR_NAME | ERR_TOOMANY);
471205194Sdelphij	/* NOTREACHED */
472205194Sdelphij	return;
473205194Sdelphij    }
474205194Sdelphij    else if ((dp = dfind(cp)) != 0) {
475205194Sdelphij	char   *tmp;
476205194Sdelphij
477205194Sdelphij	printd = 1;
478205194Sdelphij	if (chdir(tmp = short2str(dp->di_name)) < 0)
479205194Sdelphij	    stderror(ERR_SYSTEM, tmp, strerror(errno));
480205194Sdelphij	dcwd->di_prev->di_next = dcwd->di_next;
481205194Sdelphij	dcwd->di_next->di_prev = dcwd->di_prev;
482205194Sdelphij	dfree(dcwd);
483205194Sdelphij	dnewcwd(dp, dflag);
484205194Sdelphij	return;
485205194Sdelphij    }
486205194Sdelphij    else
487205194Sdelphij	if ((cp = dfollow(cp)) == NULL)
488205194Sdelphij	    return;
489205194Sdelphij    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
490205194Sdelphij    dp->di_name = cp;
491205194Sdelphij    dp->di_count = 0;
492205194Sdelphij    dp->di_next = dcwd->di_next;
493205194Sdelphij    dp->di_prev = dcwd->di_prev;
494205194Sdelphij    dp->di_prev->di_next = dp;
495205194Sdelphij    dp->di_next->di_prev = dp;
496205194Sdelphij    dfree(dcwd);
497205194Sdelphij    dnewcwd(dp, dflag);
498205194Sdelphij}
499205194Sdelphij
500205194Sdelphijstatic Char *
501205194Sdelphijdgoto(cp)
502205194Sdelphij    Char   *cp;
503205194Sdelphij{
504205194Sdelphij    Char   *dp;
505205194Sdelphij
506205194Sdelphij    if (!ABSOLUTEP(cp))
507205194Sdelphij    {
508205194Sdelphij	Char *p, *q;
509205194Sdelphij	int     cwdlen;
510205194Sdelphij
511205194Sdelphij	for (p = dcwd->di_name; *p++;)
512205194Sdelphij	    continue;
513205194Sdelphij	if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1)	/* root */
514205194Sdelphij	    cwdlen = 0;
515205194Sdelphij	for (p = cp; *p++;)
516205194Sdelphij	    continue;
517205194Sdelphij	dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
518205194Sdelphij	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
519205194Sdelphij	    continue;
520205194Sdelphij	if (cwdlen)
521205194Sdelphij	    p[-1] = '/';
522205194Sdelphij	else
523205194Sdelphij	    p--;		/* don't add a / after root */
524205194Sdelphij	for (q = cp; (*p++ = *q++) != '\0';)
525205194Sdelphij	    continue;
526205194Sdelphij	xfree((ptr_t) cp);
527237410Sdelphij	cp = dp;
528205194Sdelphij	dp += cwdlen;
529205194Sdelphij    }
530205194Sdelphij    else
531205194Sdelphij	dp = cp;
532205194Sdelphij
533205194Sdelphij#if defined(WINNT_NATIVE)
534205194Sdelphij    cp = SAVE(getcwd(NULL, 0));
535205194Sdelphij#elif defined(__CYGWIN__)
536205194Sdelphij    if (ABSOLUTEP(cp) && cp[1] == ':') /* Only DOS paths are treated that way */
537205194Sdelphij    	cp = SAVE(getcwd(NULL, 0));
538205194Sdelphij    else
539205194Sdelphij    	cp = dcanon(cp, dp);
540205194Sdelphij#else /* !WINNT_NATIVE */
541205194Sdelphij    cp = dcanon(cp, dp);
542205194Sdelphij#endif /* WINNT_NATIVE */
543205194Sdelphij    return cp;
544205194Sdelphij}
545205194Sdelphij
546205194Sdelphij/*
547250261Sdelphij * dfollow - change to arg directory; fall back on cdpath if not valid
548250261Sdelphij */
549205194Sdelphijstatic Char *
550205194Sdelphijdfollow(cp)
551205194Sdelphij    Char *cp;
552205194Sdelphij{
553205194Sdelphij    Char *dp;
554205194Sdelphij    struct varent *c;
555205194Sdelphij    char    ebuf[MAXPATHLEN];
556205194Sdelphij    int serrno;
557205194Sdelphij
558205194Sdelphij    cp = globone(cp, G_ERROR);
559205194Sdelphij#ifdef apollo
560205194Sdelphij    if (Strchr(cp, '`')) {
561205194Sdelphij	char *dptr, *ptr;
562205194Sdelphij	if (chdir(dptr = short2str(cp)) < 0)
563205194Sdelphij	    stderror(ERR_SYSTEM, dptr, strerror(errno));
564205194Sdelphij	else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
565237410Sdelphij		xfree((ptr_t) cp);
566205194Sdelphij		cp = Strsave(str2short(ptr));
567237410Sdelphij		return dgoto(cp);
568237410Sdelphij	}
569205194Sdelphij	else
570205194Sdelphij	    stderror(ERR_SYSTEM, dptr, ebuf);
571205194Sdelphij    }
572205194Sdelphij#endif /* apollo */
573205194Sdelphij
574205194Sdelphij    (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
575205194Sdelphij    ebuf[MAXPATHLEN-1] = '\0';
576205194Sdelphij    /*
577205194Sdelphij     * if we are ignoring symlinks, try to fix relatives now.
578206924Sdelphij     * if we are expading symlinks, it should be done by now.
579205194Sdelphij     */
580205194Sdelphij    dp = dnormalize(cp, symlinks == SYM_IGNORE);
581205194Sdelphij    if (chdir(short2str(dp)) >= 0) {
582205194Sdelphij        xfree((ptr_t) cp);
583205194Sdelphij        return dgoto(dp);
584205194Sdelphij    }
585205194Sdelphij    else {
586205194Sdelphij        xfree((ptr_t) dp);
587205194Sdelphij        if (chdir(short2str(cp)) >= 0)
588205194Sdelphij	    return dgoto(cp);
589205194Sdelphij	else if (errno != ENOENT && errno != ENOTDIR)
590237410Sdelphij	    stderror(ERR_SYSTEM, ebuf, strerror(errno));
591237410Sdelphij	serrno = errno;
592237410Sdelphij    }
593237410Sdelphij
594205194Sdelphij    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
595205194Sdelphij	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
596205194Sdelphij	Char  **cdp;
597205194Sdelphij	Char *p;
598205194Sdelphij	Char    buf[MAXPATHLEN];
599250261Sdelphij
600250261Sdelphij	for (cdp = c->vec; *cdp; cdp++) {
601205194Sdelphij	    for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
602205194Sdelphij		continue;
603205194Sdelphij	    dp[-1] = '/';
604250261Sdelphij	    for (p = cp; (*dp++ = *p++) != '\0';)
605250261Sdelphij		continue;
606205194Sdelphij	    /*
607205194Sdelphij	     * We always want to fix the directory here
608205194Sdelphij	     * If we are normalizing symlinks
609250261Sdelphij	     */
610250261Sdelphij	    dp = dnormalize(buf, symlinks == SYM_IGNORE ||
611250261Sdelphij				 symlinks == SYM_EXPAND);
612250261Sdelphij	    if (chdir(short2str(dp)) >= 0) {
613205194Sdelphij		printd = 1;
614205194Sdelphij		xfree((ptr_t) cp);
615205194Sdelphij		return dgoto(dp);
616250261Sdelphij	    }
617205194Sdelphij	    else if (chdir(short2str(cp)) >= 0) {
618205194Sdelphij		printd = 1;
619205194Sdelphij		xfree((ptr_t) dp);
620205194Sdelphij		return dgoto(cp);
621205194Sdelphij	    }
622205194Sdelphij	}
623205194Sdelphij    }
624205194Sdelphij    dp = varval(cp);
625206924Sdelphij    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
626205194Sdelphij	xfree((ptr_t) cp);
627205194Sdelphij	cp = Strsave(dp);
628205194Sdelphij	printd = 1;
629205194Sdelphij	return dgoto(cp);
630205194Sdelphij    }
631205194Sdelphij    xfree((ptr_t) cp);
632205194Sdelphij    /*
633205194Sdelphij     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
634205194Sdelphij     * directories we could get to.
635205194Sdelphij     */
636205194Sdelphij    if (!bequiet) {
637205194Sdelphij	stderror(ERR_SYSTEM, ebuf, strerror(serrno));
638	return (NULL);
639    }
640    else
641	return (NULL);
642}
643
644
645/*
646 * dopushd - push new directory onto directory stack.
647 *	with no arguments exchange top and second.
648 *	with numeric argument (+n) bring it to top.
649 */
650/*ARGSUSED*/
651void
652dopushd(v, c)
653    Char  **v;
654    struct command *c;
655{
656    struct directory *dp;
657    Char *cp;
658    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
659
660    USE(c);
661    printd = 1;
662    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
663
664    if (cp == NULL) {
665	if (adrof(STRpushdtohome)) {
666	    if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
667		stderror(ERR_NAME | ERR_NOHOMEDIR);
668	    if (chdir(short2str(cp)) < 0)
669		stderror(ERR_NAME | ERR_CANTCHANGE);
670	    cp = Strsave(cp);	/* hmmm... PWP */
671	    if ((cp = dfollow(cp)) == NULL)
672		return;
673	    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
674	    dp->di_name = cp;
675	    dp->di_count = 0;
676	    dp->di_prev = dcwd;
677	    dp->di_next = dcwd->di_next;
678	    dcwd->di_next = dp;
679	    dp->di_next->di_prev = dp;
680	}
681	else {
682	    char   *tmp;
683
684	    if ((dp = dcwd->di_prev) == &dhead)
685		dp = dhead.di_prev;
686	    if (dp == dcwd)
687		stderror(ERR_NAME | ERR_NODIR);
688	    if (chdir(tmp = short2str(dp->di_name)) < 0)
689		stderror(ERR_SYSTEM, tmp, strerror(errno));
690	    dp->di_prev->di_next = dp->di_next;
691	    dp->di_next->di_prev = dp->di_prev;
692	    dp->di_next = dcwd->di_next;
693	    dp->di_prev = dcwd;
694	    dcwd->di_next->di_prev = dp;
695	    dcwd->di_next = dp;
696	}
697    }
698    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
699	stderror(ERR_NAME | ERR_TOOMANY);
700	/* NOTREACHED */
701	return;
702    }
703    else if ((dp = dfind(cp)) != NULL) {
704	char   *tmp;
705
706	if (chdir(tmp = short2str(dp->di_name)) < 0)
707	    stderror(ERR_SYSTEM, tmp, strerror(errno));
708	/*
709	 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
710	 */
711	if (adrof(STRdextract))
712	    dextract(dp);
713    }
714    else {
715	Char *ccp;
716
717	if ((ccp = dfollow(cp)) == NULL)
718	    return;
719	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
720	dp->di_name = ccp;
721	dp->di_count = 0;
722	dp->di_prev = dcwd;
723	dp->di_next = dcwd->di_next;
724	dcwd->di_next = dp;
725	dp->di_next->di_prev = dp;
726    }
727    dnewcwd(dp, dflag);
728}
729
730/*
731 * dfind - find a directory if specified by numeric (+n) argument
732 */
733static struct directory *
734dfind(cp)
735    Char *cp;
736{
737    struct directory *dp;
738    int i;
739    Char *ep;
740
741    if (*cp++ != '+')
742	return (0);
743    for (ep = cp; Isdigit(*ep); ep++)
744	continue;
745    if (*ep)
746	return (0);
747    i = getn(cp);
748    if (i <= 0)
749	return (0);
750    for (dp = dcwd; i != 0; i--) {
751	if ((dp = dp->di_prev) == &dhead)
752	    dp = dp->di_prev;
753	if (dp == dcwd)
754	    stderror(ERR_NAME | ERR_DEEP);
755    }
756    return (dp);
757}
758
759/*
760 * dopopd - pop a directory out of the directory stack
761 *	with a numeric argument just discard it.
762 */
763/*ARGSUSED*/
764void
765dopopd(v, c)
766    Char  **v;
767    struct command *c;
768{
769    Char *cp;
770    struct directory *dp, *p = NULL;
771    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
772
773    USE(c);
774    printd = 1;
775    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
776
777    if (cp == NULL)
778	dp = dcwd;
779    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
780	stderror(ERR_NAME | ERR_TOOMANY);
781	/* NOTREACHED */
782	return;
783    }
784    else if ((dp = dfind(cp)) == 0)
785	stderror(ERR_NAME | ERR_BADDIR);
786    if (dp->di_prev == &dhead && dp->di_next == &dhead)
787	stderror(ERR_NAME | ERR_EMPTY);
788    if (dp == dcwd) {
789	char   *tmp;
790
791	if ((p = dp->di_prev) == &dhead)
792	    p = dhead.di_prev;
793	if (chdir(tmp = short2str(p->di_name)) < 0)
794	    stderror(ERR_SYSTEM, tmp, strerror(errno));
795    }
796    dp->di_prev->di_next = dp->di_next;
797    dp->di_next->di_prev = dp->di_prev;
798    if (dp == dcwd) {
799	dnewcwd(p, dflag);
800    }
801    else {
802	printdirs(dflag);
803    }
804    dfree(dp);
805}
806
807/*
808 * dfree - free the directory (or keep it if it still has ref count)
809 */
810void
811dfree(dp)
812    struct directory *dp;
813{
814
815    if (dp->di_count != 0) {
816	dp->di_next = dp->di_prev = 0;
817    }
818    else {
819	xfree((ptr_t) dp->di_name);
820	xfree((ptr_t) dp);
821    }
822}
823
824/*
825 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
826 *	we are of course assuming that the file system is standardly
827 *	constructed (always have ..'s, directories have links)
828 */
829Char   *
830dcanon(cp, p)
831    Char *cp, *p;
832{
833    Char *sp;
834    Char *p1, *p2;	/* general purpose */
835    int    slash;
836#ifdef apollo
837    int    slashslash;
838#endif /* apollo */
839    size_t  clen;
840
841#ifdef S_IFLNK			/* if we have symlinks */
842    Char    mlink[MAXPATHLEN];
843    char    tlink[MAXPATHLEN];
844    int     cc;
845    Char   *newcp;
846#endif /* S_IFLNK */
847
848    /*
849     * if the path given is too long truncate it!
850     */
851    if ((clen = Strlen(cp)) >= MAXPATHLEN)
852	cp[clen = MAXPATHLEN - 1] = '\0';
853
854    /*
855     * christos: if the path given does not start with a slash prepend cwd. If
856     * cwd does not start with a slash or the result would be too long try to
857     * correct it.
858     */
859    if (!ABSOLUTEP(cp)) {
860	Char    tmpdir[MAXPATHLEN];
861	size_t	len;
862
863	p1 = varval(STRcwd);
864	if (p1 == STRNULL || !ABSOLUTEP(p1)) {
865	    char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
866	    if (tmp == NULL || *tmp == '\0') {
867		xprintf("%s: %s\n", progname, strerror(errno));
868		set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
869	    } else {
870		set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
871	    }
872	    p1 = varval(STRcwd);
873	}
874	len = Strlen(p1);
875	if (len + clen + 1 >= MAXPATHLEN)
876	    cp[MAXPATHLEN - (len + 1)] = '\0';
877	(void) Strcpy(tmpdir, p1);
878	(void) Strcat(tmpdir, STRslash);
879	(void) Strcat(tmpdir, cp);
880	xfree((ptr_t) cp);
881	cp = p = Strsave(tmpdir);
882    }
883
884#ifdef apollo
885    slashslash = (cp[0] == '/' && cp[1] == '/');
886#endif /* apollo */
887
888    while (*p) {		/* for each component */
889	sp = p;			/* save slash address */
890	while (*++p == '/')	/* flush extra slashes */
891	    continue;
892	if (p != ++sp)
893	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
894		continue;
895	p = sp;			/* save start of component */
896	slash = 0;
897	if (*p)
898	    while (*++p)	/* find next slash or end of path */
899		if (*p == '/') {
900		    slash = 1;
901		    *p = 0;
902		    break;
903		}
904
905#ifdef apollo
906	if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
907	    slashslash = 1;
908#endif /* apollo */
909	if (*sp == '\0') {	/* if component is null */
910	    if (--sp == cp)	/* if path is one char (i.e. /) */
911		break;
912	    else
913		*sp = '\0';
914	}
915	else if (sp[0] == '.' && sp[1] == 0) {
916	    if (slash) {
917		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
918		    continue;
919		p = --sp;
920	    }
921	    else if (--sp != cp)
922		*sp = '\0';
923	    else
924		sp[1] = '\0';
925	}
926	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
927	    /*
928	     * We have something like "yyy/xxx/..", where "yyy" can be null or
929	     * a path starting at /, and "xxx" is a single component. Before
930	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
931	     * symbolic link.
932	     */
933	    *--sp = 0;		/* form the pathname for readlink */
934#ifdef S_IFLNK			/* if we have symlinks */
935	    if (sp != cp && /* symlinks != SYM_IGNORE && */
936		(cc = readlink(short2str(cp), tlink,
937			       sizeof(tlink) - 1)) >= 0) {
938		tlink[cc] = '\0';
939		(void) Strncpy(mlink, str2short(tlink),
940		    sizeof(mlink) / sizeof(Char));
941		mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
942
943		if (slash)
944		    *p = '/';
945		/*
946		 * Point p to the '/' in "/..", and restore the '/'.
947		 */
948		*(p = sp) = '/';
949		/*
950		 * find length of p
951		 */
952		for (p1 = p; *p1++;)
953		    continue;
954		if (*mlink != '/') {
955		    /*
956		     * Relative path, expand it between the "yyy/" and the
957		     * "/..". First, back sp up to the character past "yyy/".
958		     */
959		    while (*--sp != '/')
960			continue;
961		    sp++;
962		    *sp = 0;
963		    /*
964		     * New length is "yyy/" + mlink + "/.." and rest
965		     */
966		    p1 = newcp = (Char *) xmalloc((size_t)
967						(((sp - cp) + cc + (p1 - p)) *
968						 sizeof(Char)));
969		    /*
970		     * Copy new path into newcp
971		     */
972		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
973			continue;
974		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
975			continue;
976		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
977			continue;
978		    /*
979		     * Restart canonicalization at expanded "/xxx".
980		     */
981		    p = sp - cp - 1 + newcp;
982		}
983		else {
984		    /*
985		     * New length is mlink + "/.." and rest
986		     */
987		    p1 = newcp = (Char *) xmalloc((size_t)
988					    ((cc + (p1 - p)) * sizeof(Char)));
989		    /*
990		     * Copy new path into newcp
991		     */
992		    for (p2 = mlink; (*p1++ = *p2++) != '\0';)
993			continue;
994		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
995			continue;
996		    /*
997		     * Restart canonicalization at beginning
998		     */
999		    p = newcp;
1000		}
1001		xfree((ptr_t) cp);
1002		cp = newcp;
1003#ifdef apollo
1004                slashslash = (cp[0] == '/' && cp[1] == '/');
1005#endif /* apollo */
1006		continue;	/* canonicalize the link */
1007	    }
1008#endif /* S_IFLNK */
1009	    *sp = '/';
1010	    if (sp != cp)
1011		while (*--sp != '/')
1012		    continue;
1013	    if (slash) {
1014		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
1015		    continue;
1016		p = sp;
1017	    }
1018	    else if (cp == sp)
1019		*++sp = '\0';
1020	    else
1021		*sp = '\0';
1022	}
1023	else {			/* normal dir name (not . or .. or nothing) */
1024
1025#ifdef S_IFLNK			/* if we have symlinks */
1026	    if (sp != cp && symlinks == SYM_CHASE &&
1027		(cc = readlink(short2str(cp), tlink,
1028			       sizeof(tlink) - 1)) >= 0) {
1029		tlink[cc] = '\0';
1030		(void) Strncpy(mlink, str2short(tlink),
1031		    sizeof(mlink) / sizeof(Char));
1032		mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
1033
1034		/*
1035		 * restore the '/'.
1036		 */
1037		if (slash)
1038		    *p = '/';
1039
1040		/*
1041		 * point sp to p (rather than backing up).
1042		 */
1043		sp = p;
1044
1045		/*
1046		 * find length of p
1047		 */
1048		for (p1 = p; *p1++;)
1049		    continue;
1050		if (*mlink != '/') {
1051		    /*
1052		     * Relative path, expand it between the "yyy/" and the
1053		     * remainder. First, back sp up to the character past
1054		     * "yyy/".
1055		     */
1056		    while (*--sp != '/')
1057			continue;
1058		    sp++;
1059		    *sp = 0;
1060		    /*
1061		     * New length is "yyy/" + mlink + "/.." and rest
1062		     */
1063		    p1 = newcp = (Char *) xmalloc((size_t)
1064						  (((sp - cp) + cc + (p1 - p))
1065						   * sizeof(Char)));
1066		    /*
1067		     * Copy new path into newcp
1068		     */
1069		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
1070			continue;
1071		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
1072			continue;
1073		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1074			continue;
1075		    /*
1076		     * Restart canonicalization at expanded "/xxx".
1077		     */
1078		    p = sp - cp - 1 + newcp;
1079		}
1080		else {
1081		    /*
1082		     * New length is mlink + the rest
1083		     */
1084		    p1 = newcp = (Char *) xmalloc((size_t)
1085					    ((cc + (p1 - p)) * sizeof(Char)));
1086		    /*
1087		     * Copy new path into newcp
1088		     */
1089		    for (p2 = mlink; (*p1++ = *p2++) != '\0';)
1090			continue;
1091		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1092			continue;
1093		    /*
1094		     * Restart canonicalization at beginning
1095		     */
1096		    p = newcp;
1097		}
1098		xfree((ptr_t) cp);
1099		cp = newcp;
1100#ifdef apollo
1101                slashslash = (cp[0] == '/' && cp[1] == '/');
1102#endif /* apollo */
1103		continue;	/* canonicalize the mlink */
1104	    }
1105#endif /* S_IFLNK */
1106	    if (slash)
1107		*p = '/';
1108	}
1109    }
1110
1111    /*
1112     * fix home...
1113     */
1114#ifdef S_IFLNK
1115    p1 = varval(STRhome);
1116    cc = (int) Strlen(p1);
1117    /*
1118     * See if we're not in a subdir of STRhome
1119     */
1120    if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
1121	(cp[cc] != '/' && cp[cc] != '\0'))) {
1122	static ino_t home_ino = (ino_t) -1;
1123	static dev_t home_dev = (dev_t) -1;
1124	static Char *home_ptr = NULL;
1125	struct stat statbuf;
1126	int found;
1127
1128	/*
1129	 * Get dev and ino of STRhome
1130	 */
1131	if (home_ptr != p1 &&
1132	    stat(short2str(p1), &statbuf) != -1) {
1133	    home_dev = statbuf.st_dev;
1134	    home_ino = statbuf.st_ino;
1135	    home_ptr = p1;
1136	}
1137	/*
1138	 * Start comparing dev & ino backwards
1139	 */
1140	p2 = Strncpy(mlink, cp, sizeof(mlink) / sizeof(Char));
1141	mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
1142	found = 0;
1143	while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1144	    if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1145			statbuf.st_ino == home_ino) {
1146			found = 1;
1147			break;
1148	    }
1149	    if ((sp = Strrchr(p2, '/')) != NULL)
1150		*sp = '\0';
1151	}
1152	/*
1153	 * See if we found it
1154	 */
1155	if (*p2 && found) {
1156	    /*
1157	     * Use STRhome to make '~' work
1158	     */
1159	    newcp = Strspl(p1, cp + Strlen(p2));
1160	    xfree((ptr_t) cp);
1161	    cp = newcp;
1162	}
1163    }
1164#endif /* S_IFLNK */
1165
1166#ifdef apollo
1167    if (slashslash) {
1168	if (cp[1] != '/') {
1169	    p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1170	    *p = '/';
1171	    (void) Strcpy(&p[1], cp);
1172	    xfree((ptr_t) cp);
1173	    cp = p;
1174	}
1175    }
1176    if (cp[1] == '/' && cp[2] == '/')
1177	(void) Strcpy(&cp[1], &cp[2]);
1178#endif /* apollo */
1179    return cp;
1180}
1181
1182
1183/*
1184 * dnewcwd - make a new directory in the loop the current one
1185 */
1186static void
1187dnewcwd(dp, dflag)
1188    struct directory *dp;
1189    int dflag;
1190{
1191    int print;
1192
1193    if (adrof(STRdunique)) {
1194	struct directory *dn;
1195
1196	for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
1197	    if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1198		dn->di_next->di_prev = dn->di_prev;
1199		dn->di_prev->di_next = dn->di_next;
1200		dfree(dn);
1201		break;
1202	    }
1203    }
1204    dcwd = dp;
1205    dset(dcwd->di_name);
1206    dgetstack();
1207    print = printd;		/* if printd is set, print dirstack... */
1208    if (adrof(STRpushdsilent))	/* but pushdsilent overrides printd... */
1209	print = 0;
1210    if (dflag & DIR_PRINT)	/* but DIR_PRINT overrides pushdsilent... */
1211	print = 1;
1212    if (bequiet)		/* and bequiet overrides everything */
1213	print = 0;
1214    if (print)
1215	printdirs(dflag);
1216    cwd_cmd();			/* PWP: run the defined cwd command */
1217}
1218
1219void
1220dsetstack()
1221{
1222    Char **cp;
1223    struct varent *vp;
1224    struct directory *dn, *dp;
1225
1226    if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
1227	return;
1228
1229    /* Free the whole stack */
1230    while ((dn = dhead.di_prev) != &dhead) {
1231	dn->di_next->di_prev = dn->di_prev;
1232	dn->di_prev->di_next = dn->di_next;
1233	if (dn != dcwd)
1234	    dfree(dn);
1235    }
1236
1237    /* thread the current working directory */
1238    dhead.di_prev = dhead.di_next = dcwd;
1239    dcwd->di_next = dcwd->di_prev = &dhead;
1240
1241    /* put back the stack */
1242    for (cp = vp->vec; cp && *cp && **cp; cp++) {
1243	dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1244	dp->di_name = Strsave(*cp);
1245	dp->di_count = 0;
1246	dp->di_prev = dcwd;
1247	dp->di_next = dcwd->di_next;
1248	dcwd->di_next = dp;
1249	dp->di_next->di_prev = dp;
1250    }
1251    dgetstack();	/* Make $dirstack reflect the current state */
1252}
1253
1254static void
1255dgetstack()
1256{
1257    int i = 0;
1258    Char **dblk, **dbp;
1259    struct directory *dn;
1260
1261    if (adrof(STRdirstack) == NULL)
1262    	return;
1263
1264    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
1265	continue;
1266    dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1267    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
1268	 *dbp = Strsave(dn->di_name);
1269    *dbp = NULL;
1270    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1271}
1272
1273/*
1274 * getstakd - added by kfk 17 Jan 1984
1275 * Support routine for the stack hack.  Finds nth directory in
1276 * the directory stack, or finds last directory in stack.
1277 */
1278int
1279getstakd(s, cnt)
1280    Char   *s;
1281    int     cnt;
1282{
1283    struct directory *dp;
1284
1285    dp = dcwd;
1286    if (cnt < 0) {		/* < 0 ==> last dir requested. */
1287	dp = dp->di_next;
1288	if (dp == &dhead)
1289	    dp = dp->di_next;
1290    }
1291    else {
1292	while (cnt-- > 0) {
1293	    dp = dp->di_prev;
1294	    if (dp == &dhead)
1295		dp = dp->di_prev;
1296	    if (dp == dcwd)
1297		return (0);
1298	}
1299    }
1300    (void) Strncpy(s, dp->di_name, BUFSIZE);
1301    s[BUFSIZE - 1] = '\0';
1302    return (1);
1303}
1304
1305/*
1306 * Karl Kleinpaste - 10 Feb 1984
1307 * Added dextract(), which is used in pushd +n.
1308 * Instead of just rotating the entire stack around, dextract()
1309 * lets the user have the nth dir extracted from its current
1310 * position, and pushes it onto the top.
1311 */
1312static void
1313dextract(dp)
1314    struct directory *dp;
1315{
1316    if (dp == dcwd)
1317	return;
1318    dp->di_next->di_prev = dp->di_prev;
1319    dp->di_prev->di_next = dp->di_next;
1320    dp->di_next = dcwd->di_next;
1321    dp->di_prev = dcwd;
1322    dp->di_next->di_prev = dp;
1323    dcwd->di_next = dp;
1324}
1325
1326void
1327loaddirs(fname)
1328    Char *fname;
1329{
1330    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1331
1332    bequiet = 1;
1333    if (fname)
1334	loaddirs_cmd[1] = fname;
1335    else if ((fname = varval(STRdirsfile)) != STRNULL)
1336	loaddirs_cmd[1] = fname;
1337    else
1338	loaddirs_cmd[1] = STRtildotdirs;
1339    dosource(loaddirs_cmd, (struct command *)0);
1340    bequiet = 0;
1341}
1342
1343/*
1344 * create a file called ~/.cshdirs which has a sequence
1345 * of pushd commands which will restore the dir stack to
1346 * its state before exit/logout. remember that the order
1347 * is reversed in the file because we are pushing.
1348 * -strike
1349 */
1350void
1351recdirs(fname, def)
1352    Char *fname;
1353    int def;
1354{
1355    int     fp, ftmp, oldidfds;
1356    int     cdflag = 0;
1357    struct directory *dp;
1358    unsigned int    num;
1359    Char   *snum;
1360    Char    qname[MAXPATHLEN*2];
1361
1362    if (fname == NULL && !def)
1363	return;
1364
1365    if (fname == NULL) {
1366	if ((fname = varval(STRdirsfile)) == STRNULL)
1367	    fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1368	else
1369	    fname = Strsave(fname);
1370    }
1371    else
1372	fname = globone(fname, G_ERROR);
1373
1374    if ((fp = creat(short2str(fname), 0600)) == -1) {
1375	xfree((ptr_t) fname);
1376	return;
1377    }
1378
1379    if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
1380	num = (unsigned int) ~0;
1381    else
1382	num = (unsigned int) atoi(short2str(snum));
1383
1384    oldidfds = didfds;
1385    didfds = 0;
1386    ftmp = SHOUT;
1387    SHOUT = fp;
1388
1389    dp = dcwd->di_next;
1390    do {
1391	if (dp == &dhead)
1392	    continue;
1393
1394	if (cdflag == 0) {
1395	    cdflag = 1;
1396	    xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1397	}
1398	else
1399	    xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1400
1401	if (num-- == 0)
1402	    break;
1403
1404    } while ((dp = dp->di_next) != dcwd->di_next);
1405
1406    (void) close(fp);
1407    SHOUT = ftmp;
1408    didfds = oldidfds;
1409    xfree((ptr_t) fname);
1410}
1411