1 /* $OpenBSD: dir.c,v 1.13 2003/09/25 15:29:06 millert 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 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 5/31/93"; 36 #else 37 static char rcsid[] = "$OpenBSD: dir.c,v 1.13 2003/09/25 15:29:06 millert Exp $"; 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 #include <errno.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <stdarg.h> 48 49 #include "csh.h" 50 #include "dir.h" 51 #include "extern.h" 52 53 /* Directory management. */ 54 55 static struct directory 56 *dfind(Char *); 57 static Char *dfollow(Char *); 58 static void printdirs(void); 59 static Char *dgoto(Char *); 60 static void dnewcwd(struct directory *); 61 static void dset(Char *); 62 63 struct directory dhead; /* "head" of loop */ 64 int printd; /* force name to be printed */ 65 66 static int dirflag = 0; 67 68 /* 69 * dinit - initialize current working directory 70 */ 71 void 72 dinit(Char *hp) 73 { 74 char *tcp; 75 Char *cp; 76 struct directory *dp; 77 char path[MAXPATHLEN]; 78 static char *emsg = "csh: Trying to start from \"%s\"\n"; 79 80 /* Don't believe the login shell home, because it may be a symlink */ 81 tcp = getcwd(path, MAXPATHLEN); 82 if (tcp == NULL || *tcp == '\0') { 83 (void) fprintf(csherr, "csh: %s\n", strerror(errno)); 84 if (hp && *hp) { 85 tcp = short2str(hp); 86 if (chdir(tcp) == -1) 87 cp = NULL; 88 else 89 cp = hp; 90 (void) fprintf(csherr, emsg, vis_str(hp)); 91 } 92 else 93 cp = NULL; 94 if (cp == NULL) { 95 (void) fprintf(csherr, emsg, "/"); 96 if (chdir("/") == -1) 97 /* I am not even try to print an error message! */ 98 xexit(1); 99 cp = SAVE("/"); 100 } 101 } 102 else { 103 struct stat swd, shp; 104 105 /* 106 * See if $HOME is the working directory we got and use that 107 */ 108 if (hp && *hp && 109 stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 110 swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) 111 cp = hp; 112 else { 113 char *cwd; 114 115 /* 116 * use PWD if we have it (for subshells) 117 */ 118 if ((cwd = getenv("PWD")) != NULL) { 119 if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && 120 swd.st_ino == shp.st_ino) 121 tcp = cwd; 122 } 123 cp = dcanon(SAVE(tcp), STRNULL); 124 } 125 } 126 127 dp = (struct directory *) xcalloc(1, sizeof(struct directory)); 128 dp->di_name = Strsave(cp); 129 dp->di_count = 0; 130 dhead.di_next = dhead.di_prev = dp; 131 dp->di_next = dp->di_prev = &dhead; 132 printd = 0; 133 dnewcwd(dp); 134 } 135 136 static void 137 dset(Char *dp) 138 { 139 /* 140 * Don't call set() directly cause if the directory contains ` or 141 * other junk characters glob will fail. 142 */ 143 Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); 144 145 vec[0] = Strsave(dp); 146 vec[1] = 0; 147 setq(STRcwd, vec, &shvhed); 148 Setenv(STRPWD, dp); 149 } 150 151 #define DIR_LONG 1 152 #define DIR_VERT 2 153 #define DIR_LINE 4 154 155 static void 156 skipargs(Char ***v, char *str) 157 { 158 Char **n = *v, *s; 159 160 dirflag = 0; 161 for (n++; *n != NULL && (*n)[0] == '-'; n++) 162 for (s = &((*n)[1]); *s; s++) 163 switch (*s) { 164 case 'l': 165 dirflag |= DIR_LONG; 166 break; 167 case 'v': 168 dirflag |= DIR_VERT; 169 break; 170 case 'n': 171 dirflag |= DIR_LINE; 172 break; 173 default: 174 stderror(ERR_DIRUS, vis_str(**v), str); 175 break; 176 } 177 *v = n; 178 } 179 180 /* 181 * dodirs - list all directories in directory loop 182 */ 183 void 184 /*ARGSUSED*/ 185 dodirs(Char **v, struct command *t) 186 { 187 skipargs(&v, ""); 188 189 if (*v != NULL) 190 stderror(ERR_DIRUS, "dirs", ""); 191 printdirs(); 192 } 193 194 static void 195 printdirs(void) 196 { 197 struct directory *dp; 198 Char *s, *hp = value(STRhome); 199 int idx, len, cur; 200 201 if (*hp == '\0') 202 hp = NULL; 203 dp = dcwd; 204 idx = 0; 205 cur = 0; 206 do { 207 if (dp == &dhead) 208 continue; 209 if (dirflag & DIR_VERT) { 210 (void) fprintf(cshout, "%d\t", idx++); 211 cur = 0; 212 } 213 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 214 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 215 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 216 len = Strlen(s = (dp->di_name + len)) + 2; 217 else 218 len = Strlen(s = dp->di_name) + 1; 219 220 cur += len; 221 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 222 (void) fprintf(cshout, "\n"); 223 cur = len; 224 } 225 (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", 226 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 227 } while ((dp = dp->di_prev) != dcwd); 228 if (!(dirflag & DIR_VERT)) 229 (void) fprintf(cshout, "\n"); 230 } 231 232 void 233 dtildepr(Char *home, Char *dir) 234 { 235 236 if (!eq(home, STRslash) && prefix(home, dir)) 237 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 238 else 239 (void) fprintf(cshout, "%s", vis_str(dir)); 240 } 241 242 void 243 dtilde(void) 244 { 245 struct directory *d = dcwd; 246 247 do { 248 if (d == &dhead) 249 continue; 250 d->di_name = dcanon(d->di_name, STRNULL); 251 } while ((d = d->di_prev) != dcwd); 252 253 dset(dcwd->di_name); 254 } 255 256 257 /* dnormalize(): 258 * If the name starts with . or .. then we might need to normalize 259 * it depending on the symbolic link flags 260 */ 261 Char * 262 dnormalize(Char *cp) 263 { 264 265 #define UC (unsigned char) 266 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 267 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 268 269 if ((unsigned char) cp[0] == '/') 270 return (Strsave(cp)); 271 272 if (adrof(STRignore_symlinks)) { 273 int dotdot = 0; 274 Char *dp, *cwd; 275 size_t len; 276 277 len = (size_t) (Strlen(dcwd->di_name) + 3); 278 cwd = (Char *) xmalloc(len * sizeof(Char)); 279 (void) Strlcpy(cwd, dcwd->di_name, len); 280 281 /* 282 * Ignore . and count ..'s 283 */ 284 while (*cp) { 285 if (ISDOT(cp)) { 286 if (*++cp) 287 cp++; 288 } 289 else if (ISDOTDOT(cp)) { 290 dotdot++; 291 cp += 2; 292 if (*cp) 293 cp++; 294 } 295 else 296 break; 297 } 298 while (dotdot > 0) 299 if ((dp = Strrchr(cwd, '/'))) { 300 *dp = '\0'; 301 dotdot--; 302 } 303 else 304 break; 305 306 if (*cp) { 307 cwd[dotdot = Strlen(cwd)] = '/'; 308 cwd[dotdot + 1] = '\0'; 309 dp = Strspl(cwd, cp); 310 xfree((ptr_t) cwd); 311 return dp; 312 } 313 else { 314 if (!*cwd) { 315 cwd[0] = '/'; 316 cwd[1] = '\0'; 317 } 318 return cwd; 319 } 320 } 321 return Strsave(cp); 322 } 323 324 /* 325 * dochngd - implement chdir command. 326 */ 327 void 328 /*ARGSUSED*/ 329 dochngd(Char **v, struct command *t) 330 { 331 Char *cp; 332 struct directory *dp; 333 334 skipargs(&v, " [<dir>]"); 335 printd = 0; 336 if (*v == NULL) { 337 if ((cp = value(STRhome)) == NULL || *cp == 0) 338 stderror(ERR_NAME | ERR_NOHOMEDIR); 339 if (chdir(short2str(cp)) < 0) 340 stderror(ERR_NAME | ERR_CANTCHANGE); 341 cp = Strsave(cp); 342 } 343 else if (v[1] != NULL) { 344 stderror(ERR_NAME | ERR_TOOMANY); 345 /* NOTREACHED */ 346 return; 347 } 348 else if ((dp = dfind(*v)) != 0) { 349 char *tmp; 350 351 printd = 1; 352 if (chdir(tmp = short2str(dp->di_name)) < 0) 353 stderror(ERR_SYSTEM, tmp, strerror(errno)); 354 dcwd->di_prev->di_next = dcwd->di_next; 355 dcwd->di_next->di_prev = dcwd->di_prev; 356 dfree(dcwd); 357 dnewcwd(dp); 358 return; 359 } 360 else 361 cp = dfollow(*v); 362 dp = (struct directory *) xcalloc(1, sizeof(struct directory)); 363 dp->di_name = cp; 364 dp->di_count = 0; 365 dp->di_next = dcwd->di_next; 366 dp->di_prev = dcwd->di_prev; 367 dp->di_prev->di_next = dp; 368 dp->di_next->di_prev = dp; 369 dfree(dcwd); 370 dnewcwd(dp); 371 } 372 373 static Char * 374 dgoto(Char *cp) 375 { 376 Char *dp; 377 378 if (*cp != '/') { 379 Char *p, *q; 380 int cwdlen; 381 382 for (p = dcwd->di_name; *p++;) 383 continue; 384 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 385 cwdlen = 0; 386 for (p = cp; *p++;) 387 continue; 388 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 389 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 390 continue; 391 if (cwdlen) 392 p[-1] = '/'; 393 else 394 p--; /* don't add a / after root */ 395 for (q = cp; (*p++ = *q++) != '\0';) 396 continue; 397 xfree((ptr_t) cp); 398 cp = dp; 399 dp += cwdlen; 400 } 401 else 402 dp = cp; 403 404 cp = dcanon(cp, dp); 405 return cp; 406 } 407 408 /* 409 * dfollow - change to arg directory; fall back on cdpath if not valid 410 */ 411 static Char * 412 dfollow(Char *cp) 413 { 414 Char *dp; 415 struct varent *c; 416 char ebuf[MAXPATHLEN]; 417 int serrno; 418 419 cp = globone(cp, G_ERROR); 420 /* 421 * if we are ignoring symlinks, try to fix relatives now. 422 */ 423 dp = dnormalize(cp); 424 if (chdir(short2str(dp)) >= 0) { 425 xfree((ptr_t) cp); 426 return dgoto(dp); 427 } 428 else { 429 xfree((ptr_t) dp); 430 if (chdir(short2str(cp)) >= 0) 431 return dgoto(cp); 432 serrno = errno; 433 } 434 435 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 436 && (c = adrof(STRcdpath))) { 437 Char **cdp; 438 Char *p; 439 Char buf[MAXPATHLEN]; 440 441 for (cdp = c->vec; *cdp; cdp++) { 442 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 443 continue; 444 dp[-1] = '/'; 445 for (p = cp; (*dp++ = *p++) != '\0';) 446 continue; 447 if (chdir(short2str(buf)) >= 0) { 448 printd = 1; 449 xfree((ptr_t) cp); 450 cp = Strsave(buf); 451 return dgoto(cp); 452 } 453 } 454 } 455 dp = value(cp); 456 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 457 xfree((ptr_t) cp); 458 cp = Strsave(dp); 459 printd = 1; 460 return dgoto(cp); 461 } 462 (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); 463 xfree((ptr_t) cp); 464 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 465 return (NULL); 466 } 467 468 469 /* 470 * dopushd - push new directory onto directory stack. 471 * with no arguments exchange top and second. 472 * with numeric argument (+n) bring it to top. 473 */ 474 void 475 /*ARGSUSED*/ 476 dopushd(Char **v, struct command *t) 477 { 478 struct directory *dp; 479 480 skipargs(&v, " [<dir>|+<n>]"); 481 printd = 1; 482 if (*v == NULL) { 483 char *tmp; 484 485 if ((dp = dcwd->di_prev) == &dhead) 486 dp = dhead.di_prev; 487 if (dp == dcwd) 488 stderror(ERR_NAME | ERR_NODIR); 489 if (chdir(tmp = short2str(dp->di_name)) < 0) 490 stderror(ERR_SYSTEM, tmp, strerror(errno)); 491 dp->di_prev->di_next = dp->di_next; 492 dp->di_next->di_prev = dp->di_prev; 493 dp->di_next = dcwd->di_next; 494 dp->di_prev = dcwd; 495 dcwd->di_next->di_prev = dp; 496 dcwd->di_next = dp; 497 } 498 else if (v[1] != NULL) { 499 stderror(ERR_NAME | ERR_TOOMANY); 500 /* NOTREACHED */ 501 return; 502 } 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 = (struct directory *) xcalloc(1, sizeof(struct directory)); 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 int i; 532 Char *ep; 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 /* NOTREACHED */ 569 return; 570 } 571 else if ((dp = dfind(*v)) == 0) 572 stderror(ERR_NAME | ERR_BADDIR); 573 if (dp->di_prev == &dhead && dp->di_next == &dhead) 574 stderror(ERR_NAME | ERR_EMPTY); 575 if (dp == dcwd) { 576 char *tmp; 577 578 if ((p = dp->di_prev) == &dhead) 579 p = dhead.di_prev; 580 if (chdir(tmp = short2str(p->di_name)) < 0) 581 stderror(ERR_SYSTEM, tmp, strerror(errno)); 582 } 583 dp->di_prev->di_next = dp->di_next; 584 dp->di_next->di_prev = dp->di_prev; 585 if (dp == dcwd) 586 dnewcwd(p); 587 else { 588 printdirs(); 589 } 590 dfree(dp); 591 } 592 593 /* 594 * dfree - free the directory (or keep it if it still has ref count) 595 */ 596 void 597 dfree(struct directory *dp) 598 { 599 600 if (dp->di_count != 0) { 601 dp->di_next = dp->di_prev = 0; 602 } 603 else { 604 xfree((char *) dp->di_name); 605 xfree((ptr_t) dp); 606 } 607 } 608 609 /* 610 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 611 * we are of course assuming that the file system is standardly 612 * constructed (always have ..'s, directories have links) 613 */ 614 Char * 615 dcanon(Char *cp, Char *p) 616 { 617 Char *sp; 618 Char *p1, *p2; /* general purpose */ 619 bool slash; 620 621 Char link[MAXPATHLEN]; 622 char tlink[MAXPATHLEN]; 623 int cc; 624 Char *newcp; 625 626 /* 627 * christos: if the path given does not start with a slash prepend cwd. If 628 * cwd does not start with a path or the result would be too long abort(). 629 */ 630 if (*cp != '/') { 631 Char tmpdir[MAXPATHLEN]; 632 633 p1 = value(STRcwd); 634 if (p1 == NULL || *p1 != '/') 635 abort(); 636 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 637 abort(); 638 (void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char)); 639 (void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char)); 640 (void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char)); 641 xfree((ptr_t) cp); 642 cp = p = Strsave(tmpdir); 643 } 644 645 while (*p) { /* for each component */ 646 sp = p; /* save slash address */ 647 while (*++p == '/') /* flush extra slashes */ 648 continue; 649 if (p != ++sp) 650 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 651 continue; 652 p = sp; /* save start of component */ 653 slash = 0; 654 while (*p) /* find next slash or end of path */ 655 if (*++p == '/') { 656 slash = 1; 657 *p = 0; 658 break; 659 } 660 661 if (*sp == '\0') /* if component is null */ 662 if (--sp == cp) /* if path is one char (i.e. /) */ 663 break; 664 else 665 *sp = '\0'; 666 else if (sp[0] == '.' && sp[1] == 0) { 667 if (slash) { 668 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 669 continue; 670 p = --sp; 671 } 672 else if (--sp != cp) 673 *sp = '\0'; 674 } 675 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 676 /* 677 * We have something like "yyy/xxx/..", where "yyy" can be null or 678 * a path starting at /, and "xxx" is a single component. Before 679 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 680 * symbolic link. 681 */ 682 *--sp = 0; /* form the pathname for readlink */ 683 if (sp != cp && !adrof(STRignore_symlinks) && 684 (cc = readlink(short2str(cp), tlink, 685 sizeof tlink-1)) >= 0) { 686 tlink[cc] = '\0'; 687 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 688 689 if (slash) 690 *p = '/'; 691 /* 692 * Point p to the '/' in "/..", and restore the '/'. 693 */ 694 *(p = sp) = '/'; 695 /* 696 * find length of p 697 */ 698 for (p1 = p; *p1++;) 699 continue; 700 if (*link != '/') { 701 /* 702 * Relative path, expand it between the "yyy/" and the 703 * "/..". First, back sp up to the character past "yyy/". 704 */ 705 while (*--sp != '/') 706 continue; 707 sp++; 708 *sp = 0; 709 /* 710 * New length is "yyy/" + link + "/.." and rest 711 */ 712 p1 = newcp = (Char *) xmalloc((size_t) 713 (((sp - cp) + cc + (p1 - p)) * 714 sizeof(Char))); 715 /* 716 * Copy new path into newcp 717 */ 718 for (p2 = cp; (*p1++ = *p2++) != '\0';) 719 continue; 720 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 721 continue; 722 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 723 continue; 724 /* 725 * Restart canonicalization at expanded "/xxx". 726 */ 727 p = sp - cp - 1 + newcp; 728 } 729 else { 730 /* 731 * New length is link + "/.." and rest 732 */ 733 p1 = newcp = (Char *) xmalloc((size_t) 734 ((cc + (p1 - p)) * sizeof(Char))); 735 /* 736 * Copy new path into newcp 737 */ 738 for (p2 = link; (*p1++ = *p2++) != '\0';) 739 continue; 740 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 741 continue; 742 /* 743 * Restart canonicalization at beginning 744 */ 745 p = newcp; 746 } 747 xfree((ptr_t) cp); 748 cp = newcp; 749 continue; /* canonicalize the link */ 750 } 751 *sp = '/'; 752 if (sp != cp) 753 while (*--sp != '/') 754 continue; 755 if (slash) { 756 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 757 continue; 758 p = sp; 759 } 760 else if (cp == sp) 761 *++sp = '\0'; 762 else 763 *sp = '\0'; 764 } 765 else { /* normal dir name (not . or .. or nothing) */ 766 767 if (sp != cp && adrof(STRchase_symlinks) && 768 !adrof(STRignore_symlinks) && 769 (cc = readlink(short2str(cp), tlink, 770 sizeof tlink-1)) >= 0) { 771 (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); 772 link[cc] = '\0'; 773 774 /* 775 * restore the '/'. 776 */ 777 if (slash) 778 *p = '/'; 779 780 /* 781 * point sp to p (rather than backing up). 782 */ 783 sp = p; 784 785 /* 786 * find length of p 787 */ 788 for (p1 = p; *p1++;) 789 continue; 790 if (*link != '/') { 791 /* 792 * Relative path, expand it between the "yyy/" and the 793 * remainder. First, back sp up to the character past 794 * "yyy/". 795 */ 796 while (*--sp != '/') 797 continue; 798 sp++; 799 *sp = 0; 800 /* 801 * New length is "yyy/" + link + "/.." and rest 802 */ 803 p1 = newcp = (Char *) xmalloc((size_t) 804 (((sp - cp) + cc + (p1 - p)) 805 * sizeof(Char))); 806 /* 807 * Copy new path into newcp 808 */ 809 for (p2 = cp; (*p1++ = *p2++) != '\0';) 810 continue; 811 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 812 continue; 813 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 814 continue; 815 /* 816 * Restart canonicalization at expanded "/xxx". 817 */ 818 p = sp - cp - 1 + newcp; 819 } 820 else { 821 /* 822 * New length is link + the rest 823 */ 824 p1 = newcp = (Char *) xmalloc((size_t) 825 ((cc + (p1 - p)) * sizeof(Char))); 826 /* 827 * Copy new path into newcp 828 */ 829 for (p2 = link; (*p1++ = *p2++) != '\0';) 830 continue; 831 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 832 continue; 833 /* 834 * Restart canonicalization at beginning 835 */ 836 p = newcp; 837 } 838 xfree((ptr_t) cp); 839 cp = newcp; 840 continue; /* canonicalize the link */ 841 } 842 if (slash) 843 *p = '/'; 844 } 845 } 846 847 /* 848 * fix home... 849 */ 850 p1 = value(STRhome); 851 cc = Strlen(p1); 852 /* 853 * See if we're not in a subdir of STRhome 854 */ 855 if (p1 && *p1 == '/' && 856 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 857 static ino_t home_ino = -1; 858 static dev_t home_dev = -1; 859 static Char *home_ptr = NULL; 860 struct stat statbuf; 861 862 /* 863 * Get dev and ino of STRhome 864 */ 865 if (home_ptr != p1 && 866 stat(short2str(p1), &statbuf) != -1) { 867 home_dev = statbuf.st_dev; 868 home_ino = statbuf.st_ino; 869 home_ptr = p1; 870 } 871 /* 872 * Start comparing dev & ino backwards 873 */ 874 Strlcpy(link, cp, sizeof link/sizeof(Char)); 875 p2 = link; 876 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 877 if (statbuf.st_dev == home_dev && 878 statbuf.st_ino == home_ino) { 879 sp = (Char *) - 1; 880 break; 881 } 882 if ((sp = Strrchr(p2, '/')) != NULL) 883 *sp = '\0'; 884 } 885 /* 886 * See if we found it 887 */ 888 if (*p2 && sp == (Char *) -1) { 889 /* 890 * Use STRhome to make '~' work 891 */ 892 newcp = Strspl(p1, cp + Strlen(p2)); 893 xfree((ptr_t) cp); 894 cp = newcp; 895 } 896 } 897 return cp; 898 } 899 900 901 /* 902 * dnewcwd - make a new directory in the loop the current one 903 */ 904 static void 905 dnewcwd(struct directory *dp) 906 { 907 dcwd = dp; 908 dset(dcwd->di_name); 909 if (printd && !(adrof(STRpushdsilent))) 910 printdirs(); 911 } 912