1 /* $OpenBSD: dir.c,v 1.20 2015/02/08 06:09:50 tedu 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 int printd; /* force name to be printed */ 57 58 static int dirflag = 0; 59 60 /* 61 * dinit - initialize current working directory 62 */ 63 void 64 dinit(Char *hp) 65 { 66 char *tcp; 67 Char *cp; 68 struct directory *dp; 69 char path[PATH_MAX]; 70 static const char emsg[] = "csh: Trying to start from \"%s\"\n"; 71 72 /* Don't believe the login shell home, because it may be a symlink */ 73 tcp = getcwd(path, PATH_MAX); 74 if (tcp == NULL || *tcp == '\0') { 75 (void) fprintf(csherr, "csh: %s\n", strerror(errno)); 76 if (hp && *hp) { 77 tcp = short2str(hp); 78 if (chdir(tcp) == -1) 79 cp = NULL; 80 else 81 cp = hp; 82 (void) fprintf(csherr, emsg, vis_str(hp)); 83 } 84 else 85 cp = NULL; 86 if (cp == NULL) { 87 (void) fprintf(csherr, emsg, "/"); 88 if (chdir("/") == -1) 89 /* I am not even try to print an error message! */ 90 xexit(1); 91 cp = SAVE("/"); 92 } 93 } 94 else { 95 struct stat swd, shp; 96 97 /* 98 * See if $HOME is the working directory we got and use that 99 */ 100 if (hp && *hp && 101 stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 102 swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) 103 cp = hp; 104 else { 105 char *cwd; 106 107 /* 108 * use PWD if we have it (for subshells) 109 */ 110 if ((cwd = getenv("PWD")) != NULL) { 111 if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && 112 swd.st_ino == shp.st_ino) 113 tcp = cwd; 114 } 115 cp = dcanon(SAVE(tcp), STRNULL); 116 } 117 } 118 119 dp = xcalloc(1, sizeof(struct directory)); 120 dp->di_name = Strsave(cp); 121 dp->di_count = 0; 122 dhead.di_next = dhead.di_prev = dp; 123 dp->di_next = dp->di_prev = &dhead; 124 printd = 0; 125 dnewcwd(dp); 126 } 127 128 static void 129 dset(Char *dp) 130 { 131 /* 132 * Don't call set() directly cause if the directory contains ` or 133 * other junk characters glob will fail. 134 */ 135 Char **vec = xreallocarray(NULL, 2, sizeof(Char **)); 136 137 vec[0] = Strsave(dp); 138 vec[1] = 0; 139 setq(STRcwd, vec, &shvhed); 140 Setenv(STRPWD, dp); 141 } 142 143 #define DIR_LONG 1 144 #define DIR_VERT 2 145 #define DIR_LINE 4 146 147 static void 148 skipargs(Char ***v, char *str) 149 { 150 Char **n = *v, *s; 151 152 dirflag = 0; 153 for (n++; *n != NULL && (*n)[0] == '-'; n++) 154 for (s = &((*n)[1]); *s; s++) 155 switch (*s) { 156 case 'l': 157 dirflag |= DIR_LONG; 158 break; 159 case 'v': 160 dirflag |= DIR_VERT; 161 break; 162 case 'n': 163 dirflag |= DIR_LINE; 164 break; 165 default: 166 stderror(ERR_DIRUS, vis_str(**v), str); 167 break; 168 } 169 *v = n; 170 } 171 172 /* 173 * dodirs - list all directories in directory loop 174 */ 175 void 176 /*ARGSUSED*/ 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 xfree(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 /*ARGSUSED*/ 321 dochngd(Char **v, struct command *t) 322 { 323 Char *cp; 324 struct directory *dp; 325 326 skipargs(&v, " [<dir>]"); 327 printd = 0; 328 if (*v == NULL) { 329 if ((cp = value(STRhome)) == NULL || *cp == 0) 330 stderror(ERR_NAME | ERR_NOHOMEDIR); 331 if (chdir(short2str(cp)) < 0) 332 stderror(ERR_NAME | ERR_CANTCHANGE); 333 cp = Strsave(cp); 334 } 335 else if (v[1] != NULL) { 336 stderror(ERR_NAME | ERR_TOOMANY); 337 /* NOTREACHED */ 338 return; 339 } 340 else if ((dp = dfind(*v)) != 0) { 341 char *tmp; 342 343 printd = 1; 344 if (chdir(tmp = short2str(dp->di_name)) < 0) 345 stderror(ERR_SYSTEM, tmp, strerror(errno)); 346 dcwd->di_prev->di_next = dcwd->di_next; 347 dcwd->di_next->di_prev = dcwd->di_prev; 348 dfree(dcwd); 349 dnewcwd(dp); 350 return; 351 } 352 else 353 cp = dfollow(*v); 354 dp = xcalloc(1, sizeof(struct directory)); 355 dp->di_name = cp; 356 dp->di_count = 0; 357 dp->di_next = dcwd->di_next; 358 dp->di_prev = dcwd->di_prev; 359 dp->di_prev->di_next = dp; 360 dp->di_next->di_prev = dp; 361 dfree(dcwd); 362 dnewcwd(dp); 363 } 364 365 static Char * 366 dgoto(Char *cp) 367 { 368 Char *dp; 369 370 if (*cp != '/') { 371 Char *p, *q; 372 int cwdlen; 373 374 for (p = dcwd->di_name; *p++;) 375 continue; 376 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 377 cwdlen = 0; 378 for (p = cp; *p++;) 379 continue; 380 dp = xreallocarray(NULL, (cwdlen + (p - cp) + 1), sizeof(Char)); 381 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 382 continue; 383 if (cwdlen) 384 p[-1] = '/'; 385 else 386 p--; /* don't add a / after root */ 387 for (q = cp; (*p++ = *q++) != '\0';) 388 continue; 389 xfree(cp); 390 cp = dp; 391 dp += cwdlen; 392 } 393 else 394 dp = cp; 395 396 cp = dcanon(cp, dp); 397 return cp; 398 } 399 400 /* 401 * dfollow - change to arg directory; fall back on cdpath if not valid 402 */ 403 static Char * 404 dfollow(Char *cp) 405 { 406 Char *dp; 407 struct varent *c; 408 char ebuf[PATH_MAX]; 409 int serrno; 410 411 cp = globone(cp, G_ERROR); 412 /* 413 * if we are ignoring symlinks, try to fix relatives now. 414 */ 415 dp = dnormalize(cp); 416 if (chdir(short2str(dp)) >= 0) { 417 xfree(cp); 418 return dgoto(dp); 419 } 420 else { 421 xfree(dp); 422 if (chdir(short2str(cp)) >= 0) 423 return dgoto(cp); 424 serrno = errno; 425 } 426 427 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 428 && (c = adrof(STRcdpath))) { 429 Char **cdp; 430 Char *p; 431 Char buf[PATH_MAX]; 432 433 for (cdp = c->vec; *cdp; cdp++) { 434 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 435 continue; 436 dp[-1] = '/'; 437 for (p = cp; (*dp++ = *p++) != '\0';) 438 continue; 439 if (chdir(short2str(buf)) >= 0) { 440 printd = 1; 441 xfree(cp); 442 cp = Strsave(buf); 443 return dgoto(cp); 444 } 445 } 446 } 447 dp = value(cp); 448 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 449 xfree(cp); 450 cp = Strsave(dp); 451 printd = 1; 452 return dgoto(cp); 453 } 454 (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); 455 xfree(cp); 456 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 457 return (NULL); 458 } 459 460 461 /* 462 * dopushd - push new directory onto directory stack. 463 * with no arguments exchange top and second. 464 * with numeric argument (+n) bring it to top. 465 */ 466 void 467 /*ARGSUSED*/ 468 dopushd(Char **v, struct command *t) 469 { 470 struct directory *dp; 471 472 skipargs(&v, " [<dir>|+<n>]"); 473 printd = 1; 474 if (*v == NULL) { 475 char *tmp; 476 477 if ((dp = dcwd->di_prev) == &dhead) 478 dp = dhead.di_prev; 479 if (dp == dcwd) 480 stderror(ERR_NAME | ERR_NODIR); 481 if (chdir(tmp = short2str(dp->di_name)) < 0) 482 stderror(ERR_SYSTEM, tmp, strerror(errno)); 483 dp->di_prev->di_next = dp->di_next; 484 dp->di_next->di_prev = dp->di_prev; 485 dp->di_next = dcwd->di_next; 486 dp->di_prev = dcwd; 487 dcwd->di_next->di_prev = dp; 488 dcwd->di_next = dp; 489 } 490 else if (v[1] != NULL) { 491 stderror(ERR_NAME | ERR_TOOMANY); 492 /* NOTREACHED */ 493 return; 494 } 495 else if ((dp = dfind(*v)) != NULL) { 496 char *tmp; 497 498 if (chdir(tmp = short2str(dp->di_name)) < 0) 499 stderror(ERR_SYSTEM, tmp, strerror(errno)); 500 } 501 else { 502 Char *ccp; 503 504 ccp = dfollow(*v); 505 dp = xcalloc(1, sizeof(struct directory)); 506 dp->di_name = ccp; 507 dp->di_count = 0; 508 dp->di_prev = dcwd; 509 dp->di_next = dcwd->di_next; 510 dcwd->di_next = dp; 511 dp->di_next->di_prev = dp; 512 } 513 dnewcwd(dp); 514 } 515 516 /* 517 * dfind - find a directory if specified by numeric (+n) argument 518 */ 519 static struct directory * 520 dfind(Char *cp) 521 { 522 struct directory *dp; 523 int i; 524 Char *ep; 525 526 if (*cp++ != '+') 527 return (0); 528 for (ep = cp; Isdigit(*ep); ep++) 529 continue; 530 if (*ep) 531 return (0); 532 i = getn(cp); 533 if (i <= 0) 534 return (0); 535 for (dp = dcwd; i != 0; i--) { 536 if ((dp = dp->di_prev) == &dhead) 537 dp = dp->di_prev; 538 if (dp == dcwd) 539 stderror(ERR_NAME | ERR_DEEP); 540 } 541 return (dp); 542 } 543 544 /* 545 * dopopd - pop a directory out of the directory stack 546 * with a numeric argument just discard it. 547 */ 548 void 549 /*ARGSUSED*/ 550 dopopd(Char **v, struct command *t) 551 { 552 struct directory *dp, *p = NULL; 553 554 skipargs(&v, " [+<n>]"); 555 printd = 1; 556 if (*v == NULL) 557 dp = dcwd; 558 else if (v[1] != NULL) { 559 stderror(ERR_NAME | ERR_TOOMANY); 560 /* NOTREACHED */ 561 return; 562 } 563 else if ((dp = dfind(*v)) == 0) 564 stderror(ERR_NAME | ERR_BADDIR); 565 if (dp->di_prev == &dhead && dp->di_next == &dhead) 566 stderror(ERR_NAME | ERR_EMPTY); 567 if (dp == dcwd) { 568 char *tmp; 569 570 if ((p = dp->di_prev) == &dhead) 571 p = dhead.di_prev; 572 if (chdir(tmp = short2str(p->di_name)) < 0) 573 stderror(ERR_SYSTEM, tmp, strerror(errno)); 574 } 575 dp->di_prev->di_next = dp->di_next; 576 dp->di_next->di_prev = dp->di_prev; 577 if (dp == dcwd) 578 dnewcwd(p); 579 else { 580 printdirs(); 581 } 582 dfree(dp); 583 } 584 585 /* 586 * dfree - free the directory (or keep it if it still has ref count) 587 */ 588 void 589 dfree(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(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(Char *cp, Char *p) 608 { 609 Char *sp; 610 Char *p1, *p2; /* general purpose */ 611 bool slash; 612 613 Char link[PATH_MAX]; 614 char tlink[PATH_MAX]; 615 int cc; 616 Char *newcp; 617 618 /* 619 * christos: if the path given does not start with a slash prepend cwd. If 620 * cwd does not start with a path or the result would be too long abort(). 621 */ 622 if (*cp != '/') { 623 Char tmpdir[PATH_MAX]; 624 625 p1 = value(STRcwd); 626 if (p1 == NULL || *p1 != '/') 627 abort(); 628 if (Strlen(p1) + Strlen(cp) + 1 >= PATH_MAX) 629 abort(); 630 (void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char)); 631 (void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char)); 632 (void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char)); 633 xfree(cp); 634 cp = p = Strsave(tmpdir); 635 } 636 637 while (*p) { /* for each component */ 638 sp = p; /* save slash address */ 639 while (*++p == '/') /* flush extra slashes */ 640 continue; 641 if (p != ++sp) 642 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 643 continue; 644 p = sp; /* save start of component */ 645 slash = 0; 646 while (*p) /* find next slash or end of path */ 647 if (*++p == '/') { 648 slash = 1; 649 *p = 0; 650 break; 651 } 652 653 if (*sp == '\0') /* if component is null */ 654 if (--sp == cp) /* if path is one char (i.e. /) */ 655 break; 656 else 657 *sp = '\0'; 658 else if (sp[0] == '.' && sp[1] == 0) { 659 if (slash) { 660 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 661 continue; 662 p = --sp; 663 } 664 else if (--sp != cp) 665 *sp = '\0'; 666 } 667 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 668 /* 669 * We have something like "yyy/xxx/..", where "yyy" can be null or 670 * a path starting at /, and "xxx" is a single component. Before 671 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 672 * symbolic link. 673 */ 674 *--sp = 0; /* form the pathname for readlink */ 675 if (sp != cp && !adrof(STRignore_symlinks) && 676 (cc = readlink(short2str(cp), tlink, 677 sizeof tlink-1)) >= 0) { 678 tlink[cc] = '\0'; 679 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 680 681 if (slash) 682 *p = '/'; 683 /* 684 * Point p to the '/' in "/..", and restore the '/'. 685 */ 686 *(p = sp) = '/'; 687 /* 688 * find length of p 689 */ 690 for (p1 = p; *p1++;) 691 continue; 692 if (*link != '/') { 693 /* 694 * Relative path, expand it between the "yyy/" and the 695 * "/..". First, back sp up to the character past "yyy/". 696 */ 697 while (*--sp != '/') 698 continue; 699 sp++; 700 *sp = 0; 701 /* 702 * New length is "yyy/" + link + "/.." and rest 703 */ 704 p1 = newcp = xreallocarray(NULL, 705 (sp - cp) + cc + (p1 - p), sizeof(Char)); 706 /* 707 * Copy new path into newcp 708 */ 709 for (p2 = cp; (*p1++ = *p2++) != '\0';) 710 continue; 711 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 712 continue; 713 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 714 continue; 715 /* 716 * Restart canonicalization at expanded "/xxx". 717 */ 718 p = sp - cp - 1 + newcp; 719 } 720 else { 721 /* 722 * New length is link + "/.." and rest 723 */ 724 p1 = newcp = xreallocarray(NULL, cc + (p1 - p), 725 sizeof(Char)); 726 /* 727 * Copy new path into newcp 728 */ 729 for (p2 = link; (*p1++ = *p2++) != '\0';) 730 continue; 731 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 732 continue; 733 /* 734 * Restart canonicalization at beginning 735 */ 736 p = newcp; 737 } 738 xfree(cp); 739 cp = newcp; 740 continue; /* canonicalize the link */ 741 } 742 *sp = '/'; 743 if (sp != cp) 744 while (*--sp != '/') 745 continue; 746 if (slash) { 747 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 748 continue; 749 p = sp; 750 } 751 else if (cp == sp) 752 *++sp = '\0'; 753 else 754 *sp = '\0'; 755 } 756 else { /* normal dir name (not . or .. or nothing) */ 757 758 if (sp != cp && adrof(STRchase_symlinks) && 759 !adrof(STRignore_symlinks) && 760 (cc = readlink(short2str(cp), tlink, 761 sizeof tlink-1)) >= 0) { 762 tlink[cc] = '\0'; 763 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 764 765 /* 766 * restore the '/'. 767 */ 768 if (slash) 769 *p = '/'; 770 771 /* 772 * point sp to p (rather than backing up). 773 */ 774 sp = p; 775 776 /* 777 * find length of p 778 */ 779 for (p1 = p; *p1++;) 780 continue; 781 if (*link != '/') { 782 /* 783 * Relative path, expand it between the "yyy/" and the 784 * remainder. First, back sp up to the character past 785 * "yyy/". 786 */ 787 while (*--sp != '/') 788 continue; 789 sp++; 790 *sp = 0; 791 /* 792 * New length is "yyy/" + link + "/.." and rest 793 */ 794 p1 = newcp = xreallocarray(NULL, 795 (sp - cp) + cc + (p1 - p), sizeof(Char)); 796 /* 797 * Copy new path into newcp 798 */ 799 for (p2 = cp; (*p1++ = *p2++) != '\0';) 800 continue; 801 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 802 continue; 803 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 804 continue; 805 /* 806 * Restart canonicalization at expanded "/xxx". 807 */ 808 p = sp - cp - 1 + newcp; 809 } 810 else { 811 /* 812 * New length is link + the rest 813 */ 814 p1 = newcp = xreallocarray(NULL, cc + (p1 - p), sizeof(Char)); 815 /* 816 * Copy new path into newcp 817 */ 818 for (p2 = link; (*p1++ = *p2++) != '\0';) 819 continue; 820 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 821 continue; 822 /* 823 * Restart canonicalization at beginning 824 */ 825 p = newcp; 826 } 827 xfree(cp); 828 cp = newcp; 829 continue; /* canonicalize the link */ 830 } 831 if (slash) 832 *p = '/'; 833 } 834 } 835 836 /* 837 * fix home... 838 */ 839 p1 = value(STRhome); 840 cc = Strlen(p1); 841 /* 842 * See if we're not in a subdir of STRhome 843 */ 844 if (p1 && *p1 == '/' && 845 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 846 static ino_t home_ino = -1; 847 static dev_t home_dev = -1; 848 static Char *home_ptr = NULL; 849 struct stat statbuf; 850 851 /* 852 * Get dev and ino of STRhome 853 */ 854 if (home_ptr != p1 && 855 stat(short2str(p1), &statbuf) != -1) { 856 home_dev = statbuf.st_dev; 857 home_ino = statbuf.st_ino; 858 home_ptr = p1; 859 } 860 /* 861 * Start comparing dev & ino backwards 862 */ 863 Strlcpy(link, cp, sizeof link/sizeof(Char)); 864 p2 = link; 865 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 866 if (statbuf.st_dev == home_dev && 867 statbuf.st_ino == home_ino) { 868 sp = (Char *) - 1; 869 break; 870 } 871 if ((sp = Strrchr(p2, '/')) != NULL) 872 *sp = '\0'; 873 } 874 /* 875 * See if we found it 876 */ 877 if (*p2 && sp == (Char *) -1) { 878 /* 879 * Use STRhome to make '~' work 880 */ 881 newcp = Strspl(p1, cp + Strlen(p2)); 882 xfree(cp); 883 cp = newcp; 884 } 885 } 886 return cp; 887 } 888 889 890 /* 891 * dnewcwd - make a new directory in the loop the current one 892 */ 893 static void 894 dnewcwd(struct directory *dp) 895 { 896 dcwd = dp; 897 dset(dcwd->di_name); 898 if (printd && !(adrof(STRpushdsilent))) 899 printdirs(); 900 } 901