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