sh.dir.c revision 316958
1255570Strasz/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.85 2016/04/08 16:10:52 christos Exp $ */
2255570Strasz/*
3255570Strasz * sh.dir.c: Directory manipulation functions
4255570Strasz */
5255570Strasz/*-
6255570Strasz * Copyright (c) 1980, 1991 The Regents of the University of California.
7255570Strasz * All rights reserved.
8255570Strasz *
9255570Strasz * Redistribution and use in source and binary forms, with or without
10255570Strasz * modification, are permitted provided that the following conditions
11255570Strasz * are met:
12255570Strasz * 1. Redistributions of source code must retain the above copyright
13255570Strasz *    notice, this list of conditions and the following disclaimer.
14255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
15255570Strasz *    notice, this list of conditions and the following disclaimer in the
16255570Strasz *    documentation and/or other materials provided with the distribution.
17255570Strasz * 3. Neither the name of the University nor the names of its contributors
18255570Strasz *    may be used to endorse or promote products derived from this software
19255570Strasz *    without specific prior written permission.
20255570Strasz *
21255570Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31270279Strasz * SUCH DAMAGE.
32270279Strasz */
33270279Strasz#include "sh.h"
34255570Strasz#include "ed.h"
35255570Strasz
36255570StraszRCSID("$tcsh: sh.dir.c,v 3.85 2016/04/08 16:10:52 christos Exp $")
37255570Strasz
38255570Strasz/*
39255570Strasz * C Shell - directory management
40267606Smav */
41267606Smav
42255570Straszstatic	Char			*agetcwd	(void);
43255570Straszstatic	void			 dstart		(const char *);
44255570Straszstatic	struct directory	*dfind		(Char *);
45255570Straszstatic	Char 			*dfollow	(Char *, int);
46255570Straszstatic	void 	 	 	 printdirs	(int);
47255570Straszstatic	Char 			*dgoto		(Char *);
48255570Straszstatic	void 	 	 	 dnewcwd	(struct directory *, int);
49255570Straszstatic	void 	 	 	 dset		(Char *);
50255570Straszstatic  void 			 dextract	(struct directory *);
51255570Straszstatic  int 			 skipargs	(Char ***, const char *,
52255570Strasz						 const char *);
53255570Straszstatic	void			 dgetstack	(void);
54255570Strasz
55255570Straszstatic struct directory dhead INIT_ZERO_STRUCT;		/* "head" of loop */
56255570Straszstatic int    printd;			/* force name to be printed */
57255570Strasz
58255570Straszint     bequiet = 0;		/* do not print dir stack -strike */
59255570Strasz
60255570Straszstatic Char *
61255570Straszagetcwd(void)
62255570Strasz{
63255570Strasz    char *buf;
64255570Strasz    Char *cwd;
65255570Strasz    size_t len;
66255570Strasz
67255570Strasz    len = MAXPATHLEN;
68275864Smav    buf = xmalloc(len);
69255570Strasz    while (getcwd(buf, len) == NULL) {
70275864Smav	int err;
71255570Strasz
72255570Strasz	err = errno;
73255570Strasz	if (err != ERANGE) {
74275864Smav	    xfree(buf);
75255570Strasz	    errno = err;
76255570Strasz	    return NULL;
77255570Strasz	}
78279589Smav	len *= 2;
79279589Smav	buf = xrealloc(buf, len);
80255570Strasz    }
81255570Strasz    if (*buf == '\0') {
82255570Strasz	xfree(buf);
83255570Strasz	return NULL;
84255570Strasz    }
85255570Strasz    cwd = SAVE(buf);
86255570Strasz    xfree(buf);
87255570Strasz    return cwd;
88255570Strasz}
89255570Strasz
90255570Straszstatic void
91255570Straszdstart(const char *from)
92255570Strasz{
93255570Strasz    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
94255570Strasz}
95255570Strasz
96255570Strasz/*
97255570Strasz * dinit - initialize current working directory
98255570Strasz */
99255570Straszvoid
100255570Straszdinit(Char *hp)
101255570Strasz{
102255570Strasz    Char *cp, *tcp;
103255570Strasz    struct directory *dp;
104255570Strasz
105255570Strasz    /* Don't believe the login shell home, because it may be a symlink */
106255570Strasz    tcp = agetcwd();
107255570Strasz    if (tcp == NULL) {
108255570Strasz	xprintf("%s: %s\n", progname, strerror(errno));
109255570Strasz	if (hp && *hp) {
110255570Strasz	    char *xcp = short2str(hp);
111255570Strasz	    dstart(xcp);
112255570Strasz	    if (chdir(xcp) == -1)
113255570Strasz		cp = NULL;
114255570Strasz	    else
115255570Strasz		cp = Strsave(hp);
116255570Strasz	}
117255570Strasz	else
118255570Strasz	    cp = NULL;
119255570Strasz	if (cp == NULL) {
120255570Strasz	    dstart("/");
121255570Strasz	    if (chdir("/") == -1)
122255570Strasz		/* I am not even try to print an error message! */
123255570Strasz		xexit(1);
124255570Strasz	    cp = SAVE("/");
125275864Smav	}
126255570Strasz    }
127275864Smav    else {
128255570Strasz#ifdef S_IFLNK
129255570Strasz	struct stat swd, shp;
130255570Strasz	int swd_ok;
131255570Strasz
132275864Smav	swd_ok = stat(short2str(tcp), &swd) == 0;
133255570Strasz	/*
134255570Strasz	 * See if $HOME is the working directory we got and use that
135255570Strasz	 */
136279589Smav	if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 &&
137279589Smav	    DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
138255570Strasz		swd.st_ino == shp.st_ino)
139255570Strasz	    cp = Strsave(hp);
140255570Strasz	else {
141255570Strasz	    char   *cwd;
142255570Strasz
143255570Strasz	    /*
144255570Strasz	     * use PWD if we have it (for subshells)
145255570Strasz	     */
146255570Strasz	    if (swd_ok && (cwd = getenv("PWD")) != NULL) {
147255570Strasz		if (stat(cwd, &shp) != -1 &&
148255570Strasz			DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
149255570Strasz		        swd.st_ino == shp.st_ino) {
150255570Strasz		    tcp = SAVE(cwd);
151255570Strasz		    cleanup_push(tcp, xfree);
152255570Strasz		}
153255570Strasz	    }
154255570Strasz	    cleanup_push(tcp, xfree);
155255570Strasz	    cp = dcanon(tcp, STRNULL);
156255570Strasz	    cleanup_ignore(tcp);
157255570Strasz	    cleanup_until(tcp);
158255570Strasz	}
159255570Strasz#else /* S_IFLNK */
160255570Strasz	cleanup_push(tcp, xfree);
161255570Strasz	cp = dcanon(tcp, STRNULL);
162255570Strasz	cleanup_ignore(tcp);
163255570Strasz	cleanup_until(tcp);
164255570Strasz#endif /* S_IFLNK */
165255570Strasz    }
166267606Smav
167273543Strasz    dp = xcalloc(sizeof(struct directory), 1);
168267606Smav    dp->di_name = cp;
169278322Smav    dp->di_count = 0;
170267606Smav    dhead.di_next = dhead.di_prev = dp;
171267606Smav    dp->di_next = dp->di_prev = &dhead;
172267606Smav    printd = 0;
173267606Smav    dnewcwd(dp, 0);
174267606Smav    setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB);
175267606Smav}
176267606Smav
177278322Smavstatic void
178278322Smavdset(Char *dp)
179278322Smav{
180278322Smav    /*
181267606Smav     * Don't call set() directly cause if the directory contains ` or
182267606Smav     * other junk characters glob will fail.
183267606Smav     */
184267606Smav    setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB);
185267606Smav    setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB);
186267606Smav    tsetenv(STRPWD, dp);
187267606Smav}
188267606Smav
189267606Smav#define DIR_PRINT	0x01	/* -p */
190267606Smav#define DIR_LONG  	0x02	/* -l */
191267606Smav#define DIR_VERT  	0x04	/* -v */
192267606Smav#define DIR_LINE  	0x08	/* -n */
193267606Smav#define DIR_SAVE 	0x10	/* -S */
194278322Smav#define DIR_LOAD	0x20	/* -L */
195267606Smav#define DIR_CLEAR	0x40	/* -c */
196267606Smav#define DIR_OLD	  	0x80	/* - */
197267606Smav
198267606Smavstatic int
199267606Smavskipargs(Char ***v, const char *dstr, const char *str)
200278322Smav{
201267606Smav    Char  **n = *v, *s;
202267606Smav
203267606Smav    int dflag = 0, loop = 1;
204267606Smav    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
205267606Smav	if (*(s = &((*n)[1])) == '\0')	/* test for bare "-" argument */
206267606Smav	    dflag |= DIR_OLD;
207267606Smav	else if ((*n)[1] == '-' && (*n)[2] == '\0') {   /* test for -- */
208267606Smav	    n++;
209278322Smav	    break;
210267606Smav	} else {
211267606Smav	    char *p;
212267606Smav	    while (*s != '\0')	/* examine flags */ {
213273813Strasz		if ((p = strchr(dstr, *s++)) != NULL)
214273813Strasz		    dflag |= (1 << (p - dstr));
215278322Smav	        else
216273813Strasz		    stderror(ERR_DIRUS, short2str(**v), dstr, str);
217273813Strasz	    }
218273813Strasz	}
219278322Smav    if (*n && (dflag & DIR_OLD))
220273813Strasz	stderror(ERR_DIRUS, short2str(**v), dstr, str);
221273813Strasz    *v = n;
222273813Strasz    /* make -l, -v, and -n imply -p */
223278322Smav    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
224278322Smav	dflag |= DIR_PRINT;
225278322Smav    return dflag;
226278322Smav}
227273813Strasz
228273813Strasz/*
229273813Strasz * dodirs - list all directories in directory loop
230273813Strasz */
231273813Strasz/*ARGSUSED*/
232273813Straszvoid
233273813Straszdodirs(Char **v, struct command *c)
234273813Strasz{
235273813Strasz    static const char flags[] = "plvnSLc";
236273813Strasz    int dflag = skipargs(&v, flags, "");
237273813Strasz
238273813Strasz    USE(c);
239273813Strasz    if ((dflag & DIR_CLEAR) != 0) {
240273813Strasz	struct directory *dp, *fdp;
241273813Strasz	for (dp = dcwd->di_next; dp != dcwd; ) {
242273813Strasz	    fdp = dp;
243273813Strasz	    dp = dp->di_next;
244273813Strasz	    if (fdp != &dhead)
245273813Strasz		dfree(fdp);
246273813Strasz	}
247273813Strasz	dhead.di_next = dhead.di_prev = dp;
248273813Strasz	dp->di_next = dp->di_prev = &dhead;
249273813Strasz    }
250273813Strasz    if ((dflag & DIR_LOAD) != 0)
251273813Strasz	loaddirs(*v);
252273813Strasz    else if ((dflag & DIR_SAVE) != 0)
253273813Strasz	recdirs(*v, 1);
254273813Strasz
255273813Strasz    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
256273813Strasz	v++;
257273813Strasz
258273813Strasz    if (*v != NULL || (dflag & DIR_OLD))
259273813Strasz	stderror(ERR_DIRUS, "dirs", flags, "");
260273813Strasz    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
261273813Strasz	printdirs(dflag);
262273813Strasz}
263273813Strasz
264273813Straszstatic void
265273813Straszprintdirs(int dflag)
266273813Strasz{
267273813Strasz    struct directory *dp;
268273813Strasz    Char   *s, *user;
269273813Strasz    int     idx, len, cur;
270273813Strasz
271273813Strasz    dp = dcwd;
272273813Strasz    idx = 0;
273273813Strasz    cur = 0;
274273813Strasz    do {
275273813Strasz	if (dp == &dhead)
276255570Strasz	    continue;
277255570Strasz	if (dflag & DIR_VERT) {
278255570Strasz	    xprintf("%d\t", idx++);
279255570Strasz	    cur = 0;
280255570Strasz	}
281278322Smav	s = dp->di_name;
282273543Strasz	user = NULL;
283255570Strasz	if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
284255570Strasz	    len = (int) (Strlen(user) + Strlen(s) + 2);
285273543Strasz	else
286273543Strasz	    len = (int) (Strlen(s) + 1);
287255570Strasz
288255570Strasz	cur += len;
289255570Strasz	if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) {
290255570Strasz	    xputchar('\n');
291255570Strasz	    cur = len;
292255570Strasz	}
293255570Strasz	if (user)
294255570Strasz	    xprintf("~%S", user);
295255570Strasz	xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
296255570Strasz    } while ((dp = dp->di_prev) != dcwd);
297255570Strasz    if (!(dflag & DIR_VERT))
298255570Strasz	xputchar('\n');
299255570Strasz}
300278322Smav
301278322Smavvoid
302273813Straszdtildepr(Char *dir)
303273813Strasz{
304273813Strasz    Char* user;
305278322Smav    if ((user = getusername(&dir)) != NULL)
306255570Strasz	xprintf("~%-S%S", user, dir);
307255570Strasz    else
308278322Smav	xprintf("%S", dir);
309278322Smav}
310255570Strasz
311255570Straszvoid
312273813Straszdtilde(void)
313278322Smav{
314273813Strasz    struct directory *d = dcwd;
315273813Strasz
316278322Smav    do {
317273813Strasz	if (d == &dhead)
318273813Strasz	    continue;
319255570Strasz	d->di_name = dcanon(d->di_name, STRNULL);
320255570Strasz    } while ((d = d->di_prev) != dcwd);
321255570Strasz
322255570Strasz    dset(dcwd->di_name);
323255570Strasz}
324255570Strasz
325255570Strasz
326255570Strasz/* dnormalize():
327255570Strasz *	The path will be normalized if it
328255570Strasz *	1) is "..",
329255570Strasz *	2) or starts with "../",
330255570Strasz *	3) or ends with "/..",
331255570Strasz *	4) or contains the string "/../",
332255570Strasz *	then it will be normalized, unless those strings are quoted.
333255570Strasz *	Otherwise, a copy is made and sent back.
334255570Strasz */
335255570StraszChar   *
336255570Straszdnormalize(const Char *cp, int expnd)
337255570Strasz{
338
339/* return true if dp is of the form "../xxx" or "/../xxx" */
340#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
341#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
342
343#ifdef S_IFLNK
344    if (expnd) {
345	struct Strbuf buf = Strbuf_INIT;
346 	int     dotdot = 0;
347	Char   *dp, *cwd;
348	const Char *start = cp;
349# ifdef HAVE_SLASHSLASH
350	int slashslash;
351# endif /* HAVE_SLASHSLASH */
352
353	/*
354	 * count the number of "../xxx" or "xxx/../xxx" in the path
355	 */
356	for ( ; *cp && *(cp + 1); cp++)
357	    if (IS_DOTDOT(start, cp))
358	        dotdot++;
359
360	/*
361	 * if none, we are done.
362	 */
363        if (dotdot == 0)
364	    return (Strsave(start));
365
366# ifdef notdef
367	struct stat sb;
368	/*
369	 * We disable this test because:
370	 * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1;
371	 * echo ../../dir1 does not expand. We had enabled this before
372	 * because it was bothering people with expansions in compilation
373	 * lines like -I../../foo. Maybe we need some kind of finer grain
374	 * control?
375	 *
376	 * If the path doesn't exist, we are done too.
377	 */
378	if (lstat(short2str(start), &sb) != 0 && errno == ENOENT)
379	    return (Strsave(start));
380# endif
381
382	cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char));
383	(void) Strcpy(cwd, dcwd->di_name);
384
385	/*
386	 * If the path starts with a slash, we are not relative to
387	 * the current working directory.
388	 */
389	if (ABSOLUTEP(start))
390	    *cwd = '\0';
391# ifdef HAVE_SLASHSLASH
392	slashslash = cwd[0] == '/' && cwd[1] == '/';
393# endif /* HAVE_SLASHSLASH */
394
395	/*
396	 * Ignore . and count ..'s
397	 */
398	cp = start;
399	do {
400	    dotdot = 0;
401	    buf.len = 0;
402	    while (*cp)
403	        if (IS_DOT(start, cp)) {
404	            if (*++cp)
405	                cp++;
406	        }
407	        else if (IS_DOTDOT(start, cp)) {
408		    if (buf.len != 0)
409		        break; /* finish analyzing .././../xxx/[..] */
410		    dotdot++;
411		    cp += 2;
412		    if (*cp)
413		        cp++;
414	        }
415	        else
416		    Strbuf_append1(&buf, *cp++);
417
418	    Strbuf_terminate(&buf);
419	    while (dotdot > 0)
420	        if ((dp = Strrchr(cwd, '/')) != NULL) {
421# ifdef HAVE_SLASHSLASH
422		    if (dp == &cwd[1])
423		        slashslash = 1;
424# endif /* HAVE_SLASHSLASH */
425		        *dp = '\0';
426		        dotdot--;
427	        }
428	        else
429		    break;
430
431	    if (!*cwd) {	/* too many ..'s, starts with "/" */
432	        cwd[0] = '/';
433# ifdef HAVE_SLASHSLASH
434		/*
435		 * Only append another slash, if already the former cwd
436		 * was in a double-slash path.
437		 */
438		cwd[1] = slashslash ? '/' : '\0';
439		cwd[2] = '\0';
440# else /* !HAVE_SLASHSLASH */
441		cwd[1] = '\0';
442# endif /* HAVE_SLASHSLASH */
443	    }
444# ifdef HAVE_SLASHSLASH
445	    else if (slashslash && cwd[1] == '\0') {
446		cwd[1] = '/';
447		cwd[2] = '\0';
448	    }
449# endif /* HAVE_SLASHSLASH */
450
451	    if (buf.len != 0) {
452		size_t i;
453
454		i = Strlen(cwd);
455		if (TRM(cwd[i - 1]) != '/') {
456		    cwd[i++] = '/';
457		    cwd[i] = '\0';
458		}
459	        dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s);
460	        xfree(cwd);
461	        cwd = dp;
462		i = Strlen(cwd) - 1;
463	        if (TRM(cwd[i]) == '/')
464		    cwd[i] = '\0';
465	    }
466	    /* Reduction of ".." following the stuff we collected in buf
467	     * only makes sense if the directory item in buf really exists.
468	     * Avoid reduction of "-I../.." (typical compiler call) to ""
469	     * or "/usr/nonexistant/../bin" to "/usr/bin":
470	     */
471	    if (cwd[0]) {
472	        struct stat exists;
473		if (0 != stat(short2str(cwd), &exists)) {
474		    xfree(buf.s);
475		    xfree(cwd);
476		    return Strsave(start);
477		}
478	    }
479	} while (*cp != '\0');
480	xfree(buf.s);
481	return cwd;
482    }
483#endif /* S_IFLNK */
484    return Strsave(cp);
485}
486
487
488/*
489 * dochngd - implement chdir command.
490 */
491/*ARGSUSED*/
492void
493dochngd(Char **v, struct command *c)
494{
495    Char *cp;
496    struct directory *dp;
497    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
498
499    USE(c);
500    printd = 0;
501    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
502
503    if (cp == NULL) {
504	if (!cdtohome)
505	    stderror(ERR_NAME | ERR_TOOFEW);
506	else if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
507	    stderror(ERR_NAME | ERR_NOHOMEDIR);
508	if (chdir(short2str(cp)) < 0)
509	    stderror(ERR_NAME | ERR_CANTCHANGE);
510	cp = Strsave(cp);
511    }
512    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
513	stderror(ERR_NAME | ERR_TOOMANY);
514	/* NOTREACHED */
515	return;
516    }
517    else if ((dp = dfind(cp)) != 0) {
518	char   *tmp;
519
520	printd = 1;
521	if (chdir(tmp = short2str(dp->di_name)) < 0)
522	    stderror(ERR_SYSTEM, tmp, strerror(errno));
523	dcwd->di_prev->di_next = dcwd->di_next;
524	dcwd->di_next->di_prev = dcwd->di_prev;
525	dfree(dcwd);
526	dnewcwd(dp, dflag);
527	return;
528    }
529    else
530	if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL)
531	    return;
532    dp = xcalloc(sizeof(struct directory), 1);
533    dp->di_name = cp;
534    dp->di_count = 0;
535    dp->di_next = dcwd->di_next;
536    dp->di_prev = dcwd->di_prev;
537    dp->di_prev->di_next = dp;
538    dp->di_next->di_prev = dp;
539    dfree(dcwd);
540    dnewcwd(dp, dflag);
541}
542
543static Char *
544dgoto(Char *cp)
545{
546    Char *dp, *ret;
547
548    if (!ABSOLUTEP(cp))
549    {
550	Char *p, *q;
551	size_t cwdlen;
552
553	cwdlen = Strlen(dcwd->di_name);
554	if (cwdlen == 1)	/* root */
555	    cwdlen = 0;
556	dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char));
557	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
558	    continue;
559	if (cwdlen)
560	    p[-1] = '/';
561	else
562	    p--;		/* don't add a / after root */
563	Strcpy(p, cp);
564	xfree(cp);
565	cp = dp;
566	dp += cwdlen;
567    }
568    else
569	dp = cp;
570
571#if defined(WINNT_NATIVE)
572    return agetcwd();
573#elif defined(__CYGWIN__)
574    if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */
575	return agetcwd();
576    } else {
577	cleanup_push(cp, xfree);
578    	ret = dcanon(cp, dp);
579	cleanup_ignore(cp);
580	cleanup_until(cp);
581    }
582#else /* !WINNT_NATIVE */
583    cleanup_push(cp, xfree);
584    ret = dcanon(cp, dp);
585    cleanup_ignore(cp);
586    cleanup_until(cp);
587#endif /* WINNT_NATIVE */
588    return ret;
589}
590
591/*
592 * dfollow - change to arg directory; fall back on cdpath if not valid
593 */
594static Char *
595dfollow(Char *cp, int old)
596{
597    Char *dp;
598    struct varent *c;
599    int serrno;
600
601    cp = old ? Strsave(cp) : globone(cp, G_ERROR);
602    cleanup_push(cp, xfree);
603#ifdef apollo
604    if (Strchr(cp, '`')) {
605	char *dptr;
606	if (chdir(dptr = short2str(cp)) < 0)
607	    stderror(ERR_SYSTEM, dptr, strerror(errno));
608	dp = agetcwd();
609	cleanup_push(dp, xfree);
610	if (dp != NULL) {
611	    cleanup_until(cp);
612	    return dgoto(dp);
613	}
614	else
615	    stderror(ERR_SYSTEM, dptr, strerror(errno));
616    }
617#endif /* apollo */
618
619    /*
620     * if we are ignoring symlinks, try to fix relatives now.
621     * if we are expading symlinks, it should be done by now.
622     */
623    dp = dnormalize(cp, symlinks == SYM_IGNORE);
624    if (chdir(short2str(dp)) >= 0) {
625        cleanup_until(cp);
626        return dgoto(dp);
627    }
628    else {
629        xfree(dp);
630        if (chdir(short2str(cp)) >= 0) {
631	    cleanup_ignore(cp);
632	    cleanup_until(cp);
633	    return dgoto(cp);
634	}
635	else if (errno != ENOENT && errno != ENOTDIR) {
636	    int err;
637
638	    err = errno;
639	    stderror(ERR_SYSTEM, short2str(cp), strerror(err));
640	}
641	serrno = errno;
642    }
643
644    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
645	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
646	struct Strbuf buf = Strbuf_INIT;
647	Char  **cdp;
648
649	for (cdp = c->vec; *cdp; cdp++) {
650	    size_t len = Strlen(*cdp);
651	    buf.len = 0;
652	    if (len > 0) {
653		Strbuf_append(&buf, *cdp);
654		if ((*cdp)[len - 1] != '/')
655		    Strbuf_append1(&buf, '/');
656	    }
657	    Strbuf_append(&buf, cp);
658	    Strbuf_terminate(&buf);
659	    /*
660	     * We always want to fix the directory here
661	     * If we are normalizing symlinks
662	     */
663	    dp = dnormalize(buf.s, symlinks == SYM_IGNORE ||
664				   symlinks == SYM_EXPAND);
665	    if (chdir(short2str(dp)) >= 0) {
666		printd = 1;
667		xfree(buf.s);
668		cleanup_until(cp);
669		return dgoto(dp);
670	    }
671	    else if (chdir(short2str(cp)) >= 0) {
672		printd = 1;
673		xfree(dp);
674		xfree(buf.s);
675		cleanup_ignore(cp);
676		cleanup_until(cp);
677		return dgoto(cp);
678	    }
679	    xfree(dp);
680	}
681	xfree(buf.s);
682    }
683    dp = varval(cp);
684    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
685	cleanup_until(cp);
686	cp = Strsave(dp);
687	printd = 1;
688	return dgoto(cp);
689    }
690    /*
691     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
692     * directories we could get to.
693     */
694    if (!bequiet)
695	stderror(ERR_SYSTEM, short2str(cp), strerror(serrno));
696    cleanup_until(cp);
697    return (NULL);
698}
699
700
701/*
702 * dopushd - push new directory onto directory stack.
703 *	with no arguments exchange top and second.
704 *	with numeric argument (+n) bring it to top.
705 */
706/*ARGSUSED*/
707void
708dopushd(Char **v, struct command *c)
709{
710    struct directory *dp;
711    Char *cp;
712    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
713
714    USE(c);
715    printd = 1;
716    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
717
718    if (cp == NULL) {
719	if (adrof(STRpushdtohome)) {
720	    if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
721		stderror(ERR_NAME | ERR_NOHOMEDIR);
722	    if (chdir(short2str(cp)) < 0)
723		stderror(ERR_NAME | ERR_CANTCHANGE);
724	    if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL)
725		return;
726	    dp = xcalloc(sizeof(struct directory), 1);
727	    dp->di_name = cp;
728	    dp->di_count = 0;
729	    dp->di_prev = dcwd;
730	    dp->di_next = dcwd->di_next;
731	    dcwd->di_next = dp;
732	    dp->di_next->di_prev = dp;
733	}
734	else {
735	    char   *tmp;
736
737	    if ((dp = dcwd->di_prev) == &dhead)
738		dp = dhead.di_prev;
739	    if (dp == dcwd)
740		stderror(ERR_NAME | ERR_NODIR);
741	    if (chdir(tmp = short2str(dp->di_name)) < 0)
742		stderror(ERR_SYSTEM, tmp, strerror(errno));
743	    dp->di_prev->di_next = dp->di_next;
744	    dp->di_next->di_prev = dp->di_prev;
745	    dp->di_next = dcwd->di_next;
746	    dp->di_prev = dcwd;
747	    dcwd->di_next->di_prev = dp;
748	    dcwd->di_next = dp;
749	}
750    }
751    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
752	stderror(ERR_NAME | ERR_TOOMANY);
753	/* NOTREACHED */
754	return;
755    }
756    else if ((dp = dfind(cp)) != NULL) {
757	char   *tmp;
758
759	if (chdir(tmp = short2str(dp->di_name)) < 0)
760	    stderror(ERR_SYSTEM, tmp, strerror(errno));
761	/*
762	 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
763	 */
764	if (adrof(STRdextract))
765	    dextract(dp);
766    }
767    else {
768	Char *ccp;
769
770	if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL)
771	    return;
772	dp = xcalloc(sizeof(struct directory), 1);
773	dp->di_name = ccp;
774	dp->di_count = 0;
775	dp->di_prev = dcwd;
776	dp->di_next = dcwd->di_next;
777	dcwd->di_next = dp;
778	dp->di_next->di_prev = dp;
779    }
780    dnewcwd(dp, dflag);
781}
782
783/*
784 * dfind - find a directory if specified by numeric (+n) argument
785 */
786static struct directory *
787dfind(Char *cp)
788{
789    struct directory *dp;
790    int i;
791    Char *ep;
792
793    if (*cp++ != '+')
794	return (0);
795    for (ep = cp; Isdigit(*ep); ep++)
796	continue;
797    if (*ep)
798	return (0);
799    i = getn(cp);
800    if (i <= 0)
801	return (0);
802    for (dp = dcwd; i != 0; i--) {
803	if ((dp = dp->di_prev) == &dhead)
804	    dp = dp->di_prev;
805	if (dp == dcwd)
806	    stderror(ERR_NAME | ERR_DEEP);
807    }
808    return (dp);
809}
810
811/*
812 * dopopd - pop a directory out of the directory stack
813 *	with a numeric argument just discard it.
814 */
815/*ARGSUSED*/
816void
817dopopd(Char **v, struct command *c)
818{
819    Char *cp;
820    struct directory *dp, *p = NULL;
821    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
822
823    USE(c);
824    printd = 1;
825    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
826
827    if (cp == NULL)
828	dp = dcwd;
829    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
830	stderror(ERR_NAME | ERR_TOOMANY);
831	/* NOTREACHED */
832	return;
833    }
834    else if ((dp = dfind(cp)) == 0)
835	stderror(ERR_NAME | ERR_BADDIR);
836    if (dp->di_prev == &dhead && dp->di_next == &dhead)
837	stderror(ERR_NAME | ERR_EMPTY);
838    if (dp == dcwd) {
839	char   *tmp;
840
841	if ((p = dp->di_prev) == &dhead)
842	    p = dhead.di_prev;
843	if (chdir(tmp = short2str(p->di_name)) < 0)
844	    stderror(ERR_SYSTEM, tmp, strerror(errno));
845    }
846    dp->di_prev->di_next = dp->di_next;
847    dp->di_next->di_prev = dp->di_prev;
848    dfree(dp);
849    if (dp == dcwd) {
850        dnewcwd(p, dflag);
851    }
852    else {
853	printdirs(dflag);
854    }
855}
856
857/*
858 * dfree - free the directory (or keep it if it still has ref count)
859 */
860void
861dfree(struct directory *dp)
862{
863
864    if (dp->di_count != 0) {
865	dp->di_next = dp->di_prev = 0;
866    }
867    else {
868	xfree(dp->di_name);
869	xfree(dp);
870    }
871}
872
873/*
874 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
875 *	we are of course assuming that the file system is standardly
876 *	constructed (always have ..'s, directories have links)
877 */
878Char   *
879dcanon(Char *cp, Char *p)
880{
881    Char *sp;
882    Char *p1, *p2;	/* general purpose */
883    int    slash;
884#ifdef HAVE_SLASHSLASH
885    int    slashslash;
886#endif /* HAVE_SLASHSLASH */
887    size_t  clen;
888
889#ifdef S_IFLNK			/* if we have symlinks */
890    Char *mlink, *newcp;
891    char *tlink;
892    size_t cc;
893#endif /* S_IFLNK */
894
895    clen = Strlen(cp);
896
897    /*
898     * christos: if the path given does not start with a slash prepend cwd. If
899     * cwd does not start with a slash or the result would be too long try to
900     * correct it.
901     */
902    if (!ABSOLUTEP(cp)) {
903	Char *tmpdir;
904	size_t	len;
905
906	p1 = varval(STRcwd);
907	if (p1 == STRNULL || !ABSOLUTEP(p1)) {
908	    Char *new_cwd = agetcwd();
909
910	    if (new_cwd == NULL) {
911		xprintf("%s: %s\n", progname, strerror(errno));
912		setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB);
913	    }
914	    else
915		setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB);
916	    p1 = varval(STRcwd);
917	}
918	len = Strlen(p1);
919	tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir));
920	(void) Strcpy(tmpdir, p1);
921	(void) Strcat(tmpdir, STRslash);
922	(void) Strcat(tmpdir, cp);
923	xfree(cp);
924	cp = p = tmpdir;
925    }
926
927#ifdef HAVE_SLASHSLASH
928    slashslash = (cp[0] == '/' && cp[1] == '/');
929#endif /* HAVE_SLASHSLASH */
930
931    while (*p) {		/* for each component */
932	sp = p;			/* save slash address */
933	while (*++p == '/')	/* flush extra slashes */
934	    continue;
935	if (p != ++sp)
936	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
937		continue;
938	p = sp;			/* save start of component */
939	slash = 0;
940	if (*p)
941	    while (*++p)	/* find next slash or end of path */
942		if (*p == '/') {
943		    slash = 1;
944		    *p = 0;
945		    break;
946		}
947
948#ifdef HAVE_SLASHSLASH
949	if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
950	    slashslash = 1;
951#endif /* HAVE_SLASHSLASH */
952	if (*sp == '\0') {	/* if component is null */
953	    if (--sp == cp)	/* if path is one char (i.e. /) */
954		break;
955	    else
956		*sp = '\0';
957	}
958	else if (sp[0] == '.' && sp[1] == 0) {
959	    if (slash) {
960		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
961		    continue;
962		p = --sp;
963	    }
964	    else if (--sp != cp)
965		*sp = '\0';
966	    else
967		sp[1] = '\0';
968	}
969	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
970	    /*
971	     * We have something like "yyy/xxx/..", where "yyy" can be null or
972	     * a path starting at /, and "xxx" is a single component. Before
973	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
974	     * symbolic link.
975	     */
976	    *--sp = 0;		/* form the pathname for readlink */
977#ifdef S_IFLNK			/* if we have symlinks */
978	    if (sp != cp && /* symlinks != SYM_IGNORE && */
979		(tlink = areadlink(short2str(cp))) != NULL) {
980		mlink = str2short(tlink);
981		xfree(tlink);
982
983		if (slash)
984		    *p = '/';
985		/*
986		 * Point p to the '/' in "/..", and restore the '/'.
987		 */
988		*(p = sp) = '/';
989		if (*mlink != '/') {
990		    /*
991		     * Relative path, expand it between the "yyy/" and the
992		     * "/..". First, back sp up to the character past "yyy/".
993		     */
994		    while (*--sp != '/')
995			continue;
996		    sp++;
997		    *sp = 0;
998		    /*
999		     * New length is "yyy/" + mlink + "/.." and rest
1000		     */
1001		    p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) +
1002					  Strlen(p) + 1) * sizeof(Char));
1003		    /*
1004		     * Copy new path into newcp
1005		     */
1006		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
1007			continue;
1008		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
1009			continue;
1010		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1011			continue;
1012		    /*
1013		     * Restart canonicalization at expanded "/xxx".
1014		     */
1015		    p = sp - cp - 1 + newcp;
1016		}
1017		else {
1018		    newcp = Strspl(mlink, p);
1019		    /*
1020		     * Restart canonicalization at beginning
1021		     */
1022		    p = newcp;
1023		}
1024		xfree(cp);
1025		cp = newcp;
1026#ifdef HAVE_SLASHSLASH
1027                slashslash = (cp[0] == '/' && cp[1] == '/');
1028#endif /* HAVE_SLASHSLASH */
1029		continue;	/* canonicalize the link */
1030	    }
1031#endif /* S_IFLNK */
1032	    *sp = '/';
1033	    if (sp != cp)
1034		while (*--sp != '/')
1035		    continue;
1036	    if (slash) {
1037		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
1038		    continue;
1039		p = sp;
1040	    }
1041	    else if (cp == sp)
1042		*++sp = '\0';
1043	    else
1044		*sp = '\0';
1045	}
1046	else {			/* normal dir name (not . or .. or nothing) */
1047
1048#ifdef S_IFLNK			/* if we have symlinks */
1049	    if (sp != cp && symlinks == SYM_CHASE &&
1050		(tlink = areadlink(short2str(cp))) != NULL) {
1051		mlink = str2short(tlink);
1052		xfree(tlink);
1053
1054		/*
1055		 * restore the '/'.
1056		 */
1057		if (slash)
1058		    *p = '/';
1059
1060		/*
1061		 * point sp to p (rather than backing up).
1062		 */
1063		sp = p;
1064
1065		if (*mlink != '/') {
1066		    /*
1067		     * Relative path, expand it between the "yyy/" and the
1068		     * remainder. First, back sp up to the character past
1069		     * "yyy/".
1070		     */
1071		    while (*--sp != '/')
1072			continue;
1073		    sp++;
1074		    *sp = 0;
1075		    /*
1076		     * New length is "yyy/" + mlink + "/.." and rest
1077		     */
1078		    p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) +
1079					  Strlen(p) + 1) * sizeof(Char));
1080		    /*
1081		     * Copy new path into newcp
1082		     */
1083		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
1084			continue;
1085		    for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
1086			continue;
1087		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1088			continue;
1089		    /*
1090		     * Restart canonicalization at expanded "/xxx".
1091		     */
1092		    p = sp - cp - 1 + newcp;
1093		}
1094		else {
1095		    newcp = Strspl(mlink, p);
1096		    /*
1097		     * Restart canonicalization at beginning
1098		     */
1099		    p = newcp;
1100		}
1101		xfree(cp);
1102		cp = newcp;
1103#ifdef HAVE_SLASHSLASH
1104                slashslash = (cp[0] == '/' && cp[1] == '/');
1105#endif /* HAVE_SLASHSLASH */
1106		continue;	/* canonicalize the mlink */
1107	    }
1108#endif /* S_IFLNK */
1109	    if (slash)
1110		*p = '/';
1111	}
1112    }
1113
1114    /*
1115     * fix home...
1116     */
1117#ifdef S_IFLNK
1118    p1 = varval(STRhome);
1119    cc = Strlen(p1);
1120    /*
1121     * See if we're not in a subdir of STRhome
1122     */
1123    if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 ||
1124	(cp[cc] != '/' && cp[cc] != '\0'))) {
1125	static ino_t home_ino = (ino_t) -1;
1126	static dev_t home_dev = (dev_t) -1;
1127	static Char *home_ptr = NULL;
1128	struct stat statbuf;
1129	int found;
1130	Char *copy;
1131
1132	/*
1133	 * Get dev and ino of STRhome
1134	 */
1135	if (home_ptr != p1 &&
1136	    stat(short2str(p1), &statbuf) != -1) {
1137	    home_dev = statbuf.st_dev;
1138	    home_ino = statbuf.st_ino;
1139	    home_ptr = p1;
1140	}
1141	/*
1142	 * Start comparing dev & ino backwards
1143	 */
1144	p2 = copy = Strsave(cp);
1145	found = 0;
1146	while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1147	    if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1148			statbuf.st_ino == home_ino) {
1149			found = 1;
1150			break;
1151	    }
1152	    if ((sp = Strrchr(p2, '/')) != NULL)
1153		*sp = '\0';
1154	}
1155	/*
1156	 * See if we found it
1157	 */
1158	if (*p2 && found) {
1159	    /*
1160	     * Use STRhome to make '~' work
1161	     */
1162	    newcp = Strspl(p1, cp + Strlen(p2));
1163	    xfree(cp);
1164	    cp = newcp;
1165	}
1166	xfree(copy);
1167    }
1168#endif /* S_IFLNK */
1169
1170#ifdef HAVE_SLASHSLASH
1171    if (slashslash) {
1172	if (cp[1] != '/') {
1173	    p = xmalloc((Strlen(cp) + 2) * sizeof(Char));
1174	    *p = '/';
1175	    (void) Strcpy(&p[1], cp);
1176	    xfree(cp);
1177	    cp = p;
1178	}
1179    }
1180    if (cp[1] == '/' && cp[2] == '/') {
1181	for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';)
1182	    continue;
1183    }
1184#endif /* HAVE_SLASHSLASH */
1185    return cp;
1186}
1187
1188
1189/*
1190 * dnewcwd - make a new directory in the loop the current one
1191 */
1192static void
1193dnewcwd(struct directory *dp, int dflag)
1194{
1195    int print;
1196
1197    if (adrof(STRdunique)) {
1198	struct directory *dn;
1199
1200	for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
1201	    if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1202		dn->di_next->di_prev = dn->di_prev;
1203		dn->di_prev->di_next = dn->di_next;
1204		dfree(dn);
1205		break;
1206	    }
1207    }
1208    dcwd = dp;
1209    dset(dcwd->di_name);
1210    dgetstack();
1211    print = printd;		/* if printd is set, print dirstack... */
1212    if (adrof(STRpushdsilent))	/* but pushdsilent overrides printd... */
1213	print = 0;
1214    if (dflag & DIR_PRINT)	/* but DIR_PRINT overrides pushdsilent... */
1215	print = 1;
1216    if (bequiet)		/* and bequiet overrides everything */
1217	print = 0;
1218    if (print)
1219	printdirs(dflag);
1220    cwd_cmd();			/* PWP: run the defined cwd command */
1221}
1222
1223void
1224dsetstack(void)
1225{
1226    Char **cp;
1227    struct varent *vp;
1228    struct directory *dn, *dp;
1229
1230    if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
1231	return;
1232
1233    /* Free the whole stack */
1234    while ((dn = dhead.di_prev) != &dhead) {
1235	dn->di_next->di_prev = dn->di_prev;
1236	dn->di_prev->di_next = dn->di_next;
1237	if (dn != dcwd)
1238	    dfree(dn);
1239    }
1240
1241    /* thread the current working directory */
1242    dhead.di_prev = dhead.di_next = dcwd;
1243    dcwd->di_next = dcwd->di_prev = &dhead;
1244
1245    /* put back the stack */
1246    for (cp = vp->vec; cp && *cp && **cp; cp++) {
1247	dp = xcalloc(sizeof(struct directory), 1);
1248	dp->di_name = Strsave(*cp);
1249	dp->di_count = 0;
1250	dp->di_prev = dcwd;
1251	dp->di_next = dcwd->di_next;
1252	dcwd->di_next = dp;
1253	dp->di_next->di_prev = dp;
1254    }
1255    dgetstack();	/* Make $dirstack reflect the current state */
1256}
1257
1258static void
1259dgetstack(void)
1260{
1261    int i = 0;
1262    Char **dblk, **dbp;
1263    struct directory *dn;
1264
1265    if (adrof(STRdirstack) == NULL)
1266    	return;
1267
1268    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
1269	continue;
1270    dbp = dblk = xmalloc((i + 1) * sizeof(Char *));
1271    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
1272	 *dbp = Strsave(dn->di_name);
1273    *dbp = NULL;
1274    cleanup_push(dblk, blk_cleanup);
1275    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1276    cleanup_ignore(dblk);
1277    cleanup_until(dblk);
1278}
1279
1280/*
1281 * getstakd - added by kfk 17 Jan 1984
1282 * Support routine for the stack hack.  Finds nth directory in
1283 * the directory stack, or finds last directory in stack.
1284 */
1285const Char *
1286getstakd(int cnt)
1287{
1288    struct directory *dp;
1289
1290    dp = dcwd;
1291    if (cnt < 0) {		/* < 0 ==> last dir requested. */
1292	dp = dp->di_next;
1293	if (dp == &dhead)
1294	    dp = dp->di_next;
1295    }
1296    else {
1297	while (cnt-- > 0) {
1298	    dp = dp->di_prev;
1299	    if (dp == &dhead)
1300		dp = dp->di_prev;
1301	    if (dp == dcwd)
1302		return NULL;
1303	}
1304    }
1305    return dp->di_name;
1306}
1307
1308/*
1309 * Karl Kleinpaste - 10 Feb 1984
1310 * Added dextract(), which is used in pushd +n.
1311 * Instead of just rotating the entire stack around, dextract()
1312 * lets the user have the nth dir extracted from its current
1313 * position, and pushes it onto the top.
1314 */
1315static void
1316dextract(struct directory *dp)
1317{
1318    if (dp == dcwd)
1319	return;
1320    dp->di_next->di_prev = dp->di_prev;
1321    dp->di_prev->di_next = dp->di_next;
1322    dp->di_next = dcwd->di_next;
1323    dp->di_prev = dcwd;
1324    dp->di_next->di_prev = dp;
1325    dcwd->di_next = dp;
1326}
1327
1328static void
1329bequiet_cleanup(void *dummy)
1330{
1331    USE(dummy);
1332    bequiet = 0;
1333}
1334
1335void
1336loaddirs(Char *fname)
1337{
1338    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1339
1340    bequiet = 1;
1341    cleanup_push(&bequiet, bequiet_cleanup);
1342    if (fname)
1343	loaddirs_cmd[1] = fname;
1344    else if ((fname = varval(STRdirsfile)) != STRNULL)
1345	loaddirs_cmd[1] = fname;
1346    else
1347	loaddirs_cmd[1] = STRtildotdirs;
1348    dosource(loaddirs_cmd, NULL);
1349    cleanup_until(&bequiet);
1350}
1351
1352/*
1353 * create a file called ~/.cshdirs which has a sequence
1354 * of pushd commands which will restore the dir stack to
1355 * its state before exit/logout. remember that the order
1356 * is reversed in the file because we are pushing.
1357 * -strike
1358 */
1359void
1360recdirs(Char *fname, int def)
1361{
1362    int     fp, ftmp, oldidfds;
1363    int     cdflag = 0;
1364    struct directory *dp;
1365    unsigned int    num;
1366    Char   *snum;
1367    struct Strbuf qname = Strbuf_INIT;
1368
1369    if (fname == NULL && !def)
1370	return;
1371
1372    if (fname == NULL) {
1373	if ((fname = varval(STRdirsfile)) == STRNULL)
1374	    fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1375	else
1376	    fname = Strsave(fname);
1377    }
1378    else
1379	fname = globone(fname, G_ERROR);
1380    cleanup_push(fname, xfree);
1381
1382    if ((fp = xcreat(short2str(fname), 0600)) == -1) {
1383	cleanup_until(fname);
1384	return;
1385    }
1386
1387    if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
1388	num = (unsigned int) ~0;
1389    else
1390	num = (unsigned int) atoi(short2str(snum));
1391
1392    oldidfds = didfds;
1393    didfds = 0;
1394    ftmp = SHOUT;
1395    SHOUT = fp;
1396
1397    cleanup_push(&qname, Strbuf_cleanup);
1398    dp = dcwd->di_next;
1399    do {
1400	if (dp == &dhead)
1401	    continue;
1402
1403	if (cdflag == 0) {
1404	    cdflag = 1;
1405	    xprintf("cd %S\n", quote_meta(&qname, dp->di_name));
1406	}
1407	else
1408	    xprintf("pushd %S\n", quote_meta(&qname, dp->di_name));
1409
1410	if (num-- == 0)
1411	    break;
1412
1413    } while ((dp = dp->di_next) != dcwd->di_next);
1414
1415    xclose(fp);
1416    SHOUT = ftmp;
1417    didfds = oldidfds;
1418    cleanup_until(fname);
1419}
1420