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