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