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