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