1 /* $OpenBSD: dir.c,v 1.25 2020/08/30 22:23:47 mortimer 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 /*ARGSUSED*/ 178 dodirs(Char **v, struct command *t) 179 { 180 skipargs(&v, ""); 181 182 if (*v != NULL) 183 stderror(ERR_DIRUS, "dirs", ""); 184 printdirs(); 185 } 186 187 static void 188 printdirs(void) 189 { 190 struct directory *dp; 191 Char *s, *hp = value(STRhome); 192 int idx, len, cur; 193 194 if (*hp == '\0') 195 hp = NULL; 196 dp = dcwd; 197 idx = 0; 198 cur = 0; 199 do { 200 if (dp == &dhead) 201 continue; 202 if (dirflag & DIR_VERT) { 203 (void) fprintf(cshout, "%d\t", idx++); 204 cur = 0; 205 } 206 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 207 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 208 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 209 len = Strlen(s = (dp->di_name + len)) + 2; 210 else 211 len = Strlen(s = dp->di_name) + 1; 212 213 cur += len; 214 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 215 (void) fprintf(cshout, "\n"); 216 cur = len; 217 } 218 (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", 219 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 220 } while ((dp = dp->di_prev) != dcwd); 221 if (!(dirflag & DIR_VERT)) 222 (void) fprintf(cshout, "\n"); 223 } 224 225 void 226 dtildepr(Char *home, Char *dir) 227 { 228 229 if (!eq(home, STRslash) && prefix(home, dir)) 230 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 231 else 232 (void) fprintf(cshout, "%s", vis_str(dir)); 233 } 234 235 void 236 dtilde(void) 237 { 238 struct directory *d = dcwd; 239 240 do { 241 if (d == &dhead) 242 continue; 243 d->di_name = dcanon(d->di_name, STRNULL); 244 } while ((d = d->di_prev) != dcwd); 245 246 dset(dcwd->di_name); 247 } 248 249 250 /* dnormalize(): 251 * If the name starts with . or .. then we might need to normalize 252 * it depending on the symbolic link flags 253 */ 254 Char * 255 dnormalize(Char *cp) 256 { 257 258 #define UC (unsigned char) 259 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 260 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 261 262 if ((unsigned char) cp[0] == '/') 263 return (Strsave(cp)); 264 265 if (adrof(STRignore_symlinks)) { 266 int dotdot = 0; 267 Char *dp, *cwd; 268 size_t len; 269 270 len = (size_t) (Strlen(dcwd->di_name) + 3); 271 cwd = xreallocarray(NULL, len, sizeof(Char)); 272 (void) Strlcpy(cwd, dcwd->di_name, len); 273 274 /* 275 * Ignore . and count ..'s 276 */ 277 while (*cp) { 278 if (ISDOT(cp)) { 279 if (*++cp) 280 cp++; 281 } 282 else if (ISDOTDOT(cp)) { 283 dotdot++; 284 cp += 2; 285 if (*cp) 286 cp++; 287 } 288 else 289 break; 290 } 291 while (dotdot > 0) 292 if ((dp = Strrchr(cwd, '/'))) { 293 *dp = '\0'; 294 dotdot--; 295 } 296 else 297 break; 298 299 if (*cp) { 300 cwd[dotdot = Strlen(cwd)] = '/'; 301 cwd[dotdot + 1] = '\0'; 302 dp = Strspl(cwd, cp); 303 free(cwd); 304 return dp; 305 } 306 else { 307 if (!*cwd) { 308 cwd[0] = '/'; 309 cwd[1] = '\0'; 310 } 311 return cwd; 312 } 313 } 314 return Strsave(cp); 315 } 316 317 /* 318 * dochngd - implement chdir command. 319 */ 320 void 321 /*ARGSUSED*/ 322 dochngd(Char **v, struct command *t) 323 { 324 Char *cp; 325 struct directory *dp; 326 327 skipargs(&v, " [<dir>]"); 328 printd = 0; 329 if (*v == NULL) { 330 if ((cp = value(STRhome)) == NULL || *cp == 0) 331 stderror(ERR_NAME | ERR_NOHOMEDIR); 332 if (chdir(short2str(cp)) == -1) 333 stderror(ERR_NAME | ERR_CANTCHANGE); 334 cp = Strsave(cp); 335 } 336 else if (v[1] != NULL) { 337 stderror(ERR_NAME | ERR_TOOMANY); 338 /* NOTREACHED */ 339 return; 340 } 341 else if ((dp = dfind(*v)) != 0) { 342 char *tmp; 343 344 printd = 1; 345 if (chdir(tmp = short2str(dp->di_name)) == -1) 346 stderror(ERR_SYSTEM, tmp, strerror(errno)); 347 dcwd->di_prev->di_next = dcwd->di_next; 348 dcwd->di_next->di_prev = dcwd->di_prev; 349 dfree(dcwd); 350 dnewcwd(dp); 351 return; 352 } 353 else 354 cp = dfollow(*v); 355 dp = xcalloc(1, sizeof(struct directory)); 356 dp->di_name = cp; 357 dp->di_count = 0; 358 dp->di_next = dcwd->di_next; 359 dp->di_prev = dcwd->di_prev; 360 dp->di_prev->di_next = dp; 361 dp->di_next->di_prev = dp; 362 dfree(dcwd); 363 dnewcwd(dp); 364 } 365 366 static Char * 367 dgoto(Char *cp) 368 { 369 Char *dp; 370 371 if (*cp != '/') { 372 Char *p, *q; 373 int cwdlen; 374 375 for (p = dcwd->di_name; *p++;) 376 continue; 377 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 378 cwdlen = 0; 379 for (p = cp; *p++;) 380 continue; 381 dp = xreallocarray(NULL, (cwdlen + (p - cp) + 1), sizeof(Char)); 382 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 383 continue; 384 if (cwdlen) 385 p[-1] = '/'; 386 else 387 p--; /* don't add a / after root */ 388 for (q = cp; (*p++ = *q++) != '\0';) 389 continue; 390 free(cp); 391 cp = dp; 392 dp += cwdlen; 393 } 394 else 395 dp = cp; 396 397 cp = dcanon(cp, dp); 398 return cp; 399 } 400 401 /* 402 * dfollow - change to arg directory; fall back on cdpath if not valid 403 */ 404 static Char * 405 dfollow(Char *cp) 406 { 407 Char *dp; 408 struct varent *c; 409 char ebuf[PATH_MAX]; 410 int serrno; 411 412 cp = globone(cp, G_ERROR); 413 /* 414 * if we are ignoring symlinks, try to fix relatives now. 415 */ 416 dp = dnormalize(cp); 417 if (chdir(short2str(dp)) >= 0) { 418 free(cp); 419 return dgoto(dp); 420 } 421 else { 422 free(dp); 423 if (chdir(short2str(cp)) >= 0) 424 return dgoto(cp); 425 serrno = errno; 426 } 427 428 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 429 && (c = adrof(STRcdpath))) { 430 Char **cdp; 431 Char *p; 432 Char buf[PATH_MAX]; 433 434 for (cdp = c->vec; *cdp; cdp++) { 435 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 436 continue; 437 dp[-1] = '/'; 438 for (p = cp; (*dp++ = *p++) != '\0';) 439 continue; 440 if (chdir(short2str(buf)) >= 0) { 441 printd = 1; 442 free(cp); 443 cp = Strsave(buf); 444 return dgoto(cp); 445 } 446 } 447 } 448 dp = value(cp); 449 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 450 free(cp); 451 cp = Strsave(dp); 452 printd = 1; 453 return dgoto(cp); 454 } 455 (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); 456 free(cp); 457 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 458 return (NULL); 459 } 460 461 462 /* 463 * dopushd - push new directory onto directory stack. 464 * with no arguments exchange top and second. 465 * with numeric argument (+n) bring it to top. 466 */ 467 void 468 /*ARGSUSED*/ 469 dopushd(Char **v, struct command *t) 470 { 471 struct directory *dp; 472 473 skipargs(&v, " [<dir>|+<n>]"); 474 printd = 1; 475 if (*v == NULL) { 476 char *tmp; 477 478 if ((dp = dcwd->di_prev) == &dhead) 479 dp = dhead.di_prev; 480 if (dp == dcwd) 481 stderror(ERR_NAME | ERR_NODIR); 482 if (chdir(tmp = short2str(dp->di_name)) == -1) 483 stderror(ERR_SYSTEM, tmp, strerror(errno)); 484 dp->di_prev->di_next = dp->di_next; 485 dp->di_next->di_prev = dp->di_prev; 486 dp->di_next = dcwd->di_next; 487 dp->di_prev = dcwd; 488 dcwd->di_next->di_prev = dp; 489 dcwd->di_next = dp; 490 } 491 else if (v[1] != NULL) { 492 stderror(ERR_NAME | ERR_TOOMANY); 493 /* NOTREACHED */ 494 return; 495 } 496 else if ((dp = dfind(*v)) != NULL) { 497 char *tmp; 498 499 if (chdir(tmp = short2str(dp->di_name)) == -1) 500 stderror(ERR_SYSTEM, tmp, strerror(errno)); 501 } 502 else { 503 Char *ccp; 504 505 ccp = dfollow(*v); 506 dp = xcalloc(1, sizeof(struct directory)); 507 dp->di_name = ccp; 508 dp->di_count = 0; 509 dp->di_prev = dcwd; 510 dp->di_next = dcwd->di_next; 511 dcwd->di_next = dp; 512 dp->di_next->di_prev = dp; 513 } 514 dnewcwd(dp); 515 } 516 517 /* 518 * dfind - find a directory if specified by numeric (+n) argument 519 */ 520 static struct directory * 521 dfind(Char *cp) 522 { 523 struct directory *dp; 524 int i; 525 Char *ep; 526 527 if (*cp++ != '+') 528 return (0); 529 for (ep = cp; Isdigit(*ep); ep++) 530 continue; 531 if (*ep) 532 return (0); 533 i = getn(cp); 534 if (i <= 0) 535 return (0); 536 for (dp = dcwd; i != 0; i--) { 537 if ((dp = dp->di_prev) == &dhead) 538 dp = dp->di_prev; 539 if (dp == dcwd) 540 stderror(ERR_NAME | ERR_DEEP); 541 } 542 return (dp); 543 } 544 545 /* 546 * dopopd - pop a directory out of the directory stack 547 * with a numeric argument just discard it. 548 */ 549 void 550 /*ARGSUSED*/ 551 dopopd(Char **v, struct command *t) 552 { 553 struct directory *dp, *p = NULL; 554 555 skipargs(&v, " [+<n>]"); 556 printd = 1; 557 if (*v == NULL) 558 dp = dcwd; 559 else if (v[1] != NULL) { 560 stderror(ERR_NAME | ERR_TOOMANY); 561 /* NOTREACHED */ 562 return; 563 } 564 else if ((dp = dfind(*v)) == 0) 565 stderror(ERR_NAME | ERR_BADDIR); 566 if (dp->di_prev == &dhead && dp->di_next == &dhead) 567 stderror(ERR_NAME | ERR_EMPTY); 568 if (dp == dcwd) { 569 char *tmp; 570 571 if ((p = dp->di_prev) == &dhead) 572 p = dhead.di_prev; 573 if (chdir(tmp = short2str(p->di_name)) == -1) 574 stderror(ERR_SYSTEM, tmp, strerror(errno)); 575 } 576 dp->di_prev->di_next = dp->di_next; 577 dp->di_next->di_prev = dp->di_prev; 578 if (dp == dcwd) 579 dnewcwd(p); 580 else { 581 printdirs(); 582 } 583 dfree(dp); 584 } 585 586 /* 587 * dfree - free the directory (or keep it if it still has ref count) 588 */ 589 void 590 dfree(struct directory *dp) 591 { 592 593 if (dp->di_count != 0) { 594 dp->di_next = dp->di_prev = 0; 595 } 596 else { 597 free((char *) dp->di_name); 598 free(dp); 599 } 600 } 601 602 /* 603 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 604 * we are of course assuming that the file system is standardly 605 * constructed (always have ..'s, directories have links) 606 */ 607 Char * 608 dcanon(Char *cp, Char *p) 609 { 610 Char *sp; 611 Char *p1, *p2; /* general purpose */ 612 bool slash; 613 614 Char link[PATH_MAX]; 615 char tlink[PATH_MAX]; 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[PATH_MAX]; 625 626 p1 = value(STRcwd); 627 if (p1 == NULL || *p1 != '/') 628 abort(); 629 if (Strlen(p1) + Strlen(cp) + 1 >= PATH_MAX) 630 abort(); 631 (void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char)); 632 (void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char)); 633 (void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char)); 634 free(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-1)) >= 0) { 679 tlink[cc] = '\0'; 680 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 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 = xreallocarray(NULL, 706 (sp - cp) + cc + (p1 - p), sizeof(Char)); 707 /* 708 * Copy new path into newcp 709 */ 710 for (p2 = cp; (*p1++ = *p2++) != '\0';) 711 continue; 712 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 713 continue; 714 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 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 = xreallocarray(NULL, cc + (p1 - p), 726 sizeof(Char)); 727 /* 728 * Copy new path into newcp 729 */ 730 for (p2 = link; (*p1++ = *p2++) != '\0';) 731 continue; 732 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 733 continue; 734 /* 735 * Restart canonicalization at beginning 736 */ 737 p = newcp; 738 } 739 free(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++) != '\0';) 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-1)) >= 0) { 763 tlink[cc] = '\0'; 764 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 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 = xreallocarray(NULL, 796 (sp - cp) + cc + (p1 - p), sizeof(Char)); 797 /* 798 * Copy new path into newcp 799 */ 800 for (p2 = cp; (*p1++ = *p2++) != '\0';) 801 continue; 802 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 803 continue; 804 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 805 continue; 806 /* 807 * Restart canonicalization at expanded "/xxx". 808 */ 809 p = sp - cp - 1 + newcp; 810 } 811 else { 812 /* 813 * New length is link + the rest 814 */ 815 p1 = newcp = xreallocarray(NULL, cc + (p1 - p), sizeof(Char)); 816 /* 817 * Copy new path into newcp 818 */ 819 for (p2 = link; (*p1++ = *p2++) != '\0';) 820 continue; 821 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 822 continue; 823 /* 824 * Restart canonicalization at beginning 825 */ 826 p = newcp; 827 } 828 free(cp); 829 cp = newcp; 830 continue; /* canonicalize the link */ 831 } 832 if (slash) 833 *p = '/'; 834 } 835 } 836 837 /* 838 * fix home... 839 */ 840 p1 = value(STRhome); 841 cc = Strlen(p1); 842 /* 843 * See if we're not in a subdir of STRhome 844 */ 845 if (p1 && *p1 == '/' && 846 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 847 static ino_t home_ino = -1; 848 static dev_t home_dev = -1; 849 static Char *home_ptr = NULL; 850 struct stat statbuf; 851 852 /* 853 * Get dev and ino of STRhome 854 */ 855 if (home_ptr != p1 && 856 stat(short2str(p1), &statbuf) != -1) { 857 home_dev = statbuf.st_dev; 858 home_ino = statbuf.st_ino; 859 home_ptr = p1; 860 } 861 /* 862 * Start comparing dev & ino backwards 863 */ 864 Strlcpy(link, cp, sizeof link/sizeof(Char)); 865 p2 = link; 866 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 867 if (statbuf.st_dev == home_dev && 868 statbuf.st_ino == home_ino) { 869 sp = (Char *) - 1; 870 break; 871 } 872 if ((sp = Strrchr(p2, '/')) != NULL) 873 *sp = '\0'; 874 } 875 /* 876 * See if we found it 877 */ 878 if (*p2 && sp == (Char *) -1) { 879 /* 880 * Use STRhome to make '~' work 881 */ 882 newcp = Strspl(p1, cp + Strlen(p2)); 883 free(cp); 884 cp = newcp; 885 } 886 } 887 return cp; 888 } 889 890 891 /* 892 * dnewcwd - make a new directory in the loop the current one 893 */ 894 static void 895 dnewcwd(struct directory *dp) 896 { 897 dcwd = dp; 898 dset(dcwd->di_name); 899 if (printd && !(adrof(STRpushdsilent))) 900 printdirs(); 901 } 902