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