1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley Software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include "sh.h" 18 #include "sh.dir.h" 19 #include "sh.tconst.h" 20 21 /* 22 * C Shell - directory management 23 */ 24 25 struct directory *dfind(); 26 tchar *dfollow(); 27 tchar *dcanon(); 28 struct directory dhead; /* "head" of loop */ 29 int printd; /* force name to be printed */ 30 static tchar *fakev[] = { S_dirs, NOSTR }; 31 32 /* 33 * dinit - initialize current working directory 34 */ 35 dinit(hp) 36 tchar *hp; 37 { 38 register tchar *cp; 39 register struct directory *dp; 40 tchar path[MAXPATHLEN]; 41 42 #ifdef TRACE 43 tprintf("TRACE- dinit()\n"); 44 #endif 45 /* 46 * If this is a login shell, we should have a home directory. But, 47 * if we got here via 'su - <user>' where the user has no directory 48 * in his passwd file, then su has passed HOME=<nothing>, so hp is 49 * non-null, but has zero length. Thus, we do not know the current 50 * working directory based on the home directory. 51 */ 52 if (loginsh && hp && *hp) 53 cp = hp; 54 else { 55 cp = getwd_(path); 56 if (cp == NULL) { 57 printf ("Warning: cannot determine current directory\n"); 58 cp = S_DOT; 59 } 60 } 61 dp = (struct directory *)calloc(sizeof (struct directory), 1); 62 dp->di_name = savestr(cp); 63 dp->di_count = 0; 64 dhead.di_next = dhead.di_prev = dp; 65 dp->di_next = dp->di_prev = &dhead; 66 printd = 0; 67 dnewcwd(dp); 68 } 69 70 /* 71 * dodirs - list all directories in directory loop 72 */ 73 dodirs(v) 74 tchar **v; 75 { 76 register struct directory *dp; 77 bool lflag; 78 tchar *hp = value(S_home); 79 80 #ifdef TRACE 81 tprintf("TRACE- dodirs()\n"); 82 #endif 83 if (*hp == '\0') 84 hp = NOSTR; 85 if (*++v != NOSTR) 86 if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR) 87 lflag = 1; 88 else 89 error("Usage: dirs [ -l ]"); 90 else 91 lflag = 0; 92 dp = dcwd; 93 do { 94 if (dp == &dhead) 95 continue; 96 if (!lflag && hp != NOSTR) { 97 dtildepr(hp, dp->di_name); 98 } else 99 printf("%t", dp->di_name); 100 printf(" "); 101 } while ((dp = dp->di_prev) != dcwd); 102 printf("\n"); 103 } 104 105 dtildepr(home, dir) 106 register tchar *home, *dir; 107 { 108 109 #ifdef TRACE 110 tprintf("TRACE- dtildepr()\n"); 111 #endif 112 if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir)) 113 printf("~%t", dir + strlen_(home)); 114 else 115 printf("%t", dir); 116 } 117 118 /* 119 * dochngd - implement chdir command. 120 */ 121 dochngd(v) 122 tchar **v; 123 { 124 register tchar *cp; 125 register struct directory *dp; 126 127 #ifdef TRACE 128 tprintf("TRACE- dochngd()\n"); 129 #endif 130 printd = 0; 131 if (*++v == NOSTR) { 132 if ((cp = value(S_home)) == NOSTR || *cp == 0) 133 bferr("No home directory"); 134 if (chdir_(cp) < 0) 135 bferr("Can't change to home directory"); 136 cp = savestr(cp); 137 } else if ((dp = dfind(*v)) != 0) { 138 printd = 1; 139 if (chdir_(dp->di_name) < 0) 140 Perror(dp->di_name); 141 dcwd->di_prev->di_next = dcwd->di_next; 142 dcwd->di_next->di_prev = dcwd->di_prev; 143 goto flushcwd; 144 } else 145 cp = dfollow(*v); 146 dp = (struct directory *)calloc(sizeof (struct directory), 1); 147 dp->di_name = cp; 148 dp->di_count = 0; 149 dp->di_next = dcwd->di_next; 150 dp->di_prev = dcwd->di_prev; 151 dp->di_prev->di_next = dp; 152 dp->di_next->di_prev = dp; 153 flushcwd: 154 dfree(dcwd); 155 dnewcwd(dp); 156 } 157 158 /* 159 * dfollow - change to arg directory; fall back on cdpath if not valid 160 */ 161 tchar * 162 dfollow(cp) 163 register tchar *cp; 164 { 165 register tchar *dp; 166 struct varent *c; 167 int cdhashval, cdhashval1; 168 int index; 169 int slash; /*slashes in the argument*/ 170 tchar *fullpath; 171 tchar *slashcp; /*cp string prepended with a slash*/ 172 173 #ifdef TRACE 174 tprintf("TRACE- dfollow()\n"); 175 #endif 176 cp = globone(cp); 177 if (chdir_(cp) >= 0) 178 goto gotcha; 179 180 /* 181 * If the directory argument has a slash in it, 182 * for example, directory/directory, then can't 183 * find that in the cache table. 184 */ 185 slash = any('/', cp); 186 187 /* 188 * Try interpreting wrt successive components of cdpath. 189 * cdpath caching is turned off or directory argument 190 * has a slash in it. 191 */ 192 if (cp[0] != '/' 193 && !prefix(S_DOTSLA /* "./" */, cp) 194 && !prefix(S_DOTDOTSLA /* "../" */, cp) 195 && (c = adrof(S_cdpath)) 196 && ( !havhash2 || slash) ) { 197 tchar **cdp; 198 register tchar *p; 199 tchar buf[MAXPATHLEN]; 200 201 for (cdp = c->vec; *cdp; cdp++) { 202 for (dp = buf, p = *cdp; *dp++ = *p++;) 203 ; 204 dp[-1] = '/'; 205 for (p = cp; *dp++ = *p++;) 206 ; 207 if (chdir_(buf) >= 0) { 208 printd = 1; 209 xfree(cp); 210 cp = savestr(buf); 211 goto gotcha; 212 } 213 } 214 } 215 216 /* cdpath caching turned on */ 217 if (cp[0] != '/' 218 && !prefix(S_DOTSLA /* "./" */, cp) 219 && !prefix(S_DOTDOTSLA /* "../" */, cp) 220 && (c = adrof(S_cdpath)) 221 && havhash2 && !slash ) { 222 tchar **pv; 223 224 /* If no cdpath or no paths in cdpath, leave */ 225 if ( c == 0 || c->vec[0]== 0 ) 226 pv = justabs; 227 else 228 pv = c->vec; 229 230 slashcp = strspl(S_SLASH, cp); 231 232 cdhashval = hashname(cp); 233 234 /*index points to next path component to test*/ 235 index=0; 236 237 /* 238 * Look at each path in cdpath until get a match. 239 * Only look at those path beginning with a slash 240 */ 241 do { 242 /* only check cache for absolute pathnames */ 243 if ( pv[0][0] == '/' ) { 244 cdhashval1 = hash(cdhashval, index); 245 if (bit(xhash2, cdhashval1)) { 246 /* 247 * concatenate found path with 248 * arg directory 249 */ 250 fullpath = strspl(*pv, slashcp); 251 if (chdir_(fullpath) >= 0) { 252 printd = 1; 253 xfree(cp); 254 cp = savestr(fullpath); 255 xfree(slashcp); 256 xfree(fullpath); 257 goto gotcha; 258 } 259 } 260 } 261 /* 262 * relative pathnames are not cached, and must be 263 * checked manually 264 */ 265 else { 266 register tchar *p; 267 tchar buf[MAXPATHLEN]; 268 269 for (dp = buf, p = *pv; *dp++ = *p++; ) 270 ; 271 dp[-1] = '/'; 272 for (p = cp; *dp++ = *p++; ) 273 ; 274 if (chdir_(buf) >= 0) { 275 printd = 1; 276 xfree(cp); 277 cp = savestr(buf); 278 xfree(slashcp); 279 goto gotcha; 280 } 281 } 282 pv++; 283 index++; 284 } while (*pv); 285 } 286 287 /* 288 * Try dereferencing the variable named by the argument. 289 */ 290 dp = value(cp); 291 if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) { 292 xfree(cp); 293 cp = savestr(dp); 294 printd = 1; 295 goto gotcha; 296 } 297 xfree(cp); /* XXX, use after free */ 298 Perror(cp); 299 300 gotcha: 301 if (*cp != '/') { 302 register tchar *p, *q; 303 int cwdlen; 304 int len; 305 306 /* 307 * All in the name of efficiency? 308 */ 309 310 if ( ( cwdlen = (strlen_(dcwd->di_name) ) ) == 1 ){ 311 if ( *dcwd->di_name == '/' ) /* root */ 312 cwdlen = 0; 313 else 314 { 315 /* 316 * if we are here, when the shell started 317 * it was unable to getwd(), lets try it again 318 */ 319 tchar path[MAXPATHLEN]; 320 321 p = getwd_(path); 322 if (p == NULL) 323 error("cannot determine current directory"); 324 else 325 { 326 xfree(dcwd->di_name); 327 dcwd->di_name = savestr(p); 328 xfree(cp); 329 cp = savestr(p); 330 return dcanon(cp, cp); 331 } 332 333 } 334 } 335 /* 336 * 337 * for (p = cp; *p++;) 338 * ; 339 * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar)) 340 */ 341 len = strlen_(cp); 342 dp = (tchar *)xalloc((unsigned) (cwdlen + len + 2)*sizeof(tchar)); 343 for (p = dp, q = dcwd->di_name; *p++ = *q++;) 344 ; 345 if (cwdlen) 346 p[-1] = '/'; 347 else 348 p--; /* don't add a / after root */ 349 for (q = cp; *p++ = *q++;) 350 ; 351 xfree(cp); 352 cp = dp; 353 dp += cwdlen; 354 } else 355 dp = cp; 356 return dcanon(cp, dp); 357 } 358 359 /* 360 * dopushd - push new directory onto directory stack. 361 * with no arguments exchange top and second. 362 * with numeric argument (+n) bring it to top. 363 */ 364 dopushd(v) 365 tchar **v; 366 { 367 register struct directory *dp; 368 369 #ifdef TRACE 370 tprintf("TRACE- dopushd()\n"); 371 #endif 372 printd = 1; 373 if (*++v == NOSTR) { 374 if ((dp = dcwd->di_prev) == &dhead) 375 dp = dhead.di_prev; 376 if (dp == dcwd) 377 bferr("No other directory"); 378 if (chdir_(dp->di_name) < 0) 379 Perror(dp->di_name); 380 dp->di_prev->di_next = dp->di_next; 381 dp->di_next->di_prev = dp->di_prev; 382 dp->di_next = dcwd->di_next; 383 dp->di_prev = dcwd; 384 dcwd->di_next->di_prev = dp; 385 dcwd->di_next = dp; 386 } else if (dp = dfind(*v)) { 387 if (chdir_(dp->di_name) < 0) 388 Perror(dp->di_name); 389 } else { 390 register tchar *cp; 391 392 cp = dfollow(*v); 393 dp = (struct directory *)calloc(sizeof (struct directory), 1); 394 dp->di_name = cp; 395 dp->di_count = 0; 396 dp->di_prev = dcwd; 397 dp->di_next = dcwd->di_next; 398 dcwd->di_next = dp; 399 dp->di_next->di_prev = dp; 400 } 401 dnewcwd(dp); 402 } 403 404 /* 405 * dfind - find a directory if specified by numeric (+n) argument 406 */ 407 struct directory * 408 dfind(cp) 409 register tchar *cp; 410 { 411 register struct directory *dp; 412 register int i; 413 register tchar *ep; 414 415 #ifdef TRACE 416 tprintf("TRACE- dfind()\n"); 417 #endif 418 if (*cp++ != '+') 419 return (0); 420 for (ep = cp; digit(*ep); ep++) 421 continue; 422 if (*ep) 423 return (0); 424 i = getn(cp); 425 if (i <= 0) 426 return (0); 427 for (dp = dcwd; i != 0; i--) { 428 if ((dp = dp->di_prev) == &dhead) 429 dp = dp->di_prev; 430 if (dp == dcwd) 431 bferr("Directory stack not that deep"); 432 } 433 return (dp); 434 } 435 436 /* 437 * dopopd - pop a directory out of the directory stack 438 * with a numeric argument just discard it. 439 */ 440 dopopd(v) 441 tchar **v; 442 { 443 register struct directory *dp, *p; 444 445 #ifdef TRACE 446 tprintf("TRACE- dopopd()\n"); 447 #endif 448 printd = 1; 449 if (*++v == NOSTR) 450 dp = dcwd; 451 else if ((dp = dfind(*v)) == 0) 452 bferr("Invalid argument"); 453 if (dp->di_prev == &dhead && dp->di_next == &dhead) 454 bferr("Directory stack empty"); 455 if (dp == dcwd) { 456 if ((p = dp->di_prev) == &dhead) 457 p = dhead.di_prev; 458 if (chdir_(p->di_name) < 0) 459 Perror(p->di_name); 460 } 461 dp->di_prev->di_next = dp->di_next; 462 dp->di_next->di_prev = dp->di_prev; 463 if (dp == dcwd) 464 dnewcwd(p); 465 else 466 dodirs(fakev); 467 dfree(dp); 468 } 469 470 /* 471 * dfree - free the directory (or keep it if it still has ref count) 472 */ 473 dfree(dp) 474 register struct directory *dp; 475 { 476 477 #ifdef TRACE 478 tprintf("TRACE- dfree()\n"); 479 #endif 480 if (dp->di_count != 0) 481 dp->di_next = dp->di_prev = 0; 482 else 483 xfree(dp->di_name), xfree( (tchar *)dp); 484 } 485 486 /* 487 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 488 * We are of course assuming that the file system is standardly 489 * constructed (always have ..'s, directories have links). 490 * 491 * If the hardpaths shell variable is set, resolve the 492 * resulting pathname to contain no symbolic link components. 493 */ 494 tchar * 495 dcanon(cp, p) 496 register tchar *cp, *p; 497 { 498 register tchar *sp; /* rightmost component currently under 499 consideration */ 500 register tchar *p1, /* general purpose */ 501 *p2; 502 bool slash, dotdot, hardpaths; 503 504 #ifdef TRACE 505 tprintf("TRACE- dcannon()\n"); 506 #endif 507 508 if (*cp != '/') 509 abort(); 510 511 if (hardpaths = (adrof(S_hardpaths) != NULL)) { 512 /* 513 * Be paranoid: don't trust the initial prefix 514 * to be symlink-free. 515 */ 516 p = cp; 517 } 518 519 /* 520 * Loop invariant: cp points to the overall path start, 521 * p to its as yet uncanonicalized trailing suffix. 522 */ 523 while (*p) { /* for each component */ 524 sp = p; /* save slash address */ 525 526 while (*++p == '/') /* flush extra slashes */ 527 ; 528 if (p != ++sp) 529 for (p1 = sp, p2 = p; *p1++ = *p2++;) 530 ; 531 532 p = sp; /* save start of component */ 533 slash = 0; 534 if (*p) 535 while (*++p) /* find next slash or end of path */ 536 if (*p == '/') { 537 slash = 1; 538 *p = '\0'; 539 break; 540 } 541 542 if (*sp == '\0') { 543 /* component is null */ 544 if (--sp == cp) /* if path is one tchar (i.e. /) */ 545 break; 546 else 547 *sp = '\0'; 548 continue; 549 } 550 551 if (sp[0] == '.' && sp[1] == '\0') { 552 /* Squeeze out component consisting of "." */ 553 if (slash) { 554 for (p1 = sp, p2 = p + 1; *p1++ = *p2++;) 555 ; 556 p = --sp; 557 } else if (--sp != cp) 558 *sp = '\0'; 559 continue; 560 } 561 562 /* 563 * At this point we have a path of the form "x/yz", 564 * where "x" is null or rooted at "/", "y" is a single 565 * component, and "z" is possibly null. The pointer cp 566 * points to the start of "x", sp to the start of "y", 567 * and p to the beginning of "z", which has been forced 568 * to a null. 569 */ 570 /* 571 * Process symbolic link component. Provided that either 572 * the hardpaths shell variable is set or "y" is really 573 * ".." we replace the symlink with its contents. The 574 * second condition for replacement is necessary to make 575 * the command "cd x/.." produce the same results as the 576 * sequence "cd x; cd ..". 577 * 578 * Note that the two conditions correspond to different 579 * potential symlinks. When hardpaths is set, we must 580 * check "x/y"; otherwise, when "y" is known to be "..", 581 * we check "x". 582 */ 583 dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0'; 584 if (hardpaths || dotdot) { 585 tchar link[MAXPATHLEN]; 586 int cc; 587 tchar *newcp; 588 589 /* 590 * Isolate the end of the component that is to 591 * be checked for symlink-hood. 592 */ 593 sp--; 594 if (! hardpaths) 595 *sp = '\0'; 596 597 /* 598 * See whether the component is really a symlink by 599 * trying to read it. If the read succeeds, it is. 600 */ 601 if ((hardpaths || sp > cp) && 602 (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) { 603 /* 604 * readlink_ put null, so we don't need this. 605 */ 606 /* link[cc] = '\0'; */ 607 608 /* Restore path. */ 609 if (slash) 610 *p = '/'; 611 612 /* 613 * Point p at the start of the trailing 614 * path following the symlink component. 615 * It's already there is hardpaths is set. 616 */ 617 if (! hardpaths) { 618 /* Restore path as well. */ 619 *(p = sp) = '/'; 620 } 621 622 /* 623 * Find length of p. 624 */ 625 for (p1 = p; *p1++;) 626 ; 627 628 if (*link != '/') { 629 /* 630 * Relative path: replace the symlink 631 * component with its value. First, 632 * set sp to point to the slash at 633 * its beginning. If hardpaths is 634 * set, this is already the case. 635 */ 636 if (! hardpaths) { 637 while (*--sp != '/') 638 ; 639 } 640 641 /* 642 * Terminate the leading part of the 643 * path, including trailing slash. 644 */ 645 sp++; 646 *sp = '\0'; 647 648 /* 649 * New length is: "x/" + link + "z" 650 */ 651 p1 = newcp = (tchar *)xalloc((unsigned) 652 ((sp - cp) + cc + (p1 - p))*sizeof (tchar)); 653 /* 654 * Copy new path into newcp 655 */ 656 for (p2 = cp; *p1++ = *p2++;) 657 ; 658 for (p1--, p2 = link; *p1++ = *p2++;) 659 ; 660 for (p1--, p2 = p; *p1++ = *p2++;) 661 ; 662 /* 663 * Restart canonicalization at 664 * expanded "/y". 665 */ 666 p = sp - cp - 1 + newcp; 667 } else { 668 /* 669 * New length is: link + "z" 670 */ 671 p1 = newcp = (tchar *)xalloc((unsigned) 672 (cc + (p1 - p))*sizeof (tchar)); 673 /* 674 * Copy new path into newcp 675 */ 676 for (p2 = link; *p1++ = *p2++;) 677 ; 678 for (p1--, p2 = p; *p1++ = *p2++;) 679 ; 680 /* 681 * Restart canonicalization at beginning 682 */ 683 p = newcp; 684 } 685 xfree(cp); 686 cp = newcp; 687 continue; /* canonicalize the link */ 688 } 689 690 /* The component wasn't a symlink after all. */ 691 if (! hardpaths) 692 *sp = '/'; 693 } 694 695 if (dotdot) { 696 if (sp != cp) 697 while (*--sp != '/') 698 ; 699 if (slash) { 700 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;) 701 ; 702 p = sp; 703 } else if (cp == sp) 704 *++sp = '\0'; 705 else 706 *sp = '\0'; 707 continue; 708 } 709 710 if (slash) 711 *p = '/'; 712 } 713 return cp; 714 } 715 716 /* 717 * dnewcwd - make a new directory in the loop the current one 718 * and export its name to the PWD environment variable. 719 */ 720 dnewcwd(dp) 721 register struct directory *dp; 722 { 723 724 #ifdef TRACE 725 tprintf("TRACE- dnewcwd()\n"); 726 #endif 727 dcwd = dp; 728 #ifdef notdef 729 /* 730 * If we have a fast version of getwd available 731 * and hardpaths is set, it would be reasonable 732 * here to verify that dcwd->di_name really does 733 * name the current directory. Later... 734 */ 735 #endif notdef 736 737 didchdir=1; 738 set(S_cwd, savestr(dcwd->di_name)); 739 didchdir=0; 740 local_setenv(S_PWD, dcwd->di_name); 741 if (printd) 742 dodirs(fakev); 743 } 744