- Add ral(4) for Ralink RT2500/RT2501/RT2600 chip based wireless NIC
[dragonfly.git] / contrib / tcsh / sh.dir.c
1 /* $Header: /src/pub/tcsh/sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $ */
2 /*
3  * sh.dir.c: Directory manipulation functions
4  */
5 /*-
6  * Copyright (c) 1980, 1991 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 #include "sh.h"
34
35 RCSID("$Id: sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $")
36
37 /*
38  * C Shell - directory management
39  */
40
41 static  void                     dstart         __P((const char *));
42 static  struct directory        *dfind          __P((Char *));
43 static  Char                    *dfollow        __P((Char *));
44 static  void                     printdirs      __P((int));
45 static  Char                    *dgoto          __P((Char *));
46 static  void                     dnewcwd        __P((struct directory *, int));
47 static  void                     dset           __P((Char *));
48 static  void                     dextract       __P((struct directory *));
49 static  int                      skipargs       __P((Char ***, char *, char *));
50 static  void                     dgetstack      __P((void));
51
52 static struct directory dhead INIT_ZERO_STRUCT;         /* "head" of loop */
53 static int    printd;                   /* force name to be printed */
54
55 int     bequiet = 0;            /* do not print dir stack -strike */
56
57 static void
58 dstart(from)
59     const char *from;
60 {
61     xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
62 }
63
64 /*
65  * dinit - initialize current working directory
66  */
67 void
68 dinit(hp)
69     Char   *hp;
70 {
71     register char *tcp;
72     register Char *cp;
73     register struct directory *dp;
74     char    path[MAXPATHLEN];
75
76     /* Don't believe the login shell home, because it may be a symlink */
77     tcp = (char *) getcwd(path, sizeof(path));
78     if (tcp == NULL || *tcp == '\0') {
79         xprintf("%s: %s\n", progname, strerror(errno));
80         if (hp && *hp) {
81             tcp = short2str(hp);
82             dstart(tcp);
83             if (chdir(tcp) == -1)
84                 cp = NULL;
85             else
86                 cp = Strsave(hp);
87         }
88         else
89             cp = NULL;
90         if (cp == NULL) {
91             dstart("/");
92             if (chdir("/") == -1)
93                 /* I am not even try to print an error message! */
94                 xexit(1);
95             cp = SAVE("/");
96         }
97     }
98     else {
99 #ifdef S_IFLNK
100         struct stat swd, shp;
101
102         /*
103          * See if $HOME is the working directory we got and use that
104          */
105         if (hp && *hp &&
106             stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
107             DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
108                 swd.st_ino == shp.st_ino)
109             cp = Strsave(hp);
110         else {
111             char   *cwd;
112
113             /*
114              * use PWD if we have it (for subshells)
115              */
116             if ((cwd = getenv("PWD")) != NULL) {
117                 if (stat(cwd, &shp) != -1 && 
118                         DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
119                     swd.st_ino == shp.st_ino)
120                     tcp = cwd;
121             }
122             cp = dcanon(SAVE(tcp), STRNULL);
123         }
124 #else /* S_IFLNK */
125         cp = dcanon(SAVE(tcp), STRNULL);
126 #endif /* S_IFLNK */
127     }
128
129     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
130     dp->di_name = cp;
131     dp->di_count = 0;
132     dhead.di_next = dhead.di_prev = dp;
133     dp->di_next = dp->di_prev = &dhead;
134     printd = 0;
135     dnewcwd(dp, 0);
136     set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
137 }
138
139 static void
140 dset(dp)
141 Char *dp;
142 {
143     /*
144      * Don't call set() directly cause if the directory contains ` or
145      * other junk characters glob will fail. 
146      */
147     set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
148     set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
149
150     tsetenv(STRPWD, dp);
151 }
152
153 #define DIR_PRINT       0x01    /* -p */
154 #define DIR_LONG        0x02    /* -l */
155 #define DIR_VERT        0x04    /* -v */
156 #define DIR_LINE        0x08    /* -n */
157 #define DIR_SAVE        0x10    /* -S */
158 #define DIR_LOAD        0x20    /* -L */
159 #define DIR_CLEAR       0x40    /* -c */
160 #define DIR_OLD         0x80    /* - */
161
162 static int
163 skipargs(v, dstr, str)
164     Char ***v;
165     char   *dstr;
166     char   *str;
167 {
168     Char  **n = *v, *s;
169
170     int dflag = 0, loop = 1;
171     for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 
172         if (*(s = &((*n)[1])) == '\0')  /* test for bare "-" argument */
173             dflag |= DIR_OLD;
174         else {
175             char *p;
176             while (loop && *s != '\0')  /* examine flags */
177             {
178                 if ((p = strchr(dstr, *s++)) != NULL)
179                     dflag |= (1 << (p - dstr));
180                 else {
181                     stderror(ERR_DIRUS, short2str(**v), dstr, str);
182                     loop = 0;   /* break from both loops */
183                     break;
184                 }
185             }
186         }
187     if (*n && (dflag & DIR_OLD))
188         stderror(ERR_DIRUS, short2str(**v), dstr, str);
189     *v = n;
190     /* make -l, -v, and -n imply -p */
191     if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
192         dflag |= DIR_PRINT;
193     return dflag;
194 }
195
196 /*
197  * dodirs - list all directories in directory loop
198  */
199 /*ARGSUSED*/
200 void
201 dodirs(v, c)
202     Char  **v;
203     struct command *c;
204 {
205     static char flags[] = "plvnSLc";
206     int dflag = skipargs(&v, flags, "");
207
208     USE(c);
209     if ((dflag & DIR_CLEAR) != 0) {
210         struct directory *dp, *fdp;
211         for (dp = dcwd->di_next; dp != dcwd; ) {
212             fdp = dp;
213             dp = dp->di_next;
214             if (fdp != &dhead)
215                 dfree(fdp);
216         }
217         dhead.di_next = dhead.di_prev = dp;
218         dp->di_next = dp->di_prev = &dhead;
219     }
220     if ((dflag & DIR_LOAD) != 0) 
221         loaddirs(*v);
222     else if ((dflag & DIR_SAVE) != 0)
223         recdirs(*v, 1);
224
225     if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
226         v++;
227
228     if (*v != NULL || (dflag & DIR_OLD))
229         stderror(ERR_DIRUS, "dirs", flags, "");
230     if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
231         printdirs(dflag);
232 }
233
234 static void
235 printdirs(dflag)
236     int dflag;
237 {
238     register struct directory *dp;
239     Char   *s, *user;
240     int     idx, len, cur;
241     extern int T_Cols;
242
243     dp = dcwd;
244     idx = 0;
245     cur = 0;
246     do {
247         if (dp == &dhead)
248             continue;
249         if (dflag & DIR_VERT) {
250             xprintf("%d\t", idx++);
251             cur = 0;
252         }
253         s = dp->di_name;                
254         user = NULL;
255         if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
256             len = (int) (Strlen(user) + Strlen(s) + 2);
257         else
258             len = (int) (Strlen(s) + 1);
259
260         cur += len;
261         if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
262             xputchar('\n');
263             cur = len;
264         }
265         if (user) 
266             xprintf("~%S", user);
267         xprintf("%S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
268     } while ((dp = dp->di_prev) != dcwd);
269     if (!(dflag & DIR_VERT))
270         xputchar('\n');
271 }
272
273 void
274 dtildepr(dir)
275     Char *dir;
276 {
277     Char* user;
278     if ((user = getusername(&dir)) != NULL)
279         xprintf("~%S%S", user, dir);
280     else
281         xprintf("%S", dir);
282 }
283
284 void
285 dtilde()
286 {
287     struct directory *d = dcwd;
288
289     do {
290         if (d == &dhead)
291             continue;
292         d->di_name = dcanon(d->di_name, STRNULL);
293     } while ((d = d->di_prev) != dcwd);
294
295     dset(dcwd->di_name);
296 }
297
298
299 /* dnormalize():
300  *      The path will be normalized if it
301  *      1) is "..",
302  *      2) or starts with "../",
303  *      3) or ends with "/..",
304  *      4) or contains the string "/../",
305  *      then it will be normalized, unless those strings are quoted. 
306  *      Otherwise, a copy is made and sent back.
307  */
308 Char   *
309 dnormalize(cp, exp)
310     Char   *cp;
311     int exp;
312 {
313
314 /* return true if dp is of the form "../xxx" or "/../xxx" */
315 #define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
316 #define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
317
318 #ifdef S_IFLNK
319     if (exp) {
320         int     dotdot = 0;
321         Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
322         struct stat sb;
323 # ifdef apollo
324         bool slashslash;
325 # endif /* apollo */
326
327         /*
328          * count the number of "../xxx" or "xxx/../xxx" in the path
329          */
330         for (dp=start; *dp && *(dp+1); dp++)
331             if (IS_DOTDOT(start, dp))
332                 dotdot++;
333         /*
334          * if none, we are done.
335          */
336         if (dotdot == 0)
337             return (Strsave(cp));
338
339         /*
340          * If the path doesn't exist, we are done too.
341          */
342         if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT)
343             return (Strsave(cp));
344         
345
346         cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
347                                            sizeof(Char)));
348         (void) Strcpy(cwd, dcwd->di_name);
349
350         /*
351          * If the path starts with a slash, we are not relative to
352          * the current working directory.
353          */
354         if (ABSOLUTEP(start))
355             *cwd = '\0';
356 # ifdef apollo
357         slashslash = cwd[0] == '/' && cwd[1] == '/';
358 # endif /* apollo */
359
360         /*
361          * Ignore . and count ..'s
362          */
363         for (;;) {
364             dotdot = 0;
365             buf[0] = '\0';
366             dp = buf; 
367             while (*cp) 
368                 if (IS_DOT(start, cp)) {
369                     if (*++cp)
370                         cp++;
371                 }
372                 else if (IS_DOTDOT(start, cp)) {
373                     if (buf[0])
374                         break; /* finish analyzing .././../xxx/[..] */
375                     dotdot++;
376                     cp += 2;
377                     if (*cp)
378                         cp++;
379                 }
380                 else 
381                         *dp++ = *cp++;
382
383             *dp = '\0';
384             while (dotdot > 0) 
385                 if ((dp = Strrchr(cwd, '/')) != NULL) {
386 # ifdef apollo
387                     if (dp == &cwd[1]) 
388                         slashslash = 1;
389 # endif /* apollo */
390                         *dp = '\0';
391                         dotdot--;
392                 }
393                 else
394                     break;
395
396             if (!*cwd) {        /* too many ..'s, starts with "/" */
397                 cwd[0] = '/';
398 # ifdef apollo
399                 cwd[1] = '/';
400                 cwd[2] = '\0';
401 # else /* !apollo */
402                 cwd[1] = '\0';
403 # endif /* apollo */
404             }
405 # ifdef apollo
406             else if (slashslash && cwd[1] == '\0') {
407                 cwd[1] = '/';
408                 cwd[2] = '\0';
409             }
410 # endif /* apollo */
411
412             if (buf[0]) {
413                 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
414                     cwd[dotdot++] = '/';
415                 cwd[dotdot] = '\0';
416                 dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
417                 xfree((ptr_t) cwd);
418                 cwd = dp;
419                 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
420                     cwd[--dotdot] = '\0';
421             }
422             /* Reduction of ".." following the stuff we collected in buf
423              * only makes sense if the directory item in buf really exists.
424              * Avoid reduction of "-I../.." (typical compiler call) to ""
425              * or "/usr/nonexistant/../bin" to "/usr/bin":
426              */
427             if (cwd[0]) {
428                 struct stat exists;
429                 if (0 != stat(short2str(cwd), &exists)) {
430                     xfree((ptr_t) cwd);
431                     return Strsave(start);
432                 }
433             }
434             if (!*cp)
435                 break;
436         }
437         return cwd;
438     }
439 #endif /* S_IFLNK */
440     return Strsave(cp);
441 }
442
443
444 /*
445  * dochngd - implement chdir command.
446  */
447 /*ARGSUSED*/
448 void
449 dochngd(v, c)
450     Char  **v;
451     struct command *c;
452 {
453     register Char *cp;
454     register struct directory *dp;
455     int dflag = skipargs(&v, "plvn", "[-|<dir>]");
456
457     USE(c);
458     printd = 0;
459     cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
460
461     if (cp == NULL) {
462         if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
463             stderror(ERR_NAME | ERR_NOHOMEDIR);
464         if (chdir(short2str(cp)) < 0)
465             stderror(ERR_NAME | ERR_CANTCHANGE);
466         cp = Strsave(cp);
467     }
468     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
469         stderror(ERR_NAME | ERR_TOOMANY);
470         /* NOTREACHED */
471         return;
472     }
473     else if ((dp = dfind(cp)) != 0) {
474         char   *tmp;
475
476         printd = 1;
477         if (chdir(tmp = short2str(dp->di_name)) < 0)
478             stderror(ERR_SYSTEM, tmp, strerror(errno));
479         dcwd->di_prev->di_next = dcwd->di_next;
480         dcwd->di_next->di_prev = dcwd->di_prev;
481         dfree(dcwd);
482         dnewcwd(dp, dflag);
483         return;
484     }
485     else
486         if ((cp = dfollow(cp)) == NULL)
487             return;
488     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
489     dp->di_name = cp;
490     dp->di_count = 0;
491     dp->di_next = dcwd->di_next;
492     dp->di_prev = dcwd->di_prev;
493     dp->di_prev->di_next = dp;
494     dp->di_next->di_prev = dp;
495     dfree(dcwd);
496     dnewcwd(dp, dflag);
497 }
498
499 static Char *
500 dgoto(cp)
501     Char   *cp;
502 {
503     Char   *dp;
504
505     if (!ABSOLUTEP(cp))
506     {
507         register Char *p, *q;
508         int     cwdlen;
509
510         for (p = dcwd->di_name; *p++;)
511             continue;
512         if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1)      /* root */
513             cwdlen = 0;
514         for (p = cp; *p++;)
515             continue;
516         dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
517         for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
518             continue;
519         if (cwdlen)
520             p[-1] = '/';
521         else
522             p--;                /* don't add a / after root */
523         for (q = cp; (*p++ = *q++) != '\0';)
524             continue;
525         xfree((ptr_t) cp);
526         cp = dp;
527         dp += cwdlen;
528     }
529     else
530         dp = cp;
531
532 #ifdef WINNT_NATIVE
533     cp = SAVE(getcwd(NULL, 0));
534 #else /* !WINNT_NATIVE */
535     cp = dcanon(cp, dp);
536 #endif /* WINNT_NATIVE */
537     return cp;
538 }
539
540 /*
541  * dfollow - change to arg directory; fall back on cdpath if not valid
542  */
543 static Char *
544 dfollow(cp)
545     register Char *cp;
546 {
547     register Char *dp;
548     struct varent *c;
549     char    ebuf[MAXPATHLEN];
550     int serrno;
551
552     cp = globone(cp, G_ERROR);
553 #ifdef apollo
554     if (Strchr(cp, '`')) {
555         char *dptr, *ptr;
556         if (chdir(dptr = short2str(cp)) < 0) 
557             stderror(ERR_SYSTEM, dptr, strerror(errno));
558         else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
559                 xfree((ptr_t) cp);
560                 cp = Strsave(str2short(ptr));
561                 return dgoto(cp);
562         }
563         else 
564             stderror(ERR_SYSTEM, dptr, ebuf);
565     }
566 #endif /* apollo */
567             
568     (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
569     ebuf[MAXPATHLEN-1] = '\0';
570     /*
571      * if we are ignoring symlinks, try to fix relatives now.
572      * if we are expading symlinks, it should be done by now.
573      */ 
574     dp = dnormalize(cp, symlinks == SYM_IGNORE);
575     if (chdir(short2str(dp)) >= 0) {
576         xfree((ptr_t) cp);
577         return dgoto(dp);
578     }
579     else {
580         xfree((ptr_t) dp);
581         if (chdir(short2str(cp)) >= 0)
582             return dgoto(cp);
583         else if (errno != ENOENT && errno != ENOTDIR)
584             stderror(ERR_SYSTEM, ebuf, strerror(errno));
585         serrno = errno;
586     }
587
588     if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
589         && (c = adrof(STRcdpath)) && c->vec != NULL) {
590         Char  **cdp;
591         register Char *p;
592         Char    buf[MAXPATHLEN];
593
594         for (cdp = c->vec; *cdp; cdp++) {
595             for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
596                 continue;
597             dp[-1] = '/';
598             for (p = cp; (*dp++ = *p++) != '\0';)
599                 continue;
600             /*
601              * We always want to fix the directory here
602              * If we are normalizing symlinks
603              */
604             dp = dnormalize(buf, symlinks == SYM_IGNORE || 
605                                  symlinks == SYM_EXPAND);
606             if (chdir(short2str(dp)) >= 0) {
607                 printd = 1;
608                 xfree((ptr_t) cp);
609                 return dgoto(dp);
610             }
611             else if (chdir(short2str(cp)) >= 0) {
612                 printd = 1;
613                 xfree((ptr_t) dp);
614                 return dgoto(cp);
615             }
616         }
617     }
618     dp = varval(cp);
619     if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
620         xfree((ptr_t) cp);
621         cp = Strsave(dp);
622         printd = 1;
623         return dgoto(cp);
624     }
625     xfree((ptr_t) cp);
626     /*
627      * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
628      * directories we could get to.
629      */
630     if (!bequiet) {
631         stderror(ERR_SYSTEM, ebuf, strerror(serrno));
632         return (NULL);
633     }
634     else
635         return (NULL);
636 }
637
638
639 /*
640  * dopushd - push new directory onto directory stack.
641  *      with no arguments exchange top and second.
642  *      with numeric argument (+n) bring it to top.
643  */
644 /*ARGSUSED*/
645 void
646 dopushd(v, c)
647     Char  **v;
648     struct command *c;
649 {
650     register struct directory *dp;
651     register Char *cp;
652     int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
653     
654     USE(c);
655     printd = 1;
656     cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
657
658     if (cp == NULL) {
659         if (adrof(STRpushdtohome)) {
660             if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
661                 stderror(ERR_NAME | ERR_NOHOMEDIR);
662             if (chdir(short2str(cp)) < 0)
663                 stderror(ERR_NAME | ERR_CANTCHANGE);
664             cp = Strsave(cp);   /* hmmm... PWP */
665             if ((cp = dfollow(cp)) == NULL)
666                 return;
667             dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
668             dp->di_name = cp;
669             dp->di_count = 0;
670             dp->di_prev = dcwd;
671             dp->di_next = dcwd->di_next;
672             dcwd->di_next = dp;
673             dp->di_next->di_prev = dp;
674         }
675         else {
676             char   *tmp;
677
678             if ((dp = dcwd->di_prev) == &dhead)
679                 dp = dhead.di_prev;
680             if (dp == dcwd)
681                 stderror(ERR_NAME | ERR_NODIR);
682             if (chdir(tmp = short2str(dp->di_name)) < 0)
683                 stderror(ERR_SYSTEM, tmp, strerror(errno));
684             dp->di_prev->di_next = dp->di_next;
685             dp->di_next->di_prev = dp->di_prev;
686             dp->di_next = dcwd->di_next;
687             dp->di_prev = dcwd;
688             dcwd->di_next->di_prev = dp;
689             dcwd->di_next = dp;
690         }
691     }
692     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
693         stderror(ERR_NAME | ERR_TOOMANY);
694         /* NOTREACHED */
695         return;
696     }
697     else if ((dp = dfind(cp)) != NULL) {
698         char   *tmp;
699
700         if (chdir(tmp = short2str(dp->di_name)) < 0)
701             stderror(ERR_SYSTEM, tmp, strerror(errno));
702         /*
703          * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
704          */
705         if (adrof(STRdextract))
706             dextract(dp);
707     }
708     else {
709         register Char *ccp;
710
711         if ((ccp = dfollow(cp)) == NULL)
712             return;
713         dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
714         dp->di_name = ccp;
715         dp->di_count = 0;
716         dp->di_prev = dcwd;
717         dp->di_next = dcwd->di_next;
718         dcwd->di_next = dp;
719         dp->di_next->di_prev = dp;
720     }
721     dnewcwd(dp, dflag);
722 }
723
724 /*
725  * dfind - find a directory if specified by numeric (+n) argument
726  */
727 static struct directory *
728 dfind(cp)
729     register Char *cp;
730 {
731     register struct directory *dp;
732     register int i;
733     register Char *ep;
734
735     if (*cp++ != '+')
736         return (0);
737     for (ep = cp; Isdigit(*ep); ep++)
738         continue;
739     if (*ep)
740         return (0);
741     i = getn(cp);
742     if (i <= 0)
743         return (0);
744     for (dp = dcwd; i != 0; i--) {
745         if ((dp = dp->di_prev) == &dhead)
746             dp = dp->di_prev;
747         if (dp == dcwd)
748             stderror(ERR_NAME | ERR_DEEP);
749     }
750     return (dp);
751 }
752
753 /*
754  * dopopd - pop a directory out of the directory stack
755  *      with a numeric argument just discard it.
756  */
757 /*ARGSUSED*/
758 void
759 dopopd(v, c)
760     Char  **v;
761     struct command *c;
762 {
763     Char *cp;
764     register struct directory *dp, *p = NULL;
765     int dflag = skipargs(&v, "plvn", " [-|+<n>]");
766
767     USE(c);
768     printd = 1;
769     cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
770
771     if (cp == NULL)
772         dp = dcwd;
773     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
774         stderror(ERR_NAME | ERR_TOOMANY);
775         /* NOTREACHED */
776         return;
777     }
778     else if ((dp = dfind(cp)) == 0)
779         stderror(ERR_NAME | ERR_BADDIR);
780     if (dp->di_prev == &dhead && dp->di_next == &dhead)
781         stderror(ERR_NAME | ERR_EMPTY);
782     if (dp == dcwd) {
783         char   *tmp;
784
785         if ((p = dp->di_prev) == &dhead)
786             p = dhead.di_prev;
787         if (chdir(tmp = short2str(p->di_name)) < 0)
788             stderror(ERR_SYSTEM, tmp, strerror(errno));
789     }
790     dp->di_prev->di_next = dp->di_next;
791     dp->di_next->di_prev = dp->di_prev;
792     if (dp == dcwd) {
793         dnewcwd(p, dflag);
794     }
795     else {
796         printdirs(dflag);
797     }
798     dfree(dp);
799 }
800
801 /*
802  * dfree - free the directory (or keep it if it still has ref count)
803  */
804 void
805 dfree(dp)
806     register struct directory *dp;
807 {
808
809     if (dp->di_count != 0) {
810         dp->di_next = dp->di_prev = 0;
811     }
812     else {
813         xfree((ptr_t) dp->di_name);
814         xfree((ptr_t) dp);
815     }
816 }
817
818 /*
819  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
820  *      we are of course assuming that the file system is standardly
821  *      constructed (always have ..'s, directories have links)
822  */
823 Char   *
824 dcanon(cp, p)
825     register Char *cp, *p;
826 {
827     register Char *sp;
828     register Char *p1, *p2;     /* general purpose */
829     bool    slash;
830 #ifdef apollo
831     bool    slashslash;
832 #endif /* apollo */
833     size_t  clen;
834
835 #ifdef S_IFLNK                  /* if we have symlinks */
836     Char    link[MAXPATHLEN];
837     char    tlink[MAXPATHLEN];
838     int     cc;
839     Char   *newcp;
840 #endif /* S_IFLNK */
841
842     /*
843      * if the path given is too long truncate it!
844      */
845     if ((clen = Strlen(cp)) >= MAXPATHLEN)
846         cp[clen = MAXPATHLEN - 1] = '\0';
847
848     /*
849      * christos: if the path given does not start with a slash prepend cwd. If
850      * cwd does not start with a slash or the result would be too long try to
851      * correct it.
852      */
853     if (!ABSOLUTEP(cp)) {
854         Char    tmpdir[MAXPATHLEN];
855         size_t  len;
856
857         p1 = varval(STRcwd);
858         if (p1 == STRNULL || !ABSOLUTEP(p1)) {
859             char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
860             if (tmp == NULL || *tmp == '\0') {
861                 xprintf("%s: %s\n", progname, strerror(errno));
862                 set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
863             } else {
864                 set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
865             }
866             p1 = varval(STRcwd);
867         }
868         len = Strlen(p1);
869         if (len + clen + 1 >= MAXPATHLEN)
870             cp[MAXPATHLEN - (len + 1)] = '\0';
871         (void) Strcpy(tmpdir, p1);
872         (void) Strcat(tmpdir, STRslash);
873         (void) Strcat(tmpdir, cp);
874         xfree((ptr_t) cp);
875         cp = p = Strsave(tmpdir);
876     }
877
878 #ifdef apollo
879     slashslash = (cp[0] == '/' && cp[1] == '/');
880 #endif /* apollo */
881
882     while (*p) {                /* for each component */
883         sp = p;                 /* save slash address */
884         while (*++p == '/')     /* flush extra slashes */
885             continue;
886         if (p != ++sp)
887             for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
888                 continue;
889         p = sp;                 /* save start of component */
890         slash = 0;
891         if (*p) 
892             while (*++p)        /* find next slash or end of path */
893                 if (*p == '/') {
894                     slash = 1;
895                     *p = 0;
896                     break;
897                 }
898
899 #ifdef apollo
900         if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
901             slashslash = 1;
902 #endif /* apollo */
903         if (*sp == '\0') {      /* if component is null */
904             if (--sp == cp)     /* if path is one char (i.e. /) */ 
905                 break;
906             else
907                 *sp = '\0';
908         }
909         else if (sp[0] == '.' && sp[1] == 0) {
910             if (slash) {
911                 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
912                     continue;
913                 p = --sp;
914             }
915             else if (--sp != cp)
916                 *sp = '\0';
917             else
918                 sp[1] = '\0';
919         }
920         else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
921             /*
922              * We have something like "yyy/xxx/..", where "yyy" can be null or
923              * a path starting at /, and "xxx" is a single component. Before
924              * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
925              * symbolic link.
926              */
927             *--sp = 0;          /* form the pathname for readlink */
928 #ifdef S_IFLNK                  /* if we have symlinks */
929             if (sp != cp && /* symlinks != SYM_IGNORE && */
930                 (cc = readlink(short2str(cp), tlink,
931                                sizeof tlink)) >= 0) {
932                 tlink[cc] = '\0';
933                 (void) Strncpy(link, str2short(tlink),
934                     sizeof(link) / sizeof(Char));
935                 link[sizeof(link) / sizeof(Char) - 1] = '\0';
936
937                 if (slash)
938                     *p = '/';
939                 /*
940                  * Point p to the '/' in "/..", and restore the '/'.
941                  */
942                 *(p = sp) = '/';
943                 /*
944                  * find length of p
945                  */
946                 for (p1 = p; *p1++;)
947                     continue;
948                 if (*link != '/') {
949                     /*
950                      * Relative path, expand it between the "yyy/" and the
951                      * "/..". First, back sp up to the character past "yyy/".
952                      */
953                     while (*--sp != '/')
954                         continue;
955                     sp++;
956                     *sp = 0;
957                     /*
958                      * New length is "yyy/" + link + "/.." and rest
959                      */
960                     p1 = newcp = (Char *) xmalloc((size_t)
961                                                 (((sp - cp) + cc + (p1 - p)) *
962                                                  sizeof(Char)));
963                     /*
964                      * Copy new path into newcp
965                      */
966                     for (p2 = cp; (*p1++ = *p2++) != '\0';)
967                         continue;
968                     for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
969                         continue;
970                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
971                         continue;
972                     /*
973                      * Restart canonicalization at expanded "/xxx".
974                      */
975                     p = sp - cp - 1 + newcp;
976                 }
977                 else {
978                     /*
979                      * New length is link + "/.." and rest
980                      */
981                     p1 = newcp = (Char *) xmalloc((size_t)
982                                             ((cc + (p1 - p)) * sizeof(Char)));
983                     /*
984                      * Copy new path into newcp
985                      */
986                     for (p2 = link; (*p1++ = *p2++) != '\0';)
987                         continue;
988                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
989                         continue;
990                     /*
991                      * Restart canonicalization at beginning
992                      */
993                     p = newcp;
994                 }
995                 xfree((ptr_t) cp);
996                 cp = newcp;
997 #ifdef apollo
998                 slashslash = (cp[0] == '/' && cp[1] == '/');
999 #endif /* apollo */
1000                 continue;       /* canonicalize the link */
1001             }
1002 #endif /* S_IFLNK */
1003             *sp = '/';
1004             if (sp != cp)
1005                 while (*--sp != '/')
1006                     continue;
1007             if (slash) {
1008                 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
1009                     continue;
1010                 p = sp;
1011             }
1012             else if (cp == sp)
1013                 *++sp = '\0';
1014             else
1015                 *sp = '\0';
1016         }
1017         else {                  /* normal dir name (not . or .. or nothing) */
1018
1019 #ifdef S_IFLNK                  /* if we have symlinks */
1020             if (sp != cp && symlinks == SYM_CHASE &&
1021                 (cc = readlink(short2str(cp), tlink,
1022                                sizeof tlink)) >= 0) {
1023                 tlink[cc] = '\0';
1024                 (void) Strncpy(link, str2short(tlink),
1025                     sizeof(link) / sizeof(Char));
1026                 link[sizeof(link) / sizeof(Char) - 1] = '\0';
1027
1028                 /*
1029                  * restore the '/'.
1030                  */
1031                 if (slash)
1032                     *p = '/';
1033
1034                 /*
1035                  * point sp to p (rather than backing up).
1036                  */
1037                 sp = p;
1038
1039                 /*
1040                  * find length of p
1041                  */
1042                 for (p1 = p; *p1++;)
1043                     continue;
1044                 if (*link != '/') {
1045                     /*
1046                      * Relative path, expand it between the "yyy/" and the
1047                      * remainder. First, back sp up to the character past
1048                      * "yyy/".
1049                      */
1050                     while (*--sp != '/')
1051                         continue;
1052                     sp++;
1053                     *sp = 0;
1054                     /*
1055                      * New length is "yyy/" + link + "/.." and rest
1056                      */
1057                     p1 = newcp = (Char *) xmalloc((size_t)
1058                                                   (((sp - cp) + cc + (p1 - p))
1059                                                    * sizeof(Char)));
1060                     /*
1061                      * Copy new path into newcp
1062                      */
1063                     for (p2 = cp; (*p1++ = *p2++) != '\0';)
1064                         continue;
1065                     for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
1066                         continue;
1067                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1068                         continue;
1069                     /*
1070                      * Restart canonicalization at expanded "/xxx".
1071                      */
1072                     p = sp - cp - 1 + newcp;
1073                 }
1074                 else {
1075                     /*
1076                      * New length is link + the rest
1077                      */
1078                     p1 = newcp = (Char *) xmalloc((size_t)
1079                                             ((cc + (p1 - p)) * sizeof(Char)));
1080                     /*
1081                      * Copy new path into newcp
1082                      */
1083                     for (p2 = link; (*p1++ = *p2++) != '\0';)
1084                         continue;
1085                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1086                         continue;
1087                     /*
1088                      * Restart canonicalization at beginning
1089                      */
1090                     p = newcp;
1091                 }
1092                 xfree((ptr_t) cp);
1093                 cp = newcp;
1094 #ifdef apollo
1095                 slashslash = (cp[0] == '/' && cp[1] == '/');
1096 #endif /* apollo */
1097                 continue;       /* canonicalize the link */
1098             }
1099 #endif /* S_IFLNK */
1100             if (slash)
1101                 *p = '/';
1102         }
1103     }
1104
1105     /*
1106      * fix home...
1107      */
1108 #ifdef S_IFLNK
1109     p1 = varval(STRhome);
1110     cc = (int) Strlen(p1);
1111     /*
1112      * See if we're not in a subdir of STRhome
1113      */
1114     if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
1115         (cp[cc] != '/' && cp[cc] != '\0'))) {
1116         static ino_t home_ino = (ino_t) -1;
1117         static dev_t home_dev = (dev_t) -1;
1118         static Char *home_ptr = NULL;
1119         struct stat statbuf;
1120         int found;
1121
1122         /*
1123          * Get dev and ino of STRhome
1124          */
1125         if (home_ptr != p1 &&
1126             stat(short2str(p1), &statbuf) != -1) {
1127             home_dev = statbuf.st_dev;
1128             home_ino = statbuf.st_ino;
1129             home_ptr = p1;
1130         }
1131         /*
1132          * Start comparing dev & ino backwards
1133          */
1134         p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char));
1135         link[sizeof(link) / sizeof(Char) - 1] = '\0';
1136         found = 0;
1137         while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1138             if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1139                         statbuf.st_ino == home_ino) {
1140                         found = 1;
1141                         break;
1142             }
1143             if ((sp = Strrchr(p2, '/')) != NULL)
1144                 *sp = '\0';
1145         }
1146         /*
1147          * See if we found it
1148          */
1149         if (*p2 && found) {
1150             /*
1151              * Use STRhome to make '~' work
1152              */
1153             newcp = Strspl(p1, cp + Strlen(p2));
1154             xfree((ptr_t) cp);
1155             cp = newcp;
1156         }
1157     }
1158 #endif /* S_IFLNK */
1159
1160 #ifdef apollo
1161     if (slashslash) {
1162         if (cp[1] != '/') {
1163             p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1164             *p = '/';
1165             (void) Strcpy(&p[1], cp);
1166             xfree((ptr_t) cp);
1167             cp = p;
1168         }
1169     }
1170     if (cp[1] == '/' && cp[2] == '/') 
1171         (void) Strcpy(&cp[1], &cp[2]);
1172 #endif /* apollo */
1173     return cp;
1174 }
1175
1176
1177 /*
1178  * dnewcwd - make a new directory in the loop the current one
1179  */
1180 static void
1181 dnewcwd(dp, dflag)
1182     register struct directory *dp;
1183     int dflag;
1184 {
1185     int print;
1186
1187     if (adrof(STRdunique)) {
1188         struct directory *dn;
1189
1190         for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 
1191             if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1192                 dn->di_next->di_prev = dn->di_prev;
1193                 dn->di_prev->di_next = dn->di_next;
1194                 dfree(dn);
1195                 break;
1196             }
1197     }
1198     dcwd = dp;
1199     dset(dcwd->di_name);
1200     dgetstack();
1201     print = printd;             /* if printd is set, print dirstack... */
1202     if (adrof(STRpushdsilent))  /* but pushdsilent overrides printd... */
1203         print = 0;
1204     if (dflag & DIR_PRINT)      /* but DIR_PRINT overrides pushdsilent... */
1205         print = 1;
1206     if (bequiet)                /* and bequiet overrides everything */
1207         print = 0;
1208     if (print)
1209         printdirs(dflag);
1210     cwd_cmd();                  /* PWP: run the defined cwd command */
1211 }
1212
1213 void
1214 dsetstack()
1215 {
1216     Char **cp;
1217     struct varent *vp;
1218     struct directory *dn, *dp;
1219
1220     if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
1221         return;
1222
1223     /* Free the whole stack */
1224     while ((dn = dhead.di_prev) != &dhead) {
1225         dn->di_next->di_prev = dn->di_prev;
1226         dn->di_prev->di_next = dn->di_next;
1227         if (dn != dcwd)
1228             dfree(dn);
1229     }
1230
1231     /* thread the current working directory */
1232     dhead.di_prev = dhead.di_next = dcwd;
1233     dcwd->di_next = dcwd->di_prev = &dhead;
1234
1235     /* put back the stack */
1236     for (cp = vp->vec; cp && *cp && **cp; cp++) {
1237         dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1238         dp->di_name = Strsave(*cp);
1239         dp->di_count = 0;
1240         dp->di_prev = dcwd;
1241         dp->di_next = dcwd->di_next;
1242         dcwd->di_next = dp;
1243         dp->di_next->di_prev = dp;
1244     }
1245     dgetstack();        /* Make $dirstack reflect the current state */
1246 }
1247
1248 static void
1249 dgetstack()
1250 {
1251     int i = 0;
1252     Char **dblk, **dbp;
1253     struct directory *dn;
1254
1255     if (adrof(STRdirstack) == NULL) 
1256         return;
1257
1258     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 
1259         continue;
1260     dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1261     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 
1262          *dbp = Strsave(dn->di_name);
1263     *dbp = NULL;
1264     setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1265 }
1266
1267 /*
1268  * getstakd - added by kfk 17 Jan 1984
1269  * Support routine for the stack hack.  Finds nth directory in
1270  * the directory stack, or finds last directory in stack.
1271  */
1272 int
1273 getstakd(s, cnt)
1274     Char   *s;
1275     int     cnt;
1276 {
1277     struct directory *dp;
1278
1279     dp = dcwd;
1280     if (cnt < 0) {              /* < 0 ==> last dir requested. */
1281         dp = dp->di_next;
1282         if (dp == &dhead)
1283             dp = dp->di_next;
1284     }
1285     else {
1286         while (cnt-- > 0) {
1287             dp = dp->di_prev;
1288             if (dp == &dhead)
1289                 dp = dp->di_prev;
1290             if (dp == dcwd)
1291                 return (0);
1292         }
1293     }
1294     (void) Strncpy(s, dp->di_name, BUFSIZE);
1295     s[BUFSIZE - 1] = '\0';
1296     return (1);
1297 }
1298
1299 /*
1300  * Karl Kleinpaste - 10 Feb 1984
1301  * Added dextract(), which is used in pushd +n.
1302  * Instead of just rotating the entire stack around, dextract()
1303  * lets the user have the nth dir extracted from its current
1304  * position, and pushes it onto the top.
1305  */
1306 static void
1307 dextract(dp)
1308     struct directory *dp;
1309 {
1310     if (dp == dcwd)
1311         return;
1312     dp->di_next->di_prev = dp->di_prev;
1313     dp->di_prev->di_next = dp->di_next;
1314     dp->di_next = dcwd->di_next;
1315     dp->di_prev = dcwd;
1316     dp->di_next->di_prev = dp;
1317     dcwd->di_next = dp;
1318 }
1319
1320 void
1321 loaddirs(fname)
1322     Char *fname;
1323 {
1324     static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1325
1326     bequiet = 1;
1327     if (fname) 
1328         loaddirs_cmd[1] = fname;
1329     else if ((fname = varval(STRdirsfile)) != STRNULL)
1330         loaddirs_cmd[1] = fname;
1331     else
1332         loaddirs_cmd[1] = STRtildotdirs;
1333     dosource(loaddirs_cmd, (struct command *)0);
1334     bequiet = 0;
1335 }
1336
1337 /*
1338  * create a file called ~/.cshdirs which has a sequence
1339  * of pushd commands which will restore the dir stack to
1340  * its state before exit/logout. remember that the order
1341  * is reversed in the file because we are pushing.
1342  * -strike
1343  */
1344 void
1345 recdirs(fname, def)
1346     Char *fname;
1347     int def;
1348 {
1349     int     fp, ftmp, oldidfds;
1350     int     cdflag = 0;
1351     extern struct directory *dcwd;
1352     struct directory *dp;
1353     unsigned int    num;
1354     Char   *snum;
1355     Char    qname[MAXPATHLEN*2];
1356
1357     if (fname == NULL && !def) 
1358         return;
1359
1360     if (fname == NULL) {
1361         if ((fname = varval(STRdirsfile)) == STRNULL)
1362             fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1363         else
1364             fname = Strsave(fname);
1365     }
1366     else 
1367         fname = globone(fname, G_ERROR);
1368                 
1369     if ((fp = creat(short2str(fname), 0600)) == -1) {
1370         xfree((ptr_t) fname);
1371         return;
1372     }
1373
1374     if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 
1375         num = (unsigned int) ~0;
1376     else
1377         num = (unsigned int) atoi(short2str(snum));
1378
1379     oldidfds = didfds;
1380     didfds = 0;
1381     ftmp = SHOUT;
1382     SHOUT = fp;
1383
1384     dp = dcwd->di_next;
1385     do {
1386         if (dp == &dhead)
1387             continue;
1388
1389         if (cdflag == 0) {
1390             cdflag = 1;
1391             xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1392         }
1393         else
1394             xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1395
1396         if (num-- == 0)
1397             break;
1398
1399     } while ((dp = dp->di_next) != dcwd->di_next);
1400
1401     (void) close(fp);
1402     SHOUT = ftmp;
1403     didfds = oldidfds;
1404     xfree((ptr_t) fname);
1405 }