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