"Fossies" - the Fresh Open Source Software Archive

Member "tcsh-6.22.03/sh.dir.c" (18 Nov 2020, 33378 Bytes) of package /linux/misc/tcsh-6.22.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "sh.dir.c": 6.22.02_vs_6.22.03.

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