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