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