home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / csh / dir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-19  |  20.2 KB  |  930 lines

  1. /*-
  2.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. 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.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)dir.c    5.17 (Berkeley) 11/6/91";
  36. #endif /* not lint */
  37.  
  38. #include <sys/param.h>
  39. #include <sys/stat.h>
  40. #include <errno.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <unistd.h>
  44. #if __STDC__
  45. # include <stdarg.h>
  46. #else
  47. # include <varargs.h>
  48. #endif
  49.  
  50. #include "csh.h"
  51. #include "dir.h"
  52. #include "extern.h"
  53.  
  54. /* Directory management. */
  55.  
  56. static struct directory
  57.         *dfind __P((Char *));
  58. static Char    *dfollow __P((Char *));
  59. static void     printdirs __P((void));
  60. static Char    *dgoto __P((Char *));
  61. static void     dnewcwd __P((struct directory *));
  62. static void     dset __P((Char *));
  63.  
  64. struct directory dhead;        /* "head" of loop */
  65. int     printd;            /* force name to be printed */
  66.  
  67. static int dirflag = 0;
  68.  
  69. /*
  70.  * dinit - initialize current working directory
  71.  */
  72. void
  73. dinit(hp)
  74.     Char   *hp;
  75. {
  76.     register char *tcp;
  77.     register Char *cp;
  78.     register struct directory *dp;
  79.     char    path[MAXPATHLEN];
  80.     static char *emsg = "csh: Trying to start from \"%s\"\n";
  81.  
  82.     /* Don't believe the login shell home, because it may be a symlink */
  83.     tcp = getwd(path);        /* see ngetwd.c for System V version */
  84.     if (tcp == NULL || *tcp == '\0') {
  85.     (void) fprintf(csherr, "csh: %s\n", path);
  86.     if (hp && *hp) {
  87.         tcp = short2str(hp);
  88.         if (chdir(tcp) == -1)
  89.         cp = NULL;
  90.         else
  91.         cp = hp;
  92.         (void) fprintf(csherr, emsg, vis_str(hp));
  93.     }
  94.     else
  95.         cp = NULL;
  96.     if (cp == NULL) {
  97.         (void) fprintf(csherr, emsg, "/");
  98.         if (chdir("/") == -1)
  99.         /* I am not even try to print an error message! */
  100.         xexit(1);
  101.         cp = SAVE("/");
  102.     }
  103.     }
  104.     else {
  105.     struct stat swd, shp;
  106.  
  107.     /*
  108.      * See if $HOME is the working directory we got and use that
  109.      */
  110.     if (hp && *hp &&
  111.         stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
  112.         swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino)
  113.         cp = hp;
  114.     else {
  115.         char   *cwd;
  116.  
  117.         /*
  118.          * use PWD if we have it (for subshells)
  119.          */
  120.         if (cwd = getenv("PWD")) {
  121.         if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev &&
  122.             swd.st_ino == shp.st_ino)
  123.             tcp = cwd;
  124.         }
  125.         cp = dcanon(SAVE(tcp), STRNULL);
  126.     }
  127.     }
  128.  
  129.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  130.     dp->di_name = Strsave(cp);
  131.     dp->di_count = 0;
  132.     dhead.di_next = dhead.di_prev = dp;
  133.     dp->di_next = dp->di_prev = &dhead;
  134.     printd = 0;
  135.     dnewcwd(dp);
  136. }
  137.  
  138. static void
  139. dset(dp)
  140. Char *dp;
  141. {
  142.     /*
  143.      * Don't call set() directly cause if the directory contains ` or
  144.      * other junk characters glob will fail.
  145.      */
  146.     register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
  147.  
  148.     vec[0] = Strsave(dp);
  149.     vec[1] = 0;
  150.     setq(STRcwd, vec, &shvhed);
  151.     Setenv(STRPWD, dp);
  152. }
  153.  
  154. #define DIR_LONG 1
  155. #define DIR_VERT 2
  156. #define DIR_LINE 4
  157.  
  158. static void
  159. skipargs(v, str)
  160.     Char ***v;
  161.     char   *str;
  162. {
  163.     Char  **n = *v, *s;
  164.  
  165.     dirflag = 0;
  166.     for (n++; *n != NULL && (*n)[0] == '-'; n++)
  167.     for (s = &((*n)[1]); *s; s++)
  168.         switch (*s) {
  169.         case 'l':
  170.         dirflag |= DIR_LONG;
  171.         break;
  172.         case 'v':
  173.         dirflag |= DIR_VERT;
  174.         break;
  175.         case 'n':
  176.         dirflag |= DIR_LINE;
  177.         break;
  178.         default:
  179.         stderror(ERR_DIRUS, vis_str(**v), str);
  180.         break;
  181.         }
  182.     *v = n;
  183. }
  184.  
  185. /*
  186.  * dodirs - list all directories in directory loop
  187.  */
  188. void
  189. /*ARGSUSED*/
  190. dodirs(v, t)
  191.     Char **v;
  192.     struct command *t;
  193. {
  194.     skipargs(&v, "");
  195.  
  196.     if (*v != NULL)
  197.     stderror(ERR_DIRUS, "dirs", "");
  198.     printdirs();
  199. }
  200.  
  201. static void
  202. printdirs()
  203. {
  204.     register struct directory *dp;
  205.     Char   *s, *hp = value(STRhome);
  206.     int     idx, len, cur;
  207.  
  208.     if (*hp == '\0')
  209.     hp = NULL;
  210.     dp = dcwd;
  211.     idx = 0;
  212.     cur = 0;
  213.     do {
  214.     if (dp == &dhead)
  215.         continue;
  216.     if (dirflag & DIR_VERT) {
  217.         (void) fprintf(cshout, "%d\t", idx++);
  218.         cur = 0;
  219.     }
  220.     if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
  221.         prefix(hp, dp->di_name))
  222.         len = Strlen(s = (dp->di_name + Strlen(hp))) + 2;
  223.     else
  224.         len = Strlen(s = dp->di_name) + 1;
  225.  
  226.     cur += len;
  227.     if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) {
  228.         (void) fprintf(cshout, "\n");
  229.         cur = len;
  230.     }
  231.     (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c",
  232.         vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
  233.     } while ((dp = dp->di_prev) != dcwd);
  234.     if (!(dirflag & DIR_VERT))
  235.     (void) fprintf(cshout, "\n");
  236. }
  237.  
  238. void
  239. dtildepr(home, dir)
  240.     register Char *home, *dir;
  241. {
  242.  
  243.     if (!eq(home, STRslash) && prefix(home, dir))
  244.     (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home)));
  245.     else
  246.     (void) fprintf(cshout, "%s", vis_str(dir));
  247. }
  248.  
  249. void
  250. dtilde()
  251. {
  252.     struct directory *d = dcwd;
  253.  
  254.     do {
  255.     if (d == &dhead)
  256.         continue;
  257.     d->di_name = dcanon(d->di_name, STRNULL);
  258.     } while ((d = d->di_prev) != dcwd);
  259.  
  260.     dset(dcwd->di_name);
  261. }
  262.  
  263.  
  264. /* dnormalize():
  265.  *    If the name starts with . or .. then we might need to normalize
  266.  *    it depending on the symbolic link flags
  267.  */
  268. Char   *
  269. dnormalize(cp)
  270.     Char   *cp;
  271. {
  272.  
  273. #define UC (unsigned char)
  274. #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
  275. #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
  276.  
  277.     if ((unsigned char) cp[0] == '/')
  278.     return (Strsave(cp));
  279.  
  280.     if (adrof(STRignore_symlinks)) {
  281.     int     dotdot = 0;
  282.     Char   *dp, *cwd;
  283.  
  284.     cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) *
  285.                      sizeof(Char)));
  286.     (void) Strcpy(cwd, dcwd->di_name);
  287.  
  288.     /*
  289.      * Ignore . and count ..'s
  290.      */
  291.     while (*cp) {
  292.         if (ISDOT(cp)) {
  293.         if (*++cp)
  294.             cp++;
  295.         }
  296.         else if (ISDOTDOT(cp)) {
  297.         dotdot++;
  298.         cp += 2;
  299.         if (*cp)
  300.             cp++;
  301.         }
  302.         else
  303.         break;
  304.     }
  305.     while (dotdot > 0)
  306.         if ((dp = Strrchr(cwd, '/'))) {
  307.         *dp = '\0';
  308.         dotdot--;
  309.         }
  310.         else
  311.         break;
  312.  
  313.     if (*cp) {
  314.         cwd[dotdot = Strlen(cwd)] = '/';
  315.         cwd[dotdot + 1] = '\0';
  316.         dp = Strspl(cwd, cp);
  317.         xfree((ptr_t) cwd);
  318.         return dp;
  319.     }
  320.     else {
  321.         if (!*cwd) {
  322.         cwd[0] = '/';
  323.         cwd[1] = '\0';
  324.         }
  325.         return cwd;
  326.     }
  327.     }
  328.     return Strsave(cp);
  329. }
  330.  
  331. /*
  332.  * dochngd - implement chdir command.
  333.  */
  334. void
  335. /*ARGSUSED*/
  336. dochngd(v, t)
  337.     Char **v;
  338.     struct command *t;
  339. {
  340.     register Char *cp;
  341.     register struct directory *dp;
  342.  
  343.     skipargs(&v, " [<dir>]");
  344.     printd = 0;
  345.     if (*v == NULL) {
  346.     if ((cp = value(STRhome)) == NULL || *cp == 0)
  347.         stderror(ERR_NAME | ERR_NOHOMEDIR);
  348.     if (chdir(short2str(cp)) < 0)
  349.         stderror(ERR_NAME | ERR_CANTCHANGE);
  350.     cp = Strsave(cp);
  351.     }
  352.     else if (v[1] != NULL) {
  353.     stderror(ERR_NAME | ERR_TOOMANY);
  354.     /* NOTREACHED */
  355.     return;
  356.     }
  357.     else if ((dp = dfind(*v)) != 0) {
  358.     char   *tmp;
  359.  
  360.     printd = 1;
  361.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  362.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  363.     dcwd->di_prev->di_next = dcwd->di_next;
  364.     dcwd->di_next->di_prev = dcwd->di_prev;
  365.     dfree(dcwd);
  366.     dnewcwd(dp);
  367.     return;
  368.     }
  369.     else
  370.     cp = dfollow(*v);
  371.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  372.     dp->di_name = cp;
  373.     dp->di_count = 0;
  374.     dp->di_next = dcwd->di_next;
  375.     dp->di_prev = dcwd->di_prev;
  376.     dp->di_prev->di_next = dp;
  377.     dp->di_next->di_prev = dp;
  378.     dfree(dcwd);
  379.     dnewcwd(dp);
  380. }
  381.  
  382. static Char *
  383. dgoto(cp)
  384.     Char   *cp;
  385. {
  386.     Char   *dp;
  387.  
  388.     if (*cp != '/') {
  389.     register Char *p, *q;
  390.     int     cwdlen;
  391.  
  392.     for (p = dcwd->di_name; *p++;)
  393.         continue;
  394.     if ((cwdlen = p - dcwd->di_name - 1) == 1)    /* root */
  395.         cwdlen = 0;
  396.     for (p = cp; *p++;)
  397.         continue;
  398.     dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
  399.     for (p = dp, q = dcwd->di_name; *p++ = *q++;)
  400.         continue;
  401.     if (cwdlen)
  402.         p[-1] = '/';
  403.     else
  404.         p--;        /* don't add a / after root */
  405.     for (q = cp; *p++ = *q++;)
  406.         continue;
  407.     xfree((ptr_t) cp);
  408.     cp = dp;
  409.     dp += cwdlen;
  410.     }
  411.     else
  412.     dp = cp;
  413.  
  414.     cp = dcanon(cp, dp);
  415.     return cp;
  416. }
  417.  
  418. /*
  419.  * dfollow - change to arg directory; fall back on cdpath if not valid
  420.  */
  421. static Char *
  422. dfollow(cp)
  423.     register Char *cp;
  424. {
  425.     register Char *dp;
  426.     struct varent *c;
  427.     char    ebuf[MAXPATHLEN];
  428.     int serrno;
  429.  
  430.     cp = globone(cp, G_ERROR);
  431.     /*
  432.      * if we are ignoring symlinks, try to fix relatives now.
  433.      */
  434.     dp = dnormalize(cp);
  435.     if (chdir(short2str(dp)) >= 0) {
  436.     xfree((ptr_t) cp);
  437.     return dgoto(dp);
  438.     }
  439.     else {
  440.     xfree((ptr_t) dp);
  441.     if (chdir(short2str(cp)) >= 0)
  442.         return dgoto(cp);
  443.     serrno = errno;
  444.     }
  445.  
  446.     if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
  447.     && (c = adrof(STRcdpath))) {
  448.     Char  **cdp;
  449.     register Char *p;
  450.     Char    buf[MAXPATHLEN];
  451.  
  452.     for (cdp = c->vec; *cdp; cdp++) {
  453.         for (dp = buf, p = *cdp; *dp++ = *p++;)
  454.         continue;
  455.         dp[-1] = '/';
  456.         for (p = cp; *dp++ = *p++;)
  457.         continue;
  458.         if (chdir(short2str(buf)) >= 0) {
  459.         printd = 1;
  460.         xfree((ptr_t) cp);
  461.         cp = Strsave(buf);
  462.         return dgoto(cp);
  463.         }
  464.     }
  465.     }
  466.     dp = value(cp);
  467.     if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
  468.     xfree((ptr_t) cp);
  469.     cp = Strsave(dp);
  470.     printd = 1;
  471.     return dgoto(cp);
  472.     }
  473.     (void) strcpy(ebuf, short2str(cp));
  474.     xfree((ptr_t) cp);
  475.     stderror(ERR_SYSTEM, ebuf, strerror(serrno));
  476.     return (NULL);
  477. }
  478.  
  479.  
  480. /*
  481.  * dopushd - push new directory onto directory stack.
  482.  *    with no arguments exchange top and second.
  483.  *    with numeric argument (+n) bring it to top.
  484.  */
  485. void
  486. /*ARGSUSED*/
  487. dopushd(v, t)
  488.     Char **v;
  489.     struct command *t;
  490. {
  491.     register struct directory *dp;
  492.  
  493.     skipargs(&v, " [<dir>|+<n>]");
  494.     printd = 1;
  495.     if (*v == NULL) {
  496.     char   *tmp;
  497.  
  498.     if ((dp = dcwd->di_prev) == &dhead)
  499.         dp = dhead.di_prev;
  500.     if (dp == dcwd)
  501.         stderror(ERR_NAME | ERR_NODIR);
  502.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  503.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  504.     dp->di_prev->di_next = dp->di_next;
  505.     dp->di_next->di_prev = dp->di_prev;
  506.     dp->di_next = dcwd->di_next;
  507.     dp->di_prev = dcwd;
  508.     dcwd->di_next->di_prev = dp;
  509.     dcwd->di_next = dp;
  510.     }
  511.     else if (v[1] != NULL) {
  512.     stderror(ERR_NAME | ERR_TOOMANY);
  513.     /* NOTREACHED */
  514.     return;
  515.     }
  516.     else if (dp = dfind(*v)) {
  517.     char   *tmp;
  518.  
  519.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  520.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  521.     }
  522.     else {
  523.     register Char *ccp;
  524.  
  525.     ccp = dfollow(*v);
  526.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  527.     dp->di_name = ccp;
  528.     dp->di_count = 0;
  529.     dp->di_prev = dcwd;
  530.     dp->di_next = dcwd->di_next;
  531.     dcwd->di_next = dp;
  532.     dp->di_next->di_prev = dp;
  533.     }
  534.     dnewcwd(dp);
  535. }
  536.  
  537. /*
  538.  * dfind - find a directory if specified by numeric (+n) argument
  539.  */
  540. static struct directory *
  541. dfind(cp)
  542.     register Char *cp;
  543. {
  544.     register struct directory *dp;
  545.     register int i;
  546.     register Char *ep;
  547.  
  548.     if (*cp++ != '+')
  549.     return (0);
  550.     for (ep = cp; Isdigit(*ep); ep++)
  551.     continue;
  552.     if (*ep)
  553.     return (0);
  554.     i = getn(cp);
  555.     if (i <= 0)
  556.     return (0);
  557.     for (dp = dcwd; i != 0; i--) {
  558.     if ((dp = dp->di_prev) == &dhead)
  559.         dp = dp->di_prev;
  560.     if (dp == dcwd)
  561.         stderror(ERR_NAME | ERR_DEEP);
  562.     }
  563.     return (dp);
  564. }
  565.  
  566. /*
  567.  * dopopd - pop a directory out of the directory stack
  568.  *    with a numeric argument just discard it.
  569.  */
  570. void
  571. /*ARGSUSED*/
  572. dopopd(v, t)
  573.     Char **v;
  574.     struct command *t;
  575. {
  576.     register struct directory *dp, *p = NULL;
  577.  
  578.     skipargs(&v, " [+<n>]");
  579.     printd = 1;
  580.     if (*v == NULL)
  581.     dp = dcwd;
  582.     else if (v[1] != NULL) {
  583.     stderror(ERR_NAME | ERR_TOOMANY);
  584.     /* NOTREACHED */
  585.     return;
  586.     }
  587.     else if ((dp = dfind(*v)) == 0)
  588.     stderror(ERR_NAME | ERR_BADDIR);
  589.     if (dp->di_prev == &dhead && dp->di_next == &dhead)
  590.     stderror(ERR_NAME | ERR_EMPTY);
  591.     if (dp == dcwd) {
  592.     char   *tmp;
  593.  
  594.     if ((p = dp->di_prev) == &dhead)
  595.         p = dhead.di_prev;
  596.     if (chdir(tmp = short2str(p->di_name)) < 0)
  597.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  598.     }
  599.     dp->di_prev->di_next = dp->di_next;
  600.     dp->di_next->di_prev = dp->di_prev;
  601.     if (dp == dcwd)
  602.     dnewcwd(p);
  603.     else {
  604.     printdirs();
  605.     }
  606.     dfree(dp);
  607. }
  608.  
  609. /*
  610.  * dfree - free the directory (or keep it if it still has ref count)
  611.  */
  612. void
  613. dfree(dp)
  614.     register struct directory *dp;
  615. {
  616.  
  617.     if (dp->di_count != 0) {
  618.     dp->di_next = dp->di_prev = 0;
  619.     }
  620.     else {
  621.     xfree((char *) dp->di_name);
  622.     xfree((ptr_t) dp);
  623.     }
  624. }
  625.  
  626. /*
  627.  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
  628.  *    we are of course assuming that the file system is standardly
  629.  *    constructed (always have ..'s, directories have links)
  630.  */
  631. Char   *
  632. dcanon(cp, p)
  633.     register Char *cp, *p;
  634. {
  635.     register Char *sp;
  636.     register Char *p1, *p2;    /* general purpose */
  637.     bool    slash;
  638.  
  639.     Char    link[MAXPATHLEN];
  640.     char    tlink[MAXPATHLEN];
  641.     int     cc;
  642.     Char   *newcp;
  643.  
  644.     /*
  645.      * christos: if the path given does not start with a slash prepend cwd. If
  646.      * cwd does not start with a path or the result would be too long abort().
  647.      */
  648.     if (*cp != '/') {
  649.     Char    tmpdir[MAXPATHLEN];
  650.  
  651.     p1 = value(STRcwd);
  652.     if (p1 == NULL || *p1 != '/')
  653.         abort();
  654.     if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
  655.         abort();
  656.     (void) Strcpy(tmpdir, p1);
  657.     (void) Strcat(tmpdir, STRslash);
  658.     (void) Strcat(tmpdir, cp);
  659.     xfree((ptr_t) cp);
  660.     cp = p = Strsave(tmpdir);
  661.     }
  662.  
  663.     while (*p) {        /* for each component */
  664.     sp = p;            /* save slash address */
  665.     while (*++p == '/')    /* flush extra slashes */
  666.         continue;
  667.     if (p != ++sp)
  668.         for (p1 = sp, p2 = p; *p1++ = *p2++;)
  669.         continue;
  670.     p = sp;            /* save start of component */
  671.     slash = 0;
  672.     while (*++p)        /* find next slash or end of path */
  673.         if (*p == '/') {
  674.         slash = 1;
  675.         *p = 0;
  676.         break;
  677.         }
  678.  
  679.     if (*sp == '\0')    /* if component is null */
  680.         if (--sp == cp)    /* if path is one char (i.e. /) */
  681.         break;
  682.         else
  683.         *sp = '\0';
  684.     else if (sp[0] == '.' && sp[1] == 0) {
  685.         if (slash) {
  686.         for (p1 = sp, p2 = p + 1; *p1++ = *p2++;)
  687.             continue;
  688.         p = --sp;
  689.         }
  690.         else if (--sp != cp)
  691.         *sp = '\0';
  692.     }
  693.     else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
  694.         /*
  695.          * We have something like "yyy/xxx/..", where "yyy" can be null or
  696.          * a path starting at /, and "xxx" is a single component. Before
  697.          * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
  698.          * symbolic link.
  699.          */
  700.         *--sp = 0;        /* form the pathname for readlink */
  701.         if (sp != cp && !adrof(STRignore_symlinks) &&
  702.         (cc = readlink(short2str(cp), tlink,
  703.                    sizeof tlink)) >= 0) {
  704.         (void) Strcpy(link, str2short(tlink));
  705.         link[cc] = '\0';
  706.  
  707.         if (slash)
  708.             *p = '/';
  709.         /*
  710.          * Point p to the '/' in "/..", and restore the '/'.
  711.          */
  712.         *(p = sp) = '/';
  713.         /*
  714.          * find length of p
  715.          */
  716.         for (p1 = p; *p1++;)
  717.             continue;
  718.         if (*link != '/') {
  719.             /*
  720.              * Relative path, expand it between the "yyy/" and the
  721.              * "/..". First, back sp up to the character past "yyy/".
  722.              */
  723.             while (*--sp != '/')
  724.             continue;
  725.             sp++;
  726.             *sp = 0;
  727.             /*
  728.              * New length is "yyy/" + link + "/.." and rest
  729.              */
  730.             p1 = newcp = (Char *) xmalloc((size_t)
  731.                         (((sp - cp) + cc + (p1 - p)) *
  732.                          sizeof(Char)));
  733.             /*
  734.              * Copy new path into newcp
  735.              */
  736.             for (p2 = cp; *p1++ = *p2++;)
  737.             continue;
  738.             for (p1--, p2 = link; *p1++ = *p2++;)
  739.             continue;
  740.             for (p1--, p2 = p; *p1++ = *p2++;)
  741.             continue;
  742.             /*
  743.              * Restart canonicalization at expanded "/xxx".
  744.              */
  745.             p = sp - cp - 1 + newcp;
  746.         }
  747.         else {
  748.             /*
  749.              * New length is link + "/.." and rest
  750.              */
  751.             p1 = newcp = (Char *) xmalloc((size_t)
  752.                         ((cc + (p1 - p)) * sizeof(Char)));
  753.             /*
  754.              * Copy new path into newcp
  755.              */
  756.             for (p2 = link; *p1++ = *p2++;)
  757.             continue;
  758.             for (p1--, p2 = p; *p1++ = *p2++;)
  759.             continue;
  760.             /*
  761.              * Restart canonicalization at beginning
  762.              */
  763.             p = newcp;
  764.         }
  765.         xfree((ptr_t) cp);
  766.         cp = newcp;
  767.         continue;    /* canonicalize the link */
  768.         }
  769.         *sp = '/';
  770.         if (sp != cp)
  771.         while (*--sp != '/')
  772.             continue;
  773.         if (slash) {
  774.         for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;)
  775.             continue;
  776.         p = sp;
  777.         }
  778.         else if (cp == sp)
  779.         *++sp = '\0';
  780.         else
  781.         *sp = '\0';
  782.     }
  783.     else {            /* normal dir name (not . or .. or nothing) */
  784.  
  785.         if (sp != cp && adrof(STRchase_symlinks) &&
  786.         !adrof(STRignore_symlinks) &&
  787.         (cc = readlink(short2str(cp), tlink,
  788.                    sizeof tlink)) >= 0) {
  789.         (void) Strcpy(link, str2short(tlink));
  790.         link[cc] = '\0';
  791.  
  792.         /*
  793.          * restore the '/'.
  794.          */
  795.         if (slash)
  796.             *p = '/';
  797.  
  798.         /*
  799.          * point sp to p (rather than backing up).
  800.          */
  801.         sp = p;
  802.  
  803.         /*
  804.          * find length of p
  805.          */
  806.         for (p1 = p; *p1++;)
  807.             continue;
  808.         if (*link != '/') {
  809.             /*
  810.              * Relative path, expand it between the "yyy/" and the
  811.              * remainder. First, back sp up to the character past
  812.              * "yyy/".
  813.              */
  814.             while (*--sp != '/')
  815.             continue;
  816.             sp++;
  817.             *sp = 0;
  818.             /*
  819.              * New length is "yyy/" + link + "/.." and rest
  820.              */
  821.             p1 = newcp = (Char *) xmalloc((size_t)
  822.                           (((sp - cp) + cc + (p1 - p))
  823.                            * sizeof(Char)));
  824.             /*
  825.              * Copy new path into newcp
  826.              */
  827.             for (p2 = cp; *p1++ = *p2++;)
  828.             continue;
  829.             for (p1--, p2 = link; *p1++ = *p2++;)
  830.             continue;
  831.             for (p1--, p2 = p; *p1++ = *p2++;)
  832.             continue;
  833.             /*
  834.              * Restart canonicalization at expanded "/xxx".
  835.              */
  836.             p = sp - cp - 1 + newcp;
  837.         }
  838.         else {
  839.             /*
  840.              * New length is link + the rest
  841.              */
  842.             p1 = newcp = (Char *) xmalloc((size_t)
  843.                         ((cc + (p1 - p)) * sizeof(Char)));
  844.             /*
  845.              * Copy new path into newcp
  846.              */
  847.             for (p2 = link; *p1++ = *p2++;)
  848.             continue;
  849.             for (p1--, p2 = p; *p1++ = *p2++;)
  850.             continue;
  851.             /*
  852.              * Restart canonicalization at beginning
  853.              */
  854.             p = newcp;
  855.         }
  856.         xfree((ptr_t) cp);
  857.         cp = newcp;
  858.         continue;    /* canonicalize the link */
  859.         }
  860.         if (slash)
  861.         *p = '/';
  862.     }
  863.     }
  864.  
  865.     /*
  866.      * fix home...
  867.      */
  868.     p1 = value(STRhome);
  869.     cc = Strlen(p1);
  870.     /*
  871.      * See if we're not in a subdir of STRhome
  872.      */
  873.     if (p1 && *p1 == '/' &&
  874.     (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
  875.     static ino_t home_ino = -1;
  876.     static dev_t home_dev = -1;
  877.     static Char *home_ptr = NULL;
  878.     struct stat statbuf;
  879.  
  880.     /*
  881.      * Get dev and ino of STRhome
  882.      */
  883.     if (home_ptr != p1 &&
  884.         stat(short2str(p1), &statbuf) != -1) {
  885.         home_dev = statbuf.st_dev;
  886.         home_ino = statbuf.st_ino;
  887.         home_ptr = p1;
  888.     }
  889.     /*
  890.      * Start comparing dev & ino backwards
  891.      */
  892.     p2 = Strcpy(link, cp);
  893.     for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
  894.         if (statbuf.st_dev == home_dev &&
  895.         statbuf.st_ino == home_ino) {
  896.         sp = (Char *) - 1;
  897.         break;
  898.         }
  899.         if (sp = Strrchr(p2, '/'))
  900.         *sp = '\0';
  901.     }
  902.     /*
  903.      * See if we found it
  904.      */
  905.     if (*p2 && sp == (Char *) -1) {
  906.         /*
  907.          * Use STRhome to make '~' work
  908.          */
  909.         newcp = Strspl(p1, cp + Strlen(p2));
  910.         xfree((ptr_t) cp);
  911.         cp = newcp;
  912.     }
  913.     }
  914.     return cp;
  915. }
  916.  
  917.  
  918. /*
  919.  * dnewcwd - make a new directory in the loop the current one
  920.  */
  921. static void
  922. dnewcwd(dp)
  923.     register struct directory *dp;
  924. {
  925.     dcwd = dp;
  926.     dset(dcwd->di_name);
  927.     if (printd && !(adrof(STRpushdsilent)))
  928.     printdirs();
  929. }
  930.