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