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