1 /* $NetBSD: dir.c,v 1.35 2020/08/09 00:34:21 dholland 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.35 2020/08/09 00:34:21 dholland 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 = xmalloc((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 = xmalloc((size_t)(cwdlen + (size_t)(p - cp) + 1) * sizeof(Char)); 393 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 394 continue; 395 if (cwdlen) 396 p[-1] = '/'; 397 else 398 p--; /* don't add a / after root */ 399 for (q = cp; (*p++ = *q++) != '\0';) 400 continue; 401 free(cp); 402 cp = dp; 403 dp += cwdlen; 404 } 405 else 406 dp = cp; 407 408 cp = dcanon(cp, dp); 409 return cp; 410 } 411 412 /* 413 * dfollow - change to arg directory; fall back on cdpath if not valid 414 */ 415 static Char * 416 dfollow(Char *cp) 417 { 418 char ebuf[MAXPATHLEN]; 419 struct varent *c; 420 Char *dp; 421 int serrno; 422 423 cp = globone(cp, G_ERROR); 424 /* 425 * if we are ignoring symlinks, try to fix relatives now. 426 */ 427 dp = dnormalize(cp); 428 if (chdir(short2str(dp)) >= 0) { 429 free(cp); 430 return dgoto(dp); 431 } 432 else { 433 free(dp); 434 if (chdir(short2str(cp)) >= 0) 435 return dgoto(cp); 436 serrno = errno; 437 } 438 439 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 440 && (c = adrof(STRcdpath))) { 441 Char **cdp; 442 Char *p; 443 Char buf[MAXPATHLEN]; 444 445 for (cdp = c->vec; *cdp; cdp++) { 446 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 447 continue; 448 dp[-1] = '/'; 449 for (p = cp; (*dp++ = *p++) != '\0';) 450 continue; 451 if (chdir(short2str(buf)) >= 0) { 452 printd = 1; 453 free(cp); 454 cp = Strsave(buf); 455 return dgoto(cp); 456 } 457 } 458 } 459 dp = value(cp); 460 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 461 free(cp); 462 cp = Strsave(dp); 463 printd = 1; 464 return dgoto(cp); 465 } 466 (void)strcpy(ebuf, short2str(cp)); 467 free(cp); 468 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 469 /* NOTREACHED */ 470 } 471 472 /* 473 * dopushd - push new directory onto directory stack. 474 * with no arguments exchange top and second. 475 * with numeric argument (+n) bring it to top. 476 */ 477 void 478 /*ARGSUSED*/ 479 dopushd(Char **v, struct command *t) 480 { 481 struct directory *dp; 482 483 skipargs(&v, " [<dir>|+<n>]"); 484 printd = 1; 485 if (*v == NULL) { 486 char *tmp; 487 488 if ((dp = dcwd->di_prev) == &dhead) 489 dp = dhead.di_prev; 490 if (dp == dcwd) 491 stderror(ERR_NAME | ERR_NODIR); 492 if (chdir(tmp = short2str(dp->di_name)) < 0) 493 stderror(ERR_SYSTEM, tmp, strerror(errno)); 494 dp->di_prev->di_next = dp->di_next; 495 dp->di_next->di_prev = dp->di_prev; 496 dp->di_next = dcwd->di_next; 497 dp->di_prev = dcwd; 498 dcwd->di_next->di_prev = dp; 499 dcwd->di_next = dp; 500 } 501 else if (v[1] != NULL) 502 stderror(ERR_NAME | ERR_TOOMANY); 503 else if ((dp = dfind(*v)) != NULL) { 504 char *tmp; 505 506 if (chdir(tmp = short2str(dp->di_name)) < 0) 507 stderror(ERR_SYSTEM, tmp, strerror(errno)); 508 } 509 else { 510 Char *ccp; 511 512 ccp = dfollow(*v); 513 dp = xcalloc(1, sizeof(*dp)); 514 dp->di_name = ccp; 515 dp->di_count = 0; 516 dp->di_prev = dcwd; 517 dp->di_next = dcwd->di_next; 518 dcwd->di_next = dp; 519 dp->di_next->di_prev = dp; 520 } 521 dnewcwd(dp); 522 } 523 524 /* 525 * dfind - find a directory if specified by numeric (+n) argument 526 */ 527 static struct directory * 528 dfind(Char *cp) 529 { 530 struct directory *dp; 531 Char *ep; 532 int i; 533 534 if (*cp++ != '+') 535 return (0); 536 for (ep = cp; Isdigit(*ep); ep++) 537 continue; 538 if (*ep) 539 return (0); 540 i = getn(cp); 541 if (i <= 0) 542 return (0); 543 for (dp = dcwd; i != 0; i--) { 544 if ((dp = dp->di_prev) == &dhead) 545 dp = dp->di_prev; 546 if (dp == dcwd) 547 stderror(ERR_NAME | ERR_DEEP); 548 } 549 return (dp); 550 } 551 552 /* 553 * dopopd - pop a directory out of the directory stack 554 * with a numeric argument just discard it. 555 */ 556 void 557 /*ARGSUSED*/ 558 dopopd(Char **v, struct command *t) 559 { 560 struct directory *dp, *p = NULL; 561 562 skipargs(&v, " [+<n>]"); 563 printd = 1; 564 if (*v == NULL) 565 dp = dcwd; 566 else if (v[1] != NULL) 567 stderror(ERR_NAME | ERR_TOOMANY); 568 else if ((dp = dfind(*v)) == 0) 569 stderror(ERR_NAME | ERR_BADDIR); 570 if (dp->di_prev == &dhead && dp->di_next == &dhead) 571 stderror(ERR_NAME | ERR_EMPTY); 572 if (dp == dcwd) { 573 char *tmp; 574 575 if ((p = dp->di_prev) == &dhead) 576 p = dhead.di_prev; 577 if (chdir(tmp = short2str(p->di_name)) < 0) 578 stderror(ERR_SYSTEM, tmp, strerror(errno)); 579 } 580 dp->di_prev->di_next = dp->di_next; 581 dp->di_next->di_prev = dp->di_prev; 582 if (dp == dcwd) 583 dnewcwd(p); 584 else { 585 printdirs(); 586 } 587 dfree(dp); 588 } 589 590 /* 591 * dfree - free the directory (or keep it if it still has ref count) 592 */ 593 void 594 dfree(struct directory *dp) 595 { 596 597 if (dp->di_count != 0) { 598 dp->di_next = dp->di_prev = 0; 599 } 600 else { 601 free(dp->di_name); 602 free(dp); 603 } 604 } 605 606 /* 607 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 608 * we are of course assuming that the file system is standardly 609 * constructed (always have ..'s, directories have links) 610 */ 611 Char * 612 dcanon(Char *cp, Char *p) 613 { 614 Char slink[MAXPATHLEN]; 615 char tlink[MAXPATHLEN]; 616 Char *newcp, *sp; 617 Char *p1, *p2; /* general purpose */ 618 ssize_t cc; 619 size_t len; 620 int slash; 621 622 /* 623 * christos: if the path given does not start with a slash prepend cwd. If 624 * cwd does not start with a path or the result would be too long abort(). 625 */ 626 if (*cp != '/') { 627 Char tmpdir[MAXPATHLEN]; 628 629 p1 = value(STRcwd); 630 if (p1 == NULL || *p1 != '/') 631 abort(); 632 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 633 abort(); 634 (void)Strcpy(tmpdir, p1); 635 (void)Strcat(tmpdir, STRslash); 636 (void)Strcat(tmpdir, cp); 637 free(cp); 638 cp = p = Strsave(tmpdir); 639 } 640 641 while (*p) { /* for each component */ 642 sp = p; /* save slash address */ 643 while (*++p == '/') /* flush extra slashes */ 644 continue; 645 if (p != ++sp) 646 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 647 continue; 648 p = sp; /* save start of component */ 649 slash = 0; 650 while (*++p) /* find next slash or end of path */ 651 if (*p == '/') { 652 slash = 1; 653 *p = 0; 654 break; 655 } 656 657 if (*sp == '\0') { /* if component is null */ 658 if (--sp == cp) /* if path is one char (i.e. /) */ 659 break; 660 else 661 *sp = '\0'; 662 } else if (sp[0] == '.' && sp[1] == 0) { 663 if (slash) { 664 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 665 continue; 666 p = --sp; 667 } 668 else if (--sp != cp) 669 *sp = '\0'; 670 } 671 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 672 /* 673 * We have something like "yyy/xxx/..", where "yyy" can be null or 674 * a path starting at /, and "xxx" is a single component. Before 675 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 676 * symbolic link. 677 */ 678 *--sp = 0; /* form the pathname for readlink */ 679 if (sp != cp && !adrof(STRignore_symlinks) && 680 (cc = readlink(short2str(cp), tlink, 681 sizeof(tlink) - 1)) >= 0) { 682 tlink[cc] = '\0'; 683 (void)Strcpy(slink, str2short(tlink)); 684 685 if (slash) 686 *p = '/'; 687 /* 688 * Point p to the '/' in "/..", and restore the '/'. 689 */ 690 *(p = sp) = '/'; 691 /* 692 * find length of p 693 */ 694 for (p1 = p; *p1++;) 695 continue; 696 if (*slink != '/') { 697 /* 698 * Relative path, expand it between the "yyy/" and the 699 * "/..". First, back sp up to the character past "yyy/". 700 */ 701 while (*--sp != '/') 702 continue; 703 sp++; 704 *sp = 0; 705 /* 706 * New length is "yyy/" + slink + "/.." and rest 707 */ 708 p1 = newcp = xmalloc( 709 (size_t)((sp - cp) + cc + (p1 - p)) * sizeof(Char)); 710 /* 711 * Copy new path into newcp 712 */ 713 for (p2 = cp; (*p1++ = *p2++) != '\0';) 714 continue; 715 for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';) 716 continue; 717 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 718 continue; 719 /* 720 * Restart canonicalization at expanded "/xxx". 721 */ 722 p = sp - cp - 1 + newcp; 723 } 724 else { 725 /* 726 * New length is slink + "/.." and rest 727 */ 728 p1 = newcp = xmalloc( 729 (size_t)(cc + (p1 - p)) * sizeof(Char)); 730 /* 731 * Copy new path into newcp 732 */ 733 for (p2 = slink; (*p1++ = *p2++) != '\0';) 734 continue; 735 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 736 continue; 737 /* 738 * Restart canonicalization at beginning 739 */ 740 p = newcp; 741 } 742 free(cp); 743 cp = newcp; 744 continue; /* canonicalize the link */ 745 } 746 *sp = '/'; 747 if (sp != cp) 748 while (*--sp != '/') 749 continue; 750 if (slash) { 751 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 752 continue; 753 p = sp; 754 } 755 else if (cp == sp) 756 *++sp = '\0'; 757 else 758 *sp = '\0'; 759 } 760 else { /* normal dir name (not . or .. or nothing) */ 761 762 if (sp != cp && adrof(STRchase_symlinks) && 763 !adrof(STRignore_symlinks) && 764 (cc = readlink(short2str(cp), tlink, sizeof(tlink)-1)) >= 0) { 765 tlink[cc] = '\0'; 766 (void)Strcpy(slink, str2short(tlink)); 767 768 /* 769 * restore the '/'. 770 */ 771 if (slash) 772 *p = '/'; 773 774 /* 775 * point sp to p (rather than backing up). 776 */ 777 sp = p; 778 779 /* 780 * find length of p 781 */ 782 for (p1 = p; *p1++;) 783 continue; 784 if (*slink != '/') { 785 /* 786 * Relative path, expand it between the "yyy/" and the 787 * remainder. First, back sp up to the character past 788 * "yyy/". 789 */ 790 while (*--sp != '/') 791 continue; 792 sp++; 793 *sp = 0; 794 /* 795 * New length is "yyy/" + slink + "/.." and rest 796 */ 797 p1 = newcp = xmalloc( 798 (size_t)((sp - cp) + cc + (p1 - p)) * sizeof(Char)); 799 /* 800 * Copy new path into newcp 801 */ 802 for (p2 = cp; (*p1++ = *p2++) != '\0';) 803 continue; 804 for (p1--, p2 = slink; (*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 slink + the rest 816 */ 817 p1 = newcp = xmalloc( 818 (size_t)(cc + (p1 - p)) * sizeof(Char)); 819 /* 820 * Copy new path into newcp 821 */ 822 for (p2 = slink; (*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 free(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 len = Strlen(p1); 845 /* 846 * See if we're not in a subdir of STRhome 847 */ 848 if (p1 && *p1 == '/' && 849 (Strncmp(p1, cp, len) != 0 || (cp[len] != '/' && cp[len] != '\0'))) { 850 static ino_t home_ino; 851 static dev_t home_dev = NODEV; 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(slink, 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 free(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(struct directory *dp) 898 { 899 dcwd = dp; 900 dset(dcwd->di_name); 901 if (printd && !(adrof(STRpushdsilent))) 902 printdirs(); 903 } 904