1 /* $NetBSD: dir.c,v 1.30 2013/07/16 17:47:43 christos 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.30 2013/07/16 17:47:43 christos 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 /* 70 * dinit - initialize current working directory 71 */ 72 void 73 dinit(Char *hp) 74 { 75 static const char emsg[] = "csh: Trying to start from \"%s\"\n"; 76 char path[MAXPATHLEN]; 77 struct directory *dp; 78 const char *ecp; 79 Char *cp; 80 81 /* Don't believe the login shell home, because it may be a symlink */ 82 ecp = getcwd(path, MAXPATHLEN); 83 if (ecp == NULL || *ecp == '\0') { 84 (void)fprintf(csherr, "csh: %s\n", strerror(errno)); 85 if (hp && *hp) { 86 ecp = short2str(hp); 87 if (chdir(ecp) == -1) 88 cp = NULL; 89 else 90 cp = Strsave(hp); 91 (void)fprintf(csherr, emsg, vis_str(hp)); 92 } 93 else 94 cp = NULL; 95 if (cp == NULL) { 96 (void)fprintf(csherr, emsg, "/"); 97 if (chdir("/") == -1) { 98 /* I am not even try to print an error message! */ 99 xexit(1); 100 } 101 cp = SAVE("/"); 102 } 103 } 104 else { 105 struct stat swd, shp; 106 107 /* 108 * See if $HOME is the working directory we got and use that 109 */ 110 if (hp && *hp && 111 stat(ecp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 112 swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) 113 cp = Strsave(hp); 114 else { 115 const char *cwd; 116 117 /* 118 * use PWD if we have it (for subshells) 119 */ 120 if ((cwd = getenv("PWD")) != NULL) { 121 if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && 122 swd.st_ino == shp.st_ino) 123 ecp = cwd; 124 } 125 cp = dcanon(SAVE(ecp), STRNULL); 126 } 127 } 128 129 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 130 dp->di_name = cp; 131 dp->di_count = 0; 132 dhead.di_next = dhead.di_prev = dp; 133 dp->di_next = dp->di_prev = &dhead; 134 printd = 0; 135 dnewcwd(dp); 136 } 137 138 static void 139 dset(Char *dp) 140 { 141 Char **vec; 142 143 /* 144 * Don't call set() directly cause if the directory contains ` or 145 * other junk characters glob will fail. 146 */ 147 148 vec = xmalloc((size_t)(2 * sizeof(Char **))); 149 vec[0] = Strsave(dp); 150 vec[1] = 0; 151 setq(STRcwd, vec, &shvhed); 152 Setenv(STRPWD, dp); 153 } 154 155 #define DIR_LONG 1 156 #define DIR_VERT 2 157 #define DIR_LINE 4 158 159 static void 160 skipargs(Char ***v, const char *str) 161 { 162 Char **n, *s; 163 164 n = *v; 165 dirflag = 0; 166 for (n++; *n != NULL && (*n)[0] == '-'; n++) 167 for (s = &((*n)[1]); *s; s++) 168 switch (*s) { 169 case 'l': 170 dirflag |= DIR_LONG; 171 break; 172 case 'n': 173 dirflag |= DIR_LINE; 174 break; 175 case 'v': 176 dirflag |= DIR_VERT; 177 break; 178 default: 179 stderror(ERR_DIRUS, vis_str(**v), str); 180 /* NOTREACHED */ 181 } 182 *v = n; 183 } 184 185 /* 186 * dodirs - list all directories in directory loop 187 */ 188 void 189 /*ARGSUSED*/ 190 dodirs(Char **v, struct command *t) 191 { 192 skipargs(&v, ""); 193 194 if (*v != NULL) 195 stderror(ERR_DIRUS, "dirs", ""); 196 printdirs(); 197 } 198 199 static void 200 printdirs(void) 201 { 202 struct directory *dp; 203 Char *hp, *s; 204 size_t cur, idx, len; 205 206 hp = value(STRhome); 207 if (*hp == '\0') 208 hp = NULL; 209 dp = dcwd; 210 idx = 0; 211 cur = 0; 212 do { 213 if (dp == &dhead) 214 continue; 215 if (dirflag & DIR_VERT) { 216 (void)fprintf(cshout, "%zu\t", idx++); 217 cur = 0; 218 } 219 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 220 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 221 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 222 len = Strlen(s = (dp->di_name + len)) + 2; 223 else 224 len = Strlen(s = dp->di_name) + 1; 225 226 cur += len; 227 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 228 (void)fprintf(cshout, "\n"); 229 cur = len; 230 } 231 (void) fprintf(cshout, "%s%s%c", (s != dp->di_name)? "~" : "", 232 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 233 } while ((dp = dp->di_prev) != dcwd); 234 if (!(dirflag & DIR_VERT)) 235 (void)fprintf(cshout, "\n"); 236 } 237 238 void 239 dtildepr(Char *home, Char *dir) 240 { 241 if (!eq(home, STRslash) && prefix(home, dir)) 242 (void)fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 243 else 244 (void)fprintf(cshout, "%s", vis_str(dir)); 245 } 246 247 void 248 dtilde(void) 249 { 250 struct directory *d; 251 252 d = dcwd; 253 do { 254 if (d == &dhead) 255 continue; 256 d->di_name = dcanon(d->di_name, STRNULL); 257 } while ((d = d->di_prev) != dcwd); 258 259 dset(dcwd->di_name); 260 } 261 262 263 /* dnormalize(): 264 * If the name starts with . or .. then we might need to normalize 265 * it depending on the symbolic link flags 266 */ 267 Char * 268 dnormalize(Char *cp) 269 { 270 #define UC (unsigned char) 271 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 272 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 273 if ((unsigned char) cp[0] == '/') 274 return (Strsave(cp)); 275 276 if (adrof(STRignore_symlinks)) { 277 size_t dotdot = 0; 278 Char *dp, *cwd; 279 280 cwd = xmalloc((size_t)((Strlen(dcwd->di_name) + 3) * 281 sizeof(Char))); 282 (void)Strcpy(cwd, dcwd->di_name); 283 284 /* 285 * Ignore . and count ..'s 286 */ 287 while (*cp) { 288 if (ISDOT(cp)) { 289 if (*++cp) 290 cp++; 291 } 292 else if (ISDOTDOT(cp)) { 293 dotdot++; 294 cp += 2; 295 if (*cp) 296 cp++; 297 } 298 else 299 break; 300 } 301 while (dotdot > 0) { 302 dp = Strrchr(cwd, '/'); 303 if (dp) { 304 *dp = '\0'; 305 dotdot--; 306 } 307 else 308 break; 309 } 310 311 if (*cp) { 312 cwd[dotdot = Strlen(cwd)] = '/'; 313 cwd[dotdot + 1] = '\0'; 314 dp = Strspl(cwd, cp); 315 xfree((ptr_t) cwd); 316 return dp; 317 } 318 else { 319 if (!*cwd) { 320 cwd[0] = '/'; 321 cwd[1] = '\0'; 322 } 323 return cwd; 324 } 325 } 326 return Strsave(cp); 327 } 328 329 /* 330 * dochngd - implement chdir command. 331 */ 332 void 333 /*ARGSUSED*/ 334 dochngd(Char **v, struct command *t) 335 { 336 struct directory *dp; 337 Char *cp; 338 339 skipargs(&v, " [<dir>]"); 340 printd = 0; 341 if (*v == NULL) { 342 if ((cp = value(STRhome)) == NULL || *cp == 0) 343 stderror(ERR_NAME | ERR_NOHOMEDIR); 344 if (chdir(short2str(cp)) < 0) 345 stderror(ERR_NAME | ERR_CANTCHANGE); 346 cp = Strsave(cp); 347 } 348 else if (v[1] != NULL) 349 stderror(ERR_NAME | ERR_TOOMANY); 350 else if ((dp = dfind(*v)) != 0) { 351 char *tmp; 352 353 printd = 1; 354 if (chdir(tmp = short2str(dp->di_name)) < 0) 355 stderror(ERR_SYSTEM, tmp, strerror(errno)); 356 dcwd->di_prev->di_next = dcwd->di_next; 357 dcwd->di_next->di_prev = dcwd->di_prev; 358 dfree(dcwd); 359 dnewcwd(dp); 360 return; 361 } 362 else 363 cp = dfollow(*v); 364 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 365 dp->di_name = cp; 366 dp->di_count = 0; 367 dp->di_next = dcwd->di_next; 368 dp->di_prev = dcwd->di_prev; 369 dp->di_prev->di_next = dp; 370 dp->di_next->di_prev = dp; 371 dfree(dcwd); 372 dnewcwd(dp); 373 } 374 375 static Char * 376 dgoto(Char *cp) 377 { 378 Char *dp; 379 380 if (*cp != '/') { 381 Char *p, *q; 382 size_t cwdlen; 383 384 for (p = dcwd->di_name; *p++;) 385 continue; 386 if ((cwdlen = (size_t)(p - dcwd->di_name - 1)) == 1) /* root */ 387 cwdlen = 0; 388 for (p = cp; *p++;) 389 continue; 390 dp = xmalloc((size_t)(cwdlen + (size_t)(p - cp) + 1) * sizeof(Char)); 391 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 392 continue; 393 if (cwdlen) 394 p[-1] = '/'; 395 else 396 p--; /* don't add a / after root */ 397 for (q = cp; (*p++ = *q++) != '\0';) 398 continue; 399 xfree((ptr_t) cp); 400 cp = dp; 401 dp += cwdlen; 402 } 403 else 404 dp = cp; 405 406 cp = dcanon(cp, dp); 407 return cp; 408 } 409 410 /* 411 * dfollow - change to arg directory; fall back on cdpath if not valid 412 */ 413 static Char * 414 dfollow(Char *cp) 415 { 416 char ebuf[MAXPATHLEN]; 417 struct varent *c; 418 Char *dp; 419 int serrno; 420 421 cp = globone(cp, G_ERROR); 422 /* 423 * if we are ignoring symlinks, try to fix relatives now. 424 */ 425 dp = dnormalize(cp); 426 if (chdir(short2str(dp)) >= 0) { 427 xfree((ptr_t) cp); 428 return dgoto(dp); 429 } 430 else { 431 xfree((ptr_t) dp); 432 if (chdir(short2str(cp)) >= 0) 433 return dgoto(cp); 434 serrno = errno; 435 } 436 437 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 438 && (c = adrof(STRcdpath))) { 439 Char **cdp; 440 Char *p; 441 Char buf[MAXPATHLEN]; 442 443 for (cdp = c->vec; *cdp; cdp++) { 444 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 445 continue; 446 dp[-1] = '/'; 447 for (p = cp; (*dp++ = *p++) != '\0';) 448 continue; 449 if (chdir(short2str(buf)) >= 0) { 450 printd = 1; 451 xfree((ptr_t) cp); 452 cp = Strsave(buf); 453 return dgoto(cp); 454 } 455 } 456 } 457 dp = value(cp); 458 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 459 xfree((ptr_t) cp); 460 cp = Strsave(dp); 461 printd = 1; 462 return dgoto(cp); 463 } 464 (void)strcpy(ebuf, short2str(cp)); 465 xfree((ptr_t) cp); 466 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 467 /* NOTREACHED */ 468 } 469 470 /* 471 * dopushd - push new directory onto directory stack. 472 * with no arguments exchange top and second. 473 * with numeric argument (+n) bring it to top. 474 */ 475 void 476 /*ARGSUSED*/ 477 dopushd(Char **v, struct command *t) 478 { 479 struct directory *dp; 480 481 skipargs(&v, " [<dir>|+<n>]"); 482 printd = 1; 483 if (*v == NULL) { 484 char *tmp; 485 486 if ((dp = dcwd->di_prev) == &dhead) 487 dp = dhead.di_prev; 488 if (dp == dcwd) 489 stderror(ERR_NAME | ERR_NODIR); 490 if (chdir(tmp = short2str(dp->di_name)) < 0) 491 stderror(ERR_SYSTEM, tmp, strerror(errno)); 492 dp->di_prev->di_next = dp->di_next; 493 dp->di_next->di_prev = dp->di_prev; 494 dp->di_next = dcwd->di_next; 495 dp->di_prev = dcwd; 496 dcwd->di_next->di_prev = dp; 497 dcwd->di_next = dp; 498 } 499 else if (v[1] != NULL) 500 stderror(ERR_NAME | ERR_TOOMANY); 501 else if ((dp = dfind(*v)) != NULL) { 502 char *tmp; 503 504 if (chdir(tmp = short2str(dp->di_name)) < 0) 505 stderror(ERR_SYSTEM, tmp, strerror(errno)); 506 } 507 else { 508 Char *ccp; 509 510 ccp = dfollow(*v); 511 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 512 dp->di_name = ccp; 513 dp->di_count = 0; 514 dp->di_prev = dcwd; 515 dp->di_next = dcwd->di_next; 516 dcwd->di_next = dp; 517 dp->di_next->di_prev = dp; 518 } 519 dnewcwd(dp); 520 } 521 522 /* 523 * dfind - find a directory if specified by numeric (+n) argument 524 */ 525 static struct directory * 526 dfind(Char *cp) 527 { 528 struct directory *dp; 529 Char *ep; 530 int i; 531 532 if (*cp++ != '+') 533 return (0); 534 for (ep = cp; Isdigit(*ep); ep++) 535 continue; 536 if (*ep) 537 return (0); 538 i = getn(cp); 539 if (i <= 0) 540 return (0); 541 for (dp = dcwd; i != 0; i--) { 542 if ((dp = dp->di_prev) == &dhead) 543 dp = dp->di_prev; 544 if (dp == dcwd) 545 stderror(ERR_NAME | ERR_DEEP); 546 } 547 return (dp); 548 } 549 550 /* 551 * dopopd - pop a directory out of the directory stack 552 * with a numeric argument just discard it. 553 */ 554 void 555 /*ARGSUSED*/ 556 dopopd(Char **v, struct command *t) 557 { 558 struct directory *dp, *p = NULL; 559 560 skipargs(&v, " [+<n>]"); 561 printd = 1; 562 if (*v == NULL) 563 dp = dcwd; 564 else if (v[1] != NULL) 565 stderror(ERR_NAME | ERR_TOOMANY); 566 else if ((dp = dfind(*v)) == 0) 567 stderror(ERR_NAME | ERR_BADDIR); 568 if (dp->di_prev == &dhead && dp->di_next == &dhead) 569 stderror(ERR_NAME | ERR_EMPTY); 570 if (dp == dcwd) { 571 char *tmp; 572 573 if ((p = dp->di_prev) == &dhead) 574 p = dhead.di_prev; 575 if (chdir(tmp = short2str(p->di_name)) < 0) 576 stderror(ERR_SYSTEM, tmp, strerror(errno)); 577 } 578 dp->di_prev->di_next = dp->di_next; 579 dp->di_next->di_prev = dp->di_prev; 580 if (dp == dcwd) 581 dnewcwd(p); 582 else { 583 printdirs(); 584 } 585 dfree(dp); 586 } 587 588 /* 589 * dfree - free the directory (or keep it if it still has ref count) 590 */ 591 void 592 dfree(struct directory *dp) 593 { 594 595 if (dp->di_count != 0) { 596 dp->di_next = dp->di_prev = 0; 597 } 598 else { 599 xfree((char *) dp->di_name); 600 xfree((ptr_t) dp); 601 } 602 } 603 604 /* 605 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 606 * we are of course assuming that the file system is standardly 607 * constructed (always have ..'s, directories have links) 608 */ 609 Char * 610 dcanon(Char *cp, Char *p) 611 { 612 Char slink[MAXPATHLEN]; 613 char tlink[MAXPATHLEN]; 614 Char *newcp, *sp; 615 Char *p1, *p2; /* general purpose */ 616 ssize_t cc; 617 size_t len; 618 int slash; 619 620 /* 621 * christos: if the path given does not start with a slash prepend cwd. If 622 * cwd does not start with a path or the result would be too long abort(). 623 */ 624 if (*cp != '/') { 625 Char tmpdir[MAXPATHLEN]; 626 627 p1 = value(STRcwd); 628 if (p1 == NULL || *p1 != '/') 629 abort(); 630 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 631 abort(); 632 (void)Strcpy(tmpdir, p1); 633 (void)Strcat(tmpdir, STRslash); 634 (void)Strcat(tmpdir, cp); 635 xfree((ptr_t) cp); 636 cp = p = Strsave(tmpdir); 637 } 638 639 while (*p) { /* for each component */ 640 sp = p; /* save slash address */ 641 while (*++p == '/') /* flush extra slashes */ 642 continue; 643 if (p != ++sp) 644 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 645 continue; 646 p = sp; /* save start of component */ 647 slash = 0; 648 while (*++p) /* find next slash or end of path */ 649 if (*p == '/') { 650 slash = 1; 651 *p = 0; 652 break; 653 } 654 655 if (*sp == '\0') { /* if component is null */ 656 if (--sp == cp) /* if path is one char (i.e. /) */ 657 break; 658 else 659 *sp = '\0'; 660 } else if (sp[0] == '.' && sp[1] == 0) { 661 if (slash) { 662 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 663 continue; 664 p = --sp; 665 } 666 else if (--sp != cp) 667 *sp = '\0'; 668 } 669 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 670 /* 671 * We have something like "yyy/xxx/..", where "yyy" can be null or 672 * a path starting at /, and "xxx" is a single component. Before 673 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 674 * symbolic link. 675 */ 676 *--sp = 0; /* form the pathname for readlink */ 677 if (sp != cp && !adrof(STRignore_symlinks) && 678 (cc = readlink(short2str(cp), tlink, 679 sizeof(tlink) - 1)) >= 0) { 680 tlink[cc] = '\0'; 681 (void)Strcpy(slink, str2short(tlink)); 682 683 if (slash) 684 *p = '/'; 685 /* 686 * Point p to the '/' in "/..", and restore the '/'. 687 */ 688 *(p = sp) = '/'; 689 /* 690 * find length of p 691 */ 692 for (p1 = p; *p1++;) 693 continue; 694 if (*slink != '/') { 695 /* 696 * Relative path, expand it between the "yyy/" and the 697 * "/..". First, back sp up to the character past "yyy/". 698 */ 699 while (*--sp != '/') 700 continue; 701 sp++; 702 *sp = 0; 703 /* 704 * New length is "yyy/" + slink + "/.." and rest 705 */ 706 p1 = newcp = xmalloc( 707 (size_t)((sp - cp) + cc + (p1 - p)) * sizeof(Char)); 708 /* 709 * Copy new path into newcp 710 */ 711 for (p2 = cp; (*p1++ = *p2++) != '\0';) 712 continue; 713 for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';) 714 continue; 715 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 716 continue; 717 /* 718 * Restart canonicalization at expanded "/xxx". 719 */ 720 p = sp - cp - 1 + newcp; 721 } 722 else { 723 /* 724 * New length is slink + "/.." and rest 725 */ 726 p1 = newcp = xmalloc( 727 (size_t)(cc + (p1 - p)) * sizeof(Char)); 728 /* 729 * Copy new path into newcp 730 */ 731 for (p2 = slink; (*p1++ = *p2++) != '\0';) 732 continue; 733 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 734 continue; 735 /* 736 * Restart canonicalization at beginning 737 */ 738 p = newcp; 739 } 740 xfree((ptr_t) cp); 741 cp = newcp; 742 continue; /* canonicalize the link */ 743 } 744 *sp = '/'; 745 if (sp != cp) 746 while (*--sp != '/') 747 continue; 748 if (slash) { 749 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 750 continue; 751 p = sp; 752 } 753 else if (cp == sp) 754 *++sp = '\0'; 755 else 756 *sp = '\0'; 757 } 758 else { /* normal dir name (not . or .. or nothing) */ 759 760 if (sp != cp && adrof(STRchase_symlinks) && 761 !adrof(STRignore_symlinks) && 762 (cc = readlink(short2str(cp), tlink, sizeof(tlink)-1)) >= 0) { 763 tlink[cc] = '\0'; 764 (void)Strcpy(slink, str2short(tlink)); 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 (*slink != '/') { 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/" + slink + "/.." and rest 794 */ 795 p1 = newcp = xmalloc( 796 (size_t)((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 = slink; (*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 slink + the rest 814 */ 815 p1 = newcp = xmalloc( 816 (size_t)(cc + (p1 - p)) * sizeof(Char)); 817 /* 818 * Copy new path into newcp 819 */ 820 for (p2 = slink; (*p1++ = *p2++) != '\0';) 821 continue; 822 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 823 continue; 824 /* 825 * Restart canonicalization at beginning 826 */ 827 p = newcp; 828 } 829 xfree((ptr_t) cp); 830 cp = newcp; 831 continue; /* canonicalize the link */ 832 } 833 if (slash) 834 *p = '/'; 835 } 836 } 837 838 /* 839 * fix home... 840 */ 841 p1 = value(STRhome); 842 len = Strlen(p1); 843 /* 844 * See if we're not in a subdir of STRhome 845 */ 846 if (p1 && *p1 == '/' && 847 (Strncmp(p1, cp, len) != 0 || (cp[len] != '/' && cp[len] != '\0'))) { 848 static ino_t home_ino; 849 static dev_t home_dev = NODEV; 850 static Char *home_ptr = NULL; 851 struct stat statbuf; 852 853 /* 854 * Get dev and ino of STRhome 855 */ 856 if (home_ptr != p1 && 857 stat(short2str(p1), &statbuf) != -1) { 858 home_dev = statbuf.st_dev; 859 home_ino = statbuf.st_ino; 860 home_ptr = p1; 861 } 862 /* 863 * Start comparing dev & ino backwards 864 */ 865 p2 = Strcpy(slink, cp); 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 xfree((ptr_t) 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