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