home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume25 / tcsh-6.01 / part10 / sh.dir.c next >
C/C++ Source or Header  |  1991-12-19  |  25KB  |  1,131 lines

  1. /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.01/RCS/sh.dir.c,v 3.8 1991/12/19 22:34:14 christos Exp $ */
  2. /*
  3.  * sh.dir.c: Directory manipulation functions
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #include "sh.h"
  38.  
  39. RCSID("$Id: sh.dir.c,v 3.8 1991/12/19 22:34:14 christos Exp $")
  40.  
  41. /*
  42.  * C Shell - directory management
  43.  */
  44.  
  45. static    struct directory    *dfind        __P((Char *));
  46. static    Char             *dfollow    __P((Char *));
  47. static    void                printdirs    __P((void));
  48. static    Char             *dgoto        __P((Char *));
  49. static    void                dnewcwd    __P((struct directory *));
  50. static    void                dset        __P((Char *));
  51.  
  52. struct directory dhead;        /* "head" of loop */
  53. int     printd;            /* force name to be printed */
  54.  
  55. #ifdef CSHDIRS
  56. int     bequiet = 0;        /* do not print dir stack -strike */
  57.  
  58. #endif
  59. static int dirflag = 0;
  60.  
  61. /*
  62.  * dinit - initialize current working directory
  63.  */
  64. void
  65. dinit(hp)
  66.     Char   *hp;
  67. {
  68.     register char *tcp;
  69.     register Char *cp;
  70.     register struct directory *dp;
  71.     char    path[MAXPATHLEN];
  72.     static char *emsg = "tcsh: Trying to start from \"%s\"\n";
  73.  
  74.     /* Don't believe the login shell home, because it may be a symlink */
  75.     tcp = getwd(path);        /* see ngetwd.c for System V version */
  76.     if (tcp == NULL || *tcp == '\0') {
  77.     (void) xprintf("tcsh: %s\n", path);
  78.     if (hp && *hp) {
  79.         tcp = short2str(hp);
  80.         (void) xprintf(emsg, tcp);
  81.         if (chdir(tcp) == -1)
  82.         cp = NULL;
  83.         else
  84.         cp = hp;
  85.     }
  86.     else
  87.         cp = NULL;
  88.     if (cp == NULL) {
  89.         (void) xprintf(emsg, "/");
  90.         if (chdir("/") == -1)
  91.         /* I am not even try to print an error message! */
  92.         xexit(1);
  93.         cp = SAVE("/");
  94.     }
  95.     }
  96.     else {
  97. #ifdef S_IFLNK
  98.     struct stat swd, shp;
  99.  
  100.     /*
  101.      * See if $HOME is the working directory we got and use that
  102.      */
  103.     if (hp && *hp &&
  104.         stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
  105.         DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
  106.         swd.st_ino == shp.st_ino)
  107.         cp = hp;
  108.     else {
  109.         char   *cwd;
  110.  
  111.         /*
  112.          * use PWD if we have it (for subshells)
  113.          */
  114.         if (cwd = getenv("PWD")) {
  115.         if (stat(cwd, &shp) != -1 && 
  116.             DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
  117.             swd.st_ino == shp.st_ino)
  118.             tcp = cwd;
  119.         }
  120.         cp = dcanon(SAVE(tcp), STRNULL);
  121.     }
  122. #else                /* S_IFLNK */
  123.     cp = dcanon(SAVE(tcp), STRNULL);
  124. #endif                /* S_IFLNK */
  125.     }
  126.  
  127.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  128.     dp->di_name = Strsave(cp);
  129.     dp->di_count = 0;
  130.     dhead.di_next = dhead.di_prev = dp;
  131.     dp->di_next = dp->di_prev = &dhead;
  132.     printd = 0;
  133.     dnewcwd(dp);
  134. }
  135.  
  136. static void
  137. dset(dp)
  138. Char *dp;
  139. {
  140.     /*
  141.      * Don't call set() directly cause if the directory contains ` or
  142.      * other junk characters glob will fail. 
  143.      */
  144.     register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
  145.  
  146.     vec[0] = Strsave(dp);
  147.     vec[1] = 0;
  148.     setq(STRcwd, vec, &shvhed);
  149.     Setenv(STRPWD, dp);
  150. }
  151.  
  152. #define DIR_LONG 1
  153. #define DIR_VERT 2
  154. #define DIR_LINE 4
  155.  
  156. static void
  157. skipargs(v, str)
  158.     Char ***v;
  159.     char   *str;
  160. {
  161.     Char  **n = *v, *s;
  162.  
  163.     dirflag = 0;
  164.     for (n++; *n != NULL && (*n)[0] == '-'; n++)
  165.     for (s = &((*n)[1]); *s; s++)
  166.         switch (*s) {
  167.         case 'l':
  168.         dirflag |= DIR_LONG;
  169.         break;
  170.         case 'v':
  171.         dirflag |= DIR_VERT;
  172.         break;
  173.         case 'n':
  174.         dirflag |= DIR_LINE;
  175.         break;
  176.         default:
  177.         stderror(ERR_DIRUS, short2str(**v), str);
  178.         break;
  179.         }
  180.     *v = n;
  181. }
  182.  
  183. /*
  184.  * dodirs - list all directories in directory loop
  185.  */
  186. /*ARGSUSED*/
  187. void
  188. dodirs(v, c)
  189.     Char  **v;
  190.     struct command *c;
  191. {
  192.     skipargs(&v, "");
  193.  
  194.     if (*v != NULL)
  195.     stderror(ERR_DIRUS, "dirs", "");
  196.     printdirs();
  197. }
  198.  
  199. static void
  200. printdirs()
  201. {
  202.     register struct directory *dp;
  203.     Char   *s, *hp = value(STRhome);
  204.     int     idx, len, cur;
  205.     extern int T_Cols;
  206.  
  207.     if (*hp == '\0')
  208.     hp = NULL;
  209.     dp = dcwd;
  210.     idx = 0;
  211.     cur = 0;
  212.     do {
  213.     if (dp == &dhead)
  214.         continue;
  215.     if (dirflag & DIR_VERT) {
  216.         xprintf("%d\t", idx++);
  217.         cur = 0;
  218.     }
  219.     if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
  220.         prefix(hp, dp->di_name))
  221.         len = Strlen(s = (dp->di_name + Strlen(hp))) + 2;
  222.     else
  223.         len = Strlen(s = dp->di_name) + 1;
  224.  
  225.     cur += len;
  226.     if ((dirflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
  227.         xprintf("\n");
  228.         cur = len;
  229.     }
  230.     xprintf(s != dp->di_name ? "~%s%c" : "%s%c",
  231.         short2str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
  232.     } while ((dp = dp->di_prev) != dcwd);
  233.     if (!(dirflag & DIR_VERT))
  234.     xprintf("\n");
  235. }
  236.  
  237. void
  238. dtildepr(home, dir)
  239.     register Char *home, *dir;
  240. {
  241.  
  242.     if (!eq(home, STRslash) && prefix(home, dir))
  243.     xprintf("~%s", short2str(dir + Strlen(home)));
  244.     else
  245.     xprintf("%s", short2str(dir));
  246. }
  247.  
  248. void
  249. dtilde()
  250. {
  251.     struct directory *d = dcwd;
  252.  
  253.     do {
  254.     if (d == &dhead)
  255.         continue;
  256.     d->di_name = dcanon(d->di_name, STRNULL);
  257.     } while ((d = d->di_prev) != dcwd);
  258.  
  259.     dset(dcwd->di_name);
  260. }
  261.  
  262.  
  263. /* dnormalize():
  264.  *    If the name starts with . or .. then we might need to normalize
  265.  *    it depending on the symbolic link flags
  266.  */
  267. Char   *
  268. dnormalize(cp)
  269.     Char   *cp;
  270. {
  271.  
  272. #define TRM(a) ((a) & TRIM)
  273. #define ISDOT(c) (TRM((c)[0]) == '.' && ((TRM((c)[1]) == '\0') || \
  274.           (TRM((c)[1]) == '/')))
  275. #define ISDOTDOT(c) (TRM((c)[0]) == '.' && ISDOT(&((c)[1])))
  276.  
  277.     if (TRM(cp[0]) == '/')
  278.     return (Strsave(cp));
  279.  
  280. #ifdef S_IFLNK
  281.     if (adrof(STRignore_symlinks)) {
  282.     int     dotdot = 0;
  283.     Char   *dp, *cwd;
  284. #ifdef apollo
  285.     bool slashslash;
  286. #endif
  287.  
  288.     cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) *
  289.                      sizeof(Char)));
  290.     (void) Strcpy(cwd, dcwd->di_name);
  291. #ifdef apollo
  292.     slashslash = cwd[0] == '/' && cwd[1] == '/';
  293. #endif
  294.  
  295.     /*
  296.      * Ignore . and count ..'s
  297.      */
  298.     while (*cp) {
  299.         if (ISDOT(cp)) {
  300.         if (*++cp)
  301.             cp++;
  302.         }
  303.         else if (ISDOTDOT(cp)) {
  304.         dotdot++;
  305.         cp += 2;
  306.         if (*cp)
  307.             cp++;
  308.         }
  309.         else
  310.         break;
  311.     }
  312.     while (dotdot > 0) 
  313.         if ((dp = Strrchr(cwd, '/'))) {
  314. #ifdef apollo
  315.         if (dp == &cwd[1]) 
  316.             slashslash = 1;
  317. #endif
  318.         *dp = '\0';
  319.         dotdot--;
  320.         }
  321.         else
  322.         break;
  323.  
  324.     if (*cp) {
  325.         if ((TRM(cwd[(dotdot = Strlen(cwd)) - 1])) != '/')
  326.         cwd[dotdot++] = '/';
  327.         cwd[dotdot] = '\0';
  328.         dp = Strspl(cwd, cp);
  329.         xfree((ptr_t) cwd);
  330.         return dp;
  331.     }
  332.     else {
  333.         if (!*cwd) {
  334.         cwd[0] = '/';
  335. #ifdef apollo
  336.         cwd[1] = '/';
  337.         cwd[2] = '\0';
  338. #else
  339.         cwd[1] = '\0';
  340. #endif
  341.         }
  342. #ifdef apollo
  343.         else if (slashslash && cwd[1] == '\0') {
  344.         cwd[1] = '/';
  345.         cwd[2] = '\0';
  346.         }
  347. #endif
  348.         return cwd;
  349.     }
  350.     }
  351. #endif
  352.     return Strsave(cp);
  353. }
  354.  
  355. /*
  356.  * dochngd - implement chdir command.
  357.  */
  358. /*ARGSUSED*/
  359. void
  360. dochngd(v, c)
  361.     Char  **v;
  362.     struct command *c;
  363. {
  364.     register Char *cp;
  365.     register struct directory *dp;
  366.  
  367.     skipargs(&v, " [<dir>]");
  368.     printd = 0;
  369.     if (*v == NULL) {
  370.     if ((cp = value(STRhome)) == NULL || *cp == 0)
  371.         stderror(ERR_NAME | ERR_NOHOMEDIR);
  372.     if (chdir(short2str(cp)) < 0)
  373.         stderror(ERR_NAME | ERR_CANTCHANGE);
  374.     cp = Strsave(cp);
  375.     }
  376.     else if (v[1] != NULL) {
  377.     stderror(ERR_NAME | ERR_TOOMANY);
  378.     /* NOTREACHED */
  379.     return;
  380.     }
  381.     else if ((dp = dfind(*v)) != 0) {
  382.     char   *tmp;
  383.  
  384.     printd = 1;
  385.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  386.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  387.     dcwd->di_prev->di_next = dcwd->di_next;
  388.     dcwd->di_next->di_prev = dcwd->di_prev;
  389.     dfree(dcwd);
  390.     dnewcwd(dp);
  391.     return;
  392.     }
  393.     else
  394.     if ((cp = dfollow(*v)) == NULL)
  395.         return;
  396.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  397.     dp->di_name = cp;
  398.     dp->di_count = 0;
  399.     dp->di_next = dcwd->di_next;
  400.     dp->di_prev = dcwd->di_prev;
  401.     dp->di_prev->di_next = dp;
  402.     dp->di_next->di_prev = dp;
  403.     dfree(dcwd);
  404.     dnewcwd(dp);
  405. }
  406.  
  407. static Char *
  408. dgoto(cp)
  409.     Char   *cp;
  410. {
  411.     Char   *dp;
  412.  
  413.     if (*cp != '/') {
  414.     register Char *p, *q;
  415.     int     cwdlen;
  416.  
  417.     for (p = dcwd->di_name; *p++;);
  418.     if ((cwdlen = p - dcwd->di_name - 1) == 1)    /* root */
  419.         cwdlen = 0;
  420.     for (p = cp; *p++;);
  421.     dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
  422.     for (p = dp, q = dcwd->di_name; *p++ = *q++;);
  423.     if (cwdlen)
  424.         p[-1] = '/';
  425.     else
  426.         p--;        /* don't add a / after root */
  427.     for (q = cp; *p++ = *q++;);
  428.     xfree((ptr_t) cp);
  429.     cp = dp;
  430.     dp += cwdlen;
  431.     }
  432.     else
  433.     dp = cp;
  434.  
  435.     cp = dcanon(cp, dp);
  436.     return cp;
  437. }
  438.  
  439. /*
  440.  * dfollow - change to arg directory; fall back on cdpath if not valid
  441.  */
  442. static Char *
  443. dfollow(cp)
  444.     register Char *cp;
  445. {
  446.     register Char *dp;
  447.     struct varent *c;
  448.     char    ebuf[MAXPATHLEN];
  449.     int serrno;
  450.  
  451.     cp = globone(cp, G_ERROR);
  452. #ifdef apollo
  453.     if (Strchr(cp, '`')) {
  454.     char *dptr, *ptr;
  455.     if (chdir(dptr = short2str(cp)) < 0) 
  456.         stderror(ERR_SYSTEM, dptr, strerror(errno));
  457.     else if ((ptr = getwd(ebuf)) && *ptr != '\0') {
  458.         xfree((ptr_t) cp);
  459.         cp = Strsave(str2short(ptr));
  460.         return dgoto(cp);
  461.     }
  462.     else 
  463.         stderror(ERR_SYSTEM, dptr, ebuf);
  464.     }
  465. #endif
  466.         
  467.     /*
  468.      * if we are ignoring symlinks, try to fix relatives now.
  469.      */
  470.     dp = dnormalize(cp);
  471.     if (chdir(short2str(dp)) >= 0) {
  472.     xfree((ptr_t) cp);
  473.     return dgoto(dp);
  474.     }
  475.     else {
  476.     xfree((ptr_t) dp);
  477.     if (chdir(short2str(cp)) >= 0) 
  478.         return dgoto(cp);
  479.     serrno = errno;
  480.     }
  481.  
  482.     if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
  483.     && (c = adrof(STRcdpath))) {
  484.     Char  **cdp;
  485.     register Char *p;
  486.     Char    buf[MAXPATHLEN];
  487.  
  488.     for (cdp = c->vec; *cdp; cdp++) {
  489.         for (dp = buf, p = *cdp; *dp++ = *p++;);
  490.         dp[-1] = '/';
  491.         for (p = cp; *dp++ = *p++;);
  492.         if (chdir(short2str(buf)) >= 0) {
  493.         printd = 1;
  494.         xfree((ptr_t) cp);
  495.         cp = Strsave(buf);
  496.         return dgoto(cp);
  497.         }
  498.     }
  499.     }
  500.     dp = value(cp);
  501.     if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
  502.     xfree((ptr_t) cp);
  503.     cp = Strsave(dp);
  504.     printd = 1;
  505.     return dgoto(cp);
  506.     }
  507.     (void) strcpy(ebuf, short2str(cp));
  508.     xfree((ptr_t) cp);
  509. #ifdef CSHDIRS
  510.     /*
  511.      * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
  512.      * directories we could get to.
  513.      */
  514.     if (!bequiet)
  515.     stderror(ERR_SYSTEM, ebuf, strerror(serrno));
  516.     else
  517.     return (NULL);
  518. #else
  519.     stderror(ERR_SYSTEM, ebuf, strerror(serrno));
  520. #endif
  521.     /* NOTREACHED */
  522.     return (NULL);
  523. }
  524.  
  525.  
  526. /*
  527.  * dopushd - push new directory onto directory stack.
  528.  *    with no arguments exchange top and second.
  529.  *    with numeric argument (+n) bring it to top.
  530.  */
  531. /*ARGSUSED*/
  532. void
  533. dopushd(v, c)
  534.     Char  **v;
  535.     struct command *c;
  536. {
  537.     register struct directory *dp;
  538.     register Char *cp;
  539.  
  540.     skipargs(&v, " [<dir>|+<n>]");
  541.     printd = 1;
  542.     if (*v == NULL) {
  543.     if (adrof(STRpushdtohome)) {
  544.         if ((cp = value(STRhome)) == NULL || *cp == 0)
  545.         stderror(ERR_NAME | ERR_NOHOMEDIR);
  546.         if (chdir(short2str(cp)) < 0)
  547.         stderror(ERR_NAME | ERR_CANTCHANGE);
  548.         cp = Strsave(cp);    /* hmmm... PWP */
  549.         if ((cp = dfollow(cp)) == NULL)
  550.         return;
  551.         dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  552.         dp->di_name = cp;
  553.         dp->di_count = 0;
  554.         dp->di_prev = dcwd;
  555.         dp->di_next = dcwd->di_next;
  556.         dcwd->di_next = dp;
  557.         dp->di_next->di_prev = dp;
  558.     }
  559.     else {
  560.         char   *tmp;
  561.  
  562.         if ((dp = dcwd->di_prev) == &dhead)
  563.         dp = dhead.di_prev;
  564.         if (dp == dcwd)
  565.         stderror(ERR_NAME | ERR_NODIR);
  566.         if (chdir(tmp = short2str(dp->di_name)) < 0)
  567.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  568.         dp->di_prev->di_next = dp->di_next;
  569.         dp->di_next->di_prev = dp->di_prev;
  570.         dp->di_next = dcwd->di_next;
  571.         dp->di_prev = dcwd;
  572.         dcwd->di_next->di_prev = dp;
  573.         dcwd->di_next = dp;
  574.     }
  575.     }
  576.     else if (v[1] != NULL) {
  577.     stderror(ERR_NAME | ERR_TOOMANY);
  578.     /* NOTREACHED */
  579.     return;
  580.     }
  581.     else if (dp = dfind(*v)) {
  582.     char   *tmp;
  583.  
  584.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  585.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  586.     /*
  587.      * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
  588.      */
  589.     if (adrof(STRdextract))
  590.         dextract(dp);
  591.     }
  592.     else {
  593.     register Char *ccp;
  594.  
  595.     if ((ccp = dfollow(*v)) == NULL)
  596.         return;
  597.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  598.     dp->di_name = ccp;
  599.     dp->di_count = 0;
  600.     dp->di_prev = dcwd;
  601.     dp->di_next = dcwd->di_next;
  602.     dcwd->di_next = dp;
  603.     dp->di_next->di_prev = dp;
  604.     }
  605.     dnewcwd(dp);
  606. }
  607.  
  608. /*
  609.  * dfind - find a directory if specified by numeric (+n) argument
  610.  */
  611. static struct directory *
  612. dfind(cp)
  613.     register Char *cp;
  614. {
  615.     register struct directory *dp;
  616.     register int i;
  617.     register Char *ep;
  618.  
  619.     if (*cp++ != '+')
  620.     return (0);
  621.     for (ep = cp; Isdigit(*ep); ep++)
  622.     continue;
  623.     if (*ep)
  624.     return (0);
  625.     i = getn(cp);
  626.     if (i <= 0)
  627.     return (0);
  628.     for (dp = dcwd; i != 0; i--) {
  629.     if ((dp = dp->di_prev) == &dhead)
  630.         dp = dp->di_prev;
  631.     if (dp == dcwd)
  632.         stderror(ERR_NAME | ERR_DEEP);
  633.     }
  634.     return (dp);
  635. }
  636.  
  637. /*
  638.  * dopopd - pop a directory out of the directory stack
  639.  *    with a numeric argument just discard it.
  640.  */
  641. /*ARGSUSED*/
  642. void
  643. dopopd(v, c)
  644.     Char  **v;
  645.     struct command *c;
  646. {
  647.     register struct directory *dp, *p = NULL;
  648.  
  649.     skipargs(&v, " [+<n>]");
  650.     printd = 1;
  651.     if (*v == NULL)
  652.     dp = dcwd;
  653.     else if (v[1] != NULL) {
  654.     stderror(ERR_NAME | ERR_TOOMANY);
  655.     /* NOTREACHED */
  656.     return;
  657.     }
  658.     else if ((dp = dfind(*v)) == 0)
  659.     stderror(ERR_NAME | ERR_BADDIR);
  660.     if (dp->di_prev == &dhead && dp->di_next == &dhead)
  661.     stderror(ERR_NAME | ERR_EMPTY);
  662.     if (dp == dcwd) {
  663.     char   *tmp;
  664.  
  665.     if ((p = dp->di_prev) == &dhead)
  666.         p = dhead.di_prev;
  667.     if (chdir(tmp = short2str(p->di_name)) < 0)
  668.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  669.     }
  670.     dp->di_prev->di_next = dp->di_next;
  671.     dp->di_next->di_prev = dp->di_prev;
  672.     if (dp == dcwd)
  673.     dnewcwd(p);
  674.     else {
  675.     printdirs();
  676.     }
  677.     dfree(dp);
  678. }
  679.  
  680. /*
  681.  * dfree - free the directory (or keep it if it still has ref count)
  682.  */
  683. void
  684. dfree(dp)
  685.     register struct directory *dp;
  686. {
  687.  
  688.     if (dp->di_count != 0) {
  689.     dp->di_next = dp->di_prev = 0;
  690.     }
  691.     else {
  692.     xfree((ptr_t) dp->di_name);
  693.     xfree((ptr_t) dp);
  694.     }
  695. }
  696.  
  697. /*
  698.  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
  699.  *    we are of course assuming that the file system is standardly
  700.  *    constructed (always have ..'s, directories have links)
  701.  */
  702. Char   *
  703. dcanon(cp, p)
  704.     register Char *cp, *p;
  705. {
  706.     register Char *sp;
  707.     register Char *p1, *p2;    /* general purpose */
  708.     bool    slash;
  709. #ifdef apollo
  710.     bool    slashslash;
  711. #endif
  712.  
  713. #ifdef S_IFLNK            /* if we have symlinks */
  714.     Char    link[MAXPATHLEN];
  715.     char    tlink[MAXPATHLEN];
  716.     int     cc;
  717.     Char   *newcp;
  718. #endif                /* S_IFLNK */
  719.  
  720.     /*
  721.      * christos: if the path given does not start with a slash prepend cwd. If
  722.      * cwd does not start with a slash or the result would be too long abort().
  723.      */
  724.     if (*cp != '/') {
  725.     Char    tmpdir[MAXPATHLEN];
  726.  
  727.     p1 = value(STRcwd);
  728.     if (p1 == NULL || *p1 != '/')
  729.         abort();
  730.     if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
  731.         abort();
  732.     (void) Strcpy(tmpdir, p1);
  733.     (void) Strcat(tmpdir, STRslash);
  734.     (void) Strcat(tmpdir, cp);
  735.     xfree((ptr_t) cp);
  736.     cp = p = Strsave(tmpdir);
  737.     }
  738.  
  739. #ifdef COMMENT
  740.     if (*cp != '/')
  741.     abort();
  742. #endif
  743.  
  744. #ifdef apollo
  745.     slashslash = (cp[0] == '/' && cp[1] == '/');
  746. #endif
  747.  
  748.     while (*p) {        /* for each component */
  749.     sp = p;            /* save slash address */
  750.     while (*++p == '/')    /* flush extra slashes */
  751.         ;
  752.     if (p != ++sp)
  753.         for (p1 = sp, p2 = p; *p1++ = *p2++;);
  754.     p = sp;            /* save start of component */
  755.     slash = 0;
  756.     while (*++p)        /* find next slash or end of path */
  757.         if (*p == '/') {
  758.         slash = 1;
  759.         *p = 0;
  760.         break;
  761.         }
  762.  
  763. #ifdef apollo
  764.     if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
  765.         slashslash = 1;
  766. #endif
  767.     if (*sp == '\0')    /* if component is null */
  768.         if (--sp == cp)    /* if path is one char (i.e. /) */ 
  769.         break;
  770.         else
  771.         *sp = '\0';
  772.     else if (sp[0] == '.' && sp[1] == 0) {
  773.         if (slash) {
  774.         for (p1 = sp, p2 = p + 1; *p1++ = *p2++;);
  775.         p = --sp;
  776.         }
  777.         else if (--sp != cp)
  778.         *sp = '\0';
  779.     }
  780.     else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
  781.         /*
  782.          * We have something like "yyy/xxx/..", where "yyy" can be null or
  783.          * a path starting at /, and "xxx" is a single component. Before
  784.          * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
  785.          * symbolic link.
  786.          */
  787.         *--sp = 0;        /* form the pathname for readlink */
  788. #ifdef S_IFLNK            /* if we have symlinks */
  789.         if (sp != cp && !adrof(STRignore_symlinks) &&
  790.         (cc = readlink(short2str(cp), tlink,
  791.                    sizeof tlink)) >= 0) {
  792.         (void) Strcpy(link, str2short(tlink));
  793.         link[cc] = '\0';
  794.  
  795.         if (slash)
  796.             *p = '/';
  797.         /*
  798.          * Point p to the '/' in "/..", and restore the '/'.
  799.          */
  800.         *(p = sp) = '/';
  801.         /*
  802.          * find length of p
  803.          */
  804.         for (p1 = p; *p1++;);
  805.         if (*link != '/') {
  806.             /*
  807.              * Relative path, expand it between the "yyy/" and the
  808.              * "/..". First, back sp up to the character past "yyy/".
  809.              */
  810.             while (*--sp != '/');
  811.             sp++;
  812.             *sp = 0;
  813.             /*
  814.              * New length is "yyy/" + link + "/.." and rest
  815.              */
  816.             p1 = newcp = (Char *) xmalloc((size_t)
  817.                         (((sp - cp) + cc + (p1 - p)) *
  818.                          sizeof(Char)));
  819.             /*
  820.              * Copy new path into newcp
  821.              */
  822.             for (p2 = cp; *p1++ = *p2++;);
  823.             for (p1--, p2 = link; *p1++ = *p2++;);
  824.             for (p1--, p2 = p; *p1++ = *p2++;);
  825.             /*
  826.              * Restart canonicalization at expanded "/xxx".
  827.              */
  828.             p = sp - cp - 1 + newcp;
  829.         }
  830.         else {
  831.             /*
  832.              * New length is link + "/.." and rest
  833.              */
  834.             p1 = newcp = (Char *) xmalloc((size_t)
  835.                         ((cc + (p1 - p)) * sizeof(Char)));
  836.             /*
  837.              * Copy new path into newcp
  838.              */
  839.             for (p2 = link; *p1++ = *p2++;);
  840.             for (p1--, p2 = p; *p1++ = *p2++;);
  841.             /*
  842.              * Restart canonicalization at beginning
  843.              */
  844.             p = newcp;
  845.         }
  846.         xfree((ptr_t) cp);
  847.         cp = newcp;
  848.         continue;    /* canonicalize the link */
  849.         }
  850. #endif                /* S_IFLNK */
  851.         *sp = '/';
  852.         if (sp != cp)
  853.         while (*--sp != '/');
  854.         if (slash) {
  855.         for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;);
  856.         p = sp;
  857.         }
  858.         else if (cp == sp)
  859.         *++sp = '\0';
  860.         else
  861.         *sp = '\0';
  862.     }
  863.     else {            /* normal dir name (not . or .. or nothing) */
  864.  
  865. #ifdef S_IFLNK            /* if we have symlinks */
  866.         if (sp != cp && adrof(STRchase_symlinks) &&
  867.         !adrof(STRignore_symlinks) &&
  868.         (cc = readlink(short2str(cp), tlink,
  869.                    sizeof tlink)) >= 0) {
  870.         (void) Strcpy(link, str2short(tlink));
  871.         link[cc] = '\0';
  872.  
  873.         /*
  874.          * restore the '/'.
  875.          */
  876.         if (slash)
  877.             *p = '/';
  878.  
  879.         /*
  880.          * point sp to p (rather than backing up).
  881.          */
  882.         sp = p;
  883.  
  884.         /*
  885.          * find length of p
  886.          */
  887.         for (p1 = p; *p1++;);
  888.         if (*link != '/') {
  889.             /*
  890.              * Relative path, expand it between the "yyy/" and the
  891.              * remainder. First, back sp up to the character past
  892.              * "yyy/".
  893.              */
  894.             while (*--sp != '/');
  895.             sp++;
  896.             *sp = 0;
  897.             /*
  898.              * New length is "yyy/" + link + "/.." and rest
  899.              */
  900.             p1 = newcp = (Char *) xmalloc((size_t)
  901.                           (((sp - cp) + cc + (p1 - p))
  902.                            * sizeof(Char)));
  903.             /*
  904.              * Copy new path into newcp
  905.              */
  906.             for (p2 = cp; *p1++ = *p2++;);
  907.             for (p1--, p2 = link; *p1++ = *p2++;);
  908.             for (p1--, p2 = p; *p1++ = *p2++;);
  909.             /*
  910.              * Restart canonicalization at expanded "/xxx".
  911.              */
  912.             p = sp - cp - 1 + newcp;
  913.         }
  914.         else {
  915.             /*
  916.              * New length is link + the rest
  917.              */
  918.             p1 = newcp = (Char *) xmalloc((size_t)
  919.                         ((cc + (p1 - p)) * sizeof(Char)));
  920.             /*
  921.              * Copy new path into newcp
  922.              */
  923.             for (p2 = link; *p1++ = *p2++;);
  924.             for (p1--, p2 = p; *p1++ = *p2++;);
  925.             /*
  926.              * Restart canonicalization at beginning
  927.              */
  928.             p = newcp;
  929.         }
  930.         xfree((ptr_t) cp);
  931.         cp = newcp;
  932.         continue;    /* canonicalize the link */
  933.         }
  934. #endif                /* S_IFLNK */
  935.         if (slash)
  936.         *p = '/';
  937.     }
  938.     }
  939.  
  940.     /*
  941.      * fix home...
  942.      */
  943. #ifdef S_IFLNK
  944.     p1 = value(STRhome);
  945.     cc = Strlen(p1);
  946.     /*
  947.      * See if we're not in a subdir of STRhome
  948.      */
  949.     if (p1 && *p1 == '/' &&
  950.     (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
  951.     static ino_t home_ino = -1;
  952.     static dev_t home_dev = -1;
  953.     static Char *home_ptr = NULL;
  954.     struct stat statbuf;
  955.  
  956.     /*
  957.      * Get dev and ino of STRhome
  958.      */
  959.     if (home_ptr != p1 &&
  960.         stat(short2str(p1), &statbuf) != -1) {
  961.         home_dev = statbuf.st_dev;
  962.         home_ino = statbuf.st_ino;
  963.         home_ptr = p1;
  964.     }
  965.     /*
  966.      * Start comparing dev & ino backwards
  967.      */
  968.     p2 = Strcpy(link, cp);
  969.     for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
  970.         if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
  971.             statbuf.st_ino == home_ino) {
  972.             sp = (Char *) - 1;
  973.             break;
  974.         }
  975.         if (sp = Strrchr(p2, '/'))
  976.         *sp = '\0';
  977.     }
  978.     /*
  979.      * See if we found it
  980.      */
  981.     if (*p2 && sp == (Char *) -1) {
  982.         /*
  983.          * Use STRhome to make '~' work
  984.          */
  985.         newcp = Strspl(p1, cp + Strlen(p2));
  986.         xfree((ptr_t) cp);
  987.         cp = newcp;
  988.     }
  989.     }
  990. #endif                /* S_IFLNK */
  991.  
  992. #ifdef apollo
  993.     if (slashslash) {
  994.     if (cp[1] != '/') {
  995.         p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
  996.         *p = '/';
  997.         (void) Strcpy(&p[1], cp);
  998.         xfree((ptr_t) cp);
  999.         cp = p;
  1000.     }
  1001.     }
  1002.     if (cp[1] == '/' && cp[2] == '/') 
  1003.     (void) Strcpy(&cp[1], &cp[2]);
  1004. #endif
  1005.     return cp;
  1006. }
  1007.  
  1008.  
  1009. /*
  1010.  * dnewcwd - make a new directory in the loop the current one
  1011.  */
  1012. static void
  1013. dnewcwd(dp)
  1014.     register struct directory *dp;
  1015. {
  1016.     dcwd = dp;
  1017.     dset(dcwd->di_name);
  1018.     if (printd && !(adrof(STRpushdsilent))    /* PWP: pushdsilent */
  1019. #ifdef CSHDIRS
  1020.     && !bequiet        /* be quite while restoring stack -strike */
  1021. #endif
  1022.     )
  1023.     printdirs();
  1024.     cwd_cmd();            /* PWP: run the defined cwd command */
  1025. }
  1026.  
  1027. /*
  1028.  * getstakd - added by kfk 17 Jan 1984
  1029.  * Support routine for the stack hack.  Finds nth directory in
  1030.  * the directory stack, or finds last directory in stack.
  1031.  */
  1032. int
  1033. getstakd(s, cnt)
  1034.     Char   *s;
  1035.     int     cnt;
  1036. {
  1037.     struct directory *dp;
  1038.  
  1039.     dp = dcwd;
  1040.     if (cnt < 0) {        /* < 0 ==> last dir requested. */
  1041.     dp = dp->di_next;
  1042.     if (dp == &dhead)
  1043.         dp = dp->di_next;
  1044.     }
  1045.     else {
  1046.     while (cnt-- > 0) {
  1047.         dp = dp->di_prev;
  1048.         if (dp == &dhead)
  1049.         dp = dp->di_prev;
  1050.         if (dp == dcwd)
  1051.         return (0);
  1052.     }
  1053.     }
  1054.     (void) Strcpy(s, dp->di_name);
  1055.     return (1);
  1056. }
  1057.  
  1058. /*
  1059.  * Karl Kleinpaste - 10 Feb 1984
  1060.  * Added dextract(), which is used in pushd +n.
  1061.  * Instead of just rotating the entire stack around, dextract()
  1062.  * lets the user have the nth dir extracted from its current
  1063.  * position, and pushes it onto the top.
  1064.  */
  1065. void
  1066. dextract(dp)
  1067.     struct directory *dp;
  1068. {
  1069.     if (dp == dcwd)
  1070.     return;
  1071.     dp->di_next->di_prev = dp->di_prev;
  1072.     dp->di_prev->di_next = dp->di_next;
  1073.     dp->di_next = dcwd->di_next;
  1074.     dp->di_prev = dcwd;
  1075.     dp->di_next->di_prev = dp;
  1076.     dcwd->di_next = dp;
  1077. }
  1078.  
  1079. #ifdef CSHDIRS
  1080. /*
  1081.  * create a file called ~/.cshdirs which has a sequence
  1082.  * of pushd commands which will restore the dir stack to
  1083.  * its state before exit/logout. remember that the order
  1084.  * is reversed in the file because we are pushing.
  1085.  * -strike
  1086.  */
  1087. void
  1088. recdirs()
  1089. {
  1090.     int     fp, ftmp, oldidfds;
  1091.     int     cdflag = 0;
  1092.     extern int fast;
  1093.     Char    buf[BUFSIZE];
  1094.  
  1095.     if (!fast) {
  1096.     if (!adrof(STRsavedirs))/* does it exist */
  1097.         return;
  1098.     (void) Strcpy(buf, value(STRhome));
  1099.     (void) Strcat(buf, STRsldtdirs);
  1100.     if ((fp = creat(short2str(buf), 0666)) == -1)
  1101.         return;
  1102.     oldidfds = didfds;
  1103.     didfds = 0;
  1104.     ftmp = SHOUT;
  1105.     SHOUT = fp;
  1106.     {
  1107.         extern struct directory dhead;
  1108.         extern struct directory *dcwd;
  1109.         struct directory *dp = dcwd->di_next;
  1110.  
  1111.         do {
  1112.         if (dp == &dhead)
  1113.             continue;
  1114.         if (cdflag == 0)
  1115.             cdflag++, xprintf("cd %s\n",
  1116.                       short2str(dp->di_name));
  1117.         else
  1118.             xprintf("pushd %s\n",
  1119.                 short2str(dp->di_name));
  1120.         } while ((dp = dp->di_next) != dcwd->di_next);
  1121.     }
  1122.     xprintf("dirs\n");    /* show the dir stack */
  1123.  
  1124.     (void) close(fp);
  1125.     SHOUT = ftmp;
  1126.     didfds = oldidfds;
  1127.     }
  1128. }
  1129.  
  1130. #endif
  1131.