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