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.17 (Berkeley) 11/06/91"; 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 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; 197 else 198 len = Strlen(s = dp->di_name) + 1; 199 200 cur += len; 201 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 202 (void) fprintf(cshout, "\n"); 203 cur = len; 204 } 205 (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", 206 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 207 } while ((dp = dp->di_prev) != dcwd); 208 if (!(dirflag & DIR_VERT)) 209 (void) fprintf(cshout, "\n"); 210 } 211 212 void 213 dtildepr(home, dir) 214 register Char *home, *dir; 215 { 216 217 if (!eq(home, STRslash) && prefix(home, dir)) 218 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 219 else 220 (void) fprintf(cshout, "%s", vis_str(dir)); 221 } 222 223 void 224 dtilde() 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 */ 242 Char * 243 dnormalize(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); 303 } 304 305 /* 306 * dochngd - implement chdir command. 307 */ 308 void 309 /*ARGSUSED*/ 310 dochngd(v, t) 311 Char **v; 312 struct command *t; 313 { 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; 339 dfree(dcwd); 340 dnewcwd(dp); 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 356 static Char * 357 dgoto(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 continue; 368 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 369 cwdlen = 0; 370 for (p = cp; *p++;) 371 continue; 372 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 373 for (p = dp, q = dcwd->di_name; *p++ = *q++;) 374 continue; 375 if (cwdlen) 376 p[-1] = '/'; 377 else 378 p--; /* don't add a / after root */ 379 for (q = cp; *p++ = *q++;) 380 continue; 381 xfree((ptr_t) cp); 382 cp = dp; 383 dp += cwdlen; 384 } 385 else 386 dp = cp; 387 388 cp = dcanon(cp, dp); 389 return cp; 390 } 391 392 /* 393 * dfollow - change to arg directory; fall back on cdpath if not valid 394 */ 395 static Char * 396 dfollow(cp) 397 register Char *cp; 398 { 399 register Char *dp; 400 struct varent *c; 401 char ebuf[MAXPATHLEN]; 402 int serrno; 403 404 cp = globone(cp, G_ERROR); 405 /* 406 * if we are ignoring symlinks, try to fix relatives now. 407 */ 408 dp = dnormalize(cp); 409 if (chdir(short2str(dp)) >= 0) { 410 xfree((ptr_t) cp); 411 return dgoto(dp); 412 } 413 else { 414 xfree((ptr_t) dp); 415 if (chdir(short2str(cp)) >= 0) 416 return dgoto(cp); 417 serrno = errno; 418 } 419 420 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 421 && (c = adrof(STRcdpath))) { 422 Char **cdp; 423 register Char *p; 424 Char buf[MAXPATHLEN]; 425 426 for (cdp = c->vec; *cdp; cdp++) { 427 for (dp = buf, p = *cdp; *dp++ = *p++;) 428 continue; 429 dp[-1] = '/'; 430 for (p = cp; *dp++ = *p++;) 431 continue; 432 if (chdir(short2str(buf)) >= 0) { 433 printd = 1; 434 xfree((ptr_t) cp); 435 cp = Strsave(buf); 436 return dgoto(cp); 437 } 438 } 439 } 440 dp = value(cp); 441 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 442 xfree((ptr_t) cp); 443 cp = Strsave(dp); 444 printd = 1; 445 return dgoto(cp); 446 } 447 (void) strcpy(ebuf, short2str(cp)); 448 xfree((ptr_t) cp); 449 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 450 return (NULL); 451 } 452 453 454 /* 455 * dopushd - push new directory onto directory stack. 456 * with no arguments exchange top and second. 457 * with numeric argument (+n) bring it to top. 458 */ 459 void 460 /*ARGSUSED*/ 461 dopushd(v, t) 462 Char **v; 463 struct command *t; 464 { 465 register struct directory *dp; 466 467 skipargs(&v, " [<dir>|+<n>]"); 468 printd = 1; 469 if (*v == NULL) { 470 char *tmp; 471 472 if ((dp = dcwd->di_prev) == &dhead) 473 dp = dhead.di_prev; 474 if (dp == dcwd) 475 stderror(ERR_NAME | ERR_NODIR); 476 if (chdir(tmp = short2str(dp->di_name)) < 0) 477 stderror(ERR_SYSTEM, tmp, strerror(errno)); 478 dp->di_prev->di_next = dp->di_next; 479 dp->di_next->di_prev = dp->di_prev; 480 dp->di_next = dcwd->di_next; 481 dp->di_prev = dcwd; 482 dcwd->di_next->di_prev = dp; 483 dcwd->di_next = dp; 484 } 485 else if (v[1] != NULL) { 486 stderror(ERR_NAME | ERR_TOOMANY); 487 /* NOTREACHED */ 488 return; 489 } 490 else if (dp = dfind(*v)) { 491 char *tmp; 492 493 if (chdir(tmp = short2str(dp->di_name)) < 0) 494 stderror(ERR_SYSTEM, tmp, strerror(errno)); 495 } 496 else { 497 register Char *ccp; 498 499 ccp = dfollow(*v); 500 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 501 dp->di_name = ccp; 502 dp->di_count = 0; 503 dp->di_prev = dcwd; 504 dp->di_next = dcwd->di_next; 505 dcwd->di_next = dp; 506 dp->di_next->di_prev = dp; 507 } 508 dnewcwd(dp); 509 } 510 511 /* 512 * dfind - find a directory if specified by numeric (+n) argument 513 */ 514 static struct directory * 515 dfind(cp) 516 register Char *cp; 517 { 518 register struct directory *dp; 519 register int i; 520 register Char *ep; 521 522 if (*cp++ != '+') 523 return (0); 524 for (ep = cp; Isdigit(*ep); ep++) 525 continue; 526 if (*ep) 527 return (0); 528 i = getn(cp); 529 if (i <= 0) 530 return (0); 531 for (dp = dcwd; i != 0; i--) { 532 if ((dp = dp->di_prev) == &dhead) 533 dp = dp->di_prev; 534 if (dp == dcwd) 535 stderror(ERR_NAME | ERR_DEEP); 536 } 537 return (dp); 538 } 539 540 /* 541 * dopopd - pop a directory out of the directory stack 542 * with a numeric argument just discard it. 543 */ 544 void 545 /*ARGSUSED*/ 546 dopopd(v, t) 547 Char **v; 548 struct command *t; 549 { 550 register struct directory *dp, *p = NULL; 551 552 skipargs(&v, " [+<n>]"); 553 printd = 1; 554 if (*v == NULL) 555 dp = dcwd; 556 else if (v[1] != NULL) { 557 stderror(ERR_NAME | ERR_TOOMANY); 558 /* NOTREACHED */ 559 return; 560 } 561 else if ((dp = dfind(*v)) == 0) 562 stderror(ERR_NAME | ERR_BADDIR); 563 if (dp->di_prev == &dhead && dp->di_next == &dhead) 564 stderror(ERR_NAME | ERR_EMPTY); 565 if (dp == dcwd) { 566 char *tmp; 567 568 if ((p = dp->di_prev) == &dhead) 569 p = dhead.di_prev; 570 if (chdir(tmp = short2str(p->di_name)) < 0) 571 stderror(ERR_SYSTEM, tmp, strerror(errno)); 572 } 573 dp->di_prev->di_next = dp->di_next; 574 dp->di_next->di_prev = dp->di_prev; 575 if (dp == dcwd) 576 dnewcwd(p); 577 else { 578 printdirs(); 579 } 580 dfree(dp); 581 } 582 583 /* 584 * dfree - free the directory (or keep it if it still has ref count) 585 */ 586 void 587 dfree(dp) 588 register struct directory *dp; 589 { 590 591 if (dp->di_count != 0) { 592 dp->di_next = dp->di_prev = 0; 593 } 594 else { 595 xfree((char *) dp->di_name); 596 xfree((ptr_t) dp); 597 } 598 } 599 600 /* 601 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 602 * we are of course assuming that the file system is standardly 603 * constructed (always have ..'s, directories have links) 604 */ 605 Char * 606 dcanon(cp, p) 607 register Char *cp, *p; 608 { 609 register Char *sp; 610 register Char *p1, *p2; /* general purpose */ 611 bool slash; 612 613 Char link[MAXPATHLEN]; 614 char tlink[MAXPATHLEN]; 615 int cc; 616 Char *newcp; 617 618 /* 619 * christos: if the path given does not start with a slash prepend cwd. If 620 * cwd does not start with a path or the result would be too long abort(). 621 */ 622 if (*cp != '/') { 623 Char tmpdir[MAXPATHLEN]; 624 625 p1 = value(STRcwd); 626 if (p1 == NULL || *p1 != '/') 627 abort(); 628 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 629 abort(); 630 (void) Strcpy(tmpdir, p1); 631 (void) Strcat(tmpdir, STRslash); 632 (void) Strcat(tmpdir, cp); 633 xfree((ptr_t) cp); 634 cp = p = Strsave(tmpdir); 635 } 636 637 while (*p) { /* for each component */ 638 sp = p; /* save slash address */ 639 while (*++p == '/') /* flush extra slashes */ 640 continue; 641 if (p != ++sp) 642 for (p1 = sp, p2 = p; *p1++ = *p2++;) 643 continue; 644 p = sp; /* save start of component */ 645 slash = 0; 646 while (*++p) /* find next slash or end of path */ 647 if (*p == '/') { 648 slash = 1; 649 *p = 0; 650 break; 651 } 652 653 if (*sp == '\0') /* if component is null */ 654 if (--sp == cp) /* if path is one char (i.e. /) */ 655 break; 656 else 657 *sp = '\0'; 658 else if (sp[0] == '.' && sp[1] == 0) { 659 if (slash) { 660 for (p1 = sp, p2 = p + 1; *p1++ = *p2++;) 661 continue; 662 p = --sp; 663 } 664 else if (--sp != cp) 665 *sp = '\0'; 666 } 667 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 668 /* 669 * We have something like "yyy/xxx/..", where "yyy" can be null or 670 * a path starting at /, and "xxx" is a single component. Before 671 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 672 * symbolic link. 673 */ 674 *--sp = 0; /* form the pathname for readlink */ 675 if (sp != cp && !adrof(STRignore_symlinks) && 676 (cc = readlink(short2str(cp), tlink, 677 sizeof tlink)) >= 0) { 678 (void) Strcpy(link, str2short(tlink)); 679 link[cc] = '\0'; 680 681 if (slash) 682 *p = '/'; 683 /* 684 * Point p to the '/' in "/..", and restore the '/'. 685 */ 686 *(p = sp) = '/'; 687 /* 688 * find length of p 689 */ 690 for (p1 = p; *p1++;) 691 continue; 692 if (*link != '/') { 693 /* 694 * Relative path, expand it between the "yyy/" and the 695 * "/..". First, back sp up to the character past "yyy/". 696 */ 697 while (*--sp != '/') 698 continue; 699 sp++; 700 *sp = 0; 701 /* 702 * New length is "yyy/" + link + "/.." and rest 703 */ 704 p1 = newcp = (Char *) xmalloc((size_t) 705 (((sp - cp) + cc + (p1 - p)) * 706 sizeof(Char))); 707 /* 708 * Copy new path into newcp 709 */ 710 for (p2 = cp; *p1++ = *p2++;) 711 continue; 712 for (p1--, p2 = link; *p1++ = *p2++;) 713 continue; 714 for (p1--, p2 = p; *p1++ = *p2++;) 715 continue; 716 /* 717 * Restart canonicalization at expanded "/xxx". 718 */ 719 p = sp - cp - 1 + newcp; 720 } 721 else { 722 /* 723 * New length is link + "/.." and rest 724 */ 725 p1 = newcp = (Char *) xmalloc((size_t) 726 ((cc + (p1 - p)) * sizeof(Char))); 727 /* 728 * Copy new path into newcp 729 */ 730 for (p2 = link; *p1++ = *p2++;) 731 continue; 732 for (p1--, p2 = p; *p1++ = *p2++;) 733 continue; 734 /* 735 * Restart canonicalization at beginning 736 */ 737 p = newcp; 738 } 739 xfree((ptr_t) cp); 740 cp = newcp; 741 continue; /* canonicalize the link */ 742 } 743 *sp = '/'; 744 if (sp != cp) 745 while (*--sp != '/') 746 continue; 747 if (slash) { 748 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;) 749 continue; 750 p = sp; 751 } 752 else if (cp == sp) 753 *++sp = '\0'; 754 else 755 *sp = '\0'; 756 } 757 else { /* normal dir name (not . or .. or nothing) */ 758 759 if (sp != cp && adrof(STRchase_symlinks) && 760 !adrof(STRignore_symlinks) && 761 (cc = readlink(short2str(cp), tlink, 762 sizeof tlink)) >= 0) { 763 (void) Strcpy(link, str2short(tlink)); 764 link[cc] = '\0'; 765 766 /* 767 * restore the '/'. 768 */ 769 if (slash) 770 *p = '/'; 771 772 /* 773 * point sp to p (rather than backing up). 774 */ 775 sp = p; 776 777 /* 778 * find length of p 779 */ 780 for (p1 = p; *p1++;) 781 continue; 782 if (*link != '/') { 783 /* 784 * Relative path, expand it between the "yyy/" and the 785 * remainder. First, back sp up to the character past 786 * "yyy/". 787 */ 788 while (*--sp != '/') 789 continue; 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 continue; 803 for (p1--, p2 = link; *p1++ = *p2++;) 804 continue; 805 for (p1--, p2 = p; *p1++ = *p2++;) 806 continue; 807 /* 808 * Restart canonicalization at expanded "/xxx". 809 */ 810 p = sp - cp - 1 + newcp; 811 } 812 else { 813 /* 814 * New length is link + the rest 815 */ 816 p1 = newcp = (Char *) xmalloc((size_t) 817 ((cc + (p1 - p)) * sizeof(Char))); 818 /* 819 * Copy new path into newcp 820 */ 821 for (p2 = link; *p1++ = *p2++;) 822 continue; 823 for (p1--, p2 = p; *p1++ = *p2++;) 824 continue; 825 /* 826 * Restart canonicalization at beginning 827 */ 828 p = newcp; 829 } 830 xfree((ptr_t) cp); 831 cp = newcp; 832 continue; /* canonicalize the link */ 833 } 834 if (slash) 835 *p = '/'; 836 } 837 } 838 839 /* 840 * fix home... 841 */ 842 p1 = value(STRhome); 843 cc = Strlen(p1); 844 /* 845 * See if we're not in a subdir of STRhome 846 */ 847 if (p1 && *p1 == '/' && 848 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 849 static ino_t home_ino = -1; 850 static dev_t home_dev = -1; 851 static Char *home_ptr = NULL; 852 struct stat statbuf; 853 854 /* 855 * Get dev and ino of STRhome 856 */ 857 if (home_ptr != p1 && 858 stat(short2str(p1), &statbuf) != -1) { 859 home_dev = statbuf.st_dev; 860 home_ino = statbuf.st_ino; 861 home_ptr = p1; 862 } 863 /* 864 * Start comparing dev & ino backwards 865 */ 866 p2 = Strcpy(link, cp); 867 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 868 if (statbuf.st_dev == home_dev && 869 statbuf.st_ino == home_ino) { 870 sp = (Char *) - 1; 871 break; 872 } 873 if (sp = Strrchr(p2, '/')) 874 *sp = '\0'; 875 } 876 /* 877 * See if we found it 878 */ 879 if (*p2 && sp == (Char *) -1) { 880 /* 881 * Use STRhome to make '~' work 882 */ 883 newcp = Strspl(p1, cp + Strlen(p2)); 884 xfree((ptr_t) cp); 885 cp = newcp; 886 } 887 } 888 return cp; 889 } 890 891 892 /* 893 * dnewcwd - make a new directory in the loop the current one 894 */ 895 static void 896 dnewcwd(dp) 897 register struct directory *dp; 898 { 899 dcwd = dp; 900 dset(dcwd->di_name); 901 if (printd && !(adrof(STRpushdsilent))) 902 printdirs(); 903 } 904