sh.dir.c revision 167465
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.79 2006/09/25 18:17:26 christos Exp $ */ 259243Sobrien/* 359243Sobrien * sh.dir.c: Directory manipulation functions 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 34145479Smp#include "ed.h" 3559243Sobrien 36167465SmpRCSID("$tcsh: sh.dir.c,v 3.79 2006/09/25 18:17:26 christos Exp $") 3759243Sobrien 3859243Sobrien/* 3959243Sobrien * C Shell - directory management 4059243Sobrien */ 4159243Sobrien 42167465Smpstatic Char *agetcwd (void); 43167465Smpstatic void dstart (const char *); 44167465Smpstatic struct directory *dfind (Char *); 45167465Smpstatic Char *dfollow (Char *); 46167465Smpstatic void printdirs (int); 47167465Smpstatic Char *dgoto (Char *); 48167465Smpstatic void dnewcwd (struct directory *, int); 49167465Smpstatic void dset (Char *); 50167465Smpstatic void dextract (struct directory *); 51167465Smpstatic int skipargs (Char ***, const char *, 52167465Smp const char *); 53167465Smpstatic void dgetstack (void); 5459243Sobrien 5559243Sobrienstatic struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 5659243Sobrienstatic int printd; /* force name to be printed */ 5759243Sobrien 5859243Sobrienint bequiet = 0; /* do not print dir stack -strike */ 5959243Sobrien 60167465Smpstatic Char * 61167465Smpagetcwd(void) 62167465Smp{ 63167465Smp char *buf; 64167465Smp Char *cwd; 65167465Smp size_t len; 66167465Smp 67167465Smp len = MAXPATHLEN; 68167465Smp buf = xmalloc(len); 69167465Smp while (getcwd(buf, len) == NULL) { 70167465Smp int err; 71167465Smp 72167465Smp err = errno; 73167465Smp if (err != ERANGE) { 74167465Smp xfree(buf); 75167465Smp errno = err; 76167465Smp return NULL; 77167465Smp } 78167465Smp len *= 2; 79167465Smp buf = xrealloc(buf, len); 80167465Smp } 81167465Smp if (*buf == '\0') { 82167465Smp xfree(buf); 83167465Smp return NULL; 84167465Smp } 85167465Smp cwd = SAVE(buf); 86167465Smp xfree(buf); 87167465Smp return cwd; 88167465Smp} 89167465Smp 9059243Sobrienstatic void 91167465Smpdstart(const char *from) 9259243Sobrien{ 9359243Sobrien xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 9459243Sobrien} 9559243Sobrien 9659243Sobrien/* 9759243Sobrien * dinit - initialize current working directory 9859243Sobrien */ 9959243Sobrienvoid 100167465Smpdinit(Char *hp) 10159243Sobrien{ 102167465Smp Char *cp, *tcp; 103145479Smp struct directory *dp; 10459243Sobrien 10559243Sobrien /* Don't believe the login shell home, because it may be a symlink */ 106167465Smp tcp = agetcwd(); 107167465Smp if (tcp == NULL) { 10859243Sobrien xprintf("%s: %s\n", progname, strerror(errno)); 10959243Sobrien if (hp && *hp) { 110167465Smp char *xcp = short2str(hp); 111167465Smp dstart(xcp); 112167465Smp if (chdir(xcp) == -1) 11359243Sobrien cp = NULL; 11459243Sobrien else 11559243Sobrien cp = Strsave(hp); 11659243Sobrien } 11759243Sobrien else 11859243Sobrien cp = NULL; 11959243Sobrien if (cp == NULL) { 12059243Sobrien dstart("/"); 12159243Sobrien if (chdir("/") == -1) 12259243Sobrien /* I am not even try to print an error message! */ 12359243Sobrien xexit(1); 12459243Sobrien cp = SAVE("/"); 12559243Sobrien } 12659243Sobrien } 12759243Sobrien else { 12859243Sobrien#ifdef S_IFLNK 12959243Sobrien struct stat swd, shp; 130167465Smp int swd_ok; 13159243Sobrien 132167465Smp swd_ok = stat(short2str(tcp), &swd) == 0; 13359243Sobrien /* 13459243Sobrien * See if $HOME is the working directory we got and use that 13559243Sobrien */ 136167465Smp if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 && 13759243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 13859243Sobrien swd.st_ino == shp.st_ino) 13959243Sobrien cp = Strsave(hp); 14059243Sobrien else { 14159243Sobrien char *cwd; 14259243Sobrien 14359243Sobrien /* 14459243Sobrien * use PWD if we have it (for subshells) 14559243Sobrien */ 146167465Smp if (swd_ok && (cwd = getenv("PWD")) != NULL) { 147167465Smp if (stat(cwd, &shp) != -1 && 14859243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 149167465Smp swd.st_ino == shp.st_ino) { 150167465Smp tcp = SAVE(cwd); 151167465Smp cleanup_push(tcp, xfree); 152167465Smp } 15359243Sobrien } 154167465Smp cleanup_push(tcp, xfree); 155167465Smp cp = dcanon(tcp, STRNULL); 156167465Smp cleanup_ignore(tcp); 157167465Smp cleanup_until(tcp); 15859243Sobrien } 15959243Sobrien#else /* S_IFLNK */ 160167465Smp cleanup_push(tcp, xfree); 161167465Smp cp = dcanon(tcp, STRNULL); 162167465Smp cleanup_ignore(tcp); 163167465Smp cleanup_until(tcp); 16459243Sobrien#endif /* S_IFLNK */ 16559243Sobrien } 16659243Sobrien 167167465Smp dp = xcalloc(sizeof(struct directory), 1); 16859243Sobrien dp->di_name = cp; 16959243Sobrien dp->di_count = 0; 17059243Sobrien dhead.di_next = dhead.di_prev = dp; 17159243Sobrien dp->di_next = dp->di_prev = &dhead; 17259243Sobrien printd = 0; 17359243Sobrien dnewcwd(dp, 0); 174167465Smp setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB); 17559243Sobrien} 17659243Sobrien 17759243Sobrienstatic void 178167465Smpdset(Char *dp) 17959243Sobrien{ 18059243Sobrien /* 18159243Sobrien * Don't call set() directly cause if the directory contains ` or 18259243Sobrien * other junk characters glob will fail. 18359243Sobrien */ 184167465Smp setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB); 185167465Smp setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB); 18659243Sobrien tsetenv(STRPWD, dp); 18759243Sobrien} 18859243Sobrien 18959243Sobrien#define DIR_PRINT 0x01 /* -p */ 19059243Sobrien#define DIR_LONG 0x02 /* -l */ 19159243Sobrien#define DIR_VERT 0x04 /* -v */ 19259243Sobrien#define DIR_LINE 0x08 /* -n */ 19359243Sobrien#define DIR_SAVE 0x10 /* -S */ 19459243Sobrien#define DIR_LOAD 0x20 /* -L */ 19559243Sobrien#define DIR_CLEAR 0x40 /* -c */ 19659243Sobrien#define DIR_OLD 0x80 /* - */ 19759243Sobrien 19859243Sobrienstatic int 199167465Smpskipargs(Char ***v, const char *dstr, const char *str) 20059243Sobrien{ 20159243Sobrien Char **n = *v, *s; 20259243Sobrien 20359243Sobrien int dflag = 0, loop = 1; 20459243Sobrien for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 20559243Sobrien if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 20659243Sobrien dflag |= DIR_OLD; 20759243Sobrien else { 20859243Sobrien char *p; 209167465Smp while (*s != '\0') /* examine flags */ 21059243Sobrien { 21159243Sobrien if ((p = strchr(dstr, *s++)) != NULL) 21259243Sobrien dflag |= (1 << (p - dstr)); 213167465Smp else 21459243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21559243Sobrien } 21659243Sobrien } 21759243Sobrien if (*n && (dflag & DIR_OLD)) 21859243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21959243Sobrien *v = n; 22059243Sobrien /* make -l, -v, and -n imply -p */ 22159243Sobrien if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 22259243Sobrien dflag |= DIR_PRINT; 22359243Sobrien return dflag; 22459243Sobrien} 22559243Sobrien 22659243Sobrien/* 22759243Sobrien * dodirs - list all directories in directory loop 22859243Sobrien */ 22959243Sobrien/*ARGSUSED*/ 23059243Sobrienvoid 231167465Smpdodirs(Char **v, struct command *c) 23259243Sobrien{ 233167465Smp static const char flags[] = "plvnSLc"; 23459243Sobrien int dflag = skipargs(&v, flags, ""); 23559243Sobrien 23659243Sobrien USE(c); 23759243Sobrien if ((dflag & DIR_CLEAR) != 0) { 23859243Sobrien struct directory *dp, *fdp; 23959243Sobrien for (dp = dcwd->di_next; dp != dcwd; ) { 24059243Sobrien fdp = dp; 24159243Sobrien dp = dp->di_next; 24259243Sobrien if (fdp != &dhead) 24359243Sobrien dfree(fdp); 24459243Sobrien } 24559243Sobrien dhead.di_next = dhead.di_prev = dp; 24659243Sobrien dp->di_next = dp->di_prev = &dhead; 24759243Sobrien } 24859243Sobrien if ((dflag & DIR_LOAD) != 0) 24959243Sobrien loaddirs(*v); 25059243Sobrien else if ((dflag & DIR_SAVE) != 0) 25159243Sobrien recdirs(*v, 1); 25259243Sobrien 25359243Sobrien if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 25459243Sobrien v++; 25559243Sobrien 25659243Sobrien if (*v != NULL || (dflag & DIR_OLD)) 25759243Sobrien stderror(ERR_DIRUS, "dirs", flags, ""); 25859243Sobrien if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 25959243Sobrien printdirs(dflag); 26059243Sobrien} 26159243Sobrien 26259243Sobrienstatic void 263167465Smpprintdirs(int dflag) 26459243Sobrien{ 265145479Smp struct directory *dp; 26659243Sobrien Char *s, *user; 26759243Sobrien int idx, len, cur; 26859243Sobrien 26959243Sobrien dp = dcwd; 27059243Sobrien idx = 0; 27159243Sobrien cur = 0; 27259243Sobrien do { 27359243Sobrien if (dp == &dhead) 27459243Sobrien continue; 27559243Sobrien if (dflag & DIR_VERT) { 27659243Sobrien xprintf("%d\t", idx++); 27759243Sobrien cur = 0; 27859243Sobrien } 27959243Sobrien s = dp->di_name; 28059243Sobrien user = NULL; 28159243Sobrien if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 28259243Sobrien len = (int) (Strlen(user) + Strlen(s) + 2); 28359243Sobrien else 28459243Sobrien len = (int) (Strlen(s) + 1); 28559243Sobrien 28659243Sobrien cur += len; 287167465Smp if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) { 28859243Sobrien xputchar('\n'); 28959243Sobrien cur = len; 29059243Sobrien } 29159243Sobrien if (user) 29259243Sobrien xprintf("~%S", user); 293145479Smp xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 29459243Sobrien } while ((dp = dp->di_prev) != dcwd); 29559243Sobrien if (!(dflag & DIR_VERT)) 29659243Sobrien xputchar('\n'); 29759243Sobrien} 29859243Sobrien 29959243Sobrienvoid 300167465Smpdtildepr(Char *dir) 30159243Sobrien{ 30259243Sobrien Char* user; 30359243Sobrien if ((user = getusername(&dir)) != NULL) 304145479Smp xprintf("~%-S%S", user, dir); 30559243Sobrien else 30659243Sobrien xprintf("%S", dir); 30759243Sobrien} 30859243Sobrien 30959243Sobrienvoid 310167465Smpdtilde(void) 31159243Sobrien{ 31259243Sobrien struct directory *d = dcwd; 31359243Sobrien 31459243Sobrien do { 31559243Sobrien if (d == &dhead) 31659243Sobrien continue; 31759243Sobrien d->di_name = dcanon(d->di_name, STRNULL); 31859243Sobrien } while ((d = d->di_prev) != dcwd); 31959243Sobrien 32059243Sobrien dset(dcwd->di_name); 32159243Sobrien} 32259243Sobrien 32359243Sobrien 32459243Sobrien/* dnormalize(): 32559243Sobrien * The path will be normalized if it 32659243Sobrien * 1) is "..", 32759243Sobrien * 2) or starts with "../", 32859243Sobrien * 3) or ends with "/..", 32959243Sobrien * 4) or contains the string "/../", 33059243Sobrien * then it will be normalized, unless those strings are quoted. 33159243Sobrien * Otherwise, a copy is made and sent back. 33259243Sobrien */ 33359243SobrienChar * 334167465Smpdnormalize(const Char *cp, int expnd) 33559243Sobrien{ 33659243Sobrien 33759243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */ 33859243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 33959243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 34059243Sobrien 34159243Sobrien#ifdef S_IFLNK 342145479Smp if (expnd) { 343167465Smp struct Strbuf buf = Strbuf_INIT; 34459243Sobrien int dotdot = 0; 345167465Smp Char *dp, *cwd; 346167465Smp const Char *start = cp; 347167465Smp# ifdef HAVE_SLASHSLASH 348145479Smp int slashslash; 349167465Smp# endif /* HAVE_SLASHSLASH */ 35059243Sobrien 35159243Sobrien /* 35259243Sobrien * count the number of "../xxx" or "xxx/../xxx" in the path 35359243Sobrien */ 354167465Smp for ( ; *cp && *(cp + 1); cp++) 355167465Smp if (IS_DOTDOT(start, cp)) 35659243Sobrien dotdot++; 357167465Smp 35859243Sobrien /* 35959243Sobrien * if none, we are done. 36059243Sobrien */ 36159243Sobrien if (dotdot == 0) 362167465Smp return (Strsave(start)); 363167465Smp 364167465Smp# ifdef notdef 365167465Smp struct stat sb; 366100616Smp /* 367167465Smp * We disable this test because: 368167465Smp * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1; 369167465Smp * echo ../../dir1 does not expand. We had enabled this before 370167465Smp * because it was bothering people with expansions in compilation 371167465Smp * lines like -I../../foo. Maybe we need some kind of finer grain 372167465Smp * control? 373167465Smp * 374100616Smp * If the path doesn't exist, we are done too. 375100616Smp */ 376167465Smp if (lstat(short2str(start), &sb) != 0 && errno == ENOENT) 377167465Smp return (Strsave(start)); 378167465Smp# endif 379100616Smp 380167465Smp cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char)); 38159243Sobrien (void) Strcpy(cwd, dcwd->di_name); 38259243Sobrien 38359243Sobrien /* 38459243Sobrien * If the path starts with a slash, we are not relative to 38559243Sobrien * the current working directory. 38659243Sobrien */ 38759243Sobrien if (ABSOLUTEP(start)) 38859243Sobrien *cwd = '\0'; 389167465Smp# ifdef HAVE_SLASHSLASH 39059243Sobrien slashslash = cwd[0] == '/' && cwd[1] == '/'; 391167465Smp# endif /* HAVE_SLASHSLASH */ 39259243Sobrien 39359243Sobrien /* 39459243Sobrien * Ignore . and count ..'s 39559243Sobrien */ 396167465Smp cp = start; 397167465Smp do { 39859243Sobrien dotdot = 0; 399167465Smp buf.len = 0; 40059243Sobrien while (*cp) 40159243Sobrien if (IS_DOT(start, cp)) { 40259243Sobrien if (*++cp) 40359243Sobrien cp++; 40459243Sobrien } 40559243Sobrien else if (IS_DOTDOT(start, cp)) { 406167465Smp if (buf.len != 0) 40759243Sobrien break; /* finish analyzing .././../xxx/[..] */ 40859243Sobrien dotdot++; 40959243Sobrien cp += 2; 41059243Sobrien if (*cp) 41159243Sobrien cp++; 41259243Sobrien } 41359243Sobrien else 414167465Smp Strbuf_append1(&buf, *cp++); 41559243Sobrien 416167465Smp Strbuf_terminate(&buf); 41759243Sobrien while (dotdot > 0) 41859243Sobrien if ((dp = Strrchr(cwd, '/')) != NULL) { 419167465Smp# ifdef HAVE_SLASHSLASH 42059243Sobrien if (dp == &cwd[1]) 42159243Sobrien slashslash = 1; 422167465Smp# endif /* HAVE_SLASHSLASH */ 42359243Sobrien *dp = '\0'; 42459243Sobrien dotdot--; 42559243Sobrien } 42659243Sobrien else 42759243Sobrien break; 42859243Sobrien 42959243Sobrien if (!*cwd) { /* too many ..'s, starts with "/" */ 43059243Sobrien cwd[0] = '/'; 431167465Smp# ifdef HAVE_SLASHSLASH 432167465Smp /* 433167465Smp * Only append another slash, if already the former cwd 434167465Smp * was in a double-slash path. 435167465Smp */ 436167465Smp cwd[1] = slashslash ? '/' : '\0'; 43759243Sobrien cwd[2] = '\0'; 438167465Smp# else /* !HAVE_SLASHSLASH */ 43959243Sobrien cwd[1] = '\0'; 440167465Smp# endif /* HAVE_SLASHSLASH */ 44159243Sobrien } 442167465Smp# ifdef HAVE_SLASHSLASH 44359243Sobrien else if (slashslash && cwd[1] == '\0') { 44459243Sobrien cwd[1] = '/'; 44559243Sobrien cwd[2] = '\0'; 44659243Sobrien } 447167465Smp# endif /* HAVE_SLASHSLASH */ 44859243Sobrien 449167465Smp if (buf.len != 0) { 450167465Smp size_t i; 451167465Smp 452167465Smp i = Strlen(cwd); 453167465Smp if (TRM(cwd[i - 1]) != '/') { 454167465Smp cwd[i++] = '/'; 455167465Smp cwd[i] = '\0'; 456167465Smp } 457167465Smp dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s); 458167465Smp xfree(cwd); 45959243Sobrien cwd = dp; 460167465Smp i = Strlen(cwd) - 1; 461167465Smp if (TRM(cwd[i]) == '/') 462167465Smp cwd[i] = '\0'; 46359243Sobrien } 464100616Smp /* Reduction of ".." following the stuff we collected in buf 465100616Smp * only makes sense if the directory item in buf really exists. 466100616Smp * Avoid reduction of "-I../.." (typical compiler call) to "" 467100616Smp * or "/usr/nonexistant/../bin" to "/usr/bin": 468100616Smp */ 469100616Smp if (cwd[0]) { 470100616Smp struct stat exists; 471100616Smp if (0 != stat(short2str(cwd), &exists)) { 472167465Smp xfree(buf.s); 473167465Smp xfree(cwd); 474100616Smp return Strsave(start); 475100616Smp } 476100616Smp } 477167465Smp } while (*cp != '\0'); 478167465Smp xfree(buf.s); 47959243Sobrien return cwd; 48059243Sobrien } 48159243Sobrien#endif /* S_IFLNK */ 48259243Sobrien return Strsave(cp); 48359243Sobrien} 48459243Sobrien 48559243Sobrien 48659243Sobrien/* 48759243Sobrien * dochngd - implement chdir command. 48859243Sobrien */ 48959243Sobrien/*ARGSUSED*/ 49059243Sobrienvoid 491167465Smpdochngd(Char **v, struct command *c) 49259243Sobrien{ 493145479Smp Char *cp; 494145479Smp struct directory *dp; 49559243Sobrien int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 49659243Sobrien 49759243Sobrien USE(c); 49859243Sobrien printd = 0; 49959243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 50059243Sobrien 50159243Sobrien if (cp == NULL) { 50259243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 50359243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 50459243Sobrien if (chdir(short2str(cp)) < 0) 50559243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 50659243Sobrien cp = Strsave(cp); 50759243Sobrien } 50859243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 50959243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 51059243Sobrien /* NOTREACHED */ 51159243Sobrien return; 51259243Sobrien } 51359243Sobrien else if ((dp = dfind(cp)) != 0) { 51459243Sobrien char *tmp; 51559243Sobrien 51659243Sobrien printd = 1; 51759243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 51859243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 51959243Sobrien dcwd->di_prev->di_next = dcwd->di_next; 52059243Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 52159243Sobrien dfree(dcwd); 52259243Sobrien dnewcwd(dp, dflag); 52359243Sobrien return; 52459243Sobrien } 52559243Sobrien else 52659243Sobrien if ((cp = dfollow(cp)) == NULL) 52759243Sobrien return; 528167465Smp dp = xcalloc(sizeof(struct directory), 1); 52959243Sobrien dp->di_name = cp; 53059243Sobrien dp->di_count = 0; 53159243Sobrien dp->di_next = dcwd->di_next; 53259243Sobrien dp->di_prev = dcwd->di_prev; 53359243Sobrien dp->di_prev->di_next = dp; 53459243Sobrien dp->di_next->di_prev = dp; 53559243Sobrien dfree(dcwd); 53659243Sobrien dnewcwd(dp, dflag); 53759243Sobrien} 53859243Sobrien 53959243Sobrienstatic Char * 540167465Smpdgoto(Char *cp) 54159243Sobrien{ 542167465Smp Char *dp, *ret; 54359243Sobrien 54459243Sobrien if (!ABSOLUTEP(cp)) 54559243Sobrien { 546145479Smp Char *p, *q; 547167465Smp size_t cwdlen; 54859243Sobrien 549167465Smp cwdlen = Strlen(dcwd->di_name); 550167465Smp if (cwdlen == 1) /* root */ 55159243Sobrien cwdlen = 0; 552167465Smp dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); 55359243Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 55459243Sobrien continue; 55559243Sobrien if (cwdlen) 55659243Sobrien p[-1] = '/'; 55759243Sobrien else 55859243Sobrien p--; /* don't add a / after root */ 559167465Smp Strcpy(p, cp); 560167465Smp xfree(cp); 56159243Sobrien cp = dp; 56259243Sobrien dp += cwdlen; 56359243Sobrien } 56459243Sobrien else 56559243Sobrien dp = cp; 56659243Sobrien 567131962Smp#if defined(WINNT_NATIVE) 568167465Smp return agetcwd(); 569131962Smp#elif defined(__CYGWIN__) 570167465Smp if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ 571167465Smp return agetcwd(); 572167465Smp } else { 573167465Smp cleanup_push(cp, xfree); 574167465Smp ret = dcanon(cp, dp); 575167465Smp cleanup_ignore(cp); 576167465Smp cleanup_until(cp); 577167465Smp } 57869408Sache#else /* !WINNT_NATIVE */ 579167465Smp cleanup_push(cp, xfree); 580167465Smp ret = dcanon(cp, dp); 581167465Smp cleanup_ignore(cp); 582167465Smp cleanup_until(cp); 58369408Sache#endif /* WINNT_NATIVE */ 584167465Smp return ret; 58559243Sobrien} 58659243Sobrien 58759243Sobrien/* 58859243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 58959243Sobrien */ 59059243Sobrienstatic Char * 591167465Smpdfollow(Char *cp) 59259243Sobrien{ 593145479Smp Char *dp; 59459243Sobrien struct varent *c; 59559243Sobrien int serrno; 59659243Sobrien 59759243Sobrien cp = globone(cp, G_ERROR); 598167465Smp cleanup_push(cp, xfree); 59959243Sobrien#ifdef apollo 60059243Sobrien if (Strchr(cp, '`')) { 601167465Smp char *dptr; 60259243Sobrien if (chdir(dptr = short2str(cp)) < 0) 60359243Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 604167465Smp dp = agetcwd(); 605167465Smp cleanup_push(dp, xfree); 606167465Smp if (dp != NULL) { 607167465Smp cleanup_until(cp); 608167465Smp return dgoto(dp); 60959243Sobrien } 610167465Smp else 611167465Smp stderror(ERR_SYSTEM, dptr, strerror(errno)); 61259243Sobrien } 61359243Sobrien#endif /* apollo */ 614167465Smp 61559243Sobrien /* 61659243Sobrien * if we are ignoring symlinks, try to fix relatives now. 61759243Sobrien * if we are expading symlinks, it should be done by now. 61859243Sobrien */ 61959243Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 62059243Sobrien if (chdir(short2str(dp)) >= 0) { 621167465Smp cleanup_until(cp); 62259243Sobrien return dgoto(dp); 62359243Sobrien } 62459243Sobrien else { 625167465Smp xfree(dp); 626167465Smp if (chdir(short2str(cp)) >= 0) { 627167465Smp cleanup_ignore(cp); 628167465Smp cleanup_until(cp); 62959243Sobrien return dgoto(cp); 630167465Smp } 631167465Smp else if (errno != ENOENT && errno != ENOTDIR) { 632167465Smp int err; 633167465Smp 634167465Smp err = errno; 635167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(err)); 636167465Smp } 63759243Sobrien serrno = errno; 63859243Sobrien } 63959243Sobrien 64059243Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 641100616Smp && (c = adrof(STRcdpath)) && c->vec != NULL) { 642167465Smp struct Strbuf buf = Strbuf_INIT; 64359243Sobrien Char **cdp; 64459243Sobrien 64559243Sobrien for (cdp = c->vec; *cdp; cdp++) { 646167465Smp buf.len = 0; 647167465Smp Strbuf_append(&buf, *cdp); 648167465Smp Strbuf_append1(&buf, '/'); 649167465Smp Strbuf_append(&buf, cp); 650167465Smp Strbuf_terminate(&buf); 65159243Sobrien /* 65259243Sobrien * We always want to fix the directory here 65359243Sobrien * If we are normalizing symlinks 65459243Sobrien */ 655167465Smp dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 656167465Smp symlinks == SYM_EXPAND); 65759243Sobrien if (chdir(short2str(dp)) >= 0) { 65859243Sobrien printd = 1; 659167465Smp xfree(buf.s); 660167465Smp cleanup_until(cp); 66159243Sobrien return dgoto(dp); 66259243Sobrien } 66359243Sobrien else if (chdir(short2str(cp)) >= 0) { 66459243Sobrien printd = 1; 665167465Smp xfree(dp); 666167465Smp xfree(buf.s); 667167465Smp cleanup_ignore(cp); 668167465Smp cleanup_until(cp); 66959243Sobrien return dgoto(cp); 67059243Sobrien } 67159243Sobrien } 672167465Smp xfree(buf.s); 67359243Sobrien } 67459243Sobrien dp = varval(cp); 67559243Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 676167465Smp cleanup_until(cp); 67759243Sobrien cp = Strsave(dp); 67859243Sobrien printd = 1; 67959243Sobrien return dgoto(cp); 68059243Sobrien } 68159243Sobrien /* 68259243Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 68359243Sobrien * directories we could get to. 68459243Sobrien */ 685167465Smp if (!bequiet) 686167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 687167465Smp cleanup_until(cp); 688167465Smp return (NULL); 68959243Sobrien} 69059243Sobrien 69159243Sobrien 69259243Sobrien/* 69359243Sobrien * dopushd - push new directory onto directory stack. 69459243Sobrien * with no arguments exchange top and second. 69559243Sobrien * with numeric argument (+n) bring it to top. 69659243Sobrien */ 69759243Sobrien/*ARGSUSED*/ 69859243Sobrienvoid 699167465Smpdopushd(Char **v, struct command *c) 70059243Sobrien{ 701145479Smp struct directory *dp; 702145479Smp Char *cp; 70359243Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 70459243Sobrien 70559243Sobrien USE(c); 70659243Sobrien printd = 1; 70759243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 70859243Sobrien 70959243Sobrien if (cp == NULL) { 71059243Sobrien if (adrof(STRpushdtohome)) { 71159243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 71259243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 71359243Sobrien if (chdir(short2str(cp)) < 0) 71459243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 71559243Sobrien if ((cp = dfollow(cp)) == NULL) 71659243Sobrien return; 717167465Smp dp = xcalloc(sizeof(struct directory), 1); 71859243Sobrien dp->di_name = cp; 71959243Sobrien dp->di_count = 0; 72059243Sobrien dp->di_prev = dcwd; 72159243Sobrien dp->di_next = dcwd->di_next; 72259243Sobrien dcwd->di_next = dp; 72359243Sobrien dp->di_next->di_prev = dp; 72459243Sobrien } 72559243Sobrien else { 72659243Sobrien char *tmp; 72759243Sobrien 72859243Sobrien if ((dp = dcwd->di_prev) == &dhead) 72959243Sobrien dp = dhead.di_prev; 73059243Sobrien if (dp == dcwd) 73159243Sobrien stderror(ERR_NAME | ERR_NODIR); 73259243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 73359243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 73459243Sobrien dp->di_prev->di_next = dp->di_next; 73559243Sobrien dp->di_next->di_prev = dp->di_prev; 73659243Sobrien dp->di_next = dcwd->di_next; 73759243Sobrien dp->di_prev = dcwd; 73859243Sobrien dcwd->di_next->di_prev = dp; 73959243Sobrien dcwd->di_next = dp; 74059243Sobrien } 74159243Sobrien } 74259243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 74359243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 74459243Sobrien /* NOTREACHED */ 74559243Sobrien return; 74659243Sobrien } 74759243Sobrien else if ((dp = dfind(cp)) != NULL) { 74859243Sobrien char *tmp; 74959243Sobrien 75059243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 75159243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 75259243Sobrien /* 75359243Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 75459243Sobrien */ 75559243Sobrien if (adrof(STRdextract)) 75659243Sobrien dextract(dp); 75759243Sobrien } 75859243Sobrien else { 759145479Smp Char *ccp; 76059243Sobrien 76159243Sobrien if ((ccp = dfollow(cp)) == NULL) 76259243Sobrien return; 763167465Smp dp = xcalloc(sizeof(struct directory), 1); 76459243Sobrien dp->di_name = ccp; 76559243Sobrien dp->di_count = 0; 76659243Sobrien dp->di_prev = dcwd; 76759243Sobrien dp->di_next = dcwd->di_next; 76859243Sobrien dcwd->di_next = dp; 76959243Sobrien dp->di_next->di_prev = dp; 77059243Sobrien } 77159243Sobrien dnewcwd(dp, dflag); 77259243Sobrien} 77359243Sobrien 77459243Sobrien/* 77559243Sobrien * dfind - find a directory if specified by numeric (+n) argument 77659243Sobrien */ 77759243Sobrienstatic struct directory * 778167465Smpdfind(Char *cp) 77959243Sobrien{ 780145479Smp struct directory *dp; 781145479Smp int i; 782145479Smp Char *ep; 78359243Sobrien 78459243Sobrien if (*cp++ != '+') 78559243Sobrien return (0); 78659243Sobrien for (ep = cp; Isdigit(*ep); ep++) 78759243Sobrien continue; 78859243Sobrien if (*ep) 78959243Sobrien return (0); 79059243Sobrien i = getn(cp); 79159243Sobrien if (i <= 0) 79259243Sobrien return (0); 79359243Sobrien for (dp = dcwd; i != 0; i--) { 79459243Sobrien if ((dp = dp->di_prev) == &dhead) 79559243Sobrien dp = dp->di_prev; 79659243Sobrien if (dp == dcwd) 79759243Sobrien stderror(ERR_NAME | ERR_DEEP); 79859243Sobrien } 79959243Sobrien return (dp); 80059243Sobrien} 80159243Sobrien 80259243Sobrien/* 80359243Sobrien * dopopd - pop a directory out of the directory stack 80459243Sobrien * with a numeric argument just discard it. 80559243Sobrien */ 80659243Sobrien/*ARGSUSED*/ 80759243Sobrienvoid 808167465Smpdopopd(Char **v, struct command *c) 80959243Sobrien{ 81059243Sobrien Char *cp; 811145479Smp struct directory *dp, *p = NULL; 81259243Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 81359243Sobrien 81459243Sobrien USE(c); 81559243Sobrien printd = 1; 81659243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 81759243Sobrien 81859243Sobrien if (cp == NULL) 81959243Sobrien dp = dcwd; 82059243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 82159243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 82259243Sobrien /* NOTREACHED */ 82359243Sobrien return; 82459243Sobrien } 82559243Sobrien else if ((dp = dfind(cp)) == 0) 82659243Sobrien stderror(ERR_NAME | ERR_BADDIR); 82759243Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 82859243Sobrien stderror(ERR_NAME | ERR_EMPTY); 82959243Sobrien if (dp == dcwd) { 83059243Sobrien char *tmp; 83159243Sobrien 83259243Sobrien if ((p = dp->di_prev) == &dhead) 83359243Sobrien p = dhead.di_prev; 83459243Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 83559243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 83659243Sobrien } 83759243Sobrien dp->di_prev->di_next = dp->di_next; 83859243Sobrien dp->di_next->di_prev = dp->di_prev; 839167465Smp dfree(dp); 84059243Sobrien if (dp == dcwd) { 841167465Smp dnewcwd(p, dflag); 84259243Sobrien } 84359243Sobrien else { 84459243Sobrien printdirs(dflag); 84559243Sobrien } 84659243Sobrien} 84759243Sobrien 84859243Sobrien/* 84959243Sobrien * dfree - free the directory (or keep it if it still has ref count) 85059243Sobrien */ 85159243Sobrienvoid 852167465Smpdfree(struct directory *dp) 85359243Sobrien{ 85459243Sobrien 85559243Sobrien if (dp->di_count != 0) { 85659243Sobrien dp->di_next = dp->di_prev = 0; 85759243Sobrien } 85859243Sobrien else { 859167465Smp xfree(dp->di_name); 860167465Smp xfree(dp); 86159243Sobrien } 86259243Sobrien} 86359243Sobrien 86459243Sobrien/* 86559243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 86659243Sobrien * we are of course assuming that the file system is standardly 86759243Sobrien * constructed (always have ..'s, directories have links) 86859243Sobrien */ 86959243SobrienChar * 870167465Smpdcanon(Char *cp, Char *p) 87159243Sobrien{ 872145479Smp Char *sp; 873145479Smp Char *p1, *p2; /* general purpose */ 874145479Smp int slash; 875167465Smp#ifdef HAVE_SLASHSLASH 876145479Smp int slashslash; 877167465Smp#endif /* HAVE_SLASHSLASH */ 87873393Skris size_t clen; 87959243Sobrien 88059243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 881167465Smp Char *mlink, *newcp; 882167465Smp char *tlink; 883167465Smp size_t cc; 88459243Sobrien#endif /* S_IFLNK */ 88559243Sobrien 886167465Smp clen = Strlen(cp); 88759243Sobrien 88859243Sobrien /* 88959243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 89073393Skris * cwd does not start with a slash or the result would be too long try to 89173393Skris * correct it. 89259243Sobrien */ 89359243Sobrien if (!ABSOLUTEP(cp)) { 894167465Smp Char *tmpdir; 89573393Skris size_t len; 89659243Sobrien 89759243Sobrien p1 = varval(STRcwd); 89873393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 899167465Smp Char *new_cwd = agetcwd(); 900167465Smp 901167465Smp if (new_cwd == NULL) { 90273393Skris xprintf("%s: %s\n", progname, strerror(errno)); 903167465Smp setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 90473393Skris } 905167465Smp else 906167465Smp setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 90773393Skris p1 = varval(STRcwd); 90873393Skris } 90973393Skris len = Strlen(p1); 910167465Smp tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 91159243Sobrien (void) Strcpy(tmpdir, p1); 91259243Sobrien (void) Strcat(tmpdir, STRslash); 91359243Sobrien (void) Strcat(tmpdir, cp); 914167465Smp xfree(cp); 915167465Smp cp = p = tmpdir; 91659243Sobrien } 91759243Sobrien 918167465Smp#ifdef HAVE_SLASHSLASH 91959243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 920167465Smp#endif /* HAVE_SLASHSLASH */ 92159243Sobrien 92259243Sobrien while (*p) { /* for each component */ 92359243Sobrien sp = p; /* save slash address */ 92459243Sobrien while (*++p == '/') /* flush extra slashes */ 92559243Sobrien continue; 92659243Sobrien if (p != ++sp) 92759243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 92859243Sobrien continue; 92959243Sobrien p = sp; /* save start of component */ 93059243Sobrien slash = 0; 93159243Sobrien if (*p) 93259243Sobrien while (*++p) /* find next slash or end of path */ 93359243Sobrien if (*p == '/') { 93459243Sobrien slash = 1; 93559243Sobrien *p = 0; 93659243Sobrien break; 93759243Sobrien } 93859243Sobrien 939167465Smp#ifdef HAVE_SLASHSLASH 94059243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 94159243Sobrien slashslash = 1; 942167465Smp#endif /* HAVE_SLASHSLASH */ 94359243Sobrien if (*sp == '\0') { /* if component is null */ 94459243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 94559243Sobrien break; 94659243Sobrien else 94759243Sobrien *sp = '\0'; 94859243Sobrien } 94959243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 95059243Sobrien if (slash) { 95159243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 95259243Sobrien continue; 95359243Sobrien p = --sp; 95459243Sobrien } 95559243Sobrien else if (--sp != cp) 95659243Sobrien *sp = '\0'; 95759243Sobrien else 95859243Sobrien sp[1] = '\0'; 95959243Sobrien } 96059243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 96159243Sobrien /* 96259243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 96359243Sobrien * a path starting at /, and "xxx" is a single component. Before 96459243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 96559243Sobrien * symbolic link. 96659243Sobrien */ 96759243Sobrien *--sp = 0; /* form the pathname for readlink */ 96859243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 96959243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 970167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 971167465Smp mlink = str2short(tlink); 972167465Smp xfree(tlink); 97359243Sobrien 97459243Sobrien if (slash) 97559243Sobrien *p = '/'; 97659243Sobrien /* 97759243Sobrien * Point p to the '/' in "/..", and restore the '/'. 97859243Sobrien */ 97959243Sobrien *(p = sp) = '/'; 980145479Smp if (*mlink != '/') { 98159243Sobrien /* 98259243Sobrien * Relative path, expand it between the "yyy/" and the 98359243Sobrien * "/..". First, back sp up to the character past "yyy/". 98459243Sobrien */ 98559243Sobrien while (*--sp != '/') 98659243Sobrien continue; 98759243Sobrien sp++; 98859243Sobrien *sp = 0; 98959243Sobrien /* 990145479Smp * New length is "yyy/" + mlink + "/.." and rest 99159243Sobrien */ 992167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 993167465Smp Strlen(p) + 1) * sizeof(Char)); 99459243Sobrien /* 99559243Sobrien * Copy new path into newcp 99659243Sobrien */ 99759243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 99859243Sobrien continue; 999145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 100059243Sobrien continue; 100159243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 100259243Sobrien continue; 100359243Sobrien /* 100459243Sobrien * Restart canonicalization at expanded "/xxx". 100559243Sobrien */ 100659243Sobrien p = sp - cp - 1 + newcp; 100759243Sobrien } 100859243Sobrien else { 1009167465Smp newcp = Strspl(mlink, p); 101059243Sobrien /* 101159243Sobrien * Restart canonicalization at beginning 101259243Sobrien */ 101359243Sobrien p = newcp; 101459243Sobrien } 1015167465Smp xfree(cp); 101659243Sobrien cp = newcp; 1017167465Smp#ifdef HAVE_SLASHSLASH 101859243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1019167465Smp#endif /* HAVE_SLASHSLASH */ 102059243Sobrien continue; /* canonicalize the link */ 102159243Sobrien } 102259243Sobrien#endif /* S_IFLNK */ 102359243Sobrien *sp = '/'; 102459243Sobrien if (sp != cp) 102559243Sobrien while (*--sp != '/') 102659243Sobrien continue; 102759243Sobrien if (slash) { 102859243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 102959243Sobrien continue; 103059243Sobrien p = sp; 103159243Sobrien } 103259243Sobrien else if (cp == sp) 103359243Sobrien *++sp = '\0'; 103459243Sobrien else 103559243Sobrien *sp = '\0'; 103659243Sobrien } 103759243Sobrien else { /* normal dir name (not . or .. or nothing) */ 103859243Sobrien 103959243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 104059243Sobrien if (sp != cp && symlinks == SYM_CHASE && 1041167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 1042167465Smp mlink = str2short(tlink); 1043167465Smp xfree(tlink); 104459243Sobrien 104559243Sobrien /* 104659243Sobrien * restore the '/'. 104759243Sobrien */ 104859243Sobrien if (slash) 104959243Sobrien *p = '/'; 105059243Sobrien 105159243Sobrien /* 105259243Sobrien * point sp to p (rather than backing up). 105359243Sobrien */ 105459243Sobrien sp = p; 105559243Sobrien 1056145479Smp if (*mlink != '/') { 105759243Sobrien /* 105859243Sobrien * Relative path, expand it between the "yyy/" and the 105959243Sobrien * remainder. First, back sp up to the character past 106059243Sobrien * "yyy/". 106159243Sobrien */ 106259243Sobrien while (*--sp != '/') 106359243Sobrien continue; 106459243Sobrien sp++; 106559243Sobrien *sp = 0; 106659243Sobrien /* 1067145479Smp * New length is "yyy/" + mlink + "/.." and rest 106859243Sobrien */ 1069167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1070167465Smp Strlen(p) + 1) * sizeof(Char)); 107159243Sobrien /* 107259243Sobrien * Copy new path into newcp 107359243Sobrien */ 107459243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 107559243Sobrien continue; 1076145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 107759243Sobrien continue; 107859243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 107959243Sobrien continue; 108059243Sobrien /* 108159243Sobrien * Restart canonicalization at expanded "/xxx". 108259243Sobrien */ 108359243Sobrien p = sp - cp - 1 + newcp; 108459243Sobrien } 108559243Sobrien else { 1086167465Smp newcp = Strspl(mlink, p); 108759243Sobrien /* 108859243Sobrien * Restart canonicalization at beginning 108959243Sobrien */ 109059243Sobrien p = newcp; 109159243Sobrien } 1092167465Smp xfree(cp); 109359243Sobrien cp = newcp; 1094167465Smp#ifdef HAVE_SLASHSLASH 109559243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1096167465Smp#endif /* HAVE_SLASHSLASH */ 1097145479Smp continue; /* canonicalize the mlink */ 109859243Sobrien } 109959243Sobrien#endif /* S_IFLNK */ 110059243Sobrien if (slash) 110159243Sobrien *p = '/'; 110259243Sobrien } 110359243Sobrien } 110459243Sobrien 110559243Sobrien /* 110659243Sobrien * fix home... 110759243Sobrien */ 110859243Sobrien#ifdef S_IFLNK 110959243Sobrien p1 = varval(STRhome); 1110167465Smp cc = Strlen(p1); 111159243Sobrien /* 111259243Sobrien * See if we're not in a subdir of STRhome 111359243Sobrien */ 1114167465Smp if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 111559243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 111659243Sobrien static ino_t home_ino = (ino_t) -1; 111759243Sobrien static dev_t home_dev = (dev_t) -1; 111859243Sobrien static Char *home_ptr = NULL; 111959243Sobrien struct stat statbuf; 112059243Sobrien int found; 1121167465Smp Char *copy; 112259243Sobrien 112359243Sobrien /* 112459243Sobrien * Get dev and ino of STRhome 112559243Sobrien */ 112659243Sobrien if (home_ptr != p1 && 112759243Sobrien stat(short2str(p1), &statbuf) != -1) { 112859243Sobrien home_dev = statbuf.st_dev; 112959243Sobrien home_ino = statbuf.st_ino; 113059243Sobrien home_ptr = p1; 113159243Sobrien } 113259243Sobrien /* 113359243Sobrien * Start comparing dev & ino backwards 113459243Sobrien */ 1135167465Smp p2 = copy = Strsave(cp); 113659243Sobrien found = 0; 113759243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 113859243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 113959243Sobrien statbuf.st_ino == home_ino) { 114059243Sobrien found = 1; 114159243Sobrien break; 114259243Sobrien } 114359243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 114459243Sobrien *sp = '\0'; 114559243Sobrien } 114659243Sobrien /* 114759243Sobrien * See if we found it 114859243Sobrien */ 114959243Sobrien if (*p2 && found) { 115059243Sobrien /* 115159243Sobrien * Use STRhome to make '~' work 115259243Sobrien */ 115359243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 1154167465Smp xfree(cp); 115559243Sobrien cp = newcp; 115659243Sobrien } 1157167465Smp xfree(copy); 115859243Sobrien } 115959243Sobrien#endif /* S_IFLNK */ 116059243Sobrien 1161167465Smp#ifdef HAVE_SLASHSLASH 116259243Sobrien if (slashslash) { 116359243Sobrien if (cp[1] != '/') { 1164167465Smp p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 116559243Sobrien *p = '/'; 116659243Sobrien (void) Strcpy(&p[1], cp); 1167167465Smp xfree(cp); 116859243Sobrien cp = p; 116959243Sobrien } 117059243Sobrien } 1171167465Smp if (cp[1] == '/' && cp[2] == '/') { 1172167465Smp for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 1173167465Smp continue; 1174167465Smp } 1175167465Smp#endif /* HAVE_SLASHSLASH */ 117659243Sobrien return cp; 117759243Sobrien} 117859243Sobrien 117959243Sobrien 118059243Sobrien/* 118159243Sobrien * dnewcwd - make a new directory in the loop the current one 118259243Sobrien */ 118359243Sobrienstatic void 1184167465Smpdnewcwd(struct directory *dp, int dflag) 118559243Sobrien{ 118659243Sobrien int print; 118759243Sobrien 118859243Sobrien if (adrof(STRdunique)) { 118959243Sobrien struct directory *dn; 119059243Sobrien 119159243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 119259243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 119359243Sobrien dn->di_next->di_prev = dn->di_prev; 119459243Sobrien dn->di_prev->di_next = dn->di_next; 119559243Sobrien dfree(dn); 119659243Sobrien break; 119759243Sobrien } 119859243Sobrien } 119959243Sobrien dcwd = dp; 120059243Sobrien dset(dcwd->di_name); 120159243Sobrien dgetstack(); 120259243Sobrien print = printd; /* if printd is set, print dirstack... */ 120359243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 120459243Sobrien print = 0; 120559243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 120659243Sobrien print = 1; 120759243Sobrien if (bequiet) /* and bequiet overrides everything */ 120859243Sobrien print = 0; 120959243Sobrien if (print) 121059243Sobrien printdirs(dflag); 121159243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 121259243Sobrien} 121359243Sobrien 121459243Sobrienvoid 1215167465Smpdsetstack(void) 121659243Sobrien{ 121759243Sobrien Char **cp; 121859243Sobrien struct varent *vp; 121959243Sobrien struct directory *dn, *dp; 122059243Sobrien 1221100616Smp if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 122259243Sobrien return; 122359243Sobrien 122459243Sobrien /* Free the whole stack */ 122559243Sobrien while ((dn = dhead.di_prev) != &dhead) { 122659243Sobrien dn->di_next->di_prev = dn->di_prev; 122759243Sobrien dn->di_prev->di_next = dn->di_next; 122859243Sobrien if (dn != dcwd) 122959243Sobrien dfree(dn); 123059243Sobrien } 123159243Sobrien 123259243Sobrien /* thread the current working directory */ 123359243Sobrien dhead.di_prev = dhead.di_next = dcwd; 123459243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 123559243Sobrien 123659243Sobrien /* put back the stack */ 123759243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 1238167465Smp dp = xcalloc(sizeof(struct directory), 1); 123959243Sobrien dp->di_name = Strsave(*cp); 124059243Sobrien dp->di_count = 0; 124159243Sobrien dp->di_prev = dcwd; 124259243Sobrien dp->di_next = dcwd->di_next; 124359243Sobrien dcwd->di_next = dp; 124459243Sobrien dp->di_next->di_prev = dp; 124559243Sobrien } 124659243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 124759243Sobrien} 124859243Sobrien 124959243Sobrienstatic void 1250167465Smpdgetstack(void) 125159243Sobrien{ 125259243Sobrien int i = 0; 125359243Sobrien Char **dblk, **dbp; 125459243Sobrien struct directory *dn; 125559243Sobrien 125659243Sobrien if (adrof(STRdirstack) == NULL) 125759243Sobrien return; 125859243Sobrien 1259167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126059243Sobrien continue; 1261167465Smp dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 1262167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 126359243Sobrien *dbp = Strsave(dn->di_name); 126459243Sobrien *dbp = NULL; 1265167465Smp cleanup_push(dblk, blk_cleanup); 126659243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1267167465Smp cleanup_ignore(dblk); 1268167465Smp cleanup_until(dblk); 126959243Sobrien} 127059243Sobrien 127159243Sobrien/* 127259243Sobrien * getstakd - added by kfk 17 Jan 1984 127359243Sobrien * Support routine for the stack hack. Finds nth directory in 127459243Sobrien * the directory stack, or finds last directory in stack. 127559243Sobrien */ 1276167465Smpconst Char * 1277167465Smpgetstakd(int cnt) 127859243Sobrien{ 127959243Sobrien struct directory *dp; 128059243Sobrien 128159243Sobrien dp = dcwd; 128259243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 128359243Sobrien dp = dp->di_next; 128459243Sobrien if (dp == &dhead) 128559243Sobrien dp = dp->di_next; 128659243Sobrien } 128759243Sobrien else { 128859243Sobrien while (cnt-- > 0) { 128959243Sobrien dp = dp->di_prev; 129059243Sobrien if (dp == &dhead) 129159243Sobrien dp = dp->di_prev; 129259243Sobrien if (dp == dcwd) 1293167465Smp return NULL; 129459243Sobrien } 129559243Sobrien } 1296167465Smp return dp->di_name; 129759243Sobrien} 129859243Sobrien 129959243Sobrien/* 130059243Sobrien * Karl Kleinpaste - 10 Feb 1984 130159243Sobrien * Added dextract(), which is used in pushd +n. 130259243Sobrien * Instead of just rotating the entire stack around, dextract() 130359243Sobrien * lets the user have the nth dir extracted from its current 130459243Sobrien * position, and pushes it onto the top. 130559243Sobrien */ 130659243Sobrienstatic void 1307167465Smpdextract(struct directory *dp) 130859243Sobrien{ 130959243Sobrien if (dp == dcwd) 131059243Sobrien return; 131159243Sobrien dp->di_next->di_prev = dp->di_prev; 131259243Sobrien dp->di_prev->di_next = dp->di_next; 131359243Sobrien dp->di_next = dcwd->di_next; 131459243Sobrien dp->di_prev = dcwd; 131559243Sobrien dp->di_next->di_prev = dp; 131659243Sobrien dcwd->di_next = dp; 131759243Sobrien} 131859243Sobrien 1319167465Smpstatic void 1320167465Smpbequiet_cleanup(void *dummy) 1321167465Smp{ 1322167465Smp USE(dummy); 1323167465Smp bequiet = 0; 1324167465Smp} 1325167465Smp 132659243Sobrienvoid 1327167465Smploaddirs(Char *fname) 132859243Sobrien{ 132959243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133059243Sobrien 133159243Sobrien bequiet = 1; 1332167465Smp cleanup_push(&bequiet, bequiet_cleanup); 1333167465Smp if (fname) 133459243Sobrien loaddirs_cmd[1] = fname; 133559243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 133659243Sobrien loaddirs_cmd[1] = fname; 133759243Sobrien else 133859243Sobrien loaddirs_cmd[1] = STRtildotdirs; 1339167465Smp dosource(loaddirs_cmd, NULL); 1340167465Smp cleanup_until(&bequiet); 134159243Sobrien} 134259243Sobrien 134359243Sobrien/* 134459243Sobrien * create a file called ~/.cshdirs which has a sequence 134559243Sobrien * of pushd commands which will restore the dir stack to 134659243Sobrien * its state before exit/logout. remember that the order 134759243Sobrien * is reversed in the file because we are pushing. 134859243Sobrien * -strike 134959243Sobrien */ 135059243Sobrienvoid 1351167465Smprecdirs(Char *fname, int def) 135259243Sobrien{ 135359243Sobrien int fp, ftmp, oldidfds; 135459243Sobrien int cdflag = 0; 135559243Sobrien struct directory *dp; 135659243Sobrien unsigned int num; 135759243Sobrien Char *snum; 1358167465Smp struct Strbuf qname = Strbuf_INIT; 135959243Sobrien 136059243Sobrien if (fname == NULL && !def) 136159243Sobrien return; 136259243Sobrien 136359243Sobrien if (fname == NULL) { 136459243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 136559243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 136659243Sobrien else 136759243Sobrien fname = Strsave(fname); 136859243Sobrien } 136959243Sobrien else 137059243Sobrien fname = globone(fname, G_ERROR); 1371167465Smp cleanup_push(fname, xfree); 1372167465Smp 1373167465Smp if ((fp = xcreat(short2str(fname), 0600)) == -1) { 1374167465Smp cleanup_until(fname); 137559243Sobrien return; 137659243Sobrien } 137759243Sobrien 137883098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 137959243Sobrien num = (unsigned int) ~0; 138059243Sobrien else 138159243Sobrien num = (unsigned int) atoi(short2str(snum)); 138259243Sobrien 138359243Sobrien oldidfds = didfds; 138459243Sobrien didfds = 0; 138559243Sobrien ftmp = SHOUT; 138659243Sobrien SHOUT = fp; 138759243Sobrien 1388167465Smp cleanup_push(&qname, Strbuf_cleanup); 138959243Sobrien dp = dcwd->di_next; 139059243Sobrien do { 139159243Sobrien if (dp == &dhead) 139259243Sobrien continue; 139359243Sobrien 139459243Sobrien if (cdflag == 0) { 139559243Sobrien cdflag = 1; 1396167465Smp xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 139759243Sobrien } 139859243Sobrien else 1399167465Smp xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 140059243Sobrien 140159243Sobrien if (num-- == 0) 140259243Sobrien break; 140359243Sobrien 140459243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 140559243Sobrien 1406167465Smp xclose(fp); 140759243Sobrien SHOUT = ftmp; 140859243Sobrien didfds = oldidfds; 1409167465Smp cleanup_until(fname); 141059243Sobrien} 1411