1 /* $OpenBSD: dir.c,v 1.14 2009/10/27 23:59:21 deraadt 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/param.h> 34 #include <sys/stat.h> 35 #include <errno.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.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[MAXPATHLEN]; 70 static 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, MAXPATHLEN); 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 = (struct directory *) 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 = (Char **) xmalloc((size_t) (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 = (Char *) xmalloc(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((ptr_t) 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 = (struct directory *) 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 = (Char *) xmalloc((size_t)((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((ptr_t) 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[MAXPATHLEN]; 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((ptr_t) cp); 418 return dgoto(dp); 419 } 420 else { 421 xfree((ptr_t) 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[MAXPATHLEN]; 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((ptr_t) 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((ptr_t) cp); 450 cp = Strsave(dp); 451 printd = 1; 452 return dgoto(cp); 453 } 454 (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); 455 xfree((ptr_t) 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 = (struct directory *) 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((ptr_t) 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[MAXPATHLEN]; 614 char tlink[MAXPATHLEN]; 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[MAXPATHLEN]; 624 625 p1 = value(STRcwd); 626 if (p1 == NULL || *p1 != '/') 627 abort(); 628 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 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((ptr_t) 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 = (Char *) xmalloc((size_t) 705 (((sp - cp) + cc + (p1 - p)) * 706 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 = (Char *) xmalloc((size_t) 726 ((cc + (p1 - p)) * 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 xfree((ptr_t) 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 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 764 link[cc] = '\0'; 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 = (Char *) xmalloc((size_t) 796 (((sp - cp) + cc + (p1 - p)) 797 * sizeof(Char))); 798 /* 799 * Copy new path into newcp 800 */ 801 for (p2 = cp; (*p1++ = *p2++) != '\0';) 802 continue; 803 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 804 continue; 805 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 806 continue; 807 /* 808 * Restart canonicalization at expanded "/xxx". 809 */ 810 p = sp - cp - 1 + newcp; 811 } 812 else { 813 /* 814 * New length is link + the rest 815 */ 816 p1 = newcp = (Char *) xmalloc((size_t) 817 ((cc + (p1 - p)) * sizeof(Char))); 818 /* 819 * Copy new path into newcp 820 */ 821 for (p2 = link; (*p1++ = *p2++) != '\0';) 822 continue; 823 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 824 continue; 825 /* 826 * Restart canonicalization at beginning 827 */ 828 p = newcp; 829 } 830 xfree((ptr_t) cp); 831 cp = newcp; 832 continue; /* canonicalize the link */ 833 } 834 if (slash) 835 *p = '/'; 836 } 837 } 838 839 /* 840 * fix home... 841 */ 842 p1 = value(STRhome); 843 cc = Strlen(p1); 844 /* 845 * See if we're not in a subdir of STRhome 846 */ 847 if (p1 && *p1 == '/' && 848 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 849 static ino_t home_ino = -1; 850 static dev_t home_dev = -1; 851 static Char *home_ptr = NULL; 852 struct stat statbuf; 853 854 /* 855 * Get dev and ino of STRhome 856 */ 857 if (home_ptr != p1 && 858 stat(short2str(p1), &statbuf) != -1) { 859 home_dev = statbuf.st_dev; 860 home_ino = statbuf.st_ino; 861 home_ptr = p1; 862 } 863 /* 864 * Start comparing dev & ino backwards 865 */ 866 Strlcpy(link, cp, sizeof link/sizeof(Char)); 867 p2 = link; 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 xfree((ptr_t) 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