sh.dir.c revision 316958
1255570Strasz/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.85 2016/04/08 16:10:52 christos Exp $ */ 2255570Strasz/* 3255570Strasz * sh.dir.c: Directory manipulation functions 4255570Strasz */ 5255570Strasz/*- 6255570Strasz * Copyright (c) 1980, 1991 The Regents of the University of California. 7255570Strasz * All rights reserved. 8255570Strasz * 9255570Strasz * Redistribution and use in source and binary forms, with or without 10255570Strasz * modification, are permitted provided that the following conditions 11255570Strasz * are met: 12255570Strasz * 1. Redistributions of source code must retain the above copyright 13255570Strasz * notice, this list of conditions and the following disclaimer. 14255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 15255570Strasz * notice, this list of conditions and the following disclaimer in the 16255570Strasz * documentation and/or other materials provided with the distribution. 17255570Strasz * 3. Neither the name of the University nor the names of its contributors 18255570Strasz * may be used to endorse or promote products derived from this software 19255570Strasz * without specific prior written permission. 20255570Strasz * 21255570Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31270279Strasz * SUCH DAMAGE. 32270279Strasz */ 33270279Strasz#include "sh.h" 34255570Strasz#include "ed.h" 35255570Strasz 36255570StraszRCSID("$tcsh: sh.dir.c,v 3.85 2016/04/08 16:10:52 christos Exp $") 37255570Strasz 38255570Strasz/* 39255570Strasz * C Shell - directory management 40267606Smav */ 41267606Smav 42255570Straszstatic Char *agetcwd (void); 43255570Straszstatic void dstart (const char *); 44255570Straszstatic struct directory *dfind (Char *); 45255570Straszstatic Char *dfollow (Char *, int); 46255570Straszstatic void printdirs (int); 47255570Straszstatic Char *dgoto (Char *); 48255570Straszstatic void dnewcwd (struct directory *, int); 49255570Straszstatic void dset (Char *); 50255570Straszstatic void dextract (struct directory *); 51255570Straszstatic int skipargs (Char ***, const char *, 52255570Strasz const char *); 53255570Straszstatic void dgetstack (void); 54255570Strasz 55255570Straszstatic struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 56255570Straszstatic int printd; /* force name to be printed */ 57255570Strasz 58255570Straszint bequiet = 0; /* do not print dir stack -strike */ 59255570Strasz 60255570Straszstatic Char * 61255570Straszagetcwd(void) 62255570Strasz{ 63255570Strasz char *buf; 64255570Strasz Char *cwd; 65255570Strasz size_t len; 66255570Strasz 67255570Strasz len = MAXPATHLEN; 68275864Smav buf = xmalloc(len); 69255570Strasz while (getcwd(buf, len) == NULL) { 70275864Smav int err; 71255570Strasz 72255570Strasz err = errno; 73255570Strasz if (err != ERANGE) { 74275864Smav xfree(buf); 75255570Strasz errno = err; 76255570Strasz return NULL; 77255570Strasz } 78279589Smav len *= 2; 79279589Smav buf = xrealloc(buf, len); 80255570Strasz } 81255570Strasz if (*buf == '\0') { 82255570Strasz xfree(buf); 83255570Strasz return NULL; 84255570Strasz } 85255570Strasz cwd = SAVE(buf); 86255570Strasz xfree(buf); 87255570Strasz return cwd; 88255570Strasz} 89255570Strasz 90255570Straszstatic void 91255570Straszdstart(const char *from) 92255570Strasz{ 93255570Strasz xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 94255570Strasz} 95255570Strasz 96255570Strasz/* 97255570Strasz * dinit - initialize current working directory 98255570Strasz */ 99255570Straszvoid 100255570Straszdinit(Char *hp) 101255570Strasz{ 102255570Strasz Char *cp, *tcp; 103255570Strasz struct directory *dp; 104255570Strasz 105255570Strasz /* Don't believe the login shell home, because it may be a symlink */ 106255570Strasz tcp = agetcwd(); 107255570Strasz if (tcp == NULL) { 108255570Strasz xprintf("%s: %s\n", progname, strerror(errno)); 109255570Strasz if (hp && *hp) { 110255570Strasz char *xcp = short2str(hp); 111255570Strasz dstart(xcp); 112255570Strasz if (chdir(xcp) == -1) 113255570Strasz cp = NULL; 114255570Strasz else 115255570Strasz cp = Strsave(hp); 116255570Strasz } 117255570Strasz else 118255570Strasz cp = NULL; 119255570Strasz if (cp == NULL) { 120255570Strasz dstart("/"); 121255570Strasz if (chdir("/") == -1) 122255570Strasz /* I am not even try to print an error message! */ 123255570Strasz xexit(1); 124255570Strasz cp = SAVE("/"); 125275864Smav } 126255570Strasz } 127275864Smav else { 128255570Strasz#ifdef S_IFLNK 129255570Strasz struct stat swd, shp; 130255570Strasz int swd_ok; 131255570Strasz 132275864Smav swd_ok = stat(short2str(tcp), &swd) == 0; 133255570Strasz /* 134255570Strasz * See if $HOME is the working directory we got and use that 135255570Strasz */ 136279589Smav if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 && 137279589Smav DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 138255570Strasz swd.st_ino == shp.st_ino) 139255570Strasz cp = Strsave(hp); 140255570Strasz else { 141255570Strasz char *cwd; 142255570Strasz 143255570Strasz /* 144255570Strasz * use PWD if we have it (for subshells) 145255570Strasz */ 146255570Strasz if (swd_ok && (cwd = getenv("PWD")) != NULL) { 147255570Strasz if (stat(cwd, &shp) != -1 && 148255570Strasz DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 149255570Strasz swd.st_ino == shp.st_ino) { 150255570Strasz tcp = SAVE(cwd); 151255570Strasz cleanup_push(tcp, xfree); 152255570Strasz } 153255570Strasz } 154255570Strasz cleanup_push(tcp, xfree); 155255570Strasz cp = dcanon(tcp, STRNULL); 156255570Strasz cleanup_ignore(tcp); 157255570Strasz cleanup_until(tcp); 158255570Strasz } 159255570Strasz#else /* S_IFLNK */ 160255570Strasz cleanup_push(tcp, xfree); 161255570Strasz cp = dcanon(tcp, STRNULL); 162255570Strasz cleanup_ignore(tcp); 163255570Strasz cleanup_until(tcp); 164255570Strasz#endif /* S_IFLNK */ 165255570Strasz } 166267606Smav 167273543Strasz dp = xcalloc(sizeof(struct directory), 1); 168267606Smav dp->di_name = cp; 169278322Smav dp->di_count = 0; 170267606Smav dhead.di_next = dhead.di_prev = dp; 171267606Smav dp->di_next = dp->di_prev = &dhead; 172267606Smav printd = 0; 173267606Smav dnewcwd(dp, 0); 174267606Smav setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB); 175267606Smav} 176267606Smav 177278322Smavstatic void 178278322Smavdset(Char *dp) 179278322Smav{ 180278322Smav /* 181267606Smav * Don't call set() directly cause if the directory contains ` or 182267606Smav * other junk characters glob will fail. 183267606Smav */ 184267606Smav setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB); 185267606Smav setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB); 186267606Smav tsetenv(STRPWD, dp); 187267606Smav} 188267606Smav 189267606Smav#define DIR_PRINT 0x01 /* -p */ 190267606Smav#define DIR_LONG 0x02 /* -l */ 191267606Smav#define DIR_VERT 0x04 /* -v */ 192267606Smav#define DIR_LINE 0x08 /* -n */ 193267606Smav#define DIR_SAVE 0x10 /* -S */ 194278322Smav#define DIR_LOAD 0x20 /* -L */ 195267606Smav#define DIR_CLEAR 0x40 /* -c */ 196267606Smav#define DIR_OLD 0x80 /* - */ 197267606Smav 198267606Smavstatic int 199267606Smavskipargs(Char ***v, const char *dstr, const char *str) 200278322Smav{ 201267606Smav Char **n = *v, *s; 202267606Smav 203267606Smav int dflag = 0, loop = 1; 204267606Smav for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 205267606Smav if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 206267606Smav dflag |= DIR_OLD; 207267606Smav else if ((*n)[1] == '-' && (*n)[2] == '\0') { /* test for -- */ 208267606Smav n++; 209278322Smav break; 210267606Smav } else { 211267606Smav char *p; 212267606Smav while (*s != '\0') /* examine flags */ { 213273813Strasz if ((p = strchr(dstr, *s++)) != NULL) 214273813Strasz dflag |= (1 << (p - dstr)); 215278322Smav else 216273813Strasz stderror(ERR_DIRUS, short2str(**v), dstr, str); 217273813Strasz } 218273813Strasz } 219278322Smav if (*n && (dflag & DIR_OLD)) 220273813Strasz stderror(ERR_DIRUS, short2str(**v), dstr, str); 221273813Strasz *v = n; 222273813Strasz /* make -l, -v, and -n imply -p */ 223278322Smav if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 224278322Smav dflag |= DIR_PRINT; 225278322Smav return dflag; 226278322Smav} 227273813Strasz 228273813Strasz/* 229273813Strasz * dodirs - list all directories in directory loop 230273813Strasz */ 231273813Strasz/*ARGSUSED*/ 232273813Straszvoid 233273813Straszdodirs(Char **v, struct command *c) 234273813Strasz{ 235273813Strasz static const char flags[] = "plvnSLc"; 236273813Strasz int dflag = skipargs(&v, flags, ""); 237273813Strasz 238273813Strasz USE(c); 239273813Strasz if ((dflag & DIR_CLEAR) != 0) { 240273813Strasz struct directory *dp, *fdp; 241273813Strasz for (dp = dcwd->di_next; dp != dcwd; ) { 242273813Strasz fdp = dp; 243273813Strasz dp = dp->di_next; 244273813Strasz if (fdp != &dhead) 245273813Strasz dfree(fdp); 246273813Strasz } 247273813Strasz dhead.di_next = dhead.di_prev = dp; 248273813Strasz dp->di_next = dp->di_prev = &dhead; 249273813Strasz } 250273813Strasz if ((dflag & DIR_LOAD) != 0) 251273813Strasz loaddirs(*v); 252273813Strasz else if ((dflag & DIR_SAVE) != 0) 253273813Strasz recdirs(*v, 1); 254273813Strasz 255273813Strasz if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 256273813Strasz v++; 257273813Strasz 258273813Strasz if (*v != NULL || (dflag & DIR_OLD)) 259273813Strasz stderror(ERR_DIRUS, "dirs", flags, ""); 260273813Strasz if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 261273813Strasz printdirs(dflag); 262273813Strasz} 263273813Strasz 264273813Straszstatic void 265273813Straszprintdirs(int dflag) 266273813Strasz{ 267273813Strasz struct directory *dp; 268273813Strasz Char *s, *user; 269273813Strasz int idx, len, cur; 270273813Strasz 271273813Strasz dp = dcwd; 272273813Strasz idx = 0; 273273813Strasz cur = 0; 274273813Strasz do { 275273813Strasz if (dp == &dhead) 276255570Strasz continue; 277255570Strasz if (dflag & DIR_VERT) { 278255570Strasz xprintf("%d\t", idx++); 279255570Strasz cur = 0; 280255570Strasz } 281278322Smav s = dp->di_name; 282273543Strasz user = NULL; 283255570Strasz if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 284255570Strasz len = (int) (Strlen(user) + Strlen(s) + 2); 285273543Strasz else 286273543Strasz len = (int) (Strlen(s) + 1); 287255570Strasz 288255570Strasz cur += len; 289255570Strasz if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) { 290255570Strasz xputchar('\n'); 291255570Strasz cur = len; 292255570Strasz } 293255570Strasz if (user) 294255570Strasz xprintf("~%S", user); 295255570Strasz xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 296255570Strasz } while ((dp = dp->di_prev) != dcwd); 297255570Strasz if (!(dflag & DIR_VERT)) 298255570Strasz xputchar('\n'); 299255570Strasz} 300278322Smav 301278322Smavvoid 302273813Straszdtildepr(Char *dir) 303273813Strasz{ 304273813Strasz Char* user; 305278322Smav if ((user = getusername(&dir)) != NULL) 306255570Strasz xprintf("~%-S%S", user, dir); 307255570Strasz else 308278322Smav xprintf("%S", dir); 309278322Smav} 310255570Strasz 311255570Straszvoid 312273813Straszdtilde(void) 313278322Smav{ 314273813Strasz struct directory *d = dcwd; 315273813Strasz 316278322Smav do { 317273813Strasz if (d == &dhead) 318273813Strasz continue; 319255570Strasz d->di_name = dcanon(d->di_name, STRNULL); 320255570Strasz } while ((d = d->di_prev) != dcwd); 321255570Strasz 322255570Strasz dset(dcwd->di_name); 323255570Strasz} 324255570Strasz 325255570Strasz 326255570Strasz/* dnormalize(): 327255570Strasz * The path will be normalized if it 328255570Strasz * 1) is "..", 329255570Strasz * 2) or starts with "../", 330255570Strasz * 3) or ends with "/..", 331255570Strasz * 4) or contains the string "/../", 332255570Strasz * then it will be normalized, unless those strings are quoted. 333255570Strasz * Otherwise, a copy is made and sent back. 334255570Strasz */ 335255570StraszChar * 336255570Straszdnormalize(const Char *cp, int expnd) 337255570Strasz{ 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*/ 492void 493dochngd(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 543static Char * 544dgoto(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 */ 594static Char * 595dfollow(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 xfree(dp); 680 } 681 xfree(buf.s); 682 } 683 dp = varval(cp); 684 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 685 cleanup_until(cp); 686 cp = Strsave(dp); 687 printd = 1; 688 return dgoto(cp); 689 } 690 /* 691 * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 692 * directories we could get to. 693 */ 694 if (!bequiet) 695 stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 696 cleanup_until(cp); 697 return (NULL); 698} 699 700 701/* 702 * dopushd - push new directory onto directory stack. 703 * with no arguments exchange top and second. 704 * with numeric argument (+n) bring it to top. 705 */ 706/*ARGSUSED*/ 707void 708dopushd(Char **v, struct command *c) 709{ 710 struct directory *dp; 711 Char *cp; 712 int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 713 714 USE(c); 715 printd = 1; 716 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 717 718 if (cp == NULL) { 719 if (adrof(STRpushdtohome)) { 720 if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 721 stderror(ERR_NAME | ERR_NOHOMEDIR); 722 if (chdir(short2str(cp)) < 0) 723 stderror(ERR_NAME | ERR_CANTCHANGE); 724 if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 725 return; 726 dp = xcalloc(sizeof(struct directory), 1); 727 dp->di_name = cp; 728 dp->di_count = 0; 729 dp->di_prev = dcwd; 730 dp->di_next = dcwd->di_next; 731 dcwd->di_next = dp; 732 dp->di_next->di_prev = dp; 733 } 734 else { 735 char *tmp; 736 737 if ((dp = dcwd->di_prev) == &dhead) 738 dp = dhead.di_prev; 739 if (dp == dcwd) 740 stderror(ERR_NAME | ERR_NODIR); 741 if (chdir(tmp = short2str(dp->di_name)) < 0) 742 stderror(ERR_SYSTEM, tmp, strerror(errno)); 743 dp->di_prev->di_next = dp->di_next; 744 dp->di_next->di_prev = dp->di_prev; 745 dp->di_next = dcwd->di_next; 746 dp->di_prev = dcwd; 747 dcwd->di_next->di_prev = dp; 748 dcwd->di_next = dp; 749 } 750 } 751 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 752 stderror(ERR_NAME | ERR_TOOMANY); 753 /* NOTREACHED */ 754 return; 755 } 756 else if ((dp = dfind(cp)) != NULL) { 757 char *tmp; 758 759 if (chdir(tmp = short2str(dp->di_name)) < 0) 760 stderror(ERR_SYSTEM, tmp, strerror(errno)); 761 /* 762 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 763 */ 764 if (adrof(STRdextract)) 765 dextract(dp); 766 } 767 else { 768 Char *ccp; 769 770 if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL) 771 return; 772 dp = xcalloc(sizeof(struct directory), 1); 773 dp->di_name = ccp; 774 dp->di_count = 0; 775 dp->di_prev = dcwd; 776 dp->di_next = dcwd->di_next; 777 dcwd->di_next = dp; 778 dp->di_next->di_prev = dp; 779 } 780 dnewcwd(dp, dflag); 781} 782 783/* 784 * dfind - find a directory if specified by numeric (+n) argument 785 */ 786static struct directory * 787dfind(Char *cp) 788{ 789 struct directory *dp; 790 int i; 791 Char *ep; 792 793 if (*cp++ != '+') 794 return (0); 795 for (ep = cp; Isdigit(*ep); ep++) 796 continue; 797 if (*ep) 798 return (0); 799 i = getn(cp); 800 if (i <= 0) 801 return (0); 802 for (dp = dcwd; i != 0; i--) { 803 if ((dp = dp->di_prev) == &dhead) 804 dp = dp->di_prev; 805 if (dp == dcwd) 806 stderror(ERR_NAME | ERR_DEEP); 807 } 808 return (dp); 809} 810 811/* 812 * dopopd - pop a directory out of the directory stack 813 * with a numeric argument just discard it. 814 */ 815/*ARGSUSED*/ 816void 817dopopd(Char **v, struct command *c) 818{ 819 Char *cp; 820 struct directory *dp, *p = NULL; 821 int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 822 823 USE(c); 824 printd = 1; 825 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 826 827 if (cp == NULL) 828 dp = dcwd; 829 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 830 stderror(ERR_NAME | ERR_TOOMANY); 831 /* NOTREACHED */ 832 return; 833 } 834 else if ((dp = dfind(cp)) == 0) 835 stderror(ERR_NAME | ERR_BADDIR); 836 if (dp->di_prev == &dhead && dp->di_next == &dhead) 837 stderror(ERR_NAME | ERR_EMPTY); 838 if (dp == dcwd) { 839 char *tmp; 840 841 if ((p = dp->di_prev) == &dhead) 842 p = dhead.di_prev; 843 if (chdir(tmp = short2str(p->di_name)) < 0) 844 stderror(ERR_SYSTEM, tmp, strerror(errno)); 845 } 846 dp->di_prev->di_next = dp->di_next; 847 dp->di_next->di_prev = dp->di_prev; 848 dfree(dp); 849 if (dp == dcwd) { 850 dnewcwd(p, dflag); 851 } 852 else { 853 printdirs(dflag); 854 } 855} 856 857/* 858 * dfree - free the directory (or keep it if it still has ref count) 859 */ 860void 861dfree(struct directory *dp) 862{ 863 864 if (dp->di_count != 0) { 865 dp->di_next = dp->di_prev = 0; 866 } 867 else { 868 xfree(dp->di_name); 869 xfree(dp); 870 } 871} 872 873/* 874 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 875 * we are of course assuming that the file system is standardly 876 * constructed (always have ..'s, directories have links) 877 */ 878Char * 879dcanon(Char *cp, Char *p) 880{ 881 Char *sp; 882 Char *p1, *p2; /* general purpose */ 883 int slash; 884#ifdef HAVE_SLASHSLASH 885 int slashslash; 886#endif /* HAVE_SLASHSLASH */ 887 size_t clen; 888 889#ifdef S_IFLNK /* if we have symlinks */ 890 Char *mlink, *newcp; 891 char *tlink; 892 size_t cc; 893#endif /* S_IFLNK */ 894 895 clen = Strlen(cp); 896 897 /* 898 * christos: if the path given does not start with a slash prepend cwd. If 899 * cwd does not start with a slash or the result would be too long try to 900 * correct it. 901 */ 902 if (!ABSOLUTEP(cp)) { 903 Char *tmpdir; 904 size_t len; 905 906 p1 = varval(STRcwd); 907 if (p1 == STRNULL || !ABSOLUTEP(p1)) { 908 Char *new_cwd = agetcwd(); 909 910 if (new_cwd == NULL) { 911 xprintf("%s: %s\n", progname, strerror(errno)); 912 setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 913 } 914 else 915 setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 916 p1 = varval(STRcwd); 917 } 918 len = Strlen(p1); 919 tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 920 (void) Strcpy(tmpdir, p1); 921 (void) Strcat(tmpdir, STRslash); 922 (void) Strcat(tmpdir, cp); 923 xfree(cp); 924 cp = p = tmpdir; 925 } 926 927#ifdef HAVE_SLASHSLASH 928 slashslash = (cp[0] == '/' && cp[1] == '/'); 929#endif /* HAVE_SLASHSLASH */ 930 931 while (*p) { /* for each component */ 932 sp = p; /* save slash address */ 933 while (*++p == '/') /* flush extra slashes */ 934 continue; 935 if (p != ++sp) 936 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 937 continue; 938 p = sp; /* save start of component */ 939 slash = 0; 940 if (*p) 941 while (*++p) /* find next slash or end of path */ 942 if (*p == '/') { 943 slash = 1; 944 *p = 0; 945 break; 946 } 947 948#ifdef HAVE_SLASHSLASH 949 if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 950 slashslash = 1; 951#endif /* HAVE_SLASHSLASH */ 952 if (*sp == '\0') { /* if component is null */ 953 if (--sp == cp) /* if path is one char (i.e. /) */ 954 break; 955 else 956 *sp = '\0'; 957 } 958 else if (sp[0] == '.' && sp[1] == 0) { 959 if (slash) { 960 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 961 continue; 962 p = --sp; 963 } 964 else if (--sp != cp) 965 *sp = '\0'; 966 else 967 sp[1] = '\0'; 968 } 969 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 970 /* 971 * We have something like "yyy/xxx/..", where "yyy" can be null or 972 * a path starting at /, and "xxx" is a single component. Before 973 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 974 * symbolic link. 975 */ 976 *--sp = 0; /* form the pathname for readlink */ 977#ifdef S_IFLNK /* if we have symlinks */ 978 if (sp != cp && /* symlinks != SYM_IGNORE && */ 979 (tlink = areadlink(short2str(cp))) != NULL) { 980 mlink = str2short(tlink); 981 xfree(tlink); 982 983 if (slash) 984 *p = '/'; 985 /* 986 * Point p to the '/' in "/..", and restore the '/'. 987 */ 988 *(p = sp) = '/'; 989 if (*mlink != '/') { 990 /* 991 * Relative path, expand it between the "yyy/" and the 992 * "/..". First, back sp up to the character past "yyy/". 993 */ 994 while (*--sp != '/') 995 continue; 996 sp++; 997 *sp = 0; 998 /* 999 * New length is "yyy/" + mlink + "/.." and rest 1000 */ 1001 p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1002 Strlen(p) + 1) * sizeof(Char)); 1003 /* 1004 * Copy new path into newcp 1005 */ 1006 for (p2 = cp; (*p1++ = *p2++) != '\0';) 1007 continue; 1008 for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 1009 continue; 1010 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 1011 continue; 1012 /* 1013 * Restart canonicalization at expanded "/xxx". 1014 */ 1015 p = sp - cp - 1 + newcp; 1016 } 1017 else { 1018 newcp = Strspl(mlink, p); 1019 /* 1020 * Restart canonicalization at beginning 1021 */ 1022 p = newcp; 1023 } 1024 xfree(cp); 1025 cp = newcp; 1026#ifdef HAVE_SLASHSLASH 1027 slashslash = (cp[0] == '/' && cp[1] == '/'); 1028#endif /* HAVE_SLASHSLASH */ 1029 continue; /* canonicalize the link */ 1030 } 1031#endif /* S_IFLNK */ 1032 *sp = '/'; 1033 if (sp != cp) 1034 while (*--sp != '/') 1035 continue; 1036 if (slash) { 1037 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 1038 continue; 1039 p = sp; 1040 } 1041 else if (cp == sp) 1042 *++sp = '\0'; 1043 else 1044 *sp = '\0'; 1045 } 1046 else { /* normal dir name (not . or .. or nothing) */ 1047 1048#ifdef S_IFLNK /* if we have symlinks */ 1049 if (sp != cp && symlinks == SYM_CHASE && 1050 (tlink = areadlink(short2str(cp))) != NULL) { 1051 mlink = str2short(tlink); 1052 xfree(tlink); 1053 1054 /* 1055 * restore the '/'. 1056 */ 1057 if (slash) 1058 *p = '/'; 1059 1060 /* 1061 * point sp to p (rather than backing up). 1062 */ 1063 sp = p; 1064 1065 if (*mlink != '/') { 1066 /* 1067 * Relative path, expand it between the "yyy/" and the 1068 * remainder. First, back sp up to the character past 1069 * "yyy/". 1070 */ 1071 while (*--sp != '/') 1072 continue; 1073 sp++; 1074 *sp = 0; 1075 /* 1076 * New length is "yyy/" + mlink + "/.." and rest 1077 */ 1078 p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1079 Strlen(p) + 1) * sizeof(Char)); 1080 /* 1081 * Copy new path into newcp 1082 */ 1083 for (p2 = cp; (*p1++ = *p2++) != '\0';) 1084 continue; 1085 for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 1086 continue; 1087 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 1088 continue; 1089 /* 1090 * Restart canonicalization at expanded "/xxx". 1091 */ 1092 p = sp - cp - 1 + newcp; 1093 } 1094 else { 1095 newcp = Strspl(mlink, p); 1096 /* 1097 * Restart canonicalization at beginning 1098 */ 1099 p = newcp; 1100 } 1101 xfree(cp); 1102 cp = newcp; 1103#ifdef HAVE_SLASHSLASH 1104 slashslash = (cp[0] == '/' && cp[1] == '/'); 1105#endif /* HAVE_SLASHSLASH */ 1106 continue; /* canonicalize the mlink */ 1107 } 1108#endif /* S_IFLNK */ 1109 if (slash) 1110 *p = '/'; 1111 } 1112 } 1113 1114 /* 1115 * fix home... 1116 */ 1117#ifdef S_IFLNK 1118 p1 = varval(STRhome); 1119 cc = Strlen(p1); 1120 /* 1121 * See if we're not in a subdir of STRhome 1122 */ 1123 if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 1124 (cp[cc] != '/' && cp[cc] != '\0'))) { 1125 static ino_t home_ino = (ino_t) -1; 1126 static dev_t home_dev = (dev_t) -1; 1127 static Char *home_ptr = NULL; 1128 struct stat statbuf; 1129 int found; 1130 Char *copy; 1131 1132 /* 1133 * Get dev and ino of STRhome 1134 */ 1135 if (home_ptr != p1 && 1136 stat(short2str(p1), &statbuf) != -1) { 1137 home_dev = statbuf.st_dev; 1138 home_ino = statbuf.st_ino; 1139 home_ptr = p1; 1140 } 1141 /* 1142 * Start comparing dev & ino backwards 1143 */ 1144 p2 = copy = Strsave(cp); 1145 found = 0; 1146 while (*p2 && stat(short2str(p2), &statbuf) != -1) { 1147 if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 1148 statbuf.st_ino == home_ino) { 1149 found = 1; 1150 break; 1151 } 1152 if ((sp = Strrchr(p2, '/')) != NULL) 1153 *sp = '\0'; 1154 } 1155 /* 1156 * See if we found it 1157 */ 1158 if (*p2 && found) { 1159 /* 1160 * Use STRhome to make '~' work 1161 */ 1162 newcp = Strspl(p1, cp + Strlen(p2)); 1163 xfree(cp); 1164 cp = newcp; 1165 } 1166 xfree(copy); 1167 } 1168#endif /* S_IFLNK */ 1169 1170#ifdef HAVE_SLASHSLASH 1171 if (slashslash) { 1172 if (cp[1] != '/') { 1173 p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 1174 *p = '/'; 1175 (void) Strcpy(&p[1], cp); 1176 xfree(cp); 1177 cp = p; 1178 } 1179 } 1180 if (cp[1] == '/' && cp[2] == '/') { 1181 for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 1182 continue; 1183 } 1184#endif /* HAVE_SLASHSLASH */ 1185 return cp; 1186} 1187 1188 1189/* 1190 * dnewcwd - make a new directory in the loop the current one 1191 */ 1192static void 1193dnewcwd(struct directory *dp, int dflag) 1194{ 1195 int print; 1196 1197 if (adrof(STRdunique)) { 1198 struct directory *dn; 1199 1200 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 1201 if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 1202 dn->di_next->di_prev = dn->di_prev; 1203 dn->di_prev->di_next = dn->di_next; 1204 dfree(dn); 1205 break; 1206 } 1207 } 1208 dcwd = dp; 1209 dset(dcwd->di_name); 1210 dgetstack(); 1211 print = printd; /* if printd is set, print dirstack... */ 1212 if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 1213 print = 0; 1214 if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 1215 print = 1; 1216 if (bequiet) /* and bequiet overrides everything */ 1217 print = 0; 1218 if (print) 1219 printdirs(dflag); 1220 cwd_cmd(); /* PWP: run the defined cwd command */ 1221} 1222 1223void 1224dsetstack(void) 1225{ 1226 Char **cp; 1227 struct varent *vp; 1228 struct directory *dn, *dp; 1229 1230 if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 1231 return; 1232 1233 /* Free the whole stack */ 1234 while ((dn = dhead.di_prev) != &dhead) { 1235 dn->di_next->di_prev = dn->di_prev; 1236 dn->di_prev->di_next = dn->di_next; 1237 if (dn != dcwd) 1238 dfree(dn); 1239 } 1240 1241 /* thread the current working directory */ 1242 dhead.di_prev = dhead.di_next = dcwd; 1243 dcwd->di_next = dcwd->di_prev = &dhead; 1244 1245 /* put back the stack */ 1246 for (cp = vp->vec; cp && *cp && **cp; cp++) { 1247 dp = xcalloc(sizeof(struct directory), 1); 1248 dp->di_name = Strsave(*cp); 1249 dp->di_count = 0; 1250 dp->di_prev = dcwd; 1251 dp->di_next = dcwd->di_next; 1252 dcwd->di_next = dp; 1253 dp->di_next->di_prev = dp; 1254 } 1255 dgetstack(); /* Make $dirstack reflect the current state */ 1256} 1257 1258static void 1259dgetstack(void) 1260{ 1261 int i = 0; 1262 Char **dblk, **dbp; 1263 struct directory *dn; 1264 1265 if (adrof(STRdirstack) == NULL) 1266 return; 1267 1268 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 1269 continue; 1270 dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 1271 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 1272 *dbp = Strsave(dn->di_name); 1273 *dbp = NULL; 1274 cleanup_push(dblk, blk_cleanup); 1275 setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1276 cleanup_ignore(dblk); 1277 cleanup_until(dblk); 1278} 1279 1280/* 1281 * getstakd - added by kfk 17 Jan 1984 1282 * Support routine for the stack hack. Finds nth directory in 1283 * the directory stack, or finds last directory in stack. 1284 */ 1285const Char * 1286getstakd(int cnt) 1287{ 1288 struct directory *dp; 1289 1290 dp = dcwd; 1291 if (cnt < 0) { /* < 0 ==> last dir requested. */ 1292 dp = dp->di_next; 1293 if (dp == &dhead) 1294 dp = dp->di_next; 1295 } 1296 else { 1297 while (cnt-- > 0) { 1298 dp = dp->di_prev; 1299 if (dp == &dhead) 1300 dp = dp->di_prev; 1301 if (dp == dcwd) 1302 return NULL; 1303 } 1304 } 1305 return dp->di_name; 1306} 1307 1308/* 1309 * Karl Kleinpaste - 10 Feb 1984 1310 * Added dextract(), which is used in pushd +n. 1311 * Instead of just rotating the entire stack around, dextract() 1312 * lets the user have the nth dir extracted from its current 1313 * position, and pushes it onto the top. 1314 */ 1315static void 1316dextract(struct directory *dp) 1317{ 1318 if (dp == dcwd) 1319 return; 1320 dp->di_next->di_prev = dp->di_prev; 1321 dp->di_prev->di_next = dp->di_next; 1322 dp->di_next = dcwd->di_next; 1323 dp->di_prev = dcwd; 1324 dp->di_next->di_prev = dp; 1325 dcwd->di_next = dp; 1326} 1327 1328static void 1329bequiet_cleanup(void *dummy) 1330{ 1331 USE(dummy); 1332 bequiet = 0; 1333} 1334 1335void 1336loaddirs(Char *fname) 1337{ 1338 static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 1339 1340 bequiet = 1; 1341 cleanup_push(&bequiet, bequiet_cleanup); 1342 if (fname) 1343 loaddirs_cmd[1] = fname; 1344 else if ((fname = varval(STRdirsfile)) != STRNULL) 1345 loaddirs_cmd[1] = fname; 1346 else 1347 loaddirs_cmd[1] = STRtildotdirs; 1348 dosource(loaddirs_cmd, NULL); 1349 cleanup_until(&bequiet); 1350} 1351 1352/* 1353 * create a file called ~/.cshdirs which has a sequence 1354 * of pushd commands which will restore the dir stack to 1355 * its state before exit/logout. remember that the order 1356 * is reversed in the file because we are pushing. 1357 * -strike 1358 */ 1359void 1360recdirs(Char *fname, int def) 1361{ 1362 int fp, ftmp, oldidfds; 1363 int cdflag = 0; 1364 struct directory *dp; 1365 unsigned int num; 1366 Char *snum; 1367 struct Strbuf qname = Strbuf_INIT; 1368 1369 if (fname == NULL && !def) 1370 return; 1371 1372 if (fname == NULL) { 1373 if ((fname = varval(STRdirsfile)) == STRNULL) 1374 fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 1375 else 1376 fname = Strsave(fname); 1377 } 1378 else 1379 fname = globone(fname, G_ERROR); 1380 cleanup_push(fname, xfree); 1381 1382 if ((fp = xcreat(short2str(fname), 0600)) == -1) { 1383 cleanup_until(fname); 1384 return; 1385 } 1386 1387 if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 1388 num = (unsigned int) ~0; 1389 else 1390 num = (unsigned int) atoi(short2str(snum)); 1391 1392 oldidfds = didfds; 1393 didfds = 0; 1394 ftmp = SHOUT; 1395 SHOUT = fp; 1396 1397 cleanup_push(&qname, Strbuf_cleanup); 1398 dp = dcwd->di_next; 1399 do { 1400 if (dp == &dhead) 1401 continue; 1402 1403 if (cdflag == 0) { 1404 cdflag = 1; 1405 xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 1406 } 1407 else 1408 xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 1409 1410 if (num-- == 0) 1411 break; 1412 1413 } while ((dp = dp->di_next) != dcwd->di_next); 1414 1415 xclose(fp); 1416 SHOUT = ftmp; 1417 didfds = oldidfds; 1418 cleanup_until(fname); 1419} 1420