1 /*- 2 * Copyright (c) 1980, 1991 The Regents of the University of California. 3 * 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 5.12 (Berkeley) 6/27/91";*/ 36 static char rcsid[] = "$Id: dir.c,v 1.5 1993/08/01 19:00:49 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 = getwd(path); /* see ngetwd.c for System V version */ 85 if (tcp == NULL || *tcp == '\0') { 86 (void) xprintf("csh: %s\n", path); 87 if (hp && *hp) { 88 tcp = short2str(hp); 89 (void) xprintf(emsg, tcp); 90 if (chdir(tcp) == -1) 91 cp = NULL; 92 else 93 cp = hp; 94 } 95 else 96 cp = NULL; 97 if (cp == NULL) { 98 (void) xprintf(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")) { 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(str2short(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, short2str(**v), str); 181 break; 182 } 183 *v = n; 184 } 185 186 /* 187 * dodirs - list all directories in directory loop 188 */ 189 void 190 dodirs(v) 191 Char **v; 192 { 193 skipargs(&v, ""); 194 195 if (*v != NULL) 196 stderror(ERR_DIRUS, "dirs", ""); 197 printdirs(); 198 } 199 200 static void 201 printdirs() 202 { 203 register struct directory *dp; 204 Char *s, *hp = value(STRhome); 205 int idx, len, cur; 206 207 if (*hp == '\0') 208 hp = NULL; 209 dp = dcwd; 210 idx = 0; 211 cur = 0; 212 do { 213 if (dp == &dhead) 214 continue; 215 if (dirflag & DIR_VERT) { 216 xprintf("%d\t", idx++); 217 cur = 0; 218 } 219 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 220 prefix(hp, dp->di_name)) 221 len = Strlen(s = (dp->di_name + Strlen(hp))) + 2; 222 else 223 len = Strlen(s = dp->di_name) + 1; 224 225 cur += len; 226 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 227 xprintf("\n"); 228 cur = len; 229 } 230 xprintf(s != dp->di_name ? "~%s%c" : "%s%c", 231 short2str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 232 } while ((dp = dp->di_prev) != dcwd); 233 if (!(dirflag & DIR_VERT)) 234 xprintf("\n"); 235 } 236 237 void 238 dtildepr(home, dir) 239 register Char *home, *dir; 240 { 241 242 if (!eq(home, STRslash) && prefix(home, dir)) 243 xprintf("~%s", short2str(dir + Strlen(home))); 244 else 245 xprintf("%s", short2str(dir)); 246 } 247 248 void 249 dtilde() 250 { 251 struct directory *d = dcwd; 252 253 do { 254 if (d == &dhead) 255 continue; 256 d->di_name = dcanon(d->di_name, STRNULL); 257 } while ((d = d->di_prev) != dcwd); 258 259 dset(dcwd->di_name); 260 } 261 262 263 /* dnormalize(): 264 * If the name starts with . or .. then we might need to normalize 265 * it depending on the symbolic link flags 266 */ 267 Char * 268 dnormalize(cp) 269 Char *cp; 270 { 271 272 #define UC (unsigned char) 273 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 274 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 275 276 if ((unsigned char) cp[0] == '/') 277 return (Strsave(cp)); 278 279 if (adrof(STRignore_symlinks)) { 280 int dotdot = 0; 281 Char *dp, *cwd; 282 283 cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * 284 sizeof(Char))); 285 (void) Strcpy(cwd, dcwd->di_name); 286 287 /* 288 * Ignore . and count ..'s 289 */ 290 while (*cp) { 291 if (ISDOT(cp)) { 292 if (*++cp) 293 cp++; 294 } 295 else if (ISDOTDOT(cp)) { 296 dotdot++; 297 cp += 2; 298 if (*cp) 299 cp++; 300 } 301 else 302 break; 303 } 304 while (dotdot > 0) 305 if ((dp = Strrchr(cwd, '/'))) { 306 *dp = '\0'; 307 dotdot--; 308 } 309 else 310 break; 311 312 if (*cp) { 313 cwd[dotdot = Strlen(cwd)] = '/'; 314 cwd[dotdot + 1] = '\0'; 315 dp = Strspl(cwd, cp); 316 xfree((ptr_t) cwd); 317 return dp; 318 } 319 else { 320 if (!*cwd) { 321 cwd[0] = '/'; 322 cwd[1] = '\0'; 323 } 324 return cwd; 325 } 326 } 327 return Strsave(cp); 328 } 329 330 /* 331 * dochngd - implement chdir command. 332 */ 333 void 334 dochngd(v) 335 Char **v; 336 { 337 register Char *cp; 338 register struct directory *dp; 339 340 skipargs(&v, " [<dir>]"); 341 printd = 0; 342 if (*v == NULL) { 343 if ((cp = value(STRhome)) == NULL || *cp == 0) 344 stderror(ERR_NAME | ERR_NOHOMEDIR); 345 if (chdir(short2str(cp)) < 0) 346 stderror(ERR_NAME | ERR_CANTCHANGE); 347 cp = Strsave(cp); 348 } 349 else if (v[1] != NULL) { 350 stderror(ERR_NAME | ERR_TOOMANY); 351 /* NOTREACHED */ 352 return; 353 } 354 else if ((dp = dfind(*v)) != 0) { 355 char *tmp; 356 357 printd = 1; 358 if (chdir(tmp = short2str(dp->di_name)) < 0) 359 stderror(ERR_SYSTEM, tmp, strerror(errno)); 360 dcwd->di_prev->di_next = dcwd->di_next; 361 dcwd->di_next->di_prev = dcwd->di_prev; 362 dfree(dcwd); 363 dnewcwd(dp); 364 return; 365 } 366 else 367 cp = dfollow(*v); 368 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 369 dp->di_name = cp; 370 dp->di_count = 0; 371 dp->di_next = dcwd->di_next; 372 dp->di_prev = dcwd->di_prev; 373 dp->di_prev->di_next = dp; 374 dp->di_next->di_prev = dp; 375 dfree(dcwd); 376 dnewcwd(dp); 377 } 378 379 static Char * 380 dgoto(cp) 381 Char *cp; 382 { 383 Char *dp; 384 385 if (*cp != '/') { 386 register Char *p, *q; 387 int cwdlen; 388 389 for (p = dcwd->di_name; *p++;); 390 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 391 cwdlen = 0; 392 for (p = cp; *p++;); 393 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 394 for (p = dp, q = dcwd->di_name; *p++ = *q++;); 395 if (cwdlen) 396 p[-1] = '/'; 397 else 398 p--; /* don't add a / after root */ 399 for (q = cp; *p++ = *q++;); 400 xfree((ptr_t) cp); 401 cp = dp; 402 dp += cwdlen; 403 } 404 else 405 dp = cp; 406 407 cp = dcanon(cp, dp); 408 return cp; 409 } 410 411 /* 412 * dfollow - change to arg directory; fall back on cdpath if not valid 413 */ 414 static Char * 415 dfollow(cp) 416 register Char *cp; 417 { 418 register Char *dp; 419 struct varent *c; 420 char ebuf[MAXPATHLEN]; 421 int serrno; 422 423 cp = globone(cp, G_ERROR); 424 /* 425 * if we are ignoring symlinks, try to fix relatives now. 426 */ 427 dp = dnormalize(cp); 428 if (chdir(short2str(dp)) >= 0) { 429 xfree((ptr_t) cp); 430 return dgoto(dp); 431 } 432 else { 433 xfree((ptr_t) dp); 434 if (chdir(short2str(cp)) >= 0) 435 return dgoto(cp); 436 serrno = errno; 437 } 438 439 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 440 && (c = adrof(STRcdpath))) { 441 Char **cdp; 442 register Char *p; 443 Char buf[MAXPATHLEN]; 444 445 for (cdp = c->vec; *cdp; cdp++) { 446 for (dp = buf, p = *cdp; *dp++ = *p++;); 447 dp[-1] = '/'; 448 for (p = cp; *dp++ = *p++;); 449 if (chdir(short2str(buf)) >= 0) { 450 printd = 1; 451 xfree((ptr_t) cp); 452 cp = Strsave(buf); 453 return dgoto(cp); 454 } 455 } 456 } 457 dp = value(cp); 458 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 459 xfree((ptr_t) cp); 460 cp = Strsave(dp); 461 printd = 1; 462 return dgoto(cp); 463 } 464 (void) strcpy(ebuf, short2str(cp)); 465 xfree((ptr_t) cp); 466 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 467 return (NULL); 468 } 469 470 471 /* 472 * dopushd - push new directory onto directory stack. 473 * with no arguments exchange top and second. 474 * with numeric argument (+n) bring it to top. 475 */ 476 void 477 dopushd(v) 478 Char **v; 479 { 480 register struct directory *dp; 481 482 skipargs(&v, " [<dir>|+<n>]"); 483 printd = 1; 484 if (*v == NULL) { 485 char *tmp; 486 487 if ((dp = dcwd->di_prev) == &dhead) 488 dp = dhead.di_prev; 489 if (dp == dcwd) 490 stderror(ERR_NAME | ERR_NODIR); 491 if (chdir(tmp = short2str(dp->di_name)) < 0) 492 stderror(ERR_SYSTEM, tmp, strerror(errno)); 493 dp->di_prev->di_next = dp->di_next; 494 dp->di_next->di_prev = dp->di_prev; 495 dp->di_next = dcwd->di_next; 496 dp->di_prev = dcwd; 497 dcwd->di_next->di_prev = dp; 498 dcwd->di_next = dp; 499 } 500 else if (v[1] != NULL) { 501 stderror(ERR_NAME | ERR_TOOMANY); 502 /* NOTREACHED */ 503 return; 504 } 505 else if (dp = dfind(*v)) { 506 char *tmp; 507 508 if (chdir(tmp = short2str(dp->di_name)) < 0) 509 stderror(ERR_SYSTEM, tmp, strerror(errno)); 510 } 511 else { 512 register Char *ccp; 513 514 ccp = dfollow(*v); 515 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 516 dp->di_name = ccp; 517 dp->di_count = 0; 518 dp->di_prev = dcwd; 519 dp->di_next = dcwd->di_next; 520 dcwd->di_next = dp; 521 dp->di_next->di_prev = dp; 522 } 523 dnewcwd(dp); 524 } 525 526 /* 527 * dfind - find a directory if specified by numeric (+n) argument 528 */ 529 static struct directory * 530 dfind(cp) 531 register Char *cp; 532 { 533 register struct directory *dp; 534 register int i; 535 register Char *ep; 536 537 if (*cp++ != '+') 538 return (0); 539 for (ep = cp; Isdigit(*ep); ep++) 540 continue; 541 if (*ep) 542 return (0); 543 i = getn(cp); 544 if (i <= 0) 545 return (0); 546 for (dp = dcwd; i != 0; i--) { 547 if ((dp = dp->di_prev) == &dhead) 548 dp = dp->di_prev; 549 if (dp == dcwd) 550 stderror(ERR_NAME | ERR_DEEP); 551 } 552 return (dp); 553 } 554 555 /* 556 * dopopd - pop a directory out of the directory stack 557 * with a numeric argument just discard it. 558 */ 559 void 560 dopopd(v) 561 Char **v; 562 { 563 register struct directory *dp, *p = NULL; 564 565 skipargs(&v, " [+<n>]"); 566 printd = 1; 567 if (*v == NULL) 568 dp = dcwd; 569 else if (v[1] != NULL) { 570 stderror(ERR_NAME | ERR_TOOMANY); 571 /* NOTREACHED */ 572 return; 573 } 574 else if ((dp = dfind(*v)) == 0) 575 stderror(ERR_NAME | ERR_BADDIR); 576 if (dp->di_prev == &dhead && dp->di_next == &dhead) 577 stderror(ERR_NAME | ERR_EMPTY); 578 if (dp == dcwd) { 579 char *tmp; 580 581 if ((p = dp->di_prev) == &dhead) 582 p = dhead.di_prev; 583 if (chdir(tmp = short2str(p->di_name)) < 0) 584 stderror(ERR_SYSTEM, tmp, strerror(errno)); 585 } 586 dp->di_prev->di_next = dp->di_next; 587 dp->di_next->di_prev = dp->di_prev; 588 if (dp == dcwd) 589 dnewcwd(p); 590 else { 591 printdirs(); 592 } 593 dfree(dp); 594 } 595 596 /* 597 * dfree - free the directory (or keep it if it still has ref count) 598 */ 599 void 600 dfree(dp) 601 register struct directory *dp; 602 { 603 604 if (dp->di_count != 0) { 605 dp->di_next = dp->di_prev = 0; 606 } 607 else { 608 xfree((char *) dp->di_name); 609 xfree((ptr_t) dp); 610 } 611 } 612 613 /* 614 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 615 * we are of course assuming that the file system is standardly 616 * constructed (always have ..'s, directories have links) 617 */ 618 Char * 619 dcanon(cp, p) 620 register Char *cp, *p; 621 { 622 register Char *sp; 623 register Char *p1, *p2; /* general purpose */ 624 bool slash; 625 626 Char link[MAXPATHLEN]; 627 char tlink[MAXPATHLEN]; 628 int cc; 629 Char *newcp; 630 631 /* 632 * christos: if the path given does not start with a slash prepend cwd. If 633 * cwd does not start with a path or the result would be too long abort(). 634 */ 635 if (*cp != '/') { 636 Char tmpdir[MAXPATHLEN]; 637 638 p1 = value(STRcwd); 639 if (p1 == NULL || *p1 != '/') 640 abort(); 641 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 642 abort(); 643 (void) Strcpy(tmpdir, p1); 644 (void) Strcat(tmpdir, STRslash); 645 (void) Strcat(tmpdir, cp); 646 xfree((ptr_t) cp); 647 cp = p = Strsave(tmpdir); 648 } 649 650 while (*p) { /* for each component */ 651 sp = p; /* save slash address */ 652 while (*++p == '/') /* flush extra slashes */ 653 ; 654 if (p != ++sp) 655 for (p1 = sp, p2 = p; *p1++ = *p2++;); 656 p = sp; /* save start of component */ 657 slash = 0; 658 while (*++p) /* find next slash or end of path */ 659 if (*p == '/') { 660 slash = 1; 661 *p = 0; 662 break; 663 } 664 665 if (*sp == '\0') /* if component is null */ 666 if (--sp == cp) /* if path is one char (i.e. /) */ 667 break; 668 else 669 *sp = '\0'; 670 else if (sp[0] == '.' && sp[1] == 0) { 671 if (slash) { 672 for (p1 = sp, p2 = p + 1; *p1++ = *p2++;); 673 p = --sp; 674 } 675 else if (--sp != cp) 676 *sp = '\0'; 677 } 678 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 679 /* 680 * We have something like "yyy/xxx/..", where "yyy" can be null or 681 * a path starting at /, and "xxx" is a single component. Before 682 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 683 * symbolic link. 684 */ 685 *--sp = 0; /* form the pathname for readlink */ 686 if (sp != cp && !adrof(STRignore_symlinks) && 687 (cc = readlink(short2str(cp), tlink, 688 sizeof tlink)) >= 0) { 689 (void) Strcpy(link, str2short(tlink)); 690 link[cc] = '\0'; 691 692 if (slash) 693 *p = '/'; 694 /* 695 * Point p to the '/' in "/..", and restore the '/'. 696 */ 697 *(p = sp) = '/'; 698 /* 699 * find length of p 700 */ 701 for (p1 = p; *p1++;); 702 if (*link != '/') { 703 /* 704 * Relative path, expand it between the "yyy/" and the 705 * "/..". First, back sp up to the character past "yyy/". 706 */ 707 while (*--sp != '/'); 708 sp++; 709 *sp = 0; 710 /* 711 * New length is "yyy/" + link + "/.." and rest 712 */ 713 p1 = newcp = (Char *) xmalloc((size_t) 714 (((sp - cp) + cc + (p1 - p)) * 715 sizeof(Char))); 716 /* 717 * Copy new path into newcp 718 */ 719 for (p2 = cp; *p1++ = *p2++;); 720 for (p1--, p2 = link; *p1++ = *p2++;); 721 for (p1--, p2 = p; *p1++ = *p2++;); 722 /* 723 * Restart canonicalization at expanded "/xxx". 724 */ 725 p = sp - cp - 1 + newcp; 726 } 727 else { 728 /* 729 * New length is link + "/.." and rest 730 */ 731 p1 = newcp = (Char *) xmalloc((size_t) 732 ((cc + (p1 - p)) * sizeof(Char))); 733 /* 734 * Copy new path into newcp 735 */ 736 for (p2 = link; *p1++ = *p2++;); 737 for (p1--, p2 = p; *p1++ = *p2++;); 738 /* 739 * Restart canonicalization at beginning 740 */ 741 p = newcp; 742 } 743 xfree((ptr_t) cp); 744 cp = newcp; 745 continue; /* canonicalize the link */ 746 } 747 *sp = '/'; 748 if (sp != cp) 749 while (*--sp != '/'); 750 if (slash) { 751 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;); 752 p = sp; 753 } 754 else if (cp == sp) 755 *++sp = '\0'; 756 else 757 *sp = '\0'; 758 } 759 else { /* normal dir name (not . or .. or nothing) */ 760 761 if (sp != cp && adrof(STRchase_symlinks) && 762 !adrof(STRignore_symlinks) && 763 (cc = readlink(short2str(cp), tlink, 764 sizeof tlink)) >= 0) { 765 (void) Strcpy(link, str2short(tlink)); 766 link[cc] = '\0'; 767 768 /* 769 * restore the '/'. 770 */ 771 if (slash) 772 *p = '/'; 773 774 /* 775 * point sp to p (rather than backing up). 776 */ 777 sp = p; 778 779 /* 780 * find length of p 781 */ 782 for (p1 = p; *p1++;); 783 if (*link != '/') { 784 /* 785 * Relative path, expand it between the "yyy/" and the 786 * remainder. First, back sp up to the character past 787 * "yyy/". 788 */ 789 while (*--sp != '/'); 790 sp++; 791 *sp = 0; 792 /* 793 * New length is "yyy/" + link + "/.." and rest 794 */ 795 p1 = newcp = (Char *) xmalloc((size_t) 796 (((sp - cp) + cc + (p1 - p)) 797 * sizeof(Char))); 798 /* 799 * Copy new path into newcp 800 */ 801 for (p2 = cp; *p1++ = *p2++;); 802 for (p1--, p2 = link; *p1++ = *p2++;); 803 for (p1--, p2 = p; *p1++ = *p2++;); 804 /* 805 * Restart canonicalization at expanded "/xxx". 806 */ 807 p = sp - cp - 1 + newcp; 808 } 809 else { 810 /* 811 * New length is link + the rest 812 */ 813 p1 = newcp = (Char *) xmalloc((size_t) 814 ((cc + (p1 - p)) * sizeof(Char))); 815 /* 816 * Copy new path into newcp 817 */ 818 for (p2 = link; *p1++ = *p2++;); 819 for (p1--, p2 = p; *p1++ = *p2++;); 820 /* 821 * Restart canonicalization at beginning 822 */ 823 p = newcp; 824 } 825 xfree((ptr_t) cp); 826 cp = newcp; 827 continue; /* canonicalize the link */ 828 } 829 if (slash) 830 *p = '/'; 831 } 832 } 833 834 /* 835 * fix home... 836 */ 837 p1 = value(STRhome); 838 cc = Strlen(p1); 839 /* 840 * See if we're not in a subdir of STRhome 841 */ 842 if (p1 && *p1 == '/' && 843 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 844 static ino_t home_ino = -1; 845 static dev_t home_dev = -1; 846 static Char *home_ptr = NULL; 847 struct stat statbuf; 848 849 /* 850 * Get dev and ino of STRhome 851 */ 852 if (home_ptr != p1 && 853 stat(short2str(p1), &statbuf) != -1) { 854 home_dev = statbuf.st_dev; 855 home_ino = statbuf.st_ino; 856 home_ptr = p1; 857 } 858 /* 859 * Start comparing dev & ino backwards 860 */ 861 p2 = Strcpy(link, cp); 862 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 863 if (statbuf.st_dev == home_dev && 864 statbuf.st_ino == home_ino) { 865 sp = (Char *) - 1; 866 break; 867 } 868 if (sp = Strrchr(p2, '/')) 869 *sp = '\0'; 870 } 871 /* 872 * See if we found it 873 */ 874 if (*p2 && sp == (Char *) -1) { 875 /* 876 * Use STRhome to make '~' work 877 */ 878 p2 = cp + Strlen(p2); 879 sp = newcp = (Char *) xmalloc((size_t) 880 /* 19 Sep 92*/ ((cc + Strlen(p2) + 1) * sizeof(Char))); 881 while (*p1) 882 *sp++ = *p1++; 883 while (*p2) 884 *sp++ = *p2++; 885 *sp = '\0'; 886 xfree((ptr_t) cp); 887 cp = newcp; 888 } 889 } 890 return cp; 891 } 892 893 894 /* 895 * dnewcwd - make a new directory in the loop the current one 896 */ 897 static void 898 dnewcwd(dp) 899 register struct directory *dp; 900 { 901 dcwd = dp; 902 dset(dcwd->di_name); 903 if (printd && !(adrof(STRpushdsilent))) 904 printdirs(); 905 } 906