]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/tcsh/sh.dir.c
This commit was generated by cvs2svn to compensate for changes in r80016,
[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     size_t  clen;
818
819 #ifdef S_IFLNK                  /* if we have symlinks */
820     Char    link[MAXPATHLEN];
821     char    tlink[MAXPATHLEN];
822     int     cc;
823     Char   *newcp;
824 #endif /* S_IFLNK */
825
826     /*
827      * if the path given is too long truncate it!
828      */
829     if ((clen = Strlen(cp)) >= MAXPATHLEN)
830         cp[clen = MAXPATHLEN - 1] = '\0';
831
832     /*
833      * christos: if the path given does not start with a slash prepend cwd. If
834      * cwd does not start with a slash or the result would be too long try to
835      * correct it.
836      */
837     if (!ABSOLUTEP(cp)) {
838         Char    tmpdir[MAXPATHLEN];
839         size_t  len;
840
841         p1 = varval(STRcwd);
842         if (p1 == STRNULL || !ABSOLUTEP(p1)) {
843             char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
844             if (tmp == NULL || *tmp == '\0') {
845                 xprintf("%s: %s\n", progname, strerror(errno));
846                 set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
847             } else {
848                 set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
849             }
850             p1 = varval(STRcwd);
851         }
852         len = Strlen(p1);
853         if (len + clen + 1 >= MAXPATHLEN)
854             cp[MAXPATHLEN - (len + 1)] = '\0';
855         (void) Strcpy(tmpdir, p1);
856         (void) Strcat(tmpdir, STRslash);
857         (void) Strcat(tmpdir, cp);
858         xfree((ptr_t) cp);
859         cp = p = Strsave(tmpdir);
860     }
861
862 #ifdef apollo
863     slashslash = (cp[0] == '/' && cp[1] == '/');
864 #endif /* apollo */
865
866     while (*p) {                /* for each component */
867         sp = p;                 /* save slash address */
868         while (*++p == '/')     /* flush extra slashes */
869             continue;
870         if (p != ++sp)
871             for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
872                 continue;
873         p = sp;                 /* save start of component */
874         slash = 0;
875         if (*p) 
876             while (*++p)        /* find next slash or end of path */
877                 if (*p == '/') {
878                     slash = 1;
879                     *p = 0;
880                     break;
881                 }
882
883 #ifdef apollo
884         if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
885             slashslash = 1;
886 #endif /* apollo */
887         if (*sp == '\0') {      /* if component is null */
888             if (--sp == cp)     /* if path is one char (i.e. /) */ 
889                 break;
890             else
891                 *sp = '\0';
892         }
893         else if (sp[0] == '.' && sp[1] == 0) {
894             if (slash) {
895                 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
896                     continue;
897                 p = --sp;
898             }
899             else if (--sp != cp)
900                 *sp = '\0';
901             else
902                 sp[1] = '\0';
903         }
904         else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
905             /*
906              * We have something like "yyy/xxx/..", where "yyy" can be null or
907              * a path starting at /, and "xxx" is a single component. Before
908              * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
909              * symbolic link.
910              */
911             *--sp = 0;          /* form the pathname for readlink */
912 #ifdef S_IFLNK                  /* if we have symlinks */
913             if (sp != cp && /* symlinks != SYM_IGNORE && */
914                 (cc = readlink(short2str(cp), tlink,
915                                sizeof tlink)) >= 0) {
916                 tlink[cc] = '\0';
917                 (void) Strncpy(link, str2short(tlink),
918                     sizeof(link) / sizeof(Char));
919                 link[sizeof(link) / sizeof(Char) - 1] = '\0';
920
921                 if (slash)
922                     *p = '/';
923                 /*
924                  * Point p to the '/' in "/..", and restore the '/'.
925                  */
926                 *(p = sp) = '/';
927                 /*
928                  * find length of p
929                  */
930                 for (p1 = p; *p1++;)
931                     continue;
932                 if (*link != '/') {
933                     /*
934                      * Relative path, expand it between the "yyy/" and the
935                      * "/..". First, back sp up to the character past "yyy/".
936                      */
937                     while (*--sp != '/')
938                         continue;
939                     sp++;
940                     *sp = 0;
941                     /*
942                      * New length is "yyy/" + link + "/.." and rest
943                      */
944                     p1 = newcp = (Char *) xmalloc((size_t)
945                                                 (((sp - cp) + cc + (p1 - p)) *
946                                                  sizeof(Char)));
947                     /*
948                      * Copy new path into newcp
949                      */
950                     for (p2 = cp; (*p1++ = *p2++) != '\0';)
951                         continue;
952                     for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
953                         continue;
954                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
955                         continue;
956                     /*
957                      * Restart canonicalization at expanded "/xxx".
958                      */
959                     p = sp - cp - 1 + newcp;
960                 }
961                 else {
962                     /*
963                      * New length is link + "/.." and rest
964                      */
965                     p1 = newcp = (Char *) xmalloc((size_t)
966                                             ((cc + (p1 - p)) * sizeof(Char)));
967                     /*
968                      * Copy new path into newcp
969                      */
970                     for (p2 = link; (*p1++ = *p2++) != '\0';)
971                         continue;
972                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
973                         continue;
974                     /*
975                      * Restart canonicalization at beginning
976                      */
977                     p = newcp;
978                 }
979                 xfree((ptr_t) cp);
980                 cp = newcp;
981 #ifdef apollo
982                 slashslash = (cp[0] == '/' && cp[1] == '/');
983 #endif /* apollo */
984                 continue;       /* canonicalize the link */
985             }
986 #endif /* S_IFLNK */
987             *sp = '/';
988             if (sp != cp)
989                 while (*--sp != '/')
990                     continue;
991             if (slash) {
992                 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
993                     continue;
994                 p = sp;
995             }
996             else if (cp == sp)
997                 *++sp = '\0';
998             else
999                 *sp = '\0';
1000         }
1001         else {                  /* normal dir name (not . or .. or nothing) */
1002
1003 #ifdef S_IFLNK                  /* if we have symlinks */
1004             if (sp != cp && symlinks == SYM_CHASE &&
1005                 (cc = readlink(short2str(cp), tlink,
1006                                sizeof tlink)) >= 0) {
1007                 tlink[cc] = '\0';
1008                 (void) Strncpy(link, str2short(tlink),
1009                     sizeof(link) / sizeof(Char));
1010                 link[sizeof(link) / sizeof(Char) - 1] = '\0';
1011
1012                 /*
1013                  * restore the '/'.
1014                  */
1015                 if (slash)
1016                     *p = '/';
1017
1018                 /*
1019                  * point sp to p (rather than backing up).
1020                  */
1021                 sp = p;
1022
1023                 /*
1024                  * find length of p
1025                  */
1026                 for (p1 = p; *p1++;)
1027                     continue;
1028                 if (*link != '/') {
1029                     /*
1030                      * Relative path, expand it between the "yyy/" and the
1031                      * remainder. First, back sp up to the character past
1032                      * "yyy/".
1033                      */
1034                     while (*--sp != '/')
1035                         continue;
1036                     sp++;
1037                     *sp = 0;
1038                     /*
1039                      * New length is "yyy/" + link + "/.." and rest
1040                      */
1041                     p1 = newcp = (Char *) xmalloc((size_t)
1042                                                   (((sp - cp) + cc + (p1 - p))
1043                                                    * sizeof(Char)));
1044                     /*
1045                      * Copy new path into newcp
1046                      */
1047                     for (p2 = cp; (*p1++ = *p2++) != '\0';)
1048                         continue;
1049                     for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
1050                         continue;
1051                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1052                         continue;
1053                     /*
1054                      * Restart canonicalization at expanded "/xxx".
1055                      */
1056                     p = sp - cp - 1 + newcp;
1057                 }
1058                 else {
1059                     /*
1060                      * New length is link + the rest
1061                      */
1062                     p1 = newcp = (Char *) xmalloc((size_t)
1063                                             ((cc + (p1 - p)) * sizeof(Char)));
1064                     /*
1065                      * Copy new path into newcp
1066                      */
1067                     for (p2 = link; (*p1++ = *p2++) != '\0';)
1068                         continue;
1069                     for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1070                         continue;
1071                     /*
1072                      * Restart canonicalization at beginning
1073                      */
1074                     p = newcp;
1075                 }
1076                 xfree((ptr_t) cp);
1077                 cp = newcp;
1078 #ifdef apollo
1079                 slashslash = (cp[0] == '/' && cp[1] == '/');
1080 #endif /* apollo */
1081                 continue;       /* canonicalize the link */
1082             }
1083 #endif /* S_IFLNK */
1084             if (slash)
1085                 *p = '/';
1086         }
1087     }
1088
1089     /*
1090      * fix home...
1091      */
1092 #ifdef S_IFLNK
1093     p1 = varval(STRhome);
1094     cc = (int) Strlen(p1);
1095     /*
1096      * See if we're not in a subdir of STRhome
1097      */
1098     if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
1099         (cp[cc] != '/' && cp[cc] != '\0'))) {
1100         static ino_t home_ino = (ino_t) -1;
1101         static dev_t home_dev = (dev_t) -1;
1102         static Char *home_ptr = NULL;
1103         struct stat statbuf;
1104         int found;
1105
1106         /*
1107          * Get dev and ino of STRhome
1108          */
1109         if (home_ptr != p1 &&
1110             stat(short2str(p1), &statbuf) != -1) {
1111             home_dev = statbuf.st_dev;
1112             home_ino = statbuf.st_ino;
1113             home_ptr = p1;
1114         }
1115         /*
1116          * Start comparing dev & ino backwards
1117          */
1118         p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char));
1119         link[sizeof(link) / sizeof(Char) - 1] = '\0';
1120         found = 0;
1121         while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1122             if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1123                         statbuf.st_ino == home_ino) {
1124                         found = 1;
1125                         break;
1126             }
1127             if ((sp = Strrchr(p2, '/')) != NULL)
1128                 *sp = '\0';
1129         }
1130         /*
1131          * See if we found it
1132          */
1133         if (*p2 && found) {
1134             /*
1135              * Use STRhome to make '~' work
1136              */
1137             newcp = Strspl(p1, cp + Strlen(p2));
1138             xfree((ptr_t) cp);
1139             cp = newcp;
1140         }
1141     }
1142 #endif /* S_IFLNK */
1143
1144 #ifdef apollo
1145     if (slashslash) {
1146         if (cp[1] != '/') {
1147             p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1148             *p = '/';
1149             (void) Strcpy(&p[1], cp);
1150             xfree((ptr_t) cp);
1151             cp = p;
1152         }
1153     }
1154     if (cp[1] == '/' && cp[2] == '/') 
1155         (void) Strcpy(&cp[1], &cp[2]);
1156 #endif /* apollo */
1157     return cp;
1158 }
1159
1160
1161 /*
1162  * dnewcwd - make a new directory in the loop the current one
1163  */
1164 static void
1165 dnewcwd(dp, dflag)
1166     register struct directory *dp;
1167     int dflag;
1168 {
1169     int print;
1170
1171     if (adrof(STRdunique)) {
1172         struct directory *dn;
1173
1174         for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 
1175             if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1176                 dn->di_next->di_prev = dn->di_prev;
1177                 dn->di_prev->di_next = dn->di_next;
1178                 dfree(dn);
1179                 break;
1180             }
1181     }
1182     dcwd = dp;
1183     dset(dcwd->di_name);
1184     dgetstack();
1185     print = printd;             /* if printd is set, print dirstack... */
1186     if (adrof(STRpushdsilent))  /* but pushdsilent overrides printd... */
1187         print = 0;
1188     if (dflag & DIR_PRINT)      /* but DIR_PRINT overrides pushdsilent... */
1189         print = 1;
1190     if (bequiet)                /* and bequiet overrides everything */
1191         print = 0;
1192     if (print)
1193         printdirs(dflag);
1194     cwd_cmd();                  /* PWP: run the defined cwd command */
1195 }
1196
1197 void
1198 dsetstack()
1199 {
1200     Char **cp;
1201     struct varent *vp;
1202     struct directory *dn, *dp;
1203
1204     if ((vp = adrof(STRdirstack)) == NULL)
1205         return;
1206
1207     /* Free the whole stack */
1208     while ((dn = dhead.di_prev) != &dhead) {
1209         dn->di_next->di_prev = dn->di_prev;
1210         dn->di_prev->di_next = dn->di_next;
1211         if (dn != dcwd)
1212             dfree(dn);
1213     }
1214
1215     /* thread the current working directory */
1216     dhead.di_prev = dhead.di_next = dcwd;
1217     dcwd->di_next = dcwd->di_prev = &dhead;
1218
1219     /* put back the stack */
1220     for (cp = vp->vec; cp && *cp && **cp; cp++) {
1221         dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1222         dp->di_name = Strsave(*cp);
1223         dp->di_count = 0;
1224         dp->di_prev = dcwd;
1225         dp->di_next = dcwd->di_next;
1226         dcwd->di_next = dp;
1227         dp->di_next->di_prev = dp;
1228     }
1229     dgetstack();        /* Make $dirstack reflect the current state */
1230 }
1231
1232 static void
1233 dgetstack()
1234 {
1235     int i = 0;
1236     Char **dblk, **dbp;
1237     struct directory *dn;
1238
1239     if (adrof(STRdirstack) == NULL) 
1240         return;
1241
1242     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 
1243         continue;
1244     dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1245     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 
1246          *dbp = Strsave(dn->di_name);
1247     *dbp = NULL;
1248     setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1249 }
1250
1251 /*
1252  * getstakd - added by kfk 17 Jan 1984
1253  * Support routine for the stack hack.  Finds nth directory in
1254  * the directory stack, or finds last directory in stack.
1255  */
1256 int
1257 getstakd(s, cnt)
1258     Char   *s;
1259     int     cnt;
1260 {
1261     struct directory *dp;
1262
1263     dp = dcwd;
1264     if (cnt < 0) {              /* < 0 ==> last dir requested. */
1265         dp = dp->di_next;
1266         if (dp == &dhead)
1267             dp = dp->di_next;
1268     }
1269     else {
1270         while (cnt-- > 0) {
1271             dp = dp->di_prev;
1272             if (dp == &dhead)
1273                 dp = dp->di_prev;
1274             if (dp == dcwd)
1275                 return (0);
1276         }
1277     }
1278     (void) Strncpy(s, dp->di_name, BUFSIZE);
1279     s[BUFSIZE - 1] = '\0';
1280     return (1);
1281 }
1282
1283 /*
1284  * Karl Kleinpaste - 10 Feb 1984
1285  * Added dextract(), which is used in pushd +n.
1286  * Instead of just rotating the entire stack around, dextract()
1287  * lets the user have the nth dir extracted from its current
1288  * position, and pushes it onto the top.
1289  */
1290 static void
1291 dextract(dp)
1292     struct directory *dp;
1293 {
1294     if (dp == dcwd)
1295         return;
1296     dp->di_next->di_prev = dp->di_prev;
1297     dp->di_prev->di_next = dp->di_next;
1298     dp->di_next = dcwd->di_next;
1299     dp->di_prev = dcwd;
1300     dp->di_next->di_prev = dp;
1301     dcwd->di_next = dp;
1302 }
1303
1304 void
1305 loaddirs(fname)
1306     Char *fname;
1307 {
1308     static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1309
1310     bequiet = 1;
1311     if (fname) 
1312         loaddirs_cmd[1] = fname;
1313     else if ((fname = varval(STRdirsfile)) != STRNULL)
1314         loaddirs_cmd[1] = fname;
1315     else
1316         loaddirs_cmd[1] = STRtildotdirs;
1317     dosource(loaddirs_cmd, (struct command *)0);
1318     bequiet = 0;
1319 }
1320
1321 /*
1322  * create a file called ~/.cshdirs which has a sequence
1323  * of pushd commands which will restore the dir stack to
1324  * its state before exit/logout. remember that the order
1325  * is reversed in the file because we are pushing.
1326  * -strike
1327  */
1328 void
1329 recdirs(fname, def)
1330     Char *fname;
1331     int def;
1332 {
1333     int     fp, ftmp, oldidfds;
1334     int     cdflag = 0;
1335     extern struct directory *dcwd;
1336     struct directory *dp;
1337     unsigned int    num;
1338     Char   *snum;
1339     Char    qname[MAXPATHLEN*2];
1340
1341     if (fname == NULL && !def) 
1342         return;
1343
1344     if (fname == NULL) {
1345         if ((fname = varval(STRdirsfile)) == STRNULL)
1346             fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1347         else
1348             fname = Strsave(fname);
1349     }
1350     else 
1351         fname = globone(fname, G_ERROR);
1352                 
1353     if ((fp = creat(short2str(fname), 0600)) == -1) {
1354         xfree((ptr_t) fname);
1355         return;
1356     }
1357
1358     if ((snum = varval(STRsavedirs)) == STRNULL) 
1359         num = (unsigned int) ~0;
1360     else
1361         num = (unsigned int) atoi(short2str(snum));
1362
1363     oldidfds = didfds;
1364     didfds = 0;
1365     ftmp = SHOUT;
1366     SHOUT = fp;
1367
1368     dp = dcwd->di_next;
1369     do {
1370         if (dp == &dhead)
1371             continue;
1372
1373         if (cdflag == 0) {
1374             cdflag = 1;
1375             xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1376         }
1377         else
1378             xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1379
1380         if (num-- == 0)
1381             break;
1382
1383     } while ((dp = dp->di_next) != dcwd->di_next);
1384
1385     (void) close(fp);
1386     SHOUT = ftmp;
1387     didfds = oldidfds;
1388     xfree((ptr_t) fname);
1389 }