1 /* $Header: /src/pub/tcsh/sh.dir.c,v 3.54 2000/11/11 23:03:36 christos Exp $ */ 2 /* 3 * sh.dir.c: Directory manipulation functions 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #include "sh.h" 38 39 RCSID("$Id: sh.dir.c,v 3.54 2000/11/11 23:03:36 christos Exp $") 40 41 /* 42 * C Shell - directory management 43 */ 44 45 static void dstart __P((const char *)); 46 static struct directory *dfind __P((Char *)); 47 static Char *dfollow __P((Char *)); 48 static void printdirs __P((int)); 49 static Char *dgoto __P((Char *)); 50 static void dnewcwd __P((struct directory *, int)); 51 static void dset __P((Char *)); 52 static void dextract __P((struct directory *)); 53 static int skipargs __P((Char ***, char *, char *)); 54 static void dgetstack __P((void)); 55 56 static struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 57 static int printd; /* force name to be printed */ 58 59 int bequiet = 0; /* do not print dir stack -strike */ 60 61 static void 62 dstart(from) 63 const char *from; 64 { 65 xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 66 } 67 68 /* 69 * dinit - initialize current working directory 70 */ 71 void 72 dinit(hp) 73 Char *hp; 74 { 75 register char *tcp; 76 register Char *cp; 77 register struct directory *dp; 78 char path[MAXPATHLEN]; 79 80 /* Don't believe the login shell home, because it may be a symlink */ 81 tcp = (char *) getcwd(path, sizeof(path)); 82 if (tcp == NULL || *tcp == '\0') { 83 xprintf("%s: %s\n", progname, strerror(errno)); 84 if (hp && *hp) { 85 tcp = short2str(hp); 86 dstart(tcp); 87 if (chdir(tcp) == -1) 88 cp = NULL; 89 else 90 cp = Strsave(hp); 91 } 92 else 93 cp = NULL; 94 if (cp == NULL) { 95 dstart("/"); 96 if (chdir("/") == -1) 97 /* I am not even try to print an error message! */ 98 xexit(1); 99 cp = SAVE("/"); 100 } 101 } 102 else { 103 #ifdef S_IFLNK 104 struct stat swd, shp; 105 106 /* 107 * See if $HOME is the working directory we got and use that 108 */ 109 if (hp && *hp && 110 stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 111 DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 112 swd.st_ino == shp.st_ino) 113 cp = Strsave(hp); 114 else { 115 char *cwd; 116 117 /* 118 * use PWD if we have it (for subshells) 119 */ 120 if ((cwd = getenv("PWD")) != NULL) { 121 if (stat(cwd, &shp) != -1 && 122 DEV_DEV_COMPARE(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 #else /* S_IFLNK */ 129 cp = dcanon(SAVE(tcp), STRNULL); 130 #endif /* S_IFLNK */ 131 } 132 133 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 134 dp->di_name = cp; 135 dp->di_count = 0; 136 dhead.di_next = dhead.di_prev = dp; 137 dp->di_next = dp->di_prev = &dhead; 138 printd = 0; 139 dnewcwd(dp, 0); 140 set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB); 141 } 142 143 static void 144 dset(dp) 145 Char *dp; 146 { 147 /* 148 * Don't call set() directly cause if the directory contains ` or 149 * other junk characters glob will fail. 150 */ 151 set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB); 152 set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB); 153 154 tsetenv(STRPWD, dp); 155 } 156 157 #define DIR_PRINT 0x01 /* -p */ 158 #define DIR_LONG 0x02 /* -l */ 159 #define DIR_VERT 0x04 /* -v */ 160 #define DIR_LINE 0x08 /* -n */ 161 #define DIR_SAVE 0x10 /* -S */ 162 #define DIR_LOAD 0x20 /* -L */ 163 #define DIR_CLEAR 0x40 /* -c */ 164 #define DIR_OLD 0x80 /* - */ 165 166 static int 167 skipargs(v, dstr, str) 168 Char ***v; 169 char *dstr; 170 char *str; 171 { 172 Char **n = *v, *s; 173 174 int dflag = 0, loop = 1; 175 for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 176 if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 177 dflag |= DIR_OLD; 178 else { 179 char *p; 180 while (loop && *s != '\0') /* examine flags */ 181 { 182 if ((p = strchr(dstr, *s++)) != NULL) 183 dflag |= (1 << (p - dstr)); 184 else { 185 stderror(ERR_DIRUS, short2str(**v), dstr, str); 186 loop = 0; /* break from both loops */ 187 break; 188 } 189 } 190 } 191 if (*n && (dflag & DIR_OLD)) 192 stderror(ERR_DIRUS, short2str(**v), dstr, str); 193 *v = n; 194 /* make -l, -v, and -n imply -p */ 195 if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 196 dflag |= DIR_PRINT; 197 return dflag; 198 } 199 200 /* 201 * dodirs - list all directories in directory loop 202 */ 203 /*ARGSUSED*/ 204 void 205 dodirs(v, c) 206 Char **v; 207 struct command *c; 208 { 209 static char flags[] = "plvnSLc"; 210 int dflag = skipargs(&v, flags, ""); 211 212 USE(c); 213 if ((dflag & DIR_CLEAR) != 0) { 214 struct directory *dp, *fdp; 215 for (dp = dcwd->di_next; dp != dcwd; ) { 216 fdp = dp; 217 dp = dp->di_next; 218 if (fdp != &dhead) 219 dfree(fdp); 220 } 221 dhead.di_next = dhead.di_prev = dp; 222 dp->di_next = dp->di_prev = &dhead; 223 } 224 if ((dflag & DIR_LOAD) != 0) 225 loaddirs(*v); 226 else if ((dflag & DIR_SAVE) != 0) 227 recdirs(*v, 1); 228 229 if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 230 v++; 231 232 if (*v != NULL || (dflag & DIR_OLD)) 233 stderror(ERR_DIRUS, "dirs", flags, ""); 234 if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 235 printdirs(dflag); 236 } 237 238 static void 239 printdirs(dflag) 240 int dflag; 241 { 242 register struct directory *dp; 243 Char *s, *user; 244 int idx, len, cur; 245 extern int T_Cols; 246 247 dp = dcwd; 248 idx = 0; 249 cur = 0; 250 do { 251 if (dp == &dhead) 252 continue; 253 if (dflag & DIR_VERT) { 254 xprintf("%d\t", idx++); 255 cur = 0; 256 } 257 s = dp->di_name; 258 user = NULL; 259 if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 260 len = (int) (Strlen(user) + Strlen(s) + 2); 261 else 262 len = (int) (Strlen(s) + 1); 263 264 cur += len; 265 if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) { 266 xputchar('\n'); 267 cur = len; 268 } 269 if (user) 270 xprintf("~%S", user); 271 xprintf("%S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 272 } while ((dp = dp->di_prev) != dcwd); 273 if (!(dflag & DIR_VERT)) 274 xputchar('\n'); 275 } 276 277 void 278 dtildepr(dir) 279 Char *dir; 280 { 281 Char* user; 282 if ((user = getusername(&dir)) != NULL) 283 xprintf("~%S%S", user, dir); 284 else 285 xprintf("%S", dir); 286 } 287 288 void 289 dtilde() 290 { 291 struct directory *d = dcwd; 292 293 do { 294 if (d == &dhead) 295 continue; 296 d->di_name = dcanon(d->di_name, STRNULL); 297 } while ((d = d->di_prev) != dcwd); 298 299 dset(dcwd->di_name); 300 } 301 302 303 /* dnormalize(): 304 * The path will be normalized if it 305 * 1) is "..", 306 * 2) or starts with "../", 307 * 3) or ends with "/..", 308 * 4) or contains the string "/../", 309 * then it will be normalized, unless those strings are quoted. 310 * Otherwise, a copy is made and sent back. 311 */ 312 Char * 313 dnormalize(cp, exp) 314 Char *cp; 315 int exp; 316 { 317 318 /* return true if dp is of the form "../xxx" or "/../xxx" */ 319 #define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 320 #define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 321 322 #ifdef S_IFLNK 323 if (exp) { 324 int dotdot = 0; 325 Char *dp, *cwd, *start = cp, buf[MAXPATHLEN]; 326 # ifdef apollo 327 bool slashslash; 328 # endif /* apollo */ 329 330 /* 331 * count the number of "../xxx" or "xxx/../xxx" in the path 332 */ 333 for (dp=start; *dp && *(dp+1); dp++) 334 if (IS_DOTDOT(start, dp)) 335 dotdot++; 336 /* 337 * if none, we are done. 338 */ 339 if (dotdot == 0) 340 return (Strsave(cp)); 341 342 cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) * 343 sizeof(Char))); 344 (void) Strcpy(cwd, dcwd->di_name); 345 346 /* 347 * If the path starts with a slash, we are not relative to 348 * the current working directory. 349 */ 350 if (ABSOLUTEP(start)) 351 *cwd = '\0'; 352 # ifdef apollo 353 slashslash = cwd[0] == '/' && cwd[1] == '/'; 354 # endif /* apollo */ 355 356 /* 357 * Ignore . and count ..'s 358 */ 359 for (;;) { 360 dotdot = 0; 361 buf[0] = '\0'; 362 dp = buf; 363 while (*cp) 364 if (IS_DOT(start, cp)) { 365 if (*++cp) 366 cp++; 367 } 368 else if (IS_DOTDOT(start, cp)) { 369 if (buf[0]) 370 break; /* finish analyzing .././../xxx/[..] */ 371 dotdot++; 372 cp += 2; 373 if (*cp) 374 cp++; 375 } 376 else 377 *dp++ = *cp++; 378 379 *dp = '\0'; 380 while (dotdot > 0) 381 if ((dp = Strrchr(cwd, '/')) != NULL) { 382 # ifdef apollo 383 if (dp == &cwd[1]) 384 slashslash = 1; 385 # endif /* apollo */ 386 *dp = '\0'; 387 dotdot--; 388 } 389 else 390 break; 391 392 if (!*cwd) { /* too many ..'s, starts with "/" */ 393 cwd[0] = '/'; 394 # ifdef apollo 395 cwd[1] = '/'; 396 cwd[2] = '\0'; 397 # else /* !apollo */ 398 cwd[1] = '\0'; 399 # endif /* apollo */ 400 } 401 # ifdef apollo 402 else if (slashslash && cwd[1] == '\0') { 403 cwd[1] = '/'; 404 cwd[2] = '\0'; 405 } 406 # endif /* apollo */ 407 408 if (buf[0]) { 409 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/') 410 cwd[dotdot++] = '/'; 411 cwd[dotdot] = '\0'; 412 dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf); 413 xfree((ptr_t) cwd); 414 cwd = dp; 415 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/') 416 cwd[--dotdot] = '\0'; 417 } 418 if (!*cp) 419 break; 420 } 421 return cwd; 422 } 423 #endif /* S_IFLNK */ 424 return Strsave(cp); 425 } 426 427 428 /* 429 * dochngd - implement chdir command. 430 */ 431 /*ARGSUSED*/ 432 void 433 dochngd(v, c) 434 Char **v; 435 struct command *c; 436 { 437 register Char *cp; 438 register struct directory *dp; 439 int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 440 441 USE(c); 442 printd = 0; 443 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 444 445 if (cp == NULL) { 446 if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 447 stderror(ERR_NAME | ERR_NOHOMEDIR); 448 if (chdir(short2str(cp)) < 0) 449 stderror(ERR_NAME | ERR_CANTCHANGE); 450 cp = Strsave(cp); 451 } 452 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 453 stderror(ERR_NAME | ERR_TOOMANY); 454 /* NOTREACHED */ 455 return; 456 } 457 else if ((dp = dfind(cp)) != 0) { 458 char *tmp; 459 460 printd = 1; 461 if (chdir(tmp = short2str(dp->di_name)) < 0) 462 stderror(ERR_SYSTEM, tmp, strerror(errno)); 463 dcwd->di_prev->di_next = dcwd->di_next; 464 dcwd->di_next->di_prev = dcwd->di_prev; 465 dfree(dcwd); 466 dnewcwd(dp, dflag); 467 return; 468 } 469 else 470 if ((cp = dfollow(cp)) == NULL) 471 return; 472 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 473 dp->di_name = cp; 474 dp->di_count = 0; 475 dp->di_next = dcwd->di_next; 476 dp->di_prev = dcwd->di_prev; 477 dp->di_prev->di_next = dp; 478 dp->di_next->di_prev = dp; 479 dfree(dcwd); 480 dnewcwd(dp, dflag); 481 } 482 483 static Char * 484 dgoto(cp) 485 Char *cp; 486 { 487 Char *dp; 488 489 if (!ABSOLUTEP(cp)) 490 { 491 register Char *p, *q; 492 int cwdlen; 493 494 for (p = dcwd->di_name; *p++;) 495 continue; 496 if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1) /* root */ 497 cwdlen = 0; 498 for (p = cp; *p++;) 499 continue; 500 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 501 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 502 continue; 503 if (cwdlen) 504 p[-1] = '/'; 505 else 506 p--; /* don't add a / after root */ 507 for (q = cp; (*p++ = *q++) != '\0';) 508 continue; 509 xfree((ptr_t) cp); 510 cp = dp; 511 dp += cwdlen; 512 } 513 else 514 dp = cp; 515 516 #ifdef WINNT_NATIVE 517 cp = SAVE(getcwd(NULL, 0)); 518 #else /* !WINNT_NATIVE */ 519 cp = dcanon(cp, dp); 520 #endif /* WINNT_NATIVE */ 521 return cp; 522 } 523 524 /* 525 * dfollow - change to arg directory; fall back on cdpath if not valid 526 */ 527 static Char * 528 dfollow(cp) 529 register Char *cp; 530 { 531 register Char *dp; 532 struct varent *c; 533 char ebuf[MAXPATHLEN]; 534 int serrno; 535 536 cp = globone(cp, G_ERROR); 537 #ifdef apollo 538 if (Strchr(cp, '`')) { 539 char *dptr, *ptr; 540 if (chdir(dptr = short2str(cp)) < 0) 541 stderror(ERR_SYSTEM, dptr, strerror(errno)); 542 else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') { 543 xfree((ptr_t) cp); 544 cp = Strsave(str2short(ptr)); 545 return dgoto(cp); 546 } 547 else 548 stderror(ERR_SYSTEM, dptr, ebuf); 549 } 550 #endif /* apollo */ 551 552 (void) strncpy(ebuf, short2str(cp), MAXPATHLEN); 553 ebuf[MAXPATHLEN-1] = '\0'; 554 /* 555 * if we are ignoring symlinks, try to fix relatives now. 556 * if we are expading symlinks, it should be done by now. 557 */ 558 dp = dnormalize(cp, symlinks == SYM_IGNORE); 559 if (chdir(short2str(dp)) >= 0) { 560 xfree((ptr_t) cp); 561 return dgoto(dp); 562 } 563 else { 564 xfree((ptr_t) dp); 565 if (chdir(short2str(cp)) >= 0) 566 return dgoto(cp); 567 else if (errno != ENOENT && errno != ENOTDIR) 568 stderror(ERR_SYSTEM, ebuf, strerror(errno)); 569 serrno = errno; 570 } 571 572 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 573 && (c = adrof(STRcdpath))) { 574 Char **cdp; 575 register Char *p; 576 Char buf[MAXPATHLEN]; 577 578 for (cdp = c->vec; *cdp; cdp++) { 579 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 580 continue; 581 dp[-1] = '/'; 582 for (p = cp; (*dp++ = *p++) != '\0';) 583 continue; 584 /* 585 * We always want to fix the directory here 586 * If we are normalizing symlinks 587 */ 588 dp = dnormalize(buf, symlinks == SYM_IGNORE || 589 symlinks == SYM_EXPAND); 590 if (chdir(short2str(dp)) >= 0) { 591 printd = 1; 592 xfree((ptr_t) cp); 593 return dgoto(dp); 594 } 595 else if (chdir(short2str(cp)) >= 0) { 596 printd = 1; 597 xfree((ptr_t) dp); 598 return dgoto(cp); 599 } 600 } 601 } 602 dp = varval(cp); 603 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 604 xfree((ptr_t) cp); 605 cp = Strsave(dp); 606 printd = 1; 607 return dgoto(cp); 608 } 609 xfree((ptr_t) cp); 610 /* 611 * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 612 * directories we could get to. 613 */ 614 if (!bequiet) { 615 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 616 return (NULL); 617 } 618 else 619 return (NULL); 620 } 621 622 623 /* 624 * dopushd - push new directory onto directory stack. 625 * with no arguments exchange top and second. 626 * with numeric argument (+n) bring it to top. 627 */ 628 /*ARGSUSED*/ 629 void 630 dopushd(v, c) 631 Char **v; 632 struct command *c; 633 { 634 register struct directory *dp; 635 register Char *cp; 636 int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 637 638 USE(c); 639 printd = 1; 640 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 641 642 if (cp == NULL) { 643 if (adrof(STRpushdtohome)) { 644 if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 645 stderror(ERR_NAME | ERR_NOHOMEDIR); 646 if (chdir(short2str(cp)) < 0) 647 stderror(ERR_NAME | ERR_CANTCHANGE); 648 cp = Strsave(cp); /* hmmm... PWP */ 649 if ((cp = dfollow(cp)) == NULL) 650 return; 651 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 652 dp->di_name = cp; 653 dp->di_count = 0; 654 dp->di_prev = dcwd; 655 dp->di_next = dcwd->di_next; 656 dcwd->di_next = dp; 657 dp->di_next->di_prev = dp; 658 } 659 else { 660 char *tmp; 661 662 if ((dp = dcwd->di_prev) == &dhead) 663 dp = dhead.di_prev; 664 if (dp == dcwd) 665 stderror(ERR_NAME | ERR_NODIR); 666 if (chdir(tmp = short2str(dp->di_name)) < 0) 667 stderror(ERR_SYSTEM, tmp, strerror(errno)); 668 dp->di_prev->di_next = dp->di_next; 669 dp->di_next->di_prev = dp->di_prev; 670 dp->di_next = dcwd->di_next; 671 dp->di_prev = dcwd; 672 dcwd->di_next->di_prev = dp; 673 dcwd->di_next = dp; 674 } 675 } 676 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 677 stderror(ERR_NAME | ERR_TOOMANY); 678 /* NOTREACHED */ 679 return; 680 } 681 else if ((dp = dfind(cp)) != NULL) { 682 char *tmp; 683 684 if (chdir(tmp = short2str(dp->di_name)) < 0) 685 stderror(ERR_SYSTEM, tmp, strerror(errno)); 686 /* 687 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 688 */ 689 if (adrof(STRdextract)) 690 dextract(dp); 691 } 692 else { 693 register Char *ccp; 694 695 if ((ccp = dfollow(cp)) == NULL) 696 return; 697 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 698 dp->di_name = ccp; 699 dp->di_count = 0; 700 dp->di_prev = dcwd; 701 dp->di_next = dcwd->di_next; 702 dcwd->di_next = dp; 703 dp->di_next->di_prev = dp; 704 } 705 dnewcwd(dp, dflag); 706 } 707 708 /* 709 * dfind - find a directory if specified by numeric (+n) argument 710 */ 711 static struct directory * 712 dfind(cp) 713 register Char *cp; 714 { 715 register struct directory *dp; 716 register int i; 717 register Char *ep; 718 719 if (*cp++ != '+') 720 return (0); 721 for (ep = cp; Isdigit(*ep); ep++) 722 continue; 723 if (*ep) 724 return (0); 725 i = getn(cp); 726 if (i <= 0) 727 return (0); 728 for (dp = dcwd; i != 0; i--) { 729 if ((dp = dp->di_prev) == &dhead) 730 dp = dp->di_prev; 731 if (dp == dcwd) 732 stderror(ERR_NAME | ERR_DEEP); 733 } 734 return (dp); 735 } 736 737 /* 738 * dopopd - pop a directory out of the directory stack 739 * with a numeric argument just discard it. 740 */ 741 /*ARGSUSED*/ 742 void 743 dopopd(v, c) 744 Char **v; 745 struct command *c; 746 { 747 Char *cp; 748 register struct directory *dp, *p = NULL; 749 int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 750 751 USE(c); 752 printd = 1; 753 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 754 755 if (cp == NULL) 756 dp = dcwd; 757 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 758 stderror(ERR_NAME | ERR_TOOMANY); 759 /* NOTREACHED */ 760 return; 761 } 762 else if ((dp = dfind(cp)) == 0) 763 stderror(ERR_NAME | ERR_BADDIR); 764 if (dp->di_prev == &dhead && dp->di_next == &dhead) 765 stderror(ERR_NAME | ERR_EMPTY); 766 if (dp == dcwd) { 767 char *tmp; 768 769 if ((p = dp->di_prev) == &dhead) 770 p = dhead.di_prev; 771 if (chdir(tmp = short2str(p->di_name)) < 0) 772 stderror(ERR_SYSTEM, tmp, strerror(errno)); 773 } 774 dp->di_prev->di_next = dp->di_next; 775 dp->di_next->di_prev = dp->di_prev; 776 if (dp == dcwd) { 777 dnewcwd(p, dflag); 778 } 779 else { 780 printdirs(dflag); 781 } 782 dfree(dp); 783 } 784 785 /* 786 * dfree - free the directory (or keep it if it still has ref count) 787 */ 788 void 789 dfree(dp) 790 register struct directory *dp; 791 { 792 793 if (dp->di_count != 0) { 794 dp->di_next = dp->di_prev = 0; 795 } 796 else { 797 xfree((ptr_t) dp->di_name); 798 xfree((ptr_t) dp); 799 } 800 } 801 802 /* 803 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 804 * we are of course assuming that the file system is standardly 805 * constructed (always have ..'s, directories have links) 806 */ 807 Char * 808 dcanon(cp, p) 809 register Char *cp, *p; 810 { 811 register Char *sp; 812 register Char *p1, *p2; /* general purpose */ 813 bool slash; 814 #ifdef apollo 815 bool slashslash; 816 #endif /* apollo */ 817 size_t clen; 818 819 #ifdef S_IFLNK /* if we have symlinks */ 820 Char link[MAXPATHLEN]; 821 char tlink[MAXPATHLEN]; 822 int cc; 823 Char *newcp; 824 #endif /* S_IFLNK */ 825 826 /* 827 * if the path given is too long truncate it! 828 */ 829 if ((clen = Strlen(cp)) >= MAXPATHLEN) 830 cp[clen = MAXPATHLEN - 1] = '\0'; 831 832 /* 833 * christos: if the path given does not start with a slash prepend cwd. If 834 * cwd does not start with a slash or the result would be too long try to 835 * correct it. 836 */ 837 if (!ABSOLUTEP(cp)) { 838 Char tmpdir[MAXPATHLEN]; 839 size_t len; 840 841 p1 = varval(STRcwd); 842 if (p1 == STRNULL || !ABSOLUTEP(p1)) { 843 char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir)); 844 if (tmp == NULL || *tmp == '\0') { 845 xprintf("%s: %s\n", progname, strerror(errno)); 846 set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB); 847 } else { 848 set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB); 849 } 850 p1 = varval(STRcwd); 851 } 852 len = Strlen(p1); 853 if (len + clen + 1 >= MAXPATHLEN) 854 cp[MAXPATHLEN - (len + 1)] = '\0'; 855 (void) Strcpy(tmpdir, p1); 856 (void) Strcat(tmpdir, STRslash); 857 (void) Strcat(tmpdir, cp); 858 xfree((ptr_t) cp); 859 cp = p = Strsave(tmpdir); 860 } 861 862 #ifdef apollo 863 slashslash = (cp[0] == '/' && cp[1] == '/'); 864 #endif /* apollo */ 865 866 while (*p) { /* for each component */ 867 sp = p; /* save slash address */ 868 while (*++p == '/') /* flush extra slashes */ 869 continue; 870 if (p != ++sp) 871 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 872 continue; 873 p = sp; /* save start of component */ 874 slash = 0; 875 if (*p) 876 while (*++p) /* find next slash or end of path */ 877 if (*p == '/') { 878 slash = 1; 879 *p = 0; 880 break; 881 } 882 883 #ifdef apollo 884 if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 885 slashslash = 1; 886 #endif /* apollo */ 887 if (*sp == '\0') { /* if component is null */ 888 if (--sp == cp) /* if path is one char (i.e. /) */ 889 break; 890 else 891 *sp = '\0'; 892 } 893 else if (sp[0] == '.' && sp[1] == 0) { 894 if (slash) { 895 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 896 continue; 897 p = --sp; 898 } 899 else if (--sp != cp) 900 *sp = '\0'; 901 else 902 sp[1] = '\0'; 903 } 904 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 905 /* 906 * We have something like "yyy/xxx/..", where "yyy" can be null or 907 * a path starting at /, and "xxx" is a single component. Before 908 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 909 * symbolic link. 910 */ 911 *--sp = 0; /* form the pathname for readlink */ 912 #ifdef S_IFLNK /* if we have symlinks */ 913 if (sp != cp && /* symlinks != SYM_IGNORE && */ 914 (cc = readlink(short2str(cp), tlink, 915 sizeof tlink)) >= 0) { 916 tlink[cc] = '\0'; 917 (void) Strncpy(link, str2short(tlink), 918 sizeof(link) / sizeof(Char)); 919 link[sizeof(link) / sizeof(Char) - 1] = '\0'; 920 921 if (slash) 922 *p = '/'; 923 /* 924 * Point p to the '/' in "/..", and restore the '/'. 925 */ 926 *(p = sp) = '/'; 927 /* 928 * find length of p 929 */ 930 for (p1 = p; *p1++;) 931 continue; 932 if (*link != '/') { 933 /* 934 * Relative path, expand it between the "yyy/" and the 935 * "/..". First, back sp up to the character past "yyy/". 936 */ 937 while (*--sp != '/') 938 continue; 939 sp++; 940 *sp = 0; 941 /* 942 * New length is "yyy/" + link + "/.." and rest 943 */ 944 p1 = newcp = (Char *) xmalloc((size_t) 945 (((sp - cp) + cc + (p1 - p)) * 946 sizeof(Char))); 947 /* 948 * Copy new path into newcp 949 */ 950 for (p2 = cp; (*p1++ = *p2++) != '\0';) 951 continue; 952 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 953 continue; 954 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 955 continue; 956 /* 957 * Restart canonicalization at expanded "/xxx". 958 */ 959 p = sp - cp - 1 + newcp; 960 } 961 else { 962 /* 963 * New length is link + "/.." and rest 964 */ 965 p1 = newcp = (Char *) xmalloc((size_t) 966 ((cc + (p1 - p)) * sizeof(Char))); 967 /* 968 * Copy new path into newcp 969 */ 970 for (p2 = link; (*p1++ = *p2++) != '\0';) 971 continue; 972 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 973 continue; 974 /* 975 * Restart canonicalization at beginning 976 */ 977 p = newcp; 978 } 979 xfree((ptr_t) cp); 980 cp = newcp; 981 #ifdef apollo 982 slashslash = (cp[0] == '/' && cp[1] == '/'); 983 #endif /* apollo */ 984 continue; /* canonicalize the link */ 985 } 986 #endif /* S_IFLNK */ 987 *sp = '/'; 988 if (sp != cp) 989 while (*--sp != '/') 990 continue; 991 if (slash) { 992 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 993 continue; 994 p = sp; 995 } 996 else if (cp == sp) 997 *++sp = '\0'; 998 else 999 *sp = '\0'; 1000 } 1001 else { /* normal dir name (not . or .. or nothing) */ 1002 1003 #ifdef S_IFLNK /* if we have symlinks */ 1004 if (sp != cp && symlinks == SYM_CHASE && 1005 (cc = readlink(short2str(cp), tlink, 1006 sizeof tlink)) >= 0) { 1007 tlink[cc] = '\0'; 1008 (void) Strncpy(link, str2short(tlink), 1009 sizeof(link) / sizeof(Char)); 1010 link[sizeof(link) / sizeof(Char) - 1] = '\0'; 1011 1012 /* 1013 * restore the '/'. 1014 */ 1015 if (slash) 1016 *p = '/'; 1017 1018 /* 1019 * point sp to p (rather than backing up). 1020 */ 1021 sp = p; 1022 1023 /* 1024 * find length of p 1025 */ 1026 for (p1 = p; *p1++;) 1027 continue; 1028 if (*link != '/') { 1029 /* 1030 * Relative path, expand it between the "yyy/" and the 1031 * remainder. First, back sp up to the character past 1032 * "yyy/". 1033 */ 1034 while (*--sp != '/') 1035 continue; 1036 sp++; 1037 *sp = 0; 1038 /* 1039 * New length is "yyy/" + link + "/.." and rest 1040 */ 1041 p1 = newcp = (Char *) xmalloc((size_t) 1042 (((sp - cp) + cc + (p1 - p)) 1043 * sizeof(Char))); 1044 /* 1045 * Copy new path into newcp 1046 */ 1047 for (p2 = cp; (*p1++ = *p2++) != '\0';) 1048 continue; 1049 for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 1050 continue; 1051 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 1052 continue; 1053 /* 1054 * Restart canonicalization at expanded "/xxx". 1055 */ 1056 p = sp - cp - 1 + newcp; 1057 } 1058 else { 1059 /* 1060 * New length is link + the rest 1061 */ 1062 p1 = newcp = (Char *) xmalloc((size_t) 1063 ((cc + (p1 - p)) * sizeof(Char))); 1064 /* 1065 * Copy new path into newcp 1066 */ 1067 for (p2 = link; (*p1++ = *p2++) != '\0';) 1068 continue; 1069 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 1070 continue; 1071 /* 1072 * Restart canonicalization at beginning 1073 */ 1074 p = newcp; 1075 } 1076 xfree((ptr_t) cp); 1077 cp = newcp; 1078 #ifdef apollo 1079 slashslash = (cp[0] == '/' && cp[1] == '/'); 1080 #endif /* apollo */ 1081 continue; /* canonicalize the link */ 1082 } 1083 #endif /* S_IFLNK */ 1084 if (slash) 1085 *p = '/'; 1086 } 1087 } 1088 1089 /* 1090 * fix home... 1091 */ 1092 #ifdef S_IFLNK 1093 p1 = varval(STRhome); 1094 cc = (int) Strlen(p1); 1095 /* 1096 * See if we're not in a subdir of STRhome 1097 */ 1098 if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 || 1099 (cp[cc] != '/' && cp[cc] != '\0'))) { 1100 static ino_t home_ino = (ino_t) -1; 1101 static dev_t home_dev = (dev_t) -1; 1102 static Char *home_ptr = NULL; 1103 struct stat statbuf; 1104 int found; 1105 1106 /* 1107 * Get dev and ino of STRhome 1108 */ 1109 if (home_ptr != p1 && 1110 stat(short2str(p1), &statbuf) != -1) { 1111 home_dev = statbuf.st_dev; 1112 home_ino = statbuf.st_ino; 1113 home_ptr = p1; 1114 } 1115 /* 1116 * Start comparing dev & ino backwards 1117 */ 1118 p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char)); 1119 link[sizeof(link) / sizeof(Char) - 1] = '\0'; 1120 found = 0; 1121 while (*p2 && stat(short2str(p2), &statbuf) != -1) { 1122 if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 1123 statbuf.st_ino == home_ino) { 1124 found = 1; 1125 break; 1126 } 1127 if ((sp = Strrchr(p2, '/')) != NULL) 1128 *sp = '\0'; 1129 } 1130 /* 1131 * See if we found it 1132 */ 1133 if (*p2 && found) { 1134 /* 1135 * Use STRhome to make '~' work 1136 */ 1137 newcp = Strspl(p1, cp + Strlen(p2)); 1138 xfree((ptr_t) cp); 1139 cp = newcp; 1140 } 1141 } 1142 #endif /* S_IFLNK */ 1143 1144 #ifdef apollo 1145 if (slashslash) { 1146 if (cp[1] != '/') { 1147 p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char)); 1148 *p = '/'; 1149 (void) Strcpy(&p[1], cp); 1150 xfree((ptr_t) cp); 1151 cp = p; 1152 } 1153 } 1154 if (cp[1] == '/' && cp[2] == '/') 1155 (void) Strcpy(&cp[1], &cp[2]); 1156 #endif /* apollo */ 1157 return cp; 1158 } 1159 1160 1161 /* 1162 * dnewcwd - make a new directory in the loop the current one 1163 */ 1164 static void 1165 dnewcwd(dp, dflag) 1166 register struct directory *dp; 1167 int dflag; 1168 { 1169 int print; 1170 1171 if (adrof(STRdunique)) { 1172 struct directory *dn; 1173 1174 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 1175 if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 1176 dn->di_next->di_prev = dn->di_prev; 1177 dn->di_prev->di_next = dn->di_next; 1178 dfree(dn); 1179 break; 1180 } 1181 } 1182 dcwd = dp; 1183 dset(dcwd->di_name); 1184 dgetstack(); 1185 print = printd; /* if printd is set, print dirstack... */ 1186 if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 1187 print = 0; 1188 if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 1189 print = 1; 1190 if (bequiet) /* and bequiet overrides everything */ 1191 print = 0; 1192 if (print) 1193 printdirs(dflag); 1194 cwd_cmd(); /* PWP: run the defined cwd command */ 1195 } 1196 1197 void 1198 dsetstack() 1199 { 1200 Char **cp; 1201 struct varent *vp; 1202 struct directory *dn, *dp; 1203 1204 if ((vp = adrof(STRdirstack)) == NULL) 1205 return; 1206 1207 /* Free the whole stack */ 1208 while ((dn = dhead.di_prev) != &dhead) { 1209 dn->di_next->di_prev = dn->di_prev; 1210 dn->di_prev->di_next = dn->di_next; 1211 if (dn != dcwd) 1212 dfree(dn); 1213 } 1214 1215 /* thread the current working directory */ 1216 dhead.di_prev = dhead.di_next = dcwd; 1217 dcwd->di_next = dcwd->di_prev = &dhead; 1218 1219 /* put back the stack */ 1220 for (cp = vp->vec; cp && *cp && **cp; cp++) { 1221 dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 1222 dp->di_name = Strsave(*cp); 1223 dp->di_count = 0; 1224 dp->di_prev = dcwd; 1225 dp->di_next = dcwd->di_next; 1226 dcwd->di_next = dp; 1227 dp->di_next->di_prev = dp; 1228 } 1229 dgetstack(); /* Make $dirstack reflect the current state */ 1230 } 1231 1232 static void 1233 dgetstack() 1234 { 1235 int i = 0; 1236 Char **dblk, **dbp; 1237 struct directory *dn; 1238 1239 if (adrof(STRdirstack) == NULL) 1240 return; 1241 1242 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 1243 continue; 1244 dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *)); 1245 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 1246 *dbp = Strsave(dn->di_name); 1247 *dbp = NULL; 1248 setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1249 } 1250 1251 /* 1252 * getstakd - added by kfk 17 Jan 1984 1253 * Support routine for the stack hack. Finds nth directory in 1254 * the directory stack, or finds last directory in stack. 1255 */ 1256 int 1257 getstakd(s, cnt) 1258 Char *s; 1259 int cnt; 1260 { 1261 struct directory *dp; 1262 1263 dp = dcwd; 1264 if (cnt < 0) { /* < 0 ==> last dir requested. */ 1265 dp = dp->di_next; 1266 if (dp == &dhead) 1267 dp = dp->di_next; 1268 } 1269 else { 1270 while (cnt-- > 0) { 1271 dp = dp->di_prev; 1272 if (dp == &dhead) 1273 dp = dp->di_prev; 1274 if (dp == dcwd) 1275 return (0); 1276 } 1277 } 1278 (void) Strncpy(s, dp->di_name, BUFSIZE); 1279 s[BUFSIZE - 1] = '\0'; 1280 return (1); 1281 } 1282 1283 /* 1284 * Karl Kleinpaste - 10 Feb 1984 1285 * Added dextract(), which is used in pushd +n. 1286 * Instead of just rotating the entire stack around, dextract() 1287 * lets the user have the nth dir extracted from its current 1288 * position, and pushes it onto the top. 1289 */ 1290 static void 1291 dextract(dp) 1292 struct directory *dp; 1293 { 1294 if (dp == dcwd) 1295 return; 1296 dp->di_next->di_prev = dp->di_prev; 1297 dp->di_prev->di_next = dp->di_next; 1298 dp->di_next = dcwd->di_next; 1299 dp->di_prev = dcwd; 1300 dp->di_next->di_prev = dp; 1301 dcwd->di_next = dp; 1302 } 1303 1304 void 1305 loaddirs(fname) 1306 Char *fname; 1307 { 1308 static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 1309 1310 bequiet = 1; 1311 if (fname) 1312 loaddirs_cmd[1] = fname; 1313 else if ((fname = varval(STRdirsfile)) != STRNULL) 1314 loaddirs_cmd[1] = fname; 1315 else 1316 loaddirs_cmd[1] = STRtildotdirs; 1317 dosource(loaddirs_cmd, (struct command *)0); 1318 bequiet = 0; 1319 } 1320 1321 /* 1322 * create a file called ~/.cshdirs which has a sequence 1323 * of pushd commands which will restore the dir stack to 1324 * its state before exit/logout. remember that the order 1325 * is reversed in the file because we are pushing. 1326 * -strike 1327 */ 1328 void 1329 recdirs(fname, def) 1330 Char *fname; 1331 int def; 1332 { 1333 int fp, ftmp, oldidfds; 1334 int cdflag = 0; 1335 extern struct directory *dcwd; 1336 struct directory *dp; 1337 unsigned int num; 1338 Char *snum; 1339 Char qname[MAXPATHLEN*2]; 1340 1341 if (fname == NULL && !def) 1342 return; 1343 1344 if (fname == NULL) { 1345 if ((fname = varval(STRdirsfile)) == STRNULL) 1346 fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 1347 else 1348 fname = Strsave(fname); 1349 } 1350 else 1351 fname = globone(fname, G_ERROR); 1352 1353 if ((fp = creat(short2str(fname), 0600)) == -1) { 1354 xfree((ptr_t) fname); 1355 return; 1356 } 1357 1358 if ((snum = varval(STRsavedirs)) == STRNULL) 1359 num = (unsigned int) ~0; 1360 else 1361 num = (unsigned int) atoi(short2str(snum)); 1362 1363 oldidfds = didfds; 1364 didfds = 0; 1365 ftmp = SHOUT; 1366 SHOUT = fp; 1367 1368 dp = dcwd->di_next; 1369 do { 1370 if (dp == &dhead) 1371 continue; 1372 1373 if (cdflag == 0) { 1374 cdflag = 1; 1375 xprintf("cd %S\n", quote_meta(qname, dp->di_name)); 1376 } 1377 else 1378 xprintf("pushd %S\n", quote_meta(qname, dp->di_name)); 1379 1380 if (num-- == 0) 1381 break; 1382 1383 } while ((dp = dp->di_next) != dcwd->di_next); 1384 1385 (void) close(fp); 1386 SHOUT = ftmp; 1387 didfds = oldidfds; 1388 xfree((ptr_t) fname); 1389 } 1390