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