1 /*- 2 * Copyright (c) 1980, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)dir.c 5.18 (Berkeley) 02/12/92"; 10 #endif /* not lint */ 11 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> 18 #if __STDC__ 19 # include <stdarg.h> 20 #else 21 # include <varargs.h> 22 #endif 23 24 #include "csh.h" 25 #include "dir.h" 26 #include "extern.h" 27 28 /* Directory management. */ 29 30 static struct directory 31 *dfind __P((Char *)); 32 static Char *dfollow __P((Char *)); 33 static void printdirs __P((void)); 34 static Char *dgoto __P((Char *)); 35 static void dnewcwd __P((struct directory *)); 36 static void dset __P((Char *)); 37 38 struct directory dhead; /* "head" of loop */ 39 int printd; /* force name to be printed */ 40 41 static int dirflag = 0; 42 43 /* 44 * dinit - initialize current working directory 45 */ 46 void 47 dinit(hp) 48 Char *hp; 49 { 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"; 55 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') { 59 (void) fprintf(csherr, "csh: %s\n", path); 60 if (hp && *hp) { 61 tcp = short2str(hp); 62 if (chdir(tcp) == -1) 63 cp = NULL; 64 else 65 cp = hp; 66 (void) fprintf(csherr, emsg, vis_str(hp)); 67 } 68 else 69 cp = NULL; 70 if (cp == NULL) { 71 (void) fprintf(csherr, emsg, "/"); 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; 88 else { 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(SAVE(tcp), STRNULL); 100 } 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 112 static void 113 dset(dp) 114 Char *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 132 static void 133 skipargs(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, vis_str(**v), str); 154 break; 155 } 156 *v = n; 157 } 158 159 /* 160 * dodirs - list all directories in directory loop 161 */ 162 void 163 /*ARGSUSED*/ 164 dodirs(v, t) 165 Char **v; 166 struct command *t; 167 { 168 skipargs(&v, ""); 169 170 if (*v != NULL) 171 stderror(ERR_DIRUS, "dirs", ""); 172 printdirs(); 173 } 174 175 static void 176 printdirs() 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) { 191 (void) fprintf(cshout, "%d\t", idx++); 192 cur = 0; 193 } 194 len = Strlen(hp); 195 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 196 Strncmp(hp, dp->di_name, len) == 0 && 197 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 198 len = Strlen(s = (dp->di_name + len)) + 2; 199 else 200 len = Strlen(s = dp->di_name) + 1; 201 202 cur += len; 203 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 204 (void) fprintf(cshout, "\n"); 205 cur = len; 206 } 207 (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", 208 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 209 } while ((dp = dp->di_prev) != dcwd); 210 if (!(dirflag & DIR_VERT)) 211 (void) fprintf(cshout, "\n"); 212 } 213 214 void 215 dtildepr(home, dir) 216 register Char *home, *dir; 217 { 218 219 if (!eq(home, STRslash) && prefix(home, dir)) 220 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 221 else 222 (void) fprintf(cshout, "%s", vis_str(dir)); 223 } 224 225 void 226 dtilde() 227 { 228 struct directory *d = dcwd; 229 230 do { 231 if (d == &dhead) 232 continue; 233 d->di_name = dcanon(d->di_name, STRNULL); 234 } while ((d = d->di_prev) != dcwd); 235 236 dset(dcwd->di_name); 237 } 238 239 240 /* dnormalize(): 241 * If the name starts with . or .. then we might need to normalize 242 * it depending on the symbolic link flags 243 */ 244 Char * 245 dnormalize(cp) 246 Char *cp; 247 { 248 249 #define UC (unsigned char) 250 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 251 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 252 253 if ((unsigned char) cp[0] == '/') 254 return (Strsave(cp)); 255 256 if (adrof(STRignore_symlinks)) { 257 int dotdot = 0; 258 Char *dp, *cwd; 259 260 cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * 261 sizeof(Char))); 262 (void) Strcpy(cwd, dcwd->di_name); 263 264 /* 265 * Ignore . and count ..'s 266 */ 267 while (*cp) { 268 if (ISDOT(cp)) { 269 if (*++cp) 270 cp++; 271 } 272 else if (ISDOTDOT(cp)) { 273 dotdot++; 274 cp += 2; 275 if (*cp) 276 cp++; 277 } 278 else 279 break; 280 } 281 while (dotdot > 0) 282 if ((dp = Strrchr(cwd, '/'))) { 283 *dp = '\0'; 284 dotdot--; 285 } 286 else 287 break; 288 289 if (*cp) { 290 cwd[dotdot = Strlen(cwd)] = '/'; 291 cwd[dotdot + 1] = '\0'; 292 dp = Strspl(cwd, cp); 293 xfree((ptr_t) cwd); 294 return dp; 295 } 296 else { 297 if (!*cwd) { 298 cwd[0] = '/'; 299 cwd[1] = '\0'; 300 } 301 return cwd; 302 } 303 } 304 return Strsave(cp); 305 } 306 307 /* 308 * dochngd - implement chdir command. 309 */ 310 void 311 /*ARGSUSED*/ 312 dochngd(v, t) 313 Char **v; 314 struct command *t; 315 { 316 register Char *cp; 317 register struct directory *dp; 318 319 skipargs(&v, " [<dir>]"); 320 printd = 0; 321 if (*v == NULL) { 322 if ((cp = value(STRhome)) == NULL || *cp == 0) 323 stderror(ERR_NAME | ERR_NOHOMEDIR); 324 if (chdir(short2str(cp)) < 0) 325 stderror(ERR_NAME | ERR_CANTCHANGE); 326 cp = Strsave(cp); 327 } 328 else if (v[1] != NULL) { 329 stderror(ERR_NAME | ERR_TOOMANY); 330 /* NOTREACHED */ 331 return; 332 } 333 else if ((dp = dfind(*v)) != 0) { 334 char *tmp; 335 336 printd = 1; 337 if (chdir(tmp = short2str(dp->di_name)) < 0) 338 stderror(ERR_SYSTEM, tmp, strerror(errno)); 339 dcwd->di_prev->di_next = dcwd->di_next; 340 dcwd->di_next->di_prev = dcwd->di_prev; 341 dfree(dcwd); 342 dnewcwd(dp); 343 return; 344 } 345 else 346 cp = dfollow(*v); 347 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 348 dp->di_name = cp; 349 dp->di_count = 0; 350 dp->di_next = dcwd->di_next; 351 dp->di_prev = dcwd->di_prev; 352 dp->di_prev->di_next = dp; 353 dp->di_next->di_prev = dp; 354 dfree(dcwd); 355 dnewcwd(dp); 356 } 357 358 static Char * 359 dgoto(cp) 360 Char *cp; 361 { 362 Char *dp; 363 364 if (*cp != '/') { 365 register Char *p, *q; 366 int cwdlen; 367 368 for (p = dcwd->di_name; *p++;) 369 continue; 370 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 371 cwdlen = 0; 372 for (p = cp; *p++;) 373 continue; 374 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 375 for (p = dp, q = dcwd->di_name; *p++ = *q++;) 376 continue; 377 if (cwdlen) 378 p[-1] = '/'; 379 else 380 p--; /* don't add a / after root */ 381 for (q = cp; *p++ = *q++;) 382 continue; 383 xfree((ptr_t) cp); 384 cp = dp; 385 dp += cwdlen; 386 } 387 else 388 dp = cp; 389 390 cp = dcanon(cp, dp); 391 return cp; 392 } 393 394 /* 395 * dfollow - change to arg directory; fall back on cdpath if not valid 396 */ 397 static Char * 398 dfollow(cp) 399 register Char *cp; 400 { 401 register Char *dp; 402 struct varent *c; 403 char ebuf[MAXPATHLEN]; 404 int serrno; 405 406 cp = globone(cp, G_ERROR); 407 /* 408 * if we are ignoring symlinks, try to fix relatives now. 409 */ 410 dp = dnormalize(cp); 411 if (chdir(short2str(dp)) >= 0) { 412 xfree((ptr_t) cp); 413 return dgoto(dp); 414 } 415 else { 416 xfree((ptr_t) dp); 417 if (chdir(short2str(cp)) >= 0) 418 return dgoto(cp); 419 serrno = errno; 420 } 421 422 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 423 && (c = adrof(STRcdpath))) { 424 Char **cdp; 425 register Char *p; 426 Char buf[MAXPATHLEN]; 427 428 for (cdp = c->vec; *cdp; cdp++) { 429 for (dp = buf, p = *cdp; *dp++ = *p++;) 430 continue; 431 dp[-1] = '/'; 432 for (p = cp; *dp++ = *p++;) 433 continue; 434 if (chdir(short2str(buf)) >= 0) { 435 printd = 1; 436 xfree((ptr_t) cp); 437 cp = Strsave(buf); 438 return dgoto(cp); 439 } 440 } 441 } 442 dp = value(cp); 443 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 444 xfree((ptr_t) cp); 445 cp = Strsave(dp); 446 printd = 1; 447 return dgoto(cp); 448 } 449 (void) strcpy(ebuf, short2str(cp)); 450 xfree((ptr_t) cp); 451 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 452 return (NULL); 453 } 454 455 456 /* 457 * dopushd - push new directory onto directory stack. 458 * with no arguments exchange top and second. 459 * with numeric argument (+n) bring it to top. 460 */ 461 void 462 /*ARGSUSED*/ 463 dopushd(v, t) 464 Char **v; 465 struct command *t; 466 { 467 register struct directory *dp; 468 469 skipargs(&v, " [<dir>|+<n>]"); 470 printd = 1; 471 if (*v == NULL) { 472 char *tmp; 473 474 if ((dp = dcwd->di_prev) == &dhead) 475 dp = dhead.di_prev; 476 if (dp == dcwd) 477 stderror(ERR_NAME | ERR_NODIR); 478 if (chdir(tmp = short2str(dp->di_name)) < 0) 479 stderror(ERR_SYSTEM, tmp, strerror(errno)); 480 dp->di_prev->di_next = dp->di_next; 481 dp->di_next->di_prev = dp->di_prev; 482 dp->di_next = dcwd->di_next; 483 dp->di_prev = dcwd; 484 dcwd->di_next->di_prev = dp; 485 dcwd->di_next = dp; 486 } 487 else if (v[1] != NULL) { 488 stderror(ERR_NAME | ERR_TOOMANY); 489 /* NOTREACHED */ 490 return; 491 } 492 else if (dp = dfind(*v)) { 493 char *tmp; 494 495 if (chdir(tmp = short2str(dp->di_name)) < 0) 496 stderror(ERR_SYSTEM, tmp, strerror(errno)); 497 } 498 else { 499 register Char *ccp; 500 501 ccp = dfollow(*v); 502 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 503 dp->di_name = ccp; 504 dp->di_count = 0; 505 dp->di_prev = dcwd; 506 dp->di_next = dcwd->di_next; 507 dcwd->di_next = dp; 508 dp->di_next->di_prev = dp; 509 } 510 dnewcwd(dp); 511 } 512 513 /* 514 * dfind - find a directory if specified by numeric (+n) argument 515 */ 516 static struct directory * 517 dfind(cp) 518 register Char *cp; 519 { 520 register struct directory *dp; 521 register int i; 522 register Char *ep; 523 524 if (*cp++ != '+') 525 return (0); 526 for (ep = cp; Isdigit(*ep); ep++) 527 continue; 528 if (*ep) 529 return (0); 530 i = getn(cp); 531 if (i <= 0) 532 return (0); 533 for (dp = dcwd; i != 0; i--) { 534 if ((dp = dp->di_prev) == &dhead) 535 dp = dp->di_prev; 536 if (dp == dcwd) 537 stderror(ERR_NAME | ERR_DEEP); 538 } 539 return (dp); 540 } 541 542 /* 543 * dopopd - pop a directory out of the directory stack 544 * with a numeric argument just discard it. 545 */ 546 void 547 /*ARGSUSED*/ 548 dopopd(v, t) 549 Char **v; 550 struct command *t; 551 { 552 register struct directory *dp, *p = NULL; 553 554 skipargs(&v, " [+<n>]"); 555 printd = 1; 556 if (*v == NULL) 557 dp = dcwd; 558 else if (v[1] != NULL) { 559 stderror(ERR_NAME | ERR_TOOMANY); 560 /* NOTREACHED */ 561 return; 562 } 563 else if ((dp = dfind(*v)) == 0) 564 stderror(ERR_NAME | ERR_BADDIR); 565 if (dp->di_prev == &dhead && dp->di_next == &dhead) 566 stderror(ERR_NAME | ERR_EMPTY); 567 if (dp == dcwd) { 568 char *tmp; 569 570 if ((p = dp->di_prev) == &dhead) 571 p = dhead.di_prev; 572 if (chdir(tmp = short2str(p->di_name)) < 0) 573 stderror(ERR_SYSTEM, tmp, strerror(errno)); 574 } 575 dp->di_prev->di_next = dp->di_next; 576 dp->di_next->di_prev = dp->di_prev; 577 if (dp == dcwd) 578 dnewcwd(p); 579 else { 580 printdirs(); 581 } 582 dfree(dp); 583 } 584 585 /* 586 * dfree - free the directory (or keep it if it still has ref count) 587 */ 588 void 589 dfree(dp) 590 register struct directory *dp; 591 { 592 593 if (dp->di_count != 0) { 594 dp->di_next = dp->di_prev = 0; 595 } 596 else { 597 xfree((char *) dp->di_name); 598 xfree((ptr_t) dp); 599 } 600 } 601 602 /* 603 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 604 * we are of course assuming that the file system is standardly 605 * constructed (always have ..'s, directories have links) 606 */ 607 Char * 608 dcanon(cp, p) 609 register Char *cp, *p; 610 { 611 register Char *sp; 612 register Char *p1, *p2; /* general purpose */ 613 bool slash; 614 615 Char link[MAXPATHLEN]; 616 char tlink[MAXPATHLEN]; 617 int cc; 618 Char *newcp; 619 620 /* 621 * christos: if the path given does not start with a slash prepend cwd. If 622 * cwd does not start with a path or the result would be too long abort(). 623 */ 624 if (*cp != '/') { 625 Char tmpdir[MAXPATHLEN]; 626 627 p1 = value(STRcwd); 628 if (p1 == NULL || *p1 != '/') 629 abort(); 630 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 631 abort(); 632 (void) Strcpy(tmpdir, p1); 633 (void) Strcat(tmpdir, STRslash); 634 (void) Strcat(tmpdir, cp); 635 xfree((ptr_t) cp); 636 cp = p = Strsave(tmpdir); 637 } 638 639 while (*p) { /* for each component */ 640 sp = p; /* save slash address */ 641 while (*++p == '/') /* flush extra slashes */ 642 continue; 643 if (p != ++sp) 644 for (p1 = sp, p2 = p; *p1++ = *p2++;) 645 continue; 646 p = sp; /* save start of component */ 647 slash = 0; 648 while (*++p) /* find next slash or end of path */ 649 if (*p == '/') { 650 slash = 1; 651 *p = 0; 652 break; 653 } 654 655 if (*sp == '\0') /* if component is null */ 656 if (--sp == cp) /* if path is one char (i.e. /) */ 657 break; 658 else 659 *sp = '\0'; 660 else if (sp[0] == '.' && sp[1] == 0) { 661 if (slash) { 662 for (p1 = sp, p2 = p + 1; *p1++ = *p2++;) 663 continue; 664 p = --sp; 665 } 666 else if (--sp != cp) 667 *sp = '\0'; 668 } 669 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 670 /* 671 * We have something like "yyy/xxx/..", where "yyy" can be null or 672 * a path starting at /, and "xxx" is a single component. Before 673 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 674 * symbolic link. 675 */ 676 *--sp = 0; /* form the pathname for readlink */ 677 if (sp != cp && !adrof(STRignore_symlinks) && 678 (cc = readlink(short2str(cp), tlink, 679 sizeof tlink)) >= 0) { 680 (void) Strcpy(link, str2short(tlink)); 681 link[cc] = '\0'; 682 683 if (slash) 684 *p = '/'; 685 /* 686 * Point p to the '/' in "/..", and restore the '/'. 687 */ 688 *(p = sp) = '/'; 689 /* 690 * find length of p 691 */ 692 for (p1 = p; *p1++;) 693 continue; 694 if (*link != '/') { 695 /* 696 * Relative path, expand it between the "yyy/" and the 697 * "/..". First, back sp up to the character past "yyy/". 698 */ 699 while (*--sp != '/') 700 continue; 701 sp++; 702 *sp = 0; 703 /* 704 * New length is "yyy/" + link + "/.." and rest 705 */ 706 p1 = newcp = (Char *) xmalloc((size_t) 707 (((sp - cp) + cc + (p1 - p)) * 708 sizeof(Char))); 709 /* 710 * Copy new path into newcp 711 */ 712 for (p2 = cp; *p1++ = *p2++;) 713 continue; 714 for (p1--, p2 = link; *p1++ = *p2++;) 715 continue; 716 for (p1--, p2 = p; *p1++ = *p2++;) 717 continue; 718 /* 719 * Restart canonicalization at expanded "/xxx". 720 */ 721 p = sp - cp - 1 + newcp; 722 } 723 else { 724 /* 725 * New length is link + "/.." and rest 726 */ 727 p1 = newcp = (Char *) xmalloc((size_t) 728 ((cc + (p1 - p)) * sizeof(Char))); 729 /* 730 * Copy new path into newcp 731 */ 732 for (p2 = link; *p1++ = *p2++;) 733 continue; 734 for (p1--, p2 = p; *p1++ = *p2++;) 735 continue; 736 /* 737 * Restart canonicalization at beginning 738 */ 739 p = newcp; 740 } 741 xfree((ptr_t) cp); 742 cp = newcp; 743 continue; /* canonicalize the link */ 744 } 745 *sp = '/'; 746 if (sp != cp) 747 while (*--sp != '/') 748 continue; 749 if (slash) { 750 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;) 751 continue; 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 continue; 784 if (*link != '/') { 785 /* 786 * Relative path, expand it between the "yyy/" and the 787 * remainder. First, back sp up to the character past 788 * "yyy/". 789 */ 790 while (*--sp != '/') 791 continue; 792 sp++; 793 *sp = 0; 794 /* 795 * New length is "yyy/" + link + "/.." and rest 796 */ 797 p1 = newcp = (Char *) xmalloc((size_t) 798 (((sp - cp) + cc + (p1 - p)) 799 * sizeof(Char))); 800 /* 801 * Copy new path into newcp 802 */ 803 for (p2 = cp; *p1++ = *p2++;) 804 continue; 805 for (p1--, p2 = link; *p1++ = *p2++;) 806 continue; 807 for (p1--, p2 = p; *p1++ = *p2++;) 808 continue; 809 /* 810 * Restart canonicalization at expanded "/xxx". 811 */ 812 p = sp - cp - 1 + newcp; 813 } 814 else { 815 /* 816 * New length is link + the rest 817 */ 818 p1 = newcp = (Char *) xmalloc((size_t) 819 ((cc + (p1 - p)) * sizeof(Char))); 820 /* 821 * Copy new path into newcp 822 */ 823 for (p2 = link; *p1++ = *p2++;) 824 continue; 825 for (p1--, p2 = p; *p1++ = *p2++;) 826 continue; 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 newcp = Strspl(p1, cp + Strlen(p2)); 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