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