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