159243Sobrien/* 259243Sobrien * sh.dir.c: Directory manipulation functions 359243Sobrien */ 459243Sobrien/*- 559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 16100616Smp * 3. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien#include "sh.h" 33145479Smp#include "ed.h" 3459243Sobrien 3559243Sobrien/* 3659243Sobrien * C Shell - directory management 3759243Sobrien */ 3859243Sobrien 39167465Smpstatic Char *agetcwd (void); 40167465Smpstatic void dstart (const char *); 41167465Smpstatic struct directory *dfind (Char *); 42195609Smpstatic Char *dfollow (Char *, int); 43167465Smpstatic void printdirs (int); 44167465Smpstatic Char *dgoto (Char *); 45167465Smpstatic void dnewcwd (struct directory *, int); 46167465Smpstatic void dset (Char *); 47167465Smpstatic void dextract (struct directory *); 48167465Smpstatic int skipargs (Char ***, const char *, 49167465Smp const char *); 50167465Smpstatic void dgetstack (void); 5159243Sobrien 5259243Sobrienstatic struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 5359243Sobrienstatic int printd; /* force name to be printed */ 5459243Sobrien 5559243Sobrienint bequiet = 0; /* do not print dir stack -strike */ 5659243Sobrien 57167465Smpstatic Char * 58167465Smpagetcwd(void) 59167465Smp{ 60167465Smp char *buf; 61167465Smp Char *cwd; 62167465Smp size_t len; 63167465Smp 64167465Smp len = MAXPATHLEN; 65167465Smp buf = xmalloc(len); 66167465Smp while (getcwd(buf, len) == NULL) { 67167465Smp int err; 68167465Smp 69167465Smp err = errno; 70167465Smp if (err != ERANGE) { 71167465Smp xfree(buf); 72167465Smp errno = err; 73167465Smp return NULL; 74167465Smp } 75167465Smp len *= 2; 76167465Smp buf = xrealloc(buf, len); 77167465Smp } 78167465Smp if (*buf == '\0') { 79167465Smp xfree(buf); 80167465Smp return NULL; 81167465Smp } 82167465Smp cwd = SAVE(buf); 83167465Smp xfree(buf); 84167465Smp return cwd; 85167465Smp} 86167465Smp 8759243Sobrienstatic void 88167465Smpdstart(const char *from) 8959243Sobrien{ 9059243Sobrien xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 9159243Sobrien} 9259243Sobrien 9359243Sobrien/* 9459243Sobrien * dinit - initialize current working directory 9559243Sobrien */ 9659243Sobrienvoid 97167465Smpdinit(Char *hp) 9859243Sobrien{ 99167465Smp Char *cp, *tcp; 100145479Smp struct directory *dp; 10159243Sobrien 10259243Sobrien /* Don't believe the login shell home, because it may be a symlink */ 103167465Smp tcp = agetcwd(); 104167465Smp if (tcp == NULL) { 10559243Sobrien xprintf("%s: %s\n", progname, strerror(errno)); 10659243Sobrien if (hp && *hp) { 107167465Smp char *xcp = short2str(hp); 108167465Smp dstart(xcp); 109167465Smp if (chdir(xcp) == -1) 11059243Sobrien cp = NULL; 11159243Sobrien else 11259243Sobrien cp = Strsave(hp); 11359243Sobrien } 11459243Sobrien else 11559243Sobrien cp = NULL; 11659243Sobrien if (cp == NULL) { 11759243Sobrien dstart("/"); 11859243Sobrien if (chdir("/") == -1) 11959243Sobrien /* I am not even try to print an error message! */ 12059243Sobrien xexit(1); 12159243Sobrien cp = SAVE("/"); 12259243Sobrien } 12359243Sobrien } 12459243Sobrien else { 12559243Sobrien#ifdef S_IFLNK 12659243Sobrien struct stat swd, shp; 127167465Smp int swd_ok; 12859243Sobrien 129167465Smp swd_ok = stat(short2str(tcp), &swd) == 0; 13059243Sobrien /* 13159243Sobrien * See if $HOME is the working directory we got and use that 13259243Sobrien */ 133167465Smp if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 && 13459243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 13559243Sobrien swd.st_ino == shp.st_ino) 13659243Sobrien cp = Strsave(hp); 13759243Sobrien else { 13859243Sobrien char *cwd; 13959243Sobrien 14059243Sobrien /* 14159243Sobrien * use PWD if we have it (for subshells) 14259243Sobrien */ 143167465Smp if (swd_ok && (cwd = getenv("PWD")) != NULL) { 144167465Smp if (stat(cwd, &shp) != -1 && 14559243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 146167465Smp swd.st_ino == shp.st_ino) { 147167465Smp tcp = SAVE(cwd); 148167465Smp cleanup_push(tcp, xfree); 149167465Smp } 15059243Sobrien } 151167465Smp cleanup_push(tcp, xfree); 152167465Smp cp = dcanon(tcp, STRNULL); 153167465Smp cleanup_ignore(tcp); 154167465Smp cleanup_until(tcp); 15559243Sobrien } 15659243Sobrien#else /* S_IFLNK */ 157167465Smp cleanup_push(tcp, xfree); 158167465Smp cp = dcanon(tcp, STRNULL); 159167465Smp cleanup_ignore(tcp); 160167465Smp cleanup_until(tcp); 16159243Sobrien#endif /* S_IFLNK */ 16259243Sobrien } 16359243Sobrien 164167465Smp dp = xcalloc(sizeof(struct directory), 1); 16559243Sobrien dp->di_name = cp; 16659243Sobrien dp->di_count = 0; 16759243Sobrien dhead.di_next = dhead.di_prev = dp; 16859243Sobrien dp->di_next = dp->di_prev = &dhead; 16959243Sobrien printd = 0; 17059243Sobrien dnewcwd(dp, 0); 171167465Smp setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB); 17259243Sobrien} 17359243Sobrien 17459243Sobrienstatic void 175167465Smpdset(Char *dp) 17659243Sobrien{ 17759243Sobrien /* 17859243Sobrien * Don't call set() directly cause if the directory contains ` or 17959243Sobrien * other junk characters glob will fail. 18059243Sobrien */ 181167465Smp setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB); 182167465Smp setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB); 18359243Sobrien tsetenv(STRPWD, dp); 18459243Sobrien} 18559243Sobrien 18659243Sobrien#define DIR_PRINT 0x01 /* -p */ 18759243Sobrien#define DIR_LONG 0x02 /* -l */ 18859243Sobrien#define DIR_VERT 0x04 /* -v */ 18959243Sobrien#define DIR_LINE 0x08 /* -n */ 19059243Sobrien#define DIR_SAVE 0x10 /* -S */ 19159243Sobrien#define DIR_LOAD 0x20 /* -L */ 19259243Sobrien#define DIR_CLEAR 0x40 /* -c */ 19359243Sobrien#define DIR_OLD 0x80 /* - */ 19459243Sobrien 19559243Sobrienstatic int 196167465Smpskipargs(Char ***v, const char *dstr, const char *str) 19759243Sobrien{ 19859243Sobrien Char **n = *v, *s; 19959243Sobrien 20059243Sobrien int dflag = 0, loop = 1; 20159243Sobrien for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 20259243Sobrien if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 20359243Sobrien dflag |= DIR_OLD; 204231990Smp else if ((*n)[1] == '-' && (*n)[2] == '\0') { /* test for -- */ 205231990Smp n++; 206231990Smp break; 207231990Smp } else { 20859243Sobrien char *p; 209231990Smp while (*s != '\0') /* examine flags */ { 21059243Sobrien if ((p = strchr(dstr, *s++)) != NULL) 21159243Sobrien dflag |= (1 << (p - dstr)); 212167465Smp else 21359243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21459243Sobrien } 21559243Sobrien } 21659243Sobrien if (*n && (dflag & DIR_OLD)) 21759243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21859243Sobrien *v = n; 21959243Sobrien /* make -l, -v, and -n imply -p */ 22059243Sobrien if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 22159243Sobrien dflag |= DIR_PRINT; 22259243Sobrien return dflag; 22359243Sobrien} 22459243Sobrien 22559243Sobrien/* 22659243Sobrien * dodirs - list all directories in directory loop 22759243Sobrien */ 22859243Sobrien/*ARGSUSED*/ 22959243Sobrienvoid 230167465Smpdodirs(Char **v, struct command *c) 23159243Sobrien{ 232167465Smp static const char flags[] = "plvnSLc"; 23359243Sobrien int dflag = skipargs(&v, flags, ""); 23459243Sobrien 23559243Sobrien USE(c); 23659243Sobrien if ((dflag & DIR_CLEAR) != 0) { 23759243Sobrien struct directory *dp, *fdp; 23859243Sobrien for (dp = dcwd->di_next; dp != dcwd; ) { 23959243Sobrien fdp = dp; 24059243Sobrien dp = dp->di_next; 24159243Sobrien if (fdp != &dhead) 24259243Sobrien dfree(fdp); 24359243Sobrien } 24459243Sobrien dhead.di_next = dhead.di_prev = dp; 24559243Sobrien dp->di_next = dp->di_prev = &dhead; 24659243Sobrien } 24759243Sobrien if ((dflag & DIR_LOAD) != 0) 24859243Sobrien loaddirs(*v); 24959243Sobrien else if ((dflag & DIR_SAVE) != 0) 25059243Sobrien recdirs(*v, 1); 25159243Sobrien 25259243Sobrien if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 25359243Sobrien v++; 25459243Sobrien 25559243Sobrien if (*v != NULL || (dflag & DIR_OLD)) 25659243Sobrien stderror(ERR_DIRUS, "dirs", flags, ""); 25759243Sobrien if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 25859243Sobrien printdirs(dflag); 25959243Sobrien} 26059243Sobrien 26159243Sobrienstatic void 262167465Smpprintdirs(int dflag) 26359243Sobrien{ 264145479Smp struct directory *dp; 26559243Sobrien Char *s, *user; 26659243Sobrien int idx, len, cur; 26759243Sobrien 26859243Sobrien dp = dcwd; 26959243Sobrien idx = 0; 27059243Sobrien cur = 0; 27159243Sobrien do { 27259243Sobrien if (dp == &dhead) 27359243Sobrien continue; 27459243Sobrien if (dflag & DIR_VERT) { 27559243Sobrien xprintf("%d\t", idx++); 27659243Sobrien cur = 0; 27759243Sobrien } 27859243Sobrien s = dp->di_name; 27959243Sobrien user = NULL; 28059243Sobrien if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 28159243Sobrien len = (int) (Strlen(user) + Strlen(s) + 2); 28259243Sobrien else 28359243Sobrien len = (int) (Strlen(s) + 1); 28459243Sobrien 28559243Sobrien cur += len; 286167465Smp if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) { 28759243Sobrien xputchar('\n'); 28859243Sobrien cur = len; 28959243Sobrien } 29059243Sobrien if (user) 29159243Sobrien xprintf("~%S", user); 292145479Smp xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 29359243Sobrien } while ((dp = dp->di_prev) != dcwd); 29459243Sobrien if (!(dflag & DIR_VERT)) 29559243Sobrien xputchar('\n'); 29659243Sobrien} 29759243Sobrien 29859243Sobrienvoid 299167465Smpdtildepr(Char *dir) 30059243Sobrien{ 30159243Sobrien Char* user; 30259243Sobrien if ((user = getusername(&dir)) != NULL) 303145479Smp xprintf("~%-S%S", user, dir); 30459243Sobrien else 30559243Sobrien xprintf("%S", dir); 30659243Sobrien} 30759243Sobrien 30859243Sobrienvoid 309167465Smpdtilde(void) 31059243Sobrien{ 31159243Sobrien struct directory *d = dcwd; 31259243Sobrien 31359243Sobrien do { 31459243Sobrien if (d == &dhead) 31559243Sobrien continue; 31659243Sobrien d->di_name = dcanon(d->di_name, STRNULL); 31759243Sobrien } while ((d = d->di_prev) != dcwd); 31859243Sobrien 31959243Sobrien dset(dcwd->di_name); 32059243Sobrien} 32159243Sobrien 32259243Sobrien 32359243Sobrien/* dnormalize(): 32459243Sobrien * The path will be normalized if it 32559243Sobrien * 1) is "..", 32659243Sobrien * 2) or starts with "../", 32759243Sobrien * 3) or ends with "/..", 32859243Sobrien * 4) or contains the string "/../", 32959243Sobrien * then it will be normalized, unless those strings are quoted. 33059243Sobrien * Otherwise, a copy is made and sent back. 33159243Sobrien */ 33259243SobrienChar * 333167465Smpdnormalize(const Char *cp, int expnd) 33459243Sobrien{ 33559243Sobrien 33659243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */ 33759243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 33859243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 33959243Sobrien 34059243Sobrien#ifdef S_IFLNK 341145479Smp if (expnd) { 342167465Smp struct Strbuf buf = Strbuf_INIT; 34359243Sobrien int dotdot = 0; 344167465Smp Char *dp, *cwd; 345167465Smp const Char *start = cp; 346167465Smp# ifdef HAVE_SLASHSLASH 347145479Smp int slashslash; 348167465Smp# endif /* HAVE_SLASHSLASH */ 34959243Sobrien 35059243Sobrien /* 35159243Sobrien * count the number of "../xxx" or "xxx/../xxx" in the path 35259243Sobrien */ 353167465Smp for ( ; *cp && *(cp + 1); cp++) 354167465Smp if (IS_DOTDOT(start, cp)) 35559243Sobrien dotdot++; 356167465Smp 35759243Sobrien /* 35859243Sobrien * if none, we are done. 35959243Sobrien */ 36059243Sobrien if (dotdot == 0) 361167465Smp return (Strsave(start)); 362167465Smp 363167465Smp# ifdef notdef 364167465Smp struct stat sb; 365100616Smp /* 366167465Smp * We disable this test because: 367167465Smp * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1; 368167465Smp * echo ../../dir1 does not expand. We had enabled this before 369167465Smp * because it was bothering people with expansions in compilation 370167465Smp * lines like -I../../foo. Maybe we need some kind of finer grain 371167465Smp * control? 372167465Smp * 373100616Smp * If the path doesn't exist, we are done too. 374100616Smp */ 375167465Smp if (lstat(short2str(start), &sb) != 0 && errno == ENOENT) 376167465Smp return (Strsave(start)); 377167465Smp# endif 378100616Smp 379167465Smp cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char)); 38059243Sobrien (void) Strcpy(cwd, dcwd->di_name); 38159243Sobrien 38259243Sobrien /* 38359243Sobrien * If the path starts with a slash, we are not relative to 38459243Sobrien * the current working directory. 38559243Sobrien */ 38659243Sobrien if (ABSOLUTEP(start)) 38759243Sobrien *cwd = '\0'; 388167465Smp# ifdef HAVE_SLASHSLASH 38959243Sobrien slashslash = cwd[0] == '/' && cwd[1] == '/'; 390167465Smp# endif /* HAVE_SLASHSLASH */ 39159243Sobrien 39259243Sobrien /* 39359243Sobrien * Ignore . and count ..'s 39459243Sobrien */ 395167465Smp cp = start; 396167465Smp do { 39759243Sobrien dotdot = 0; 398167465Smp buf.len = 0; 39959243Sobrien while (*cp) 40059243Sobrien if (IS_DOT(start, cp)) { 40159243Sobrien if (*++cp) 40259243Sobrien cp++; 40359243Sobrien } 40459243Sobrien else if (IS_DOTDOT(start, cp)) { 405167465Smp if (buf.len != 0) 40659243Sobrien break; /* finish analyzing .././../xxx/[..] */ 40759243Sobrien dotdot++; 40859243Sobrien cp += 2; 40959243Sobrien if (*cp) 41059243Sobrien cp++; 41159243Sobrien } 41259243Sobrien else 413167465Smp Strbuf_append1(&buf, *cp++); 41459243Sobrien 415167465Smp Strbuf_terminate(&buf); 41659243Sobrien while (dotdot > 0) 41759243Sobrien if ((dp = Strrchr(cwd, '/')) != NULL) { 418167465Smp# ifdef HAVE_SLASHSLASH 41959243Sobrien if (dp == &cwd[1]) 42059243Sobrien slashslash = 1; 421167465Smp# endif /* HAVE_SLASHSLASH */ 42259243Sobrien *dp = '\0'; 42359243Sobrien dotdot--; 42459243Sobrien } 42559243Sobrien else 42659243Sobrien break; 42759243Sobrien 42859243Sobrien if (!*cwd) { /* too many ..'s, starts with "/" */ 42959243Sobrien cwd[0] = '/'; 430167465Smp# ifdef HAVE_SLASHSLASH 431167465Smp /* 432167465Smp * Only append another slash, if already the former cwd 433167465Smp * was in a double-slash path. 434167465Smp */ 435167465Smp cwd[1] = slashslash ? '/' : '\0'; 43659243Sobrien cwd[2] = '\0'; 437167465Smp# else /* !HAVE_SLASHSLASH */ 43859243Sobrien cwd[1] = '\0'; 439167465Smp# endif /* HAVE_SLASHSLASH */ 44059243Sobrien } 441167465Smp# ifdef HAVE_SLASHSLASH 44259243Sobrien else if (slashslash && cwd[1] == '\0') { 44359243Sobrien cwd[1] = '/'; 44459243Sobrien cwd[2] = '\0'; 44559243Sobrien } 446167465Smp# endif /* HAVE_SLASHSLASH */ 44759243Sobrien 448167465Smp if (buf.len != 0) { 449167465Smp size_t i; 450167465Smp 451167465Smp i = Strlen(cwd); 452167465Smp if (TRM(cwd[i - 1]) != '/') { 453167465Smp cwd[i++] = '/'; 454167465Smp cwd[i] = '\0'; 455167465Smp } 456167465Smp dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s); 457167465Smp xfree(cwd); 45859243Sobrien cwd = dp; 459167465Smp i = Strlen(cwd) - 1; 460167465Smp if (TRM(cwd[i]) == '/') 461167465Smp cwd[i] = '\0'; 46259243Sobrien } 463100616Smp /* Reduction of ".." following the stuff we collected in buf 464100616Smp * only makes sense if the directory item in buf really exists. 465100616Smp * Avoid reduction of "-I../.." (typical compiler call) to "" 466100616Smp * or "/usr/nonexistant/../bin" to "/usr/bin": 467100616Smp */ 468100616Smp if (cwd[0]) { 469100616Smp struct stat exists; 470100616Smp if (0 != stat(short2str(cwd), &exists)) { 471167465Smp xfree(buf.s); 472167465Smp xfree(cwd); 473100616Smp return Strsave(start); 474100616Smp } 475100616Smp } 476167465Smp } while (*cp != '\0'); 477167465Smp xfree(buf.s); 47859243Sobrien return cwd; 47959243Sobrien } 48059243Sobrien#endif /* S_IFLNK */ 48159243Sobrien return Strsave(cp); 48259243Sobrien} 48359243Sobrien 48459243Sobrien 48559243Sobrien/* 48659243Sobrien * dochngd - implement chdir command. 48759243Sobrien */ 48859243Sobrien/*ARGSUSED*/ 48959243Sobrienvoid 490167465Smpdochngd(Char **v, struct command *c) 49159243Sobrien{ 492145479Smp Char *cp; 493145479Smp struct directory *dp; 49459243Sobrien int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 49559243Sobrien 49659243Sobrien USE(c); 49759243Sobrien printd = 0; 49859243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 49959243Sobrien 50059243Sobrien if (cp == NULL) { 501316957Sdchagin if (!cdtohome) 502316957Sdchagin stderror(ERR_NAME | ERR_TOOFEW); 503316957Sdchagin else if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 50459243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 50559243Sobrien if (chdir(short2str(cp)) < 0) 50659243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 50759243Sobrien cp = Strsave(cp); 50859243Sobrien } 50959243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 51059243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 51159243Sobrien /* NOTREACHED */ 51259243Sobrien return; 51359243Sobrien } 51459243Sobrien else if ((dp = dfind(cp)) != 0) { 51559243Sobrien char *tmp; 51659243Sobrien 51759243Sobrien printd = 1; 51859243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 51959243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 52059243Sobrien dcwd->di_prev->di_next = dcwd->di_next; 52159243Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 52259243Sobrien dfree(dcwd); 52359243Sobrien dnewcwd(dp, dflag); 52459243Sobrien return; 52559243Sobrien } 52659243Sobrien else 527195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 52859243Sobrien return; 529167465Smp dp = xcalloc(sizeof(struct directory), 1); 53059243Sobrien dp->di_name = cp; 53159243Sobrien dp->di_count = 0; 53259243Sobrien dp->di_next = dcwd->di_next; 53359243Sobrien dp->di_prev = dcwd->di_prev; 53459243Sobrien dp->di_prev->di_next = dp; 53559243Sobrien dp->di_next->di_prev = dp; 53659243Sobrien dfree(dcwd); 53759243Sobrien dnewcwd(dp, dflag); 53859243Sobrien} 53959243Sobrien 54059243Sobrienstatic Char * 541167465Smpdgoto(Char *cp) 54259243Sobrien{ 543167465Smp Char *dp, *ret; 54459243Sobrien 54559243Sobrien if (!ABSOLUTEP(cp)) 54659243Sobrien { 547145479Smp Char *p, *q; 548167465Smp size_t cwdlen; 54959243Sobrien 550167465Smp cwdlen = Strlen(dcwd->di_name); 551167465Smp if (cwdlen == 1) /* root */ 55259243Sobrien cwdlen = 0; 553167465Smp dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); 55459243Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 55559243Sobrien continue; 55659243Sobrien if (cwdlen) 55759243Sobrien p[-1] = '/'; 55859243Sobrien else 55959243Sobrien p--; /* don't add a / after root */ 560167465Smp Strcpy(p, cp); 561167465Smp xfree(cp); 56259243Sobrien cp = dp; 56359243Sobrien dp += cwdlen; 56459243Sobrien } 56559243Sobrien else 56659243Sobrien dp = cp; 56759243Sobrien 568131962Smp#if defined(WINNT_NATIVE) 569167465Smp return agetcwd(); 570131962Smp#elif defined(__CYGWIN__) 571167465Smp if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ 572167465Smp return agetcwd(); 573167465Smp } else { 574167465Smp cleanup_push(cp, xfree); 575167465Smp ret = dcanon(cp, dp); 576167465Smp cleanup_ignore(cp); 577167465Smp cleanup_until(cp); 578167465Smp } 57969408Sache#else /* !WINNT_NATIVE */ 580167465Smp cleanup_push(cp, xfree); 581167465Smp ret = dcanon(cp, dp); 582167465Smp cleanup_ignore(cp); 583167465Smp cleanup_until(cp); 58469408Sache#endif /* WINNT_NATIVE */ 585167465Smp return ret; 58659243Sobrien} 58759243Sobrien 58859243Sobrien/* 58959243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 59059243Sobrien */ 59159243Sobrienstatic Char * 592195609Smpdfollow(Char *cp, int old) 59359243Sobrien{ 594145479Smp Char *dp; 59559243Sobrien struct varent *c; 59659243Sobrien int serrno; 59759243Sobrien 598195609Smp cp = old ? Strsave(cp) : globone(cp, G_ERROR); 599167465Smp cleanup_push(cp, xfree); 60059243Sobrien#ifdef apollo 60159243Sobrien if (Strchr(cp, '`')) { 602167465Smp char *dptr; 60359243Sobrien if (chdir(dptr = short2str(cp)) < 0) 60459243Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 605167465Smp dp = agetcwd(); 606167465Smp cleanup_push(dp, xfree); 607167465Smp if (dp != NULL) { 608167465Smp cleanup_until(cp); 609167465Smp return dgoto(dp); 61059243Sobrien } 611167465Smp else 612167465Smp stderror(ERR_SYSTEM, dptr, strerror(errno)); 61359243Sobrien } 61459243Sobrien#endif /* apollo */ 615167465Smp 61659243Sobrien /* 61759243Sobrien * if we are ignoring symlinks, try to fix relatives now. 61859243Sobrien * if we are expading symlinks, it should be done by now. 61959243Sobrien */ 62059243Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 62159243Sobrien if (chdir(short2str(dp)) >= 0) { 622167465Smp cleanup_until(cp); 62359243Sobrien return dgoto(dp); 62459243Sobrien } 62559243Sobrien else { 626167465Smp xfree(dp); 627167465Smp if (chdir(short2str(cp)) >= 0) { 628167465Smp cleanup_ignore(cp); 629167465Smp cleanup_until(cp); 63059243Sobrien return dgoto(cp); 631167465Smp } 632167465Smp else if (errno != ENOENT && errno != ENOTDIR) { 633167465Smp int err; 634167465Smp 635167465Smp err = errno; 636167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(err)); 637167465Smp } 63859243Sobrien serrno = errno; 63959243Sobrien } 64059243Sobrien 64159243Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 642100616Smp && (c = adrof(STRcdpath)) && c->vec != NULL) { 643167465Smp struct Strbuf buf = Strbuf_INIT; 64459243Sobrien Char **cdp; 64559243Sobrien 64659243Sobrien for (cdp = c->vec; *cdp; cdp++) { 647231990Smp size_t len = Strlen(*cdp); 648167465Smp buf.len = 0; 649231990Smp if (len > 0) { 650231990Smp Strbuf_append(&buf, *cdp); 651231990Smp if ((*cdp)[len - 1] != '/') 652231990Smp Strbuf_append1(&buf, '/'); 653231990Smp } 654167465Smp Strbuf_append(&buf, cp); 655167465Smp Strbuf_terminate(&buf); 65659243Sobrien /* 65759243Sobrien * We always want to fix the directory here 65859243Sobrien * If we are normalizing symlinks 65959243Sobrien */ 660167465Smp dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 661167465Smp symlinks == SYM_EXPAND); 66259243Sobrien if (chdir(short2str(dp)) >= 0) { 66359243Sobrien printd = 1; 664167465Smp xfree(buf.s); 665167465Smp cleanup_until(cp); 66659243Sobrien return dgoto(dp); 66759243Sobrien } 66859243Sobrien else if (chdir(short2str(cp)) >= 0) { 66959243Sobrien printd = 1; 670167465Smp xfree(dp); 671167465Smp xfree(buf.s); 672167465Smp cleanup_ignore(cp); 673167465Smp cleanup_until(cp); 67459243Sobrien return dgoto(cp); 67559243Sobrien } 676316957Sdchagin xfree(dp); 67759243Sobrien } 678167465Smp xfree(buf.s); 67959243Sobrien } 68059243Sobrien dp = varval(cp); 68159243Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 682167465Smp cleanup_until(cp); 68359243Sobrien cp = Strsave(dp); 68459243Sobrien printd = 1; 68559243Sobrien return dgoto(cp); 68659243Sobrien } 68759243Sobrien /* 68859243Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 68959243Sobrien * directories we could get to. 69059243Sobrien */ 691167465Smp if (!bequiet) 692167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 693167465Smp cleanup_until(cp); 694167465Smp return (NULL); 69559243Sobrien} 69659243Sobrien 69759243Sobrien 69859243Sobrien/* 69959243Sobrien * dopushd - push new directory onto directory stack. 70059243Sobrien * with no arguments exchange top and second. 70159243Sobrien * with numeric argument (+n) bring it to top. 70259243Sobrien */ 70359243Sobrien/*ARGSUSED*/ 70459243Sobrienvoid 705167465Smpdopushd(Char **v, struct command *c) 70659243Sobrien{ 707145479Smp struct directory *dp; 708145479Smp Char *cp; 70959243Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 71059243Sobrien 71159243Sobrien USE(c); 71259243Sobrien printd = 1; 71359243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 71459243Sobrien 71559243Sobrien if (cp == NULL) { 71659243Sobrien if (adrof(STRpushdtohome)) { 71759243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 71859243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 71959243Sobrien if (chdir(short2str(cp)) < 0) 72059243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 721195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 72259243Sobrien return; 723167465Smp dp = xcalloc(sizeof(struct directory), 1); 72459243Sobrien dp->di_name = cp; 72559243Sobrien dp->di_count = 0; 72659243Sobrien dp->di_prev = dcwd; 72759243Sobrien dp->di_next = dcwd->di_next; 72859243Sobrien dcwd->di_next = dp; 72959243Sobrien dp->di_next->di_prev = dp; 73059243Sobrien } 73159243Sobrien else { 73259243Sobrien char *tmp; 73359243Sobrien 73459243Sobrien if ((dp = dcwd->di_prev) == &dhead) 73559243Sobrien dp = dhead.di_prev; 73659243Sobrien if (dp == dcwd) 73759243Sobrien stderror(ERR_NAME | ERR_NODIR); 73859243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 73959243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 74059243Sobrien dp->di_prev->di_next = dp->di_next; 74159243Sobrien dp->di_next->di_prev = dp->di_prev; 74259243Sobrien dp->di_next = dcwd->di_next; 74359243Sobrien dp->di_prev = dcwd; 74459243Sobrien dcwd->di_next->di_prev = dp; 74559243Sobrien dcwd->di_next = dp; 74659243Sobrien } 74759243Sobrien } 74859243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 74959243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 75059243Sobrien /* NOTREACHED */ 75159243Sobrien return; 75259243Sobrien } 75359243Sobrien else if ((dp = dfind(cp)) != NULL) { 75459243Sobrien char *tmp; 75559243Sobrien 75659243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 75759243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 75859243Sobrien /* 75959243Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 76059243Sobrien */ 76159243Sobrien if (adrof(STRdextract)) 76259243Sobrien dextract(dp); 76359243Sobrien } 76459243Sobrien else { 765145479Smp Char *ccp; 76659243Sobrien 767195609Smp if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL) 76859243Sobrien return; 769167465Smp dp = xcalloc(sizeof(struct directory), 1); 77059243Sobrien dp->di_name = ccp; 77159243Sobrien dp->di_count = 0; 77259243Sobrien dp->di_prev = dcwd; 77359243Sobrien dp->di_next = dcwd->di_next; 77459243Sobrien dcwd->di_next = dp; 77559243Sobrien dp->di_next->di_prev = dp; 77659243Sobrien } 77759243Sobrien dnewcwd(dp, dflag); 77859243Sobrien} 77959243Sobrien 78059243Sobrien/* 78159243Sobrien * dfind - find a directory if specified by numeric (+n) argument 78259243Sobrien */ 78359243Sobrienstatic struct directory * 784167465Smpdfind(Char *cp) 78559243Sobrien{ 786145479Smp struct directory *dp; 787145479Smp int i; 788145479Smp Char *ep; 78959243Sobrien 79059243Sobrien if (*cp++ != '+') 79159243Sobrien return (0); 79259243Sobrien for (ep = cp; Isdigit(*ep); ep++) 79359243Sobrien continue; 79459243Sobrien if (*ep) 79559243Sobrien return (0); 79659243Sobrien i = getn(cp); 79759243Sobrien if (i <= 0) 79859243Sobrien return (0); 79959243Sobrien for (dp = dcwd; i != 0; i--) { 80059243Sobrien if ((dp = dp->di_prev) == &dhead) 80159243Sobrien dp = dp->di_prev; 80259243Sobrien if (dp == dcwd) 80359243Sobrien stderror(ERR_NAME | ERR_DEEP); 80459243Sobrien } 80559243Sobrien return (dp); 80659243Sobrien} 80759243Sobrien 80859243Sobrien/* 80959243Sobrien * dopopd - pop a directory out of the directory stack 81059243Sobrien * with a numeric argument just discard it. 81159243Sobrien */ 81259243Sobrien/*ARGSUSED*/ 81359243Sobrienvoid 814167465Smpdopopd(Char **v, struct command *c) 81559243Sobrien{ 81659243Sobrien Char *cp; 817145479Smp struct directory *dp, *p = NULL; 81859243Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 81959243Sobrien 82059243Sobrien USE(c); 82159243Sobrien printd = 1; 82259243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 82359243Sobrien 82459243Sobrien if (cp == NULL) 82559243Sobrien dp = dcwd; 82659243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 82759243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 82859243Sobrien /* NOTREACHED */ 82959243Sobrien return; 83059243Sobrien } 83159243Sobrien else if ((dp = dfind(cp)) == 0) 83259243Sobrien stderror(ERR_NAME | ERR_BADDIR); 83359243Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 83459243Sobrien stderror(ERR_NAME | ERR_EMPTY); 83559243Sobrien if (dp == dcwd) { 83659243Sobrien char *tmp; 83759243Sobrien 83859243Sobrien if ((p = dp->di_prev) == &dhead) 83959243Sobrien p = dhead.di_prev; 84059243Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 84159243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 84259243Sobrien } 84359243Sobrien dp->di_prev->di_next = dp->di_next; 84459243Sobrien dp->di_next->di_prev = dp->di_prev; 845167465Smp dfree(dp); 84659243Sobrien if (dp == dcwd) { 847167465Smp dnewcwd(p, dflag); 84859243Sobrien } 84959243Sobrien else { 85059243Sobrien printdirs(dflag); 85159243Sobrien } 85259243Sobrien} 85359243Sobrien 85459243Sobrien/* 85559243Sobrien * dfree - free the directory (or keep it if it still has ref count) 85659243Sobrien */ 85759243Sobrienvoid 858167465Smpdfree(struct directory *dp) 85959243Sobrien{ 86059243Sobrien 86159243Sobrien if (dp->di_count != 0) { 86259243Sobrien dp->di_next = dp->di_prev = 0; 86359243Sobrien } 86459243Sobrien else { 865167465Smp xfree(dp->di_name); 866167465Smp xfree(dp); 86759243Sobrien } 86859243Sobrien} 86959243Sobrien 87059243Sobrien/* 87159243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 87259243Sobrien * we are of course assuming that the file system is standardly 87359243Sobrien * constructed (always have ..'s, directories have links) 87459243Sobrien */ 87559243SobrienChar * 876167465Smpdcanon(Char *cp, Char *p) 87759243Sobrien{ 878145479Smp Char *sp; 879145479Smp Char *p1, *p2; /* general purpose */ 880145479Smp int slash; 881167465Smp#ifdef HAVE_SLASHSLASH 882145479Smp int slashslash; 883167465Smp#endif /* HAVE_SLASHSLASH */ 88473393Skris size_t clen; 88559243Sobrien 88659243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 887167465Smp Char *mlink, *newcp; 888167465Smp char *tlink; 889167465Smp size_t cc; 89059243Sobrien#endif /* S_IFLNK */ 89159243Sobrien 892167465Smp clen = Strlen(cp); 89359243Sobrien 89459243Sobrien /* 89559243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 89673393Skris * cwd does not start with a slash or the result would be too long try to 89773393Skris * correct it. 89859243Sobrien */ 89959243Sobrien if (!ABSOLUTEP(cp)) { 900167465Smp Char *tmpdir; 90173393Skris size_t len; 90259243Sobrien 90359243Sobrien p1 = varval(STRcwd); 90473393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 905167465Smp Char *new_cwd = agetcwd(); 906167465Smp 907167465Smp if (new_cwd == NULL) { 90873393Skris xprintf("%s: %s\n", progname, strerror(errno)); 909167465Smp setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 91073393Skris } 911167465Smp else 912167465Smp setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 91373393Skris p1 = varval(STRcwd); 91473393Skris } 91573393Skris len = Strlen(p1); 916167465Smp tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 91759243Sobrien (void) Strcpy(tmpdir, p1); 91859243Sobrien (void) Strcat(tmpdir, STRslash); 91959243Sobrien (void) Strcat(tmpdir, cp); 920167465Smp xfree(cp); 921167465Smp cp = p = tmpdir; 92259243Sobrien } 92359243Sobrien 924167465Smp#ifdef HAVE_SLASHSLASH 92559243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 926167465Smp#endif /* HAVE_SLASHSLASH */ 92759243Sobrien 92859243Sobrien while (*p) { /* for each component */ 92959243Sobrien sp = p; /* save slash address */ 93059243Sobrien while (*++p == '/') /* flush extra slashes */ 93159243Sobrien continue; 93259243Sobrien if (p != ++sp) 93359243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 93459243Sobrien continue; 93559243Sobrien p = sp; /* save start of component */ 93659243Sobrien slash = 0; 93759243Sobrien if (*p) 93859243Sobrien while (*++p) /* find next slash or end of path */ 93959243Sobrien if (*p == '/') { 94059243Sobrien slash = 1; 94159243Sobrien *p = 0; 94259243Sobrien break; 94359243Sobrien } 94459243Sobrien 945167465Smp#ifdef HAVE_SLASHSLASH 94659243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 94759243Sobrien slashslash = 1; 948167465Smp#endif /* HAVE_SLASHSLASH */ 94959243Sobrien if (*sp == '\0') { /* if component is null */ 95059243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 95159243Sobrien break; 95259243Sobrien else 95359243Sobrien *sp = '\0'; 95459243Sobrien } 95559243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 95659243Sobrien if (slash) { 95759243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 95859243Sobrien continue; 95959243Sobrien p = --sp; 96059243Sobrien } 96159243Sobrien else if (--sp != cp) 96259243Sobrien *sp = '\0'; 96359243Sobrien else 96459243Sobrien sp[1] = '\0'; 96559243Sobrien } 96659243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 96759243Sobrien /* 96859243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 96959243Sobrien * a path starting at /, and "xxx" is a single component. Before 97059243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 97159243Sobrien * symbolic link. 97259243Sobrien */ 97359243Sobrien *--sp = 0; /* form the pathname for readlink */ 97459243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 97559243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 976167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 977167465Smp mlink = str2short(tlink); 978167465Smp xfree(tlink); 97959243Sobrien 98059243Sobrien if (slash) 98159243Sobrien *p = '/'; 98259243Sobrien /* 98359243Sobrien * Point p to the '/' in "/..", and restore the '/'. 98459243Sobrien */ 98559243Sobrien *(p = sp) = '/'; 986145479Smp if (*mlink != '/') { 98759243Sobrien /* 98859243Sobrien * Relative path, expand it between the "yyy/" and the 98959243Sobrien * "/..". First, back sp up to the character past "yyy/". 99059243Sobrien */ 99159243Sobrien while (*--sp != '/') 99259243Sobrien continue; 99359243Sobrien sp++; 99459243Sobrien *sp = 0; 99559243Sobrien /* 996145479Smp * New length is "yyy/" + mlink + "/.." and rest 99759243Sobrien */ 998167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 999167465Smp Strlen(p) + 1) * sizeof(Char)); 100059243Sobrien /* 100159243Sobrien * Copy new path into newcp 100259243Sobrien */ 100359243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 100459243Sobrien continue; 1005145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 100659243Sobrien continue; 100759243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 100859243Sobrien continue; 100959243Sobrien /* 101059243Sobrien * Restart canonicalization at expanded "/xxx". 101159243Sobrien */ 101259243Sobrien p = sp - cp - 1 + newcp; 101359243Sobrien } 101459243Sobrien else { 1015167465Smp newcp = Strspl(mlink, p); 101659243Sobrien /* 101759243Sobrien * Restart canonicalization at beginning 101859243Sobrien */ 101959243Sobrien p = newcp; 102059243Sobrien } 1021167465Smp xfree(cp); 102259243Sobrien cp = newcp; 1023167465Smp#ifdef HAVE_SLASHSLASH 102459243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1025167465Smp#endif /* HAVE_SLASHSLASH */ 102659243Sobrien continue; /* canonicalize the link */ 102759243Sobrien } 102859243Sobrien#endif /* S_IFLNK */ 102959243Sobrien *sp = '/'; 103059243Sobrien if (sp != cp) 103159243Sobrien while (*--sp != '/') 103259243Sobrien continue; 103359243Sobrien if (slash) { 103459243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 103559243Sobrien continue; 103659243Sobrien p = sp; 103759243Sobrien } 103859243Sobrien else if (cp == sp) 103959243Sobrien *++sp = '\0'; 104059243Sobrien else 104159243Sobrien *sp = '\0'; 104259243Sobrien } 104359243Sobrien else { /* normal dir name (not . or .. or nothing) */ 104459243Sobrien 104559243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 104659243Sobrien if (sp != cp && symlinks == SYM_CHASE && 1047167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 1048167465Smp mlink = str2short(tlink); 1049167465Smp xfree(tlink); 105059243Sobrien 105159243Sobrien /* 105259243Sobrien * restore the '/'. 105359243Sobrien */ 105459243Sobrien if (slash) 105559243Sobrien *p = '/'; 105659243Sobrien 105759243Sobrien /* 105859243Sobrien * point sp to p (rather than backing up). 105959243Sobrien */ 106059243Sobrien sp = p; 106159243Sobrien 1062145479Smp if (*mlink != '/') { 106359243Sobrien /* 106459243Sobrien * Relative path, expand it between the "yyy/" and the 106559243Sobrien * remainder. First, back sp up to the character past 106659243Sobrien * "yyy/". 106759243Sobrien */ 106859243Sobrien while (*--sp != '/') 106959243Sobrien continue; 107059243Sobrien sp++; 107159243Sobrien *sp = 0; 107259243Sobrien /* 1073145479Smp * New length is "yyy/" + mlink + "/.." and rest 107459243Sobrien */ 1075167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1076167465Smp Strlen(p) + 1) * sizeof(Char)); 107759243Sobrien /* 107859243Sobrien * Copy new path into newcp 107959243Sobrien */ 108059243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 108159243Sobrien continue; 1082145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 108359243Sobrien continue; 108459243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 108559243Sobrien continue; 108659243Sobrien /* 108759243Sobrien * Restart canonicalization at expanded "/xxx". 108859243Sobrien */ 108959243Sobrien p = sp - cp - 1 + newcp; 109059243Sobrien } 109159243Sobrien else { 1092167465Smp newcp = Strspl(mlink, p); 109359243Sobrien /* 109459243Sobrien * Restart canonicalization at beginning 109559243Sobrien */ 109659243Sobrien p = newcp; 109759243Sobrien } 1098167465Smp xfree(cp); 109959243Sobrien cp = newcp; 1100167465Smp#ifdef HAVE_SLASHSLASH 110159243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1102167465Smp#endif /* HAVE_SLASHSLASH */ 1103145479Smp continue; /* canonicalize the mlink */ 110459243Sobrien } 110559243Sobrien#endif /* S_IFLNK */ 110659243Sobrien if (slash) 110759243Sobrien *p = '/'; 110859243Sobrien } 110959243Sobrien } 111059243Sobrien 111159243Sobrien /* 111259243Sobrien * fix home... 111359243Sobrien */ 111459243Sobrien#ifdef S_IFLNK 111559243Sobrien p1 = varval(STRhome); 1116167465Smp cc = Strlen(p1); 111759243Sobrien /* 111859243Sobrien * See if we're not in a subdir of STRhome 111959243Sobrien */ 1120167465Smp if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 112159243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 112259243Sobrien static ino_t home_ino = (ino_t) -1; 112359243Sobrien static dev_t home_dev = (dev_t) -1; 112459243Sobrien static Char *home_ptr = NULL; 112559243Sobrien struct stat statbuf; 112659243Sobrien int found; 1127167465Smp Char *copy; 112859243Sobrien 112959243Sobrien /* 113059243Sobrien * Get dev and ino of STRhome 113159243Sobrien */ 113259243Sobrien if (home_ptr != p1 && 113359243Sobrien stat(short2str(p1), &statbuf) != -1) { 113459243Sobrien home_dev = statbuf.st_dev; 113559243Sobrien home_ino = statbuf.st_ino; 113659243Sobrien home_ptr = p1; 113759243Sobrien } 113859243Sobrien /* 113959243Sobrien * Start comparing dev & ino backwards 114059243Sobrien */ 1141167465Smp p2 = copy = Strsave(cp); 114259243Sobrien found = 0; 114359243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 114459243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 114559243Sobrien statbuf.st_ino == home_ino) { 114659243Sobrien found = 1; 114759243Sobrien break; 114859243Sobrien } 114959243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 115059243Sobrien *sp = '\0'; 115159243Sobrien } 115259243Sobrien /* 115359243Sobrien * See if we found it 115459243Sobrien */ 115559243Sobrien if (*p2 && found) { 115659243Sobrien /* 115759243Sobrien * Use STRhome to make '~' work 115859243Sobrien */ 115959243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 1160167465Smp xfree(cp); 116159243Sobrien cp = newcp; 116259243Sobrien } 1163167465Smp xfree(copy); 116459243Sobrien } 116559243Sobrien#endif /* S_IFLNK */ 116659243Sobrien 1167167465Smp#ifdef HAVE_SLASHSLASH 116859243Sobrien if (slashslash) { 116959243Sobrien if (cp[1] != '/') { 1170167465Smp p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 117159243Sobrien *p = '/'; 117259243Sobrien (void) Strcpy(&p[1], cp); 1173167465Smp xfree(cp); 117459243Sobrien cp = p; 117559243Sobrien } 117659243Sobrien } 1177167465Smp if (cp[1] == '/' && cp[2] == '/') { 1178167465Smp for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 1179167465Smp continue; 1180167465Smp } 1181167465Smp#endif /* HAVE_SLASHSLASH */ 118259243Sobrien return cp; 118359243Sobrien} 118459243Sobrien 118559243Sobrien 118659243Sobrien/* 118759243Sobrien * dnewcwd - make a new directory in the loop the current one 118859243Sobrien */ 118959243Sobrienstatic void 1190167465Smpdnewcwd(struct directory *dp, int dflag) 119159243Sobrien{ 119259243Sobrien int print; 119359243Sobrien 119459243Sobrien if (adrof(STRdunique)) { 119559243Sobrien struct directory *dn; 119659243Sobrien 119759243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 119859243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 119959243Sobrien dn->di_next->di_prev = dn->di_prev; 120059243Sobrien dn->di_prev->di_next = dn->di_next; 120159243Sobrien dfree(dn); 120259243Sobrien break; 120359243Sobrien } 120459243Sobrien } 120559243Sobrien dcwd = dp; 120659243Sobrien dset(dcwd->di_name); 120759243Sobrien dgetstack(); 120859243Sobrien print = printd; /* if printd is set, print dirstack... */ 120959243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 121059243Sobrien print = 0; 121159243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 121259243Sobrien print = 1; 121359243Sobrien if (bequiet) /* and bequiet overrides everything */ 121459243Sobrien print = 0; 121559243Sobrien if (print) 121659243Sobrien printdirs(dflag); 121759243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 121859243Sobrien} 121959243Sobrien 122059243Sobrienvoid 1221167465Smpdsetstack(void) 122259243Sobrien{ 122359243Sobrien Char **cp; 122459243Sobrien struct varent *vp; 122559243Sobrien struct directory *dn, *dp; 122659243Sobrien 1227100616Smp if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 122859243Sobrien return; 122959243Sobrien 123059243Sobrien /* Free the whole stack */ 123159243Sobrien while ((dn = dhead.di_prev) != &dhead) { 123259243Sobrien dn->di_next->di_prev = dn->di_prev; 123359243Sobrien dn->di_prev->di_next = dn->di_next; 123459243Sobrien if (dn != dcwd) 123559243Sobrien dfree(dn); 123659243Sobrien } 123759243Sobrien 123859243Sobrien /* thread the current working directory */ 123959243Sobrien dhead.di_prev = dhead.di_next = dcwd; 124059243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 124159243Sobrien 124259243Sobrien /* put back the stack */ 124359243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 1244167465Smp dp = xcalloc(sizeof(struct directory), 1); 124559243Sobrien dp->di_name = Strsave(*cp); 124659243Sobrien dp->di_count = 0; 124759243Sobrien dp->di_prev = dcwd; 124859243Sobrien dp->di_next = dcwd->di_next; 124959243Sobrien dcwd->di_next = dp; 125059243Sobrien dp->di_next->di_prev = dp; 125159243Sobrien } 125259243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 125359243Sobrien} 125459243Sobrien 125559243Sobrienstatic void 1256167465Smpdgetstack(void) 125759243Sobrien{ 125859243Sobrien int i = 0; 125959243Sobrien Char **dblk, **dbp; 126059243Sobrien struct directory *dn; 126159243Sobrien 126259243Sobrien if (adrof(STRdirstack) == NULL) 126359243Sobrien return; 126459243Sobrien 1265167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126659243Sobrien continue; 1267167465Smp dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 1268167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 126959243Sobrien *dbp = Strsave(dn->di_name); 127059243Sobrien *dbp = NULL; 1271167465Smp cleanup_push(dblk, blk_cleanup); 127259243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1273167465Smp cleanup_ignore(dblk); 1274167465Smp cleanup_until(dblk); 127559243Sobrien} 127659243Sobrien 127759243Sobrien/* 127859243Sobrien * getstakd - added by kfk 17 Jan 1984 127959243Sobrien * Support routine for the stack hack. Finds nth directory in 128059243Sobrien * the directory stack, or finds last directory in stack. 128159243Sobrien */ 1282167465Smpconst Char * 1283167465Smpgetstakd(int cnt) 128459243Sobrien{ 128559243Sobrien struct directory *dp; 128659243Sobrien 128759243Sobrien dp = dcwd; 128859243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 128959243Sobrien dp = dp->di_next; 129059243Sobrien if (dp == &dhead) 129159243Sobrien dp = dp->di_next; 129259243Sobrien } 129359243Sobrien else { 129459243Sobrien while (cnt-- > 0) { 129559243Sobrien dp = dp->di_prev; 129659243Sobrien if (dp == &dhead) 129759243Sobrien dp = dp->di_prev; 129859243Sobrien if (dp == dcwd) 1299167465Smp return NULL; 130059243Sobrien } 130159243Sobrien } 1302167465Smp return dp->di_name; 130359243Sobrien} 130459243Sobrien 130559243Sobrien/* 130659243Sobrien * Karl Kleinpaste - 10 Feb 1984 130759243Sobrien * Added dextract(), which is used in pushd +n. 130859243Sobrien * Instead of just rotating the entire stack around, dextract() 130959243Sobrien * lets the user have the nth dir extracted from its current 131059243Sobrien * position, and pushes it onto the top. 131159243Sobrien */ 131259243Sobrienstatic void 1313167465Smpdextract(struct directory *dp) 131459243Sobrien{ 131559243Sobrien if (dp == dcwd) 131659243Sobrien return; 131759243Sobrien dp->di_next->di_prev = dp->di_prev; 131859243Sobrien dp->di_prev->di_next = dp->di_next; 131959243Sobrien dp->di_next = dcwd->di_next; 132059243Sobrien dp->di_prev = dcwd; 132159243Sobrien dp->di_next->di_prev = dp; 132259243Sobrien dcwd->di_next = dp; 132359243Sobrien} 132459243Sobrien 1325167465Smpstatic void 1326167465Smpbequiet_cleanup(void *dummy) 1327167465Smp{ 1328167465Smp USE(dummy); 1329167465Smp bequiet = 0; 1330167465Smp} 1331167465Smp 133259243Sobrienvoid 1333167465Smploaddirs(Char *fname) 133459243Sobrien{ 133559243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133659243Sobrien 133759243Sobrien bequiet = 1; 1338167465Smp cleanup_push(&bequiet, bequiet_cleanup); 1339167465Smp if (fname) 134059243Sobrien loaddirs_cmd[1] = fname; 134159243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 134259243Sobrien loaddirs_cmd[1] = fname; 134359243Sobrien else 134459243Sobrien loaddirs_cmd[1] = STRtildotdirs; 1345167465Smp dosource(loaddirs_cmd, NULL); 1346167465Smp cleanup_until(&bequiet); 134759243Sobrien} 134859243Sobrien 134959243Sobrien/* 135059243Sobrien * create a file called ~/.cshdirs which has a sequence 135159243Sobrien * of pushd commands which will restore the dir stack to 135259243Sobrien * its state before exit/logout. remember that the order 135359243Sobrien * is reversed in the file because we are pushing. 135459243Sobrien * -strike 135559243Sobrien */ 135659243Sobrienvoid 1357167465Smprecdirs(Char *fname, int def) 135859243Sobrien{ 135959243Sobrien int fp, ftmp, oldidfds; 136059243Sobrien int cdflag = 0; 136159243Sobrien struct directory *dp; 136259243Sobrien unsigned int num; 136359243Sobrien Char *snum; 1364167465Smp struct Strbuf qname = Strbuf_INIT; 136559243Sobrien 136659243Sobrien if (fname == NULL && !def) 136759243Sobrien return; 136859243Sobrien 136959243Sobrien if (fname == NULL) { 137059243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 137159243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 137259243Sobrien else 137359243Sobrien fname = Strsave(fname); 137459243Sobrien } 137559243Sobrien else 137659243Sobrien fname = globone(fname, G_ERROR); 1377167465Smp cleanup_push(fname, xfree); 1378167465Smp 1379167465Smp if ((fp = xcreat(short2str(fname), 0600)) == -1) { 1380167465Smp cleanup_until(fname); 138159243Sobrien return; 138259243Sobrien } 138359243Sobrien 138483098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 138559243Sobrien num = (unsigned int) ~0; 138659243Sobrien else 138759243Sobrien num = (unsigned int) atoi(short2str(snum)); 138859243Sobrien 138959243Sobrien oldidfds = didfds; 139059243Sobrien didfds = 0; 139159243Sobrien ftmp = SHOUT; 139259243Sobrien SHOUT = fp; 139359243Sobrien 1394167465Smp cleanup_push(&qname, Strbuf_cleanup); 139559243Sobrien dp = dcwd->di_next; 139659243Sobrien do { 139759243Sobrien if (dp == &dhead) 139859243Sobrien continue; 139959243Sobrien 140059243Sobrien if (cdflag == 0) { 140159243Sobrien cdflag = 1; 1402167465Smp xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 140359243Sobrien } 140459243Sobrien else 1405167465Smp xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 140659243Sobrien 140759243Sobrien if (num-- == 0) 140859243Sobrien break; 140959243Sobrien 141059243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 141159243Sobrien 1412167465Smp xclose(fp); 141359243Sobrien SHOUT = ftmp; 141459243Sobrien didfds = oldidfds; 1415167465Smp cleanup_until(fname); 141659243Sobrien} 1417