1 /* $NetBSD: dir.c,v 1.16 2000/10/08 09:33:31 is 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.16 2000/10/08 09:33:31 is 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 const char *ecp; 85 Char *cp; 86 struct directory *dp; 87 char path[MAXPATHLEN]; 88 static const 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 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(dp) 149 Char *dp; 150 { 151 /* 152 * Don't call set() directly cause if the directory contains ` or 153 * other junk characters glob will fail. 154 */ 155 Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); 156 157 vec[0] = Strsave(dp); 158 vec[1] = 0; 159 setq(STRcwd, vec, &shvhed); 160 Setenv(STRPWD, dp); 161 } 162 163 #define DIR_LONG 1 164 #define DIR_VERT 2 165 #define DIR_LINE 4 166 167 static void 168 skipargs(v, str) 169 Char ***v; 170 char *str; 171 { 172 Char **n = *v, *s; 173 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 'v': 182 dirflag |= DIR_VERT; 183 break; 184 case 'n': 185 dirflag |= DIR_LINE; 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(v, t) 200 Char **v; 201 struct command *t; 202 { 203 skipargs(&v, ""); 204 205 if (*v != NULL) 206 stderror(ERR_DIRUS, "dirs", ""); 207 printdirs(); 208 } 209 210 static void 211 printdirs() 212 { 213 struct directory *dp; 214 Char *s, *hp = value(STRhome); 215 int idx, len, cur; 216 217 if (*hp == '\0') 218 hp = NULL; 219 dp = dcwd; 220 idx = 0; 221 cur = 0; 222 do { 223 if (dp == &dhead) 224 continue; 225 if (dirflag & DIR_VERT) { 226 (void) fprintf(cshout, "%d\t", idx++); 227 cur = 0; 228 } 229 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 230 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 231 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 232 len = Strlen(s = (dp->di_name + len)) + 2; 233 else 234 len = Strlen(s = dp->di_name) + 1; 235 236 cur += len; 237 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 238 (void) fprintf(cshout, "\n"); 239 cur = len; 240 } 241 (void) fprintf(cshout, "%s%s%c", (s != dp->di_name)? "~" : "", 242 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 243 } while ((dp = dp->di_prev) != dcwd); 244 if (!(dirflag & DIR_VERT)) 245 (void) fprintf(cshout, "\n"); 246 } 247 248 void 249 dtildepr(home, dir) 250 Char *home, *dir; 251 { 252 253 if (!eq(home, STRslash) && prefix(home, dir)) 254 (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 255 else 256 (void) fprintf(cshout, "%s", vis_str(dir)); 257 } 258 259 void 260 dtilde() 261 { 262 struct directory *d = dcwd; 263 264 do { 265 if (d == &dhead) 266 continue; 267 d->di_name = dcanon(d->di_name, STRNULL); 268 } while ((d = d->di_prev) != dcwd); 269 270 dset(dcwd->di_name); 271 } 272 273 274 /* dnormalize(): 275 * If the name starts with . or .. then we might need to normalize 276 * it depending on the symbolic link flags 277 */ 278 Char * 279 dnormalize(cp) 280 Char *cp; 281 { 282 283 #define UC (unsigned char) 284 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 285 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 286 287 if ((unsigned char) cp[0] == '/') 288 return (Strsave(cp)); 289 290 if (adrof(STRignore_symlinks)) { 291 int dotdot = 0; 292 Char *dp, *cwd; 293 294 cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * 295 sizeof(Char))); 296 (void) Strcpy(cwd, dcwd->di_name); 297 298 /* 299 * Ignore . and count ..'s 300 */ 301 while (*cp) { 302 if (ISDOT(cp)) { 303 if (*++cp) 304 cp++; 305 } 306 else if (ISDOTDOT(cp)) { 307 dotdot++; 308 cp += 2; 309 if (*cp) 310 cp++; 311 } 312 else 313 break; 314 } 315 while (dotdot > 0) { 316 dp = Strrchr(cwd, '/'); 317 if (dp) { 318 *dp = '\0'; 319 dotdot--; 320 } 321 else 322 break; 323 } 324 325 if (*cp) { 326 cwd[dotdot = Strlen(cwd)] = '/'; 327 cwd[dotdot + 1] = '\0'; 328 dp = Strspl(cwd, cp); 329 xfree((ptr_t) cwd); 330 return dp; 331 } 332 else { 333 if (!*cwd) { 334 cwd[0] = '/'; 335 cwd[1] = '\0'; 336 } 337 return cwd; 338 } 339 } 340 return Strsave(cp); 341 } 342 343 /* 344 * dochngd - implement chdir command. 345 */ 346 void 347 /*ARGSUSED*/ 348 dochngd(v, t) 349 Char **v; 350 struct command *t; 351 { 352 Char *cp; 353 struct directory *dp; 354 355 skipargs(&v, " [<dir>]"); 356 printd = 0; 357 if (*v == NULL) { 358 if ((cp = value(STRhome)) == NULL || *cp == 0) 359 stderror(ERR_NAME | ERR_NOHOMEDIR); 360 if (chdir(short2str(cp)) < 0) 361 stderror(ERR_NAME | ERR_CANTCHANGE); 362 cp = Strsave(cp); 363 } 364 else if (v[1] != NULL) 365 stderror(ERR_NAME | ERR_TOOMANY); 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 /* NOTREACHED */ 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 else if ((dp = dfind(*v)) != NULL) { 523 char *tmp; 524 525 if (chdir(tmp = short2str(dp->di_name)) < 0) 526 stderror(ERR_SYSTEM, tmp, strerror(errno)); 527 } 528 else { 529 Char *ccp; 530 531 ccp = dfollow(*v); 532 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 533 dp->di_name = ccp; 534 dp->di_count = 0; 535 dp->di_prev = dcwd; 536 dp->di_next = dcwd->di_next; 537 dcwd->di_next = dp; 538 dp->di_next->di_prev = dp; 539 } 540 dnewcwd(dp); 541 } 542 543 /* 544 * dfind - find a directory if specified by numeric (+n) argument 545 */ 546 static struct directory * 547 dfind(cp) 548 Char *cp; 549 { 550 struct directory *dp; 551 int i; 552 Char *ep; 553 554 if (*cp++ != '+') 555 return (0); 556 for (ep = cp; Isdigit(*ep); ep++) 557 continue; 558 if (*ep) 559 return (0); 560 i = getn(cp); 561 if (i <= 0) 562 return (0); 563 for (dp = dcwd; i != 0; i--) { 564 if ((dp = dp->di_prev) == &dhead) 565 dp = dp->di_prev; 566 if (dp == dcwd) 567 stderror(ERR_NAME | ERR_DEEP); 568 } 569 return (dp); 570 } 571 572 /* 573 * dopopd - pop a directory out of the directory stack 574 * with a numeric argument just discard it. 575 */ 576 void 577 /*ARGSUSED*/ 578 dopopd(v, t) 579 Char **v; 580 struct command *t; 581 { 582 struct directory *dp, *p = NULL; 583 584 skipargs(&v, " [+<n>]"); 585 printd = 1; 586 if (*v == NULL) 587 dp = dcwd; 588 else if (v[1] != NULL) 589 stderror(ERR_NAME | ERR_TOOMANY); 590 else if ((dp = dfind(*v)) == 0) 591 stderror(ERR_NAME | ERR_BADDIR); 592 if (dp->di_prev == &dhead && dp->di_next == &dhead) 593 stderror(ERR_NAME | ERR_EMPTY); 594 if (dp == dcwd) { 595 char *tmp; 596 597 if ((p = dp->di_prev) == &dhead) 598 p = dhead.di_prev; 599 if (chdir(tmp = short2str(p->di_name)) < 0) 600 stderror(ERR_SYSTEM, tmp, strerror(errno)); 601 } 602 dp->di_prev->di_next = dp->di_next; 603 dp->di_next->di_prev = dp->di_prev; 604 if (dp == dcwd) 605 dnewcwd(p); 606 else { 607 printdirs(); 608 } 609 dfree(dp); 610 } 611 612 /* 613 * dfree - free the directory (or keep it if it still has ref count) 614 */ 615 void 616 dfree(dp) 617 struct directory *dp; 618 { 619 620 if (dp->di_count != 0) { 621 dp->di_next = dp->di_prev = 0; 622 } 623 else { 624 xfree((char *) dp->di_name); 625 xfree((ptr_t) dp); 626 } 627 } 628 629 /* 630 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 631 * we are of course assuming that the file system is standardly 632 * constructed (always have ..'s, directories have links) 633 */ 634 Char * 635 dcanon(cp, p) 636 Char *cp, *p; 637 { 638 Char *sp; 639 Char *p1, *p2; /* general purpose */ 640 bool slash; 641 642 Char link[MAXPATHLEN]; 643 char tlink[MAXPATHLEN]; 644 int cc; 645 Char *newcp; 646 647 /* 648 * christos: if the path given does not start with a slash prepend cwd. If 649 * cwd does not start with a path or the result would be too long abort(). 650 */ 651 if (*cp != '/') { 652 Char tmpdir[MAXPATHLEN]; 653 654 p1 = value(STRcwd); 655 if (p1 == NULL || *p1 != '/') 656 abort(); 657 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 658 abort(); 659 (void) Strcpy(tmpdir, p1); 660 (void) Strcat(tmpdir, STRslash); 661 (void) Strcat(tmpdir, cp); 662 xfree((ptr_t) cp); 663 cp = p = Strsave(tmpdir); 664 } 665 666 while (*p) { /* for each component */ 667 sp = p; /* save slash address */ 668 while (*++p == '/') /* flush extra slashes */ 669 continue; 670 if (p != ++sp) 671 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 672 continue; 673 p = sp; /* save start of component */ 674 slash = 0; 675 while (*++p) /* find next slash or end of path */ 676 if (*p == '/') { 677 slash = 1; 678 *p = 0; 679 break; 680 } 681 682 if (*sp == '\0') { /* if component is null */ 683 if (--sp == cp) /* if path is one char (i.e. /) */ 684 break; 685 else 686 *sp = '\0'; 687 } else if (sp[0] == '.' && sp[1] == 0) { 688 if (slash) { 689 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 690 continue; 691 p = --sp; 692 } 693 else if (--sp != cp) 694 *sp = '\0'; 695 } 696 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 697 /* 698 * We have something like "yyy/xxx/..", where "yyy" can be null or 699 * a path starting at /, and "xxx" is a single component. Before 700 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 701 * symbolic link. 702 */ 703 *--sp = 0; /* form the pathname for readlink */ 704 if (sp != cp && !adrof(STRignore_symlinks) && 705 (cc = readlink(short2str(cp), tlink, 706 sizeof tlink)) >= 0) { 707 (void) Strcpy(link, str2short(tlink)); 708 link[cc] = '\0'; 709 710 if (slash) 711 *p = '/'; 712 /* 713 * Point p to the '/' in "/..", and restore the '/'. 714 */ 715 *(p = sp) = '/'; 716 /* 717 * find length of p 718 */ 719 for (p1 = p; *p1++;) 720 continue; 721 if (*link != '/') { 722 /* 723 * Relative path, expand it between the "yyy/" and the 724 * "/..". First, back sp up to the character past "yyy/". 725 */ 726 while (*--sp != '/') 727 continue; 728 sp++; 729 *sp = 0; 730 /* 731 * New length is "yyy/" + link + "/.." and rest 732 */ 733 p1 = newcp = (Char *) xmalloc((size_t) 734 (((sp - cp) + cc + (p1 - p)) * 735 sizeof(Char))); 736 /* 737 * Copy new path into newcp 738 */ 739 for (p2 = cp; (*p1++ = *p2++) != '\0';) 740 continue; 741 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 742 continue; 743 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 744 continue; 745 /* 746 * Restart canonicalization at expanded "/xxx". 747 */ 748 p = sp - cp - 1 + newcp; 749 } 750 else { 751 /* 752 * New length is link + "/.." and rest 753 */ 754 p1 = newcp = (Char *) xmalloc((size_t) 755 ((cc + (p1 - p)) * sizeof(Char))); 756 /* 757 * Copy new path into newcp 758 */ 759 for (p2 = link; (*p1++ = *p2++) != '\0';) 760 continue; 761 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 762 continue; 763 /* 764 * Restart canonicalization at beginning 765 */ 766 p = newcp; 767 } 768 xfree((ptr_t) cp); 769 cp = newcp; 770 continue; /* canonicalize the link */ 771 } 772 *sp = '/'; 773 if (sp != cp) 774 while (*--sp != '/') 775 continue; 776 if (slash) { 777 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 778 continue; 779 p = sp; 780 } 781 else if (cp == sp) 782 *++sp = '\0'; 783 else 784 *sp = '\0'; 785 } 786 else { /* normal dir name (not . or .. or nothing) */ 787 788 if (sp != cp && adrof(STRchase_symlinks) && 789 !adrof(STRignore_symlinks) && 790 (cc = readlink(short2str(cp), tlink, 791 sizeof tlink)) >= 0) { 792 (void) Strcpy(link, str2short(tlink)); 793 link[cc] = '\0'; 794 795 /* 796 * restore the '/'. 797 */ 798 if (slash) 799 *p = '/'; 800 801 /* 802 * point sp to p (rather than backing up). 803 */ 804 sp = p; 805 806 /* 807 * find length of p 808 */ 809 for (p1 = p; *p1++;) 810 continue; 811 if (*link != '/') { 812 /* 813 * Relative path, expand it between the "yyy/" and the 814 * remainder. First, back sp up to the character past 815 * "yyy/". 816 */ 817 while (*--sp != '/') 818 continue; 819 sp++; 820 *sp = 0; 821 /* 822 * New length is "yyy/" + link + "/.." and rest 823 */ 824 p1 = newcp = (Char *) xmalloc((size_t) 825 (((sp - cp) + cc + (p1 - p)) 826 * sizeof(Char))); 827 /* 828 * Copy new path into newcp 829 */ 830 for (p2 = cp; (*p1++ = *p2++) != '\0';) 831 continue; 832 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 833 continue; 834 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 835 continue; 836 /* 837 * Restart canonicalization at expanded "/xxx". 838 */ 839 p = sp - cp - 1 + newcp; 840 } 841 else { 842 /* 843 * New length is link + the rest 844 */ 845 p1 = newcp = (Char *) xmalloc((size_t) 846 ((cc + (p1 - p)) * sizeof(Char))); 847 /* 848 * Copy new path into newcp 849 */ 850 for (p2 = link; (*p1++ = *p2++) != '\0';) 851 continue; 852 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 853 continue; 854 /* 855 * Restart canonicalization at beginning 856 */ 857 p = newcp; 858 } 859 xfree((ptr_t) cp); 860 cp = newcp; 861 continue; /* canonicalize the link */ 862 } 863 if (slash) 864 *p = '/'; 865 } 866 } 867 868 /* 869 * fix home... 870 */ 871 p1 = value(STRhome); 872 cc = Strlen(p1); 873 /* 874 * See if we're not in a subdir of STRhome 875 */ 876 if (p1 && *p1 == '/' && 877 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 878 static ino_t home_ino; 879 static dev_t home_dev = NODEV; 880 static Char *home_ptr = NULL; 881 struct stat statbuf; 882 883 /* 884 * Get dev and ino of STRhome 885 */ 886 if (home_ptr != p1 && 887 stat(short2str(p1), &statbuf) != -1) { 888 home_dev = statbuf.st_dev; 889 home_ino = statbuf.st_ino; 890 home_ptr = p1; 891 } 892 /* 893 * Start comparing dev & ino backwards 894 */ 895 p2 = Strcpy(link, cp); 896 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 897 if (statbuf.st_dev == home_dev && 898 statbuf.st_ino == home_ino) { 899 sp = (Char *) - 1; 900 break; 901 } 902 if ((sp = Strrchr(p2, '/')) != NULL) 903 *sp = '\0'; 904 } 905 /* 906 * See if we found it 907 */ 908 if (*p2 && sp == (Char *) -1) { 909 /* 910 * Use STRhome to make '~' work 911 */ 912 newcp = Strspl(p1, cp + Strlen(p2)); 913 xfree((ptr_t) cp); 914 cp = newcp; 915 } 916 } 917 return cp; 918 } 919 920 921 /* 922 * dnewcwd - make a new directory in the loop the current one 923 */ 924 static void 925 dnewcwd(dp) 926 struct directory *dp; 927 { 928 dcwd = dp; 929 dset(dcwd->di_name); 930 if (printd && !(adrof(STRpushdsilent))) 931 printdirs(); 932 } 933