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