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