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