1 /* $OpenBSD: dir.c,v 1.26 2023/03/08 04:43:04 guenther Exp $ */ 2 /* $NetBSD: dir.c,v 1.9 1995/03/21 09:02:42 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/stat.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <limits.h> 39 #include <stdarg.h> 40 41 #include "csh.h" 42 #include "dir.h" 43 #include "extern.h" 44 45 /* Directory management. */ 46 47 static struct directory 48 *dfind(Char *); 49 static Char *dfollow(Char *); 50 static void printdirs(void); 51 static Char *dgoto(Char *); 52 static void dnewcwd(struct directory *); 53 static void dset(Char *); 54 55 struct directory dhead; /* "head" of loop */ 56 struct directory *dcwd; /* the one we are in now */ 57 int printd; /* force name to be printed */ 58 59 static int dirflag = 0; 60 61 /* 62 * dinit - initialize current working directory 63 */ 64 void 65 dinit(Char *hp) 66 { 67 char *tcp; 68 Char *cp = NULL; 69 struct directory *dp; 70 char path[PATH_MAX]; 71 static const char emsg[] = "csh: Trying to start from \"%s\"\n"; 72 73 /* Don't believe the login shell home, because it may be a symlink */ 74 tcp = getcwd(path, PATH_MAX); 75 if (tcp == NULL || *tcp == '\0') { 76 (void) fprintf(csherr, "csh: %s\n", strerror(errno)); 77 if (hp && *hp) { 78 tcp = short2str(hp); 79 if (chdir(tcp) == 0) 80 cp = hp; 81 (void) fprintf(csherr, emsg, vis_str(hp)); 82 } 83 } 84 else { 85 struct stat swd, shp; 86 87 if (stat(tcp, &swd) == -1) { 88 (void) fprintf(csherr, "csh: %s: %s\n", tcp, strerror(errno)); 89 } else { 90 /* 91 * See if $HOME is the working directory we got and use that 92 */ 93 if (hp && *hp && stat(short2str(hp), &shp) != -1 && 94 swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) 95 cp = hp; 96 else { 97 char *cwd; 98 99 /* 100 * use PWD if we have it (for subshells) 101 */ 102 if ((cwd = getenv("PWD")) != NULL) { 103 if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && 104 swd.st_ino == shp.st_ino) 105 tcp = cwd; 106 } 107 cp = dcanon(SAVE(tcp), STRNULL); 108 } 109 } 110 } 111 112 if (cp == NULL) { 113 (void) fprintf(csherr, emsg, "/"); 114 if (chdir("/") == -1) 115 /* I am not even try to print an error message! */ 116 xexit(1); 117 cp = SAVE("/"); 118 } 119 120 dp = xcalloc(1, sizeof(struct directory)); 121 dp->di_name = Strsave(cp); 122 dp->di_count = 0; 123 dhead.di_next = dhead.di_prev = dp; 124 dp->di_next = dp->di_prev = &dhead; 125 printd = 0; 126 dnewcwd(dp); 127 } 128 129 static void 130 dset(Char *dp) 131 { 132 /* 133 * Don't call set() directly cause if the directory contains ` or 134 * other junk characters glob will fail. 135 */ 136 Char **vec = xreallocarray(NULL, 2, sizeof(*vec)); 137 138 vec[0] = Strsave(dp); 139 vec[1] = 0; 140 setq(STRcwd, vec, &shvhed); 141 Setenv(STRPWD, dp); 142 } 143 144 #define DIR_LONG 1 145 #define DIR_VERT 2 146 #define DIR_LINE 4 147 148 static void 149 skipargs(Char ***v, char *str) 150 { 151 Char **n = *v, *s; 152 153 dirflag = 0; 154 for (n++; *n != NULL && (*n)[0] == '-'; n++) 155 for (s = &((*n)[1]); *s; s++) 156 switch (*s) { 157 case 'l': 158 dirflag |= DIR_LONG; 159 break; 160 case 'v': 161 dirflag |= DIR_VERT; 162 break; 163 case 'n': 164 dirflag |= DIR_LINE; 165 break; 166 default: 167 stderror(ERR_DIRUS, vis_str(**v), str); 168 break; 169 } 170 *v = n; 171 } 172 173 /* 174 * dodirs - list all directories in directory loop 175 */ 176 void 177 dodirs(Char **v, struct command *t) 178 { 179 skipargs(&v, ""); 180 181 if (*v != NULL) 182 stderror(ERR_DIRUS, "dirs", ""); 183 printdirs(); 184 } 185 186 static void 187 printdirs(void) 188 { 189 struct directory *dp; 190 Char *s, *hp = value(STRhome); 191 int idx, len, cur; 192 193 if (*hp == '\0') 194 hp = NULL; 195 dp = dcwd; 196 idx = 0; 197 cur = 0; 198 do { 199 if (dp == &dhead) 200 continue; 201 if (dirflag & DIR_VERT) { 202 (void) fprintf(cshout, "%d\t", idx++); 203 cur = 0; 204 } 205 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 206 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 207 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 208 len = Strlen(s = (dp->di_name + len)) + 2; 209 else 210 len = Strlen(s = dp->di_name) + 1; 211 212 cur += len; 213 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 214 (void) fprintf(cshout, "\n"); 215 cur = len; 216 } 217 (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", 218 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 219 } while ((dp = dp->di_prev) != dcwd); 220 if (!(dirflag & DIR_VERT)) 221 (void) fprintf(cshout, "\n"); 222 } 223 224 void 225 dtildepr(Char *home, Char *dir) 226 { 227 228 if (!eq(home, STRslash) && prefix(home, dir)) 229 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 230 else 231 (void) fprintf(cshout, "%s", vis_str(dir)); 232 } 233 234 void 235 dtilde(void) 236 { 237 struct directory *d = dcwd; 238 239 do { 240 if (d == &dhead) 241 continue; 242 d->di_name = dcanon(d->di_name, STRNULL); 243 } while ((d = d->di_prev) != dcwd); 244 245 dset(dcwd->di_name); 246 } 247 248 249 /* dnormalize(): 250 * If the name starts with . or .. then we might need to normalize 251 * it depending on the symbolic link flags 252 */ 253 Char * 254 dnormalize(Char *cp) 255 { 256 257 #define UC (unsigned char) 258 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 259 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 260 261 if ((unsigned char) cp[0] == '/') 262 return (Strsave(cp)); 263 264 if (adrof(STRignore_symlinks)) { 265 int dotdot = 0; 266 Char *dp, *cwd; 267 size_t len; 268 269 len = (size_t) (Strlen(dcwd->di_name) + 3); 270 cwd = xreallocarray(NULL, len, sizeof(Char)); 271 (void) Strlcpy(cwd, dcwd->di_name, len); 272 273 /* 274 * Ignore . and count ..'s 275 */ 276 while (*cp) { 277 if (ISDOT(cp)) { 278 if (*++cp) 279 cp++; 280 } 281 else if (ISDOTDOT(cp)) { 282 dotdot++; 283 cp += 2; 284 if (*cp) 285 cp++; 286 } 287 else 288 break; 289 } 290 while (dotdot > 0) 291 if ((dp = Strrchr(cwd, '/'))) { 292 *dp = '\0'; 293 dotdot--; 294 } 295 else 296 break; 297 298 if (*cp) { 299 cwd[dotdot = Strlen(cwd)] = '/'; 300 cwd[dotdot + 1] = '\0'; 301 dp = Strspl(cwd, cp); 302 free(cwd); 303 return dp; 304 } 305 else { 306 if (!*cwd) { 307 cwd[0] = '/'; 308 cwd[1] = '\0'; 309 } 310 return cwd; 311 } 312 } 313 return Strsave(cp); 314 } 315 316 /* 317 * dochngd - implement chdir command. 318 */ 319 void 320 dochngd(Char **v, struct command *t) 321 { 322 Char *cp; 323 struct directory *dp; 324 325 skipargs(&v, " [<dir>]"); 326 printd = 0; 327 if (*v == NULL) { 328 if ((cp = value(STRhome)) == NULL || *cp == 0) 329 stderror(ERR_NAME | ERR_NOHOMEDIR); 330 if (chdir(short2str(cp)) == -1) 331 stderror(ERR_NAME | ERR_CANTCHANGE); 332 cp = Strsave(cp); 333 } 334 else if (v[1] != NULL) { 335 stderror(ERR_NAME | ERR_TOOMANY); 336 /* NOTREACHED */ 337 return; 338 } 339 else if ((dp = dfind(*v)) != 0) { 340 char *tmp; 341 342 printd = 1; 343 if (chdir(tmp = short2str(dp->di_name)) == -1) 344 stderror(ERR_SYSTEM, tmp, strerror(errno)); 345 dcwd->di_prev->di_next = dcwd->di_next; 346 dcwd->di_next->di_prev = dcwd->di_prev; 347 dfree(dcwd); 348 dnewcwd(dp); 349 return; 350 } 351 else 352 cp = dfollow(*v); 353 dp = xcalloc(1, sizeof(struct directory)); 354 dp->di_name = cp; 355 dp->di_count = 0; 356 dp->di_next = dcwd->di_next; 357 dp->di_prev = dcwd->di_prev; 358 dp->di_prev->di_next = dp; 359 dp->di_next->di_prev = dp; 360 dfree(dcwd); 361 dnewcwd(dp); 362 } 363 364 static Char * 365 dgoto(Char *cp) 366 { 367 Char *dp; 368 369 if (*cp != '/') { 370 Char *p, *q; 371 int cwdlen; 372 373 for (p = dcwd->di_name; *p++;) 374 continue; 375 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 376 cwdlen = 0; 377 for (p = cp; *p++;) 378 continue; 379 dp = xreallocarray(NULL, (cwdlen + (p - cp) + 1), sizeof(Char)); 380 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 381 continue; 382 if (cwdlen) 383 p[-1] = '/'; 384 else 385 p--; /* don't add a / after root */ 386 for (q = cp; (*p++ = *q++) != '\0';) 387 continue; 388 free(cp); 389 cp = dp; 390 dp += cwdlen; 391 } 392 else 393 dp = cp; 394 395 cp = dcanon(cp, dp); 396 return cp; 397 } 398 399 /* 400 * dfollow - change to arg directory; fall back on cdpath if not valid 401 */ 402 static Char * 403 dfollow(Char *cp) 404 { 405 Char *dp; 406 struct varent *c; 407 char ebuf[PATH_MAX]; 408 int serrno; 409 410 cp = globone(cp, G_ERROR); 411 /* 412 * if we are ignoring symlinks, try to fix relatives now. 413 */ 414 dp = dnormalize(cp); 415 if (chdir(short2str(dp)) >= 0) { 416 free(cp); 417 return dgoto(dp); 418 } 419 else { 420 free(dp); 421 if (chdir(short2str(cp)) >= 0) 422 return dgoto(cp); 423 serrno = errno; 424 } 425 426 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 427 && (c = adrof(STRcdpath))) { 428 Char **cdp; 429 Char *p; 430 Char buf[PATH_MAX]; 431 432 for (cdp = c->vec; *cdp; cdp++) { 433 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 434 continue; 435 dp[-1] = '/'; 436 for (p = cp; (*dp++ = *p++) != '\0';) 437 continue; 438 if (chdir(short2str(buf)) >= 0) { 439 printd = 1; 440 free(cp); 441 cp = Strsave(buf); 442 return dgoto(cp); 443 } 444 } 445 } 446 dp = value(cp); 447 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 448 free(cp); 449 cp = Strsave(dp); 450 printd = 1; 451 return dgoto(cp); 452 } 453 (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); 454 free(cp); 455 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 456 return (NULL); 457 } 458 459 460 /* 461 * dopushd - push new directory onto directory stack. 462 * with no arguments exchange top and second. 463 * with numeric argument (+n) bring it to top. 464 */ 465 void 466 dopushd(Char **v, struct command *t) 467 { 468 struct directory *dp; 469 470 skipargs(&v, " [<dir>|+<n>]"); 471 printd = 1; 472 if (*v == NULL) { 473 char *tmp; 474 475 if ((dp = dcwd->di_prev) == &dhead) 476 dp = dhead.di_prev; 477 if (dp == dcwd) 478 stderror(ERR_NAME | ERR_NODIR); 479 if (chdir(tmp = short2str(dp->di_name)) == -1) 480 stderror(ERR_SYSTEM, tmp, strerror(errno)); 481 dp->di_prev->di_next = dp->di_next; 482 dp->di_next->di_prev = dp->di_prev; 483 dp->di_next = dcwd->di_next; 484 dp->di_prev = dcwd; 485 dcwd->di_next->di_prev = dp; 486 dcwd->di_next = dp; 487 } 488 else if (v[1] != NULL) { 489 stderror(ERR_NAME | ERR_TOOMANY); 490 /* NOTREACHED */ 491 return; 492 } 493 else if ((dp = dfind(*v)) != NULL) { 494 char *tmp; 495 496 if (chdir(tmp = short2str(dp->di_name)) == -1) 497 stderror(ERR_SYSTEM, tmp, strerror(errno)); 498 } 499 else { 500 Char *ccp; 501 502 ccp = dfollow(*v); 503 dp = xcalloc(1, sizeof(struct directory)); 504 dp->di_name = ccp; 505 dp->di_count = 0; 506 dp->di_prev = dcwd; 507 dp->di_next = dcwd->di_next; 508 dcwd->di_next = dp; 509 dp->di_next->di_prev = dp; 510 } 511 dnewcwd(dp); 512 } 513 514 /* 515 * dfind - find a directory if specified by numeric (+n) argument 516 */ 517 static struct directory * 518 dfind(Char *cp) 519 { 520 struct directory *dp; 521 int i; 522 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 dopopd(Char **v, struct command *t) 548 { 549 struct directory *dp, *p = NULL; 550 551 skipargs(&v, " [+<n>]"); 552 printd = 1; 553 if (*v == NULL) 554 dp = dcwd; 555 else if (v[1] != NULL) { 556 stderror(ERR_NAME | ERR_TOOMANY); 557 /* NOTREACHED */ 558 return; 559 } 560 else if ((dp = dfind(*v)) == 0) 561 stderror(ERR_NAME | ERR_BADDIR); 562 if (dp->di_prev == &dhead && dp->di_next == &dhead) 563 stderror(ERR_NAME | ERR_EMPTY); 564 if (dp == dcwd) { 565 char *tmp; 566 567 if ((p = dp->di_prev) == &dhead) 568 p = dhead.di_prev; 569 if (chdir(tmp = short2str(p->di_name)) == -1) 570 stderror(ERR_SYSTEM, tmp, strerror(errno)); 571 } 572 dp->di_prev->di_next = dp->di_next; 573 dp->di_next->di_prev = dp->di_prev; 574 if (dp == dcwd) 575 dnewcwd(p); 576 else { 577 printdirs(); 578 } 579 dfree(dp); 580 } 581 582 /* 583 * dfree - free the directory (or keep it if it still has ref count) 584 */ 585 void 586 dfree(struct directory *dp) 587 { 588 589 if (dp->di_count != 0) { 590 dp->di_next = dp->di_prev = 0; 591 } 592 else { 593 free((char *) dp->di_name); 594 free(dp); 595 } 596 } 597 598 /* 599 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 600 * we are of course assuming that the file system is standardly 601 * constructed (always have ..'s, directories have links) 602 */ 603 Char * 604 dcanon(Char *cp, Char *p) 605 { 606 Char *sp; 607 Char *p1, *p2; /* general purpose */ 608 bool slash; 609 610 Char link[PATH_MAX]; 611 char tlink[PATH_MAX]; 612 int cc; 613 Char *newcp; 614 615 /* 616 * christos: if the path given does not start with a slash prepend cwd. If 617 * cwd does not start with a path or the result would be too long abort(). 618 */ 619 if (*cp != '/') { 620 Char tmpdir[PATH_MAX]; 621 622 p1 = value(STRcwd); 623 if (p1 == NULL || *p1 != '/') 624 abort(); 625 if (Strlen(p1) + Strlen(cp) + 1 >= PATH_MAX) 626 abort(); 627 (void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char)); 628 (void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char)); 629 (void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char)); 630 free(cp); 631 cp = p = Strsave(tmpdir); 632 } 633 634 while (*p) { /* for each component */ 635 sp = p; /* save slash address */ 636 while (*++p == '/') /* flush extra slashes */ 637 continue; 638 if (p != ++sp) 639 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 640 continue; 641 p = sp; /* save start of component */ 642 slash = 0; 643 while (*p) /* find next slash or end of path */ 644 if (*++p == '/') { 645 slash = 1; 646 *p = 0; 647 break; 648 } 649 650 if (*sp == '\0') /* if component is null */ 651 if (--sp == cp) /* if path is one char (i.e. /) */ 652 break; 653 else 654 *sp = '\0'; 655 else if (sp[0] == '.' && sp[1] == 0) { 656 if (slash) { 657 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 658 continue; 659 p = --sp; 660 } 661 else if (--sp != cp) 662 *sp = '\0'; 663 } 664 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 665 /* 666 * We have something like "yyy/xxx/..", where "yyy" can be null or 667 * a path starting at /, and "xxx" is a single component. Before 668 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 669 * symbolic link. 670 */ 671 *--sp = 0; /* form the pathname for readlink */ 672 if (sp != cp && !adrof(STRignore_symlinks) && 673 (cc = readlink(short2str(cp), tlink, 674 sizeof tlink-1)) >= 0) { 675 tlink[cc] = '\0'; 676 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 677 678 if (slash) 679 *p = '/'; 680 /* 681 * Point p to the '/' in "/..", and restore the '/'. 682 */ 683 *(p = sp) = '/'; 684 /* 685 * find length of p 686 */ 687 for (p1 = p; *p1++;) 688 continue; 689 if (*link != '/') { 690 /* 691 * Relative path, expand it between the "yyy/" and the 692 * "/..". First, back sp up to the character past "yyy/". 693 */ 694 while (*--sp != '/') 695 continue; 696 sp++; 697 *sp = 0; 698 /* 699 * New length is "yyy/" + link + "/.." and rest 700 */ 701 p1 = newcp = xreallocarray(NULL, 702 (sp - cp) + cc + (p1 - p), sizeof(Char)); 703 /* 704 * Copy new path into newcp 705 */ 706 for (p2 = cp; (*p1++ = *p2++) != '\0';) 707 continue; 708 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 709 continue; 710 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 711 continue; 712 /* 713 * Restart canonicalization at expanded "/xxx". 714 */ 715 p = sp - cp - 1 + newcp; 716 } 717 else { 718 /* 719 * New length is link + "/.." and rest 720 */ 721 p1 = newcp = xreallocarray(NULL, cc + (p1 - p), 722 sizeof(Char)); 723 /* 724 * Copy new path into newcp 725 */ 726 for (p2 = link; (*p1++ = *p2++) != '\0';) 727 continue; 728 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 729 continue; 730 /* 731 * Restart canonicalization at beginning 732 */ 733 p = newcp; 734 } 735 free(cp); 736 cp = newcp; 737 continue; /* canonicalize the link */ 738 } 739 *sp = '/'; 740 if (sp != cp) 741 while (*--sp != '/') 742 continue; 743 if (slash) { 744 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 745 continue; 746 p = sp; 747 } 748 else if (cp == sp) 749 *++sp = '\0'; 750 else 751 *sp = '\0'; 752 } 753 else { /* normal dir name (not . or .. or nothing) */ 754 755 if (sp != cp && adrof(STRchase_symlinks) && 756 !adrof(STRignore_symlinks) && 757 (cc = readlink(short2str(cp), tlink, 758 sizeof tlink-1)) >= 0) { 759 tlink[cc] = '\0'; 760 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 761 762 /* 763 * restore the '/'. 764 */ 765 if (slash) 766 *p = '/'; 767 768 /* 769 * point sp to p (rather than backing up). 770 */ 771 sp = p; 772 773 /* 774 * find length of p 775 */ 776 for (p1 = p; *p1++;) 777 continue; 778 if (*link != '/') { 779 /* 780 * Relative path, expand it between the "yyy/" and the 781 * remainder. First, back sp up to the character past 782 * "yyy/". 783 */ 784 while (*--sp != '/') 785 continue; 786 sp++; 787 *sp = 0; 788 /* 789 * New length is "yyy/" + link + "/.." and rest 790 */ 791 p1 = newcp = xreallocarray(NULL, 792 (sp - cp) + cc + (p1 - p), sizeof(Char)); 793 /* 794 * Copy new path into newcp 795 */ 796 for (p2 = cp; (*p1++ = *p2++) != '\0';) 797 continue; 798 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 799 continue; 800 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 801 continue; 802 /* 803 * Restart canonicalization at expanded "/xxx". 804 */ 805 p = sp - cp - 1 + newcp; 806 } 807 else { 808 /* 809 * New length is link + the rest 810 */ 811 p1 = newcp = xreallocarray(NULL, cc + (p1 - p), sizeof(Char)); 812 /* 813 * Copy new path into newcp 814 */ 815 for (p2 = link; (*p1++ = *p2++) != '\0';) 816 continue; 817 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 818 continue; 819 /* 820 * Restart canonicalization at beginning 821 */ 822 p = newcp; 823 } 824 free(cp); 825 cp = newcp; 826 continue; /* canonicalize the link */ 827 } 828 if (slash) 829 *p = '/'; 830 } 831 } 832 833 /* 834 * fix home... 835 */ 836 p1 = value(STRhome); 837 cc = Strlen(p1); 838 /* 839 * See if we're not in a subdir of STRhome 840 */ 841 if (p1 && *p1 == '/' && 842 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 843 static ino_t home_ino = -1; 844 static dev_t home_dev = -1; 845 static Char *home_ptr = NULL; 846 struct stat statbuf; 847 848 /* 849 * Get dev and ino of STRhome 850 */ 851 if (home_ptr != p1 && 852 stat(short2str(p1), &statbuf) != -1) { 853 home_dev = statbuf.st_dev; 854 home_ino = statbuf.st_ino; 855 home_ptr = p1; 856 } 857 /* 858 * Start comparing dev & ino backwards 859 */ 860 Strlcpy(link, cp, sizeof link/sizeof(Char)); 861 p2 = link; 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, '/')) != NULL) 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 newcp = Strspl(p1, cp + Strlen(p2)); 879 free(cp); 880 cp = newcp; 881 } 882 } 883 return cp; 884 } 885 886 887 /* 888 * dnewcwd - make a new directory in the loop the current one 889 */ 890 static void 891 dnewcwd(struct directory *dp) 892 { 893 dcwd = dp; 894 dset(dcwd->di_name); 895 if (printd && !(adrof(STRpushdsilent))) 896 printdirs(); 897 } 898