sh.dir.c revision 316957
1316957Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.85 2016/04/08 16:10:52 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 36316957SdchaginRCSID("$tcsh: sh.dir.c,v 3.85 2016/04/08 16:10:52 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 *); 45195609Smpstatic Char *dfollow (Char *, int); 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; 207231990Smp else if ((*n)[1] == '-' && (*n)[2] == '\0') { /* test for -- */ 208231990Smp n++; 209231990Smp break; 210231990Smp } else { 21159243Sobrien char *p; 212231990Smp while (*s != '\0') /* examine flags */ { 21359243Sobrien if ((p = strchr(dstr, *s++)) != NULL) 21459243Sobrien dflag |= (1 << (p - dstr)); 215167465Smp else 21659243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21759243Sobrien } 21859243Sobrien } 21959243Sobrien if (*n && (dflag & DIR_OLD)) 22059243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 22159243Sobrien *v = n; 22259243Sobrien /* make -l, -v, and -n imply -p */ 22359243Sobrien if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 22459243Sobrien dflag |= DIR_PRINT; 22559243Sobrien return dflag; 22659243Sobrien} 22759243Sobrien 22859243Sobrien/* 22959243Sobrien * dodirs - list all directories in directory loop 23059243Sobrien */ 23159243Sobrien/*ARGSUSED*/ 23259243Sobrienvoid 233167465Smpdodirs(Char **v, struct command *c) 23459243Sobrien{ 235167465Smp static const char flags[] = "plvnSLc"; 23659243Sobrien int dflag = skipargs(&v, flags, ""); 23759243Sobrien 23859243Sobrien USE(c); 23959243Sobrien if ((dflag & DIR_CLEAR) != 0) { 24059243Sobrien struct directory *dp, *fdp; 24159243Sobrien for (dp = dcwd->di_next; dp != dcwd; ) { 24259243Sobrien fdp = dp; 24359243Sobrien dp = dp->di_next; 24459243Sobrien if (fdp != &dhead) 24559243Sobrien dfree(fdp); 24659243Sobrien } 24759243Sobrien dhead.di_next = dhead.di_prev = dp; 24859243Sobrien dp->di_next = dp->di_prev = &dhead; 24959243Sobrien } 25059243Sobrien if ((dflag & DIR_LOAD) != 0) 25159243Sobrien loaddirs(*v); 25259243Sobrien else if ((dflag & DIR_SAVE) != 0) 25359243Sobrien recdirs(*v, 1); 25459243Sobrien 25559243Sobrien if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 25659243Sobrien v++; 25759243Sobrien 25859243Sobrien if (*v != NULL || (dflag & DIR_OLD)) 25959243Sobrien stderror(ERR_DIRUS, "dirs", flags, ""); 26059243Sobrien if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 26159243Sobrien printdirs(dflag); 26259243Sobrien} 26359243Sobrien 26459243Sobrienstatic void 265167465Smpprintdirs(int dflag) 26659243Sobrien{ 267145479Smp struct directory *dp; 26859243Sobrien Char *s, *user; 26959243Sobrien int idx, len, cur; 27059243Sobrien 27159243Sobrien dp = dcwd; 27259243Sobrien idx = 0; 27359243Sobrien cur = 0; 27459243Sobrien do { 27559243Sobrien if (dp == &dhead) 27659243Sobrien continue; 27759243Sobrien if (dflag & DIR_VERT) { 27859243Sobrien xprintf("%d\t", idx++); 27959243Sobrien cur = 0; 28059243Sobrien } 28159243Sobrien s = dp->di_name; 28259243Sobrien user = NULL; 28359243Sobrien if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 28459243Sobrien len = (int) (Strlen(user) + Strlen(s) + 2); 28559243Sobrien else 28659243Sobrien len = (int) (Strlen(s) + 1); 28759243Sobrien 28859243Sobrien cur += len; 289167465Smp if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) { 29059243Sobrien xputchar('\n'); 29159243Sobrien cur = len; 29259243Sobrien } 29359243Sobrien if (user) 29459243Sobrien xprintf("~%S", user); 295145479Smp xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 29659243Sobrien } while ((dp = dp->di_prev) != dcwd); 29759243Sobrien if (!(dflag & DIR_VERT)) 29859243Sobrien xputchar('\n'); 29959243Sobrien} 30059243Sobrien 30159243Sobrienvoid 302167465Smpdtildepr(Char *dir) 30359243Sobrien{ 30459243Sobrien Char* user; 30559243Sobrien if ((user = getusername(&dir)) != NULL) 306145479Smp xprintf("~%-S%S", user, dir); 30759243Sobrien else 30859243Sobrien xprintf("%S", dir); 30959243Sobrien} 31059243Sobrien 31159243Sobrienvoid 312167465Smpdtilde(void) 31359243Sobrien{ 31459243Sobrien struct directory *d = dcwd; 31559243Sobrien 31659243Sobrien do { 31759243Sobrien if (d == &dhead) 31859243Sobrien continue; 31959243Sobrien d->di_name = dcanon(d->di_name, STRNULL); 32059243Sobrien } while ((d = d->di_prev) != dcwd); 32159243Sobrien 32259243Sobrien dset(dcwd->di_name); 32359243Sobrien} 32459243Sobrien 32559243Sobrien 32659243Sobrien/* dnormalize(): 32759243Sobrien * The path will be normalized if it 32859243Sobrien * 1) is "..", 32959243Sobrien * 2) or starts with "../", 33059243Sobrien * 3) or ends with "/..", 33159243Sobrien * 4) or contains the string "/../", 33259243Sobrien * then it will be normalized, unless those strings are quoted. 33359243Sobrien * Otherwise, a copy is made and sent back. 33459243Sobrien */ 33559243SobrienChar * 336167465Smpdnormalize(const Char *cp, int expnd) 33759243Sobrien{ 33859243Sobrien 33959243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */ 34059243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 34159243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 34259243Sobrien 34359243Sobrien#ifdef S_IFLNK 344145479Smp if (expnd) { 345167465Smp struct Strbuf buf = Strbuf_INIT; 34659243Sobrien int dotdot = 0; 347167465Smp Char *dp, *cwd; 348167465Smp const Char *start = cp; 349167465Smp# ifdef HAVE_SLASHSLASH 350145479Smp int slashslash; 351167465Smp# endif /* HAVE_SLASHSLASH */ 35259243Sobrien 35359243Sobrien /* 35459243Sobrien * count the number of "../xxx" or "xxx/../xxx" in the path 35559243Sobrien */ 356167465Smp for ( ; *cp && *(cp + 1); cp++) 357167465Smp if (IS_DOTDOT(start, cp)) 35859243Sobrien dotdot++; 359167465Smp 36059243Sobrien /* 36159243Sobrien * if none, we are done. 36259243Sobrien */ 36359243Sobrien if (dotdot == 0) 364167465Smp return (Strsave(start)); 365167465Smp 366167465Smp# ifdef notdef 367167465Smp struct stat sb; 368100616Smp /* 369167465Smp * We disable this test because: 370167465Smp * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1; 371167465Smp * echo ../../dir1 does not expand. We had enabled this before 372167465Smp * because it was bothering people with expansions in compilation 373167465Smp * lines like -I../../foo. Maybe we need some kind of finer grain 374167465Smp * control? 375167465Smp * 376100616Smp * If the path doesn't exist, we are done too. 377100616Smp */ 378167465Smp if (lstat(short2str(start), &sb) != 0 && errno == ENOENT) 379167465Smp return (Strsave(start)); 380167465Smp# endif 381100616Smp 382167465Smp cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char)); 38359243Sobrien (void) Strcpy(cwd, dcwd->di_name); 38459243Sobrien 38559243Sobrien /* 38659243Sobrien * If the path starts with a slash, we are not relative to 38759243Sobrien * the current working directory. 38859243Sobrien */ 38959243Sobrien if (ABSOLUTEP(start)) 39059243Sobrien *cwd = '\0'; 391167465Smp# ifdef HAVE_SLASHSLASH 39259243Sobrien slashslash = cwd[0] == '/' && cwd[1] == '/'; 393167465Smp# endif /* HAVE_SLASHSLASH */ 39459243Sobrien 39559243Sobrien /* 39659243Sobrien * Ignore . and count ..'s 39759243Sobrien */ 398167465Smp cp = start; 399167465Smp do { 40059243Sobrien dotdot = 0; 401167465Smp buf.len = 0; 40259243Sobrien while (*cp) 40359243Sobrien if (IS_DOT(start, cp)) { 40459243Sobrien if (*++cp) 40559243Sobrien cp++; 40659243Sobrien } 40759243Sobrien else if (IS_DOTDOT(start, cp)) { 408167465Smp if (buf.len != 0) 40959243Sobrien break; /* finish analyzing .././../xxx/[..] */ 41059243Sobrien dotdot++; 41159243Sobrien cp += 2; 41259243Sobrien if (*cp) 41359243Sobrien cp++; 41459243Sobrien } 41559243Sobrien else 416167465Smp Strbuf_append1(&buf, *cp++); 41759243Sobrien 418167465Smp Strbuf_terminate(&buf); 41959243Sobrien while (dotdot > 0) 42059243Sobrien if ((dp = Strrchr(cwd, '/')) != NULL) { 421167465Smp# ifdef HAVE_SLASHSLASH 42259243Sobrien if (dp == &cwd[1]) 42359243Sobrien slashslash = 1; 424167465Smp# endif /* HAVE_SLASHSLASH */ 42559243Sobrien *dp = '\0'; 42659243Sobrien dotdot--; 42759243Sobrien } 42859243Sobrien else 42959243Sobrien break; 43059243Sobrien 43159243Sobrien if (!*cwd) { /* too many ..'s, starts with "/" */ 43259243Sobrien cwd[0] = '/'; 433167465Smp# ifdef HAVE_SLASHSLASH 434167465Smp /* 435167465Smp * Only append another slash, if already the former cwd 436167465Smp * was in a double-slash path. 437167465Smp */ 438167465Smp cwd[1] = slashslash ? '/' : '\0'; 43959243Sobrien cwd[2] = '\0'; 440167465Smp# else /* !HAVE_SLASHSLASH */ 44159243Sobrien cwd[1] = '\0'; 442167465Smp# endif /* HAVE_SLASHSLASH */ 44359243Sobrien } 444167465Smp# ifdef HAVE_SLASHSLASH 44559243Sobrien else if (slashslash && cwd[1] == '\0') { 44659243Sobrien cwd[1] = '/'; 44759243Sobrien cwd[2] = '\0'; 44859243Sobrien } 449167465Smp# endif /* HAVE_SLASHSLASH */ 45059243Sobrien 451167465Smp if (buf.len != 0) { 452167465Smp size_t i; 453167465Smp 454167465Smp i = Strlen(cwd); 455167465Smp if (TRM(cwd[i - 1]) != '/') { 456167465Smp cwd[i++] = '/'; 457167465Smp cwd[i] = '\0'; 458167465Smp } 459167465Smp dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s); 460167465Smp xfree(cwd); 46159243Sobrien cwd = dp; 462167465Smp i = Strlen(cwd) - 1; 463167465Smp if (TRM(cwd[i]) == '/') 464167465Smp cwd[i] = '\0'; 46559243Sobrien } 466100616Smp /* Reduction of ".." following the stuff we collected in buf 467100616Smp * only makes sense if the directory item in buf really exists. 468100616Smp * Avoid reduction of "-I../.." (typical compiler call) to "" 469100616Smp * or "/usr/nonexistant/../bin" to "/usr/bin": 470100616Smp */ 471100616Smp if (cwd[0]) { 472100616Smp struct stat exists; 473100616Smp if (0 != stat(short2str(cwd), &exists)) { 474167465Smp xfree(buf.s); 475167465Smp xfree(cwd); 476100616Smp return Strsave(start); 477100616Smp } 478100616Smp } 479167465Smp } while (*cp != '\0'); 480167465Smp xfree(buf.s); 48159243Sobrien return cwd; 48259243Sobrien } 48359243Sobrien#endif /* S_IFLNK */ 48459243Sobrien return Strsave(cp); 48559243Sobrien} 48659243Sobrien 48759243Sobrien 48859243Sobrien/* 48959243Sobrien * dochngd - implement chdir command. 49059243Sobrien */ 49159243Sobrien/*ARGSUSED*/ 49259243Sobrienvoid 493167465Smpdochngd(Char **v, struct command *c) 49459243Sobrien{ 495145479Smp Char *cp; 496145479Smp struct directory *dp; 49759243Sobrien int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 49859243Sobrien 49959243Sobrien USE(c); 50059243Sobrien printd = 0; 50159243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 50259243Sobrien 50359243Sobrien if (cp == NULL) { 504316957Sdchagin if (!cdtohome) 505316957Sdchagin stderror(ERR_NAME | ERR_TOOFEW); 506316957Sdchagin else if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 50759243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 50859243Sobrien if (chdir(short2str(cp)) < 0) 50959243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 51059243Sobrien cp = Strsave(cp); 51159243Sobrien } 51259243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 51359243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 51459243Sobrien /* NOTREACHED */ 51559243Sobrien return; 51659243Sobrien } 51759243Sobrien else if ((dp = dfind(cp)) != 0) { 51859243Sobrien char *tmp; 51959243Sobrien 52059243Sobrien printd = 1; 52159243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 52259243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 52359243Sobrien dcwd->di_prev->di_next = dcwd->di_next; 52459243Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 52559243Sobrien dfree(dcwd); 52659243Sobrien dnewcwd(dp, dflag); 52759243Sobrien return; 52859243Sobrien } 52959243Sobrien else 530195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 53159243Sobrien return; 532167465Smp dp = xcalloc(sizeof(struct directory), 1); 53359243Sobrien dp->di_name = cp; 53459243Sobrien dp->di_count = 0; 53559243Sobrien dp->di_next = dcwd->di_next; 53659243Sobrien dp->di_prev = dcwd->di_prev; 53759243Sobrien dp->di_prev->di_next = dp; 53859243Sobrien dp->di_next->di_prev = dp; 53959243Sobrien dfree(dcwd); 54059243Sobrien dnewcwd(dp, dflag); 54159243Sobrien} 54259243Sobrien 54359243Sobrienstatic Char * 544167465Smpdgoto(Char *cp) 54559243Sobrien{ 546167465Smp Char *dp, *ret; 54759243Sobrien 54859243Sobrien if (!ABSOLUTEP(cp)) 54959243Sobrien { 550145479Smp Char *p, *q; 551167465Smp size_t cwdlen; 55259243Sobrien 553167465Smp cwdlen = Strlen(dcwd->di_name); 554167465Smp if (cwdlen == 1) /* root */ 55559243Sobrien cwdlen = 0; 556167465Smp dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); 55759243Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 55859243Sobrien continue; 55959243Sobrien if (cwdlen) 56059243Sobrien p[-1] = '/'; 56159243Sobrien else 56259243Sobrien p--; /* don't add a / after root */ 563167465Smp Strcpy(p, cp); 564167465Smp xfree(cp); 56559243Sobrien cp = dp; 56659243Sobrien dp += cwdlen; 56759243Sobrien } 56859243Sobrien else 56959243Sobrien dp = cp; 57059243Sobrien 571131962Smp#if defined(WINNT_NATIVE) 572167465Smp return agetcwd(); 573131962Smp#elif defined(__CYGWIN__) 574167465Smp if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ 575167465Smp return agetcwd(); 576167465Smp } else { 577167465Smp cleanup_push(cp, xfree); 578167465Smp ret = dcanon(cp, dp); 579167465Smp cleanup_ignore(cp); 580167465Smp cleanup_until(cp); 581167465Smp } 58269408Sache#else /* !WINNT_NATIVE */ 583167465Smp cleanup_push(cp, xfree); 584167465Smp ret = dcanon(cp, dp); 585167465Smp cleanup_ignore(cp); 586167465Smp cleanup_until(cp); 58769408Sache#endif /* WINNT_NATIVE */ 588167465Smp return ret; 58959243Sobrien} 59059243Sobrien 59159243Sobrien/* 59259243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 59359243Sobrien */ 59459243Sobrienstatic Char * 595195609Smpdfollow(Char *cp, int old) 59659243Sobrien{ 597145479Smp Char *dp; 59859243Sobrien struct varent *c; 59959243Sobrien int serrno; 60059243Sobrien 601195609Smp cp = old ? Strsave(cp) : globone(cp, G_ERROR); 602167465Smp cleanup_push(cp, xfree); 60359243Sobrien#ifdef apollo 60459243Sobrien if (Strchr(cp, '`')) { 605167465Smp char *dptr; 60659243Sobrien if (chdir(dptr = short2str(cp)) < 0) 60759243Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 608167465Smp dp = agetcwd(); 609167465Smp cleanup_push(dp, xfree); 610167465Smp if (dp != NULL) { 611167465Smp cleanup_until(cp); 612167465Smp return dgoto(dp); 61359243Sobrien } 614167465Smp else 615167465Smp stderror(ERR_SYSTEM, dptr, strerror(errno)); 61659243Sobrien } 61759243Sobrien#endif /* apollo */ 618167465Smp 61959243Sobrien /* 62059243Sobrien * if we are ignoring symlinks, try to fix relatives now. 62159243Sobrien * if we are expading symlinks, it should be done by now. 62259243Sobrien */ 62359243Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 62459243Sobrien if (chdir(short2str(dp)) >= 0) { 625167465Smp cleanup_until(cp); 62659243Sobrien return dgoto(dp); 62759243Sobrien } 62859243Sobrien else { 629167465Smp xfree(dp); 630167465Smp if (chdir(short2str(cp)) >= 0) { 631167465Smp cleanup_ignore(cp); 632167465Smp cleanup_until(cp); 63359243Sobrien return dgoto(cp); 634167465Smp } 635167465Smp else if (errno != ENOENT && errno != ENOTDIR) { 636167465Smp int err; 637167465Smp 638167465Smp err = errno; 639167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(err)); 640167465Smp } 64159243Sobrien serrno = errno; 64259243Sobrien } 64359243Sobrien 64459243Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 645100616Smp && (c = adrof(STRcdpath)) && c->vec != NULL) { 646167465Smp struct Strbuf buf = Strbuf_INIT; 64759243Sobrien Char **cdp; 64859243Sobrien 64959243Sobrien for (cdp = c->vec; *cdp; cdp++) { 650231990Smp size_t len = Strlen(*cdp); 651167465Smp buf.len = 0; 652231990Smp if (len > 0) { 653231990Smp Strbuf_append(&buf, *cdp); 654231990Smp if ((*cdp)[len - 1] != '/') 655231990Smp Strbuf_append1(&buf, '/'); 656231990Smp } 657167465Smp Strbuf_append(&buf, cp); 658167465Smp Strbuf_terminate(&buf); 65959243Sobrien /* 66059243Sobrien * We always want to fix the directory here 66159243Sobrien * If we are normalizing symlinks 66259243Sobrien */ 663167465Smp dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 664167465Smp symlinks == SYM_EXPAND); 66559243Sobrien if (chdir(short2str(dp)) >= 0) { 66659243Sobrien printd = 1; 667167465Smp xfree(buf.s); 668167465Smp cleanup_until(cp); 66959243Sobrien return dgoto(dp); 67059243Sobrien } 67159243Sobrien else if (chdir(short2str(cp)) >= 0) { 67259243Sobrien printd = 1; 673167465Smp xfree(dp); 674167465Smp xfree(buf.s); 675167465Smp cleanup_ignore(cp); 676167465Smp cleanup_until(cp); 67759243Sobrien return dgoto(cp); 67859243Sobrien } 679316957Sdchagin xfree(dp); 68059243Sobrien } 681167465Smp xfree(buf.s); 68259243Sobrien } 68359243Sobrien dp = varval(cp); 68459243Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 685167465Smp cleanup_until(cp); 68659243Sobrien cp = Strsave(dp); 68759243Sobrien printd = 1; 68859243Sobrien return dgoto(cp); 68959243Sobrien } 69059243Sobrien /* 69159243Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 69259243Sobrien * directories we could get to. 69359243Sobrien */ 694167465Smp if (!bequiet) 695167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 696167465Smp cleanup_until(cp); 697167465Smp return (NULL); 69859243Sobrien} 69959243Sobrien 70059243Sobrien 70159243Sobrien/* 70259243Sobrien * dopushd - push new directory onto directory stack. 70359243Sobrien * with no arguments exchange top and second. 70459243Sobrien * with numeric argument (+n) bring it to top. 70559243Sobrien */ 70659243Sobrien/*ARGSUSED*/ 70759243Sobrienvoid 708167465Smpdopushd(Char **v, struct command *c) 70959243Sobrien{ 710145479Smp struct directory *dp; 711145479Smp Char *cp; 71259243Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 71359243Sobrien 71459243Sobrien USE(c); 71559243Sobrien printd = 1; 71659243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 71759243Sobrien 71859243Sobrien if (cp == NULL) { 71959243Sobrien if (adrof(STRpushdtohome)) { 72059243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 72159243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 72259243Sobrien if (chdir(short2str(cp)) < 0) 72359243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 724195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 72559243Sobrien return; 726167465Smp dp = xcalloc(sizeof(struct directory), 1); 72759243Sobrien dp->di_name = cp; 72859243Sobrien dp->di_count = 0; 72959243Sobrien dp->di_prev = dcwd; 73059243Sobrien dp->di_next = dcwd->di_next; 73159243Sobrien dcwd->di_next = dp; 73259243Sobrien dp->di_next->di_prev = dp; 73359243Sobrien } 73459243Sobrien else { 73559243Sobrien char *tmp; 73659243Sobrien 73759243Sobrien if ((dp = dcwd->di_prev) == &dhead) 73859243Sobrien dp = dhead.di_prev; 73959243Sobrien if (dp == dcwd) 74059243Sobrien stderror(ERR_NAME | ERR_NODIR); 74159243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 74259243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 74359243Sobrien dp->di_prev->di_next = dp->di_next; 74459243Sobrien dp->di_next->di_prev = dp->di_prev; 74559243Sobrien dp->di_next = dcwd->di_next; 74659243Sobrien dp->di_prev = dcwd; 74759243Sobrien dcwd->di_next->di_prev = dp; 74859243Sobrien dcwd->di_next = dp; 74959243Sobrien } 75059243Sobrien } 75159243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 75259243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 75359243Sobrien /* NOTREACHED */ 75459243Sobrien return; 75559243Sobrien } 75659243Sobrien else if ((dp = dfind(cp)) != NULL) { 75759243Sobrien char *tmp; 75859243Sobrien 75959243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 76059243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 76159243Sobrien /* 76259243Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 76359243Sobrien */ 76459243Sobrien if (adrof(STRdextract)) 76559243Sobrien dextract(dp); 76659243Sobrien } 76759243Sobrien else { 768145479Smp Char *ccp; 76959243Sobrien 770195609Smp if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL) 77159243Sobrien return; 772167465Smp dp = xcalloc(sizeof(struct directory), 1); 77359243Sobrien dp->di_name = ccp; 77459243Sobrien dp->di_count = 0; 77559243Sobrien dp->di_prev = dcwd; 77659243Sobrien dp->di_next = dcwd->di_next; 77759243Sobrien dcwd->di_next = dp; 77859243Sobrien dp->di_next->di_prev = dp; 77959243Sobrien } 78059243Sobrien dnewcwd(dp, dflag); 78159243Sobrien} 78259243Sobrien 78359243Sobrien/* 78459243Sobrien * dfind - find a directory if specified by numeric (+n) argument 78559243Sobrien */ 78659243Sobrienstatic struct directory * 787167465Smpdfind(Char *cp) 78859243Sobrien{ 789145479Smp struct directory *dp; 790145479Smp int i; 791145479Smp Char *ep; 79259243Sobrien 79359243Sobrien if (*cp++ != '+') 79459243Sobrien return (0); 79559243Sobrien for (ep = cp; Isdigit(*ep); ep++) 79659243Sobrien continue; 79759243Sobrien if (*ep) 79859243Sobrien return (0); 79959243Sobrien i = getn(cp); 80059243Sobrien if (i <= 0) 80159243Sobrien return (0); 80259243Sobrien for (dp = dcwd; i != 0; i--) { 80359243Sobrien if ((dp = dp->di_prev) == &dhead) 80459243Sobrien dp = dp->di_prev; 80559243Sobrien if (dp == dcwd) 80659243Sobrien stderror(ERR_NAME | ERR_DEEP); 80759243Sobrien } 80859243Sobrien return (dp); 80959243Sobrien} 81059243Sobrien 81159243Sobrien/* 81259243Sobrien * dopopd - pop a directory out of the directory stack 81359243Sobrien * with a numeric argument just discard it. 81459243Sobrien */ 81559243Sobrien/*ARGSUSED*/ 81659243Sobrienvoid 817167465Smpdopopd(Char **v, struct command *c) 81859243Sobrien{ 81959243Sobrien Char *cp; 820145479Smp struct directory *dp, *p = NULL; 82159243Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 82259243Sobrien 82359243Sobrien USE(c); 82459243Sobrien printd = 1; 82559243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 82659243Sobrien 82759243Sobrien if (cp == NULL) 82859243Sobrien dp = dcwd; 82959243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 83059243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 83159243Sobrien /* NOTREACHED */ 83259243Sobrien return; 83359243Sobrien } 83459243Sobrien else if ((dp = dfind(cp)) == 0) 83559243Sobrien stderror(ERR_NAME | ERR_BADDIR); 83659243Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 83759243Sobrien stderror(ERR_NAME | ERR_EMPTY); 83859243Sobrien if (dp == dcwd) { 83959243Sobrien char *tmp; 84059243Sobrien 84159243Sobrien if ((p = dp->di_prev) == &dhead) 84259243Sobrien p = dhead.di_prev; 84359243Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 84459243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 84559243Sobrien } 84659243Sobrien dp->di_prev->di_next = dp->di_next; 84759243Sobrien dp->di_next->di_prev = dp->di_prev; 848167465Smp dfree(dp); 84959243Sobrien if (dp == dcwd) { 850167465Smp dnewcwd(p, dflag); 85159243Sobrien } 85259243Sobrien else { 85359243Sobrien printdirs(dflag); 85459243Sobrien } 85559243Sobrien} 85659243Sobrien 85759243Sobrien/* 85859243Sobrien * dfree - free the directory (or keep it if it still has ref count) 85959243Sobrien */ 86059243Sobrienvoid 861167465Smpdfree(struct directory *dp) 86259243Sobrien{ 86359243Sobrien 86459243Sobrien if (dp->di_count != 0) { 86559243Sobrien dp->di_next = dp->di_prev = 0; 86659243Sobrien } 86759243Sobrien else { 868167465Smp xfree(dp->di_name); 869167465Smp xfree(dp); 87059243Sobrien } 87159243Sobrien} 87259243Sobrien 87359243Sobrien/* 87459243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 87559243Sobrien * we are of course assuming that the file system is standardly 87659243Sobrien * constructed (always have ..'s, directories have links) 87759243Sobrien */ 87859243SobrienChar * 879167465Smpdcanon(Char *cp, Char *p) 88059243Sobrien{ 881145479Smp Char *sp; 882145479Smp Char *p1, *p2; /* general purpose */ 883145479Smp int slash; 884167465Smp#ifdef HAVE_SLASHSLASH 885145479Smp int slashslash; 886167465Smp#endif /* HAVE_SLASHSLASH */ 88773393Skris size_t clen; 88859243Sobrien 88959243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 890167465Smp Char *mlink, *newcp; 891167465Smp char *tlink; 892167465Smp size_t cc; 89359243Sobrien#endif /* S_IFLNK */ 89459243Sobrien 895167465Smp clen = Strlen(cp); 89659243Sobrien 89759243Sobrien /* 89859243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 89973393Skris * cwd does not start with a slash or the result would be too long try to 90073393Skris * correct it. 90159243Sobrien */ 90259243Sobrien if (!ABSOLUTEP(cp)) { 903167465Smp Char *tmpdir; 90473393Skris size_t len; 90559243Sobrien 90659243Sobrien p1 = varval(STRcwd); 90773393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 908167465Smp Char *new_cwd = agetcwd(); 909167465Smp 910167465Smp if (new_cwd == NULL) { 91173393Skris xprintf("%s: %s\n", progname, strerror(errno)); 912167465Smp setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 91373393Skris } 914167465Smp else 915167465Smp setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 91673393Skris p1 = varval(STRcwd); 91773393Skris } 91873393Skris len = Strlen(p1); 919167465Smp tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 92059243Sobrien (void) Strcpy(tmpdir, p1); 92159243Sobrien (void) Strcat(tmpdir, STRslash); 92259243Sobrien (void) Strcat(tmpdir, cp); 923167465Smp xfree(cp); 924167465Smp cp = p = tmpdir; 92559243Sobrien } 92659243Sobrien 927167465Smp#ifdef HAVE_SLASHSLASH 92859243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 929167465Smp#endif /* HAVE_SLASHSLASH */ 93059243Sobrien 93159243Sobrien while (*p) { /* for each component */ 93259243Sobrien sp = p; /* save slash address */ 93359243Sobrien while (*++p == '/') /* flush extra slashes */ 93459243Sobrien continue; 93559243Sobrien if (p != ++sp) 93659243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 93759243Sobrien continue; 93859243Sobrien p = sp; /* save start of component */ 93959243Sobrien slash = 0; 94059243Sobrien if (*p) 94159243Sobrien while (*++p) /* find next slash or end of path */ 94259243Sobrien if (*p == '/') { 94359243Sobrien slash = 1; 94459243Sobrien *p = 0; 94559243Sobrien break; 94659243Sobrien } 94759243Sobrien 948167465Smp#ifdef HAVE_SLASHSLASH 94959243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 95059243Sobrien slashslash = 1; 951167465Smp#endif /* HAVE_SLASHSLASH */ 95259243Sobrien if (*sp == '\0') { /* if component is null */ 95359243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 95459243Sobrien break; 95559243Sobrien else 95659243Sobrien *sp = '\0'; 95759243Sobrien } 95859243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 95959243Sobrien if (slash) { 96059243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 96159243Sobrien continue; 96259243Sobrien p = --sp; 96359243Sobrien } 96459243Sobrien else if (--sp != cp) 96559243Sobrien *sp = '\0'; 96659243Sobrien else 96759243Sobrien sp[1] = '\0'; 96859243Sobrien } 96959243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 97059243Sobrien /* 97159243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 97259243Sobrien * a path starting at /, and "xxx" is a single component. Before 97359243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 97459243Sobrien * symbolic link. 97559243Sobrien */ 97659243Sobrien *--sp = 0; /* form the pathname for readlink */ 97759243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 97859243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 979167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 980167465Smp mlink = str2short(tlink); 981167465Smp xfree(tlink); 98259243Sobrien 98359243Sobrien if (slash) 98459243Sobrien *p = '/'; 98559243Sobrien /* 98659243Sobrien * Point p to the '/' in "/..", and restore the '/'. 98759243Sobrien */ 98859243Sobrien *(p = sp) = '/'; 989145479Smp if (*mlink != '/') { 99059243Sobrien /* 99159243Sobrien * Relative path, expand it between the "yyy/" and the 99259243Sobrien * "/..". First, back sp up to the character past "yyy/". 99359243Sobrien */ 99459243Sobrien while (*--sp != '/') 99559243Sobrien continue; 99659243Sobrien sp++; 99759243Sobrien *sp = 0; 99859243Sobrien /* 999145479Smp * New length is "yyy/" + mlink + "/.." and rest 100059243Sobrien */ 1001167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1002167465Smp Strlen(p) + 1) * sizeof(Char)); 100359243Sobrien /* 100459243Sobrien * Copy new path into newcp 100559243Sobrien */ 100659243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 100759243Sobrien continue; 1008145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 100959243Sobrien continue; 101059243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 101159243Sobrien continue; 101259243Sobrien /* 101359243Sobrien * Restart canonicalization at expanded "/xxx". 101459243Sobrien */ 101559243Sobrien p = sp - cp - 1 + newcp; 101659243Sobrien } 101759243Sobrien else { 1018167465Smp newcp = Strspl(mlink, p); 101959243Sobrien /* 102059243Sobrien * Restart canonicalization at beginning 102159243Sobrien */ 102259243Sobrien p = newcp; 102359243Sobrien } 1024167465Smp xfree(cp); 102559243Sobrien cp = newcp; 1026167465Smp#ifdef HAVE_SLASHSLASH 102759243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1028167465Smp#endif /* HAVE_SLASHSLASH */ 102959243Sobrien continue; /* canonicalize the link */ 103059243Sobrien } 103159243Sobrien#endif /* S_IFLNK */ 103259243Sobrien *sp = '/'; 103359243Sobrien if (sp != cp) 103459243Sobrien while (*--sp != '/') 103559243Sobrien continue; 103659243Sobrien if (slash) { 103759243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 103859243Sobrien continue; 103959243Sobrien p = sp; 104059243Sobrien } 104159243Sobrien else if (cp == sp) 104259243Sobrien *++sp = '\0'; 104359243Sobrien else 104459243Sobrien *sp = '\0'; 104559243Sobrien } 104659243Sobrien else { /* normal dir name (not . or .. or nothing) */ 104759243Sobrien 104859243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 104959243Sobrien if (sp != cp && symlinks == SYM_CHASE && 1050167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 1051167465Smp mlink = str2short(tlink); 1052167465Smp xfree(tlink); 105359243Sobrien 105459243Sobrien /* 105559243Sobrien * restore the '/'. 105659243Sobrien */ 105759243Sobrien if (slash) 105859243Sobrien *p = '/'; 105959243Sobrien 106059243Sobrien /* 106159243Sobrien * point sp to p (rather than backing up). 106259243Sobrien */ 106359243Sobrien sp = p; 106459243Sobrien 1065145479Smp if (*mlink != '/') { 106659243Sobrien /* 106759243Sobrien * Relative path, expand it between the "yyy/" and the 106859243Sobrien * remainder. First, back sp up to the character past 106959243Sobrien * "yyy/". 107059243Sobrien */ 107159243Sobrien while (*--sp != '/') 107259243Sobrien continue; 107359243Sobrien sp++; 107459243Sobrien *sp = 0; 107559243Sobrien /* 1076145479Smp * New length is "yyy/" + mlink + "/.." and rest 107759243Sobrien */ 1078167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1079167465Smp Strlen(p) + 1) * sizeof(Char)); 108059243Sobrien /* 108159243Sobrien * Copy new path into newcp 108259243Sobrien */ 108359243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 108459243Sobrien continue; 1085145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 108659243Sobrien continue; 108759243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 108859243Sobrien continue; 108959243Sobrien /* 109059243Sobrien * Restart canonicalization at expanded "/xxx". 109159243Sobrien */ 109259243Sobrien p = sp - cp - 1 + newcp; 109359243Sobrien } 109459243Sobrien else { 1095167465Smp newcp = Strspl(mlink, p); 109659243Sobrien /* 109759243Sobrien * Restart canonicalization at beginning 109859243Sobrien */ 109959243Sobrien p = newcp; 110059243Sobrien } 1101167465Smp xfree(cp); 110259243Sobrien cp = newcp; 1103167465Smp#ifdef HAVE_SLASHSLASH 110459243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1105167465Smp#endif /* HAVE_SLASHSLASH */ 1106145479Smp continue; /* canonicalize the mlink */ 110759243Sobrien } 110859243Sobrien#endif /* S_IFLNK */ 110959243Sobrien if (slash) 111059243Sobrien *p = '/'; 111159243Sobrien } 111259243Sobrien } 111359243Sobrien 111459243Sobrien /* 111559243Sobrien * fix home... 111659243Sobrien */ 111759243Sobrien#ifdef S_IFLNK 111859243Sobrien p1 = varval(STRhome); 1119167465Smp cc = Strlen(p1); 112059243Sobrien /* 112159243Sobrien * See if we're not in a subdir of STRhome 112259243Sobrien */ 1123167465Smp if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 112459243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 112559243Sobrien static ino_t home_ino = (ino_t) -1; 112659243Sobrien static dev_t home_dev = (dev_t) -1; 112759243Sobrien static Char *home_ptr = NULL; 112859243Sobrien struct stat statbuf; 112959243Sobrien int found; 1130167465Smp Char *copy; 113159243Sobrien 113259243Sobrien /* 113359243Sobrien * Get dev and ino of STRhome 113459243Sobrien */ 113559243Sobrien if (home_ptr != p1 && 113659243Sobrien stat(short2str(p1), &statbuf) != -1) { 113759243Sobrien home_dev = statbuf.st_dev; 113859243Sobrien home_ino = statbuf.st_ino; 113959243Sobrien home_ptr = p1; 114059243Sobrien } 114159243Sobrien /* 114259243Sobrien * Start comparing dev & ino backwards 114359243Sobrien */ 1144167465Smp p2 = copy = Strsave(cp); 114559243Sobrien found = 0; 114659243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 114759243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 114859243Sobrien statbuf.st_ino == home_ino) { 114959243Sobrien found = 1; 115059243Sobrien break; 115159243Sobrien } 115259243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 115359243Sobrien *sp = '\0'; 115459243Sobrien } 115559243Sobrien /* 115659243Sobrien * See if we found it 115759243Sobrien */ 115859243Sobrien if (*p2 && found) { 115959243Sobrien /* 116059243Sobrien * Use STRhome to make '~' work 116159243Sobrien */ 116259243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 1163167465Smp xfree(cp); 116459243Sobrien cp = newcp; 116559243Sobrien } 1166167465Smp xfree(copy); 116759243Sobrien } 116859243Sobrien#endif /* S_IFLNK */ 116959243Sobrien 1170167465Smp#ifdef HAVE_SLASHSLASH 117159243Sobrien if (slashslash) { 117259243Sobrien if (cp[1] != '/') { 1173167465Smp p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 117459243Sobrien *p = '/'; 117559243Sobrien (void) Strcpy(&p[1], cp); 1176167465Smp xfree(cp); 117759243Sobrien cp = p; 117859243Sobrien } 117959243Sobrien } 1180167465Smp if (cp[1] == '/' && cp[2] == '/') { 1181167465Smp for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 1182167465Smp continue; 1183167465Smp } 1184167465Smp#endif /* HAVE_SLASHSLASH */ 118559243Sobrien return cp; 118659243Sobrien} 118759243Sobrien 118859243Sobrien 118959243Sobrien/* 119059243Sobrien * dnewcwd - make a new directory in the loop the current one 119159243Sobrien */ 119259243Sobrienstatic void 1193167465Smpdnewcwd(struct directory *dp, int dflag) 119459243Sobrien{ 119559243Sobrien int print; 119659243Sobrien 119759243Sobrien if (adrof(STRdunique)) { 119859243Sobrien struct directory *dn; 119959243Sobrien 120059243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 120159243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 120259243Sobrien dn->di_next->di_prev = dn->di_prev; 120359243Sobrien dn->di_prev->di_next = dn->di_next; 120459243Sobrien dfree(dn); 120559243Sobrien break; 120659243Sobrien } 120759243Sobrien } 120859243Sobrien dcwd = dp; 120959243Sobrien dset(dcwd->di_name); 121059243Sobrien dgetstack(); 121159243Sobrien print = printd; /* if printd is set, print dirstack... */ 121259243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 121359243Sobrien print = 0; 121459243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 121559243Sobrien print = 1; 121659243Sobrien if (bequiet) /* and bequiet overrides everything */ 121759243Sobrien print = 0; 121859243Sobrien if (print) 121959243Sobrien printdirs(dflag); 122059243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 122159243Sobrien} 122259243Sobrien 122359243Sobrienvoid 1224167465Smpdsetstack(void) 122559243Sobrien{ 122659243Sobrien Char **cp; 122759243Sobrien struct varent *vp; 122859243Sobrien struct directory *dn, *dp; 122959243Sobrien 1230100616Smp if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 123159243Sobrien return; 123259243Sobrien 123359243Sobrien /* Free the whole stack */ 123459243Sobrien while ((dn = dhead.di_prev) != &dhead) { 123559243Sobrien dn->di_next->di_prev = dn->di_prev; 123659243Sobrien dn->di_prev->di_next = dn->di_next; 123759243Sobrien if (dn != dcwd) 123859243Sobrien dfree(dn); 123959243Sobrien } 124059243Sobrien 124159243Sobrien /* thread the current working directory */ 124259243Sobrien dhead.di_prev = dhead.di_next = dcwd; 124359243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 124459243Sobrien 124559243Sobrien /* put back the stack */ 124659243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 1247167465Smp dp = xcalloc(sizeof(struct directory), 1); 124859243Sobrien dp->di_name = Strsave(*cp); 124959243Sobrien dp->di_count = 0; 125059243Sobrien dp->di_prev = dcwd; 125159243Sobrien dp->di_next = dcwd->di_next; 125259243Sobrien dcwd->di_next = dp; 125359243Sobrien dp->di_next->di_prev = dp; 125459243Sobrien } 125559243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 125659243Sobrien} 125759243Sobrien 125859243Sobrienstatic void 1259167465Smpdgetstack(void) 126059243Sobrien{ 126159243Sobrien int i = 0; 126259243Sobrien Char **dblk, **dbp; 126359243Sobrien struct directory *dn; 126459243Sobrien 126559243Sobrien if (adrof(STRdirstack) == NULL) 126659243Sobrien return; 126759243Sobrien 1268167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126959243Sobrien continue; 1270167465Smp dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 1271167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 127259243Sobrien *dbp = Strsave(dn->di_name); 127359243Sobrien *dbp = NULL; 1274167465Smp cleanup_push(dblk, blk_cleanup); 127559243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1276167465Smp cleanup_ignore(dblk); 1277167465Smp cleanup_until(dblk); 127859243Sobrien} 127959243Sobrien 128059243Sobrien/* 128159243Sobrien * getstakd - added by kfk 17 Jan 1984 128259243Sobrien * Support routine for the stack hack. Finds nth directory in 128359243Sobrien * the directory stack, or finds last directory in stack. 128459243Sobrien */ 1285167465Smpconst Char * 1286167465Smpgetstakd(int cnt) 128759243Sobrien{ 128859243Sobrien struct directory *dp; 128959243Sobrien 129059243Sobrien dp = dcwd; 129159243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 129259243Sobrien dp = dp->di_next; 129359243Sobrien if (dp == &dhead) 129459243Sobrien dp = dp->di_next; 129559243Sobrien } 129659243Sobrien else { 129759243Sobrien while (cnt-- > 0) { 129859243Sobrien dp = dp->di_prev; 129959243Sobrien if (dp == &dhead) 130059243Sobrien dp = dp->di_prev; 130159243Sobrien if (dp == dcwd) 1302167465Smp return NULL; 130359243Sobrien } 130459243Sobrien } 1305167465Smp return dp->di_name; 130659243Sobrien} 130759243Sobrien 130859243Sobrien/* 130959243Sobrien * Karl Kleinpaste - 10 Feb 1984 131059243Sobrien * Added dextract(), which is used in pushd +n. 131159243Sobrien * Instead of just rotating the entire stack around, dextract() 131259243Sobrien * lets the user have the nth dir extracted from its current 131359243Sobrien * position, and pushes it onto the top. 131459243Sobrien */ 131559243Sobrienstatic void 1316167465Smpdextract(struct directory *dp) 131759243Sobrien{ 131859243Sobrien if (dp == dcwd) 131959243Sobrien return; 132059243Sobrien dp->di_next->di_prev = dp->di_prev; 132159243Sobrien dp->di_prev->di_next = dp->di_next; 132259243Sobrien dp->di_next = dcwd->di_next; 132359243Sobrien dp->di_prev = dcwd; 132459243Sobrien dp->di_next->di_prev = dp; 132559243Sobrien dcwd->di_next = dp; 132659243Sobrien} 132759243Sobrien 1328167465Smpstatic void 1329167465Smpbequiet_cleanup(void *dummy) 1330167465Smp{ 1331167465Smp USE(dummy); 1332167465Smp bequiet = 0; 1333167465Smp} 1334167465Smp 133559243Sobrienvoid 1336167465Smploaddirs(Char *fname) 133759243Sobrien{ 133859243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133959243Sobrien 134059243Sobrien bequiet = 1; 1341167465Smp cleanup_push(&bequiet, bequiet_cleanup); 1342167465Smp if (fname) 134359243Sobrien loaddirs_cmd[1] = fname; 134459243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 134559243Sobrien loaddirs_cmd[1] = fname; 134659243Sobrien else 134759243Sobrien loaddirs_cmd[1] = STRtildotdirs; 1348167465Smp dosource(loaddirs_cmd, NULL); 1349167465Smp cleanup_until(&bequiet); 135059243Sobrien} 135159243Sobrien 135259243Sobrien/* 135359243Sobrien * create a file called ~/.cshdirs which has a sequence 135459243Sobrien * of pushd commands which will restore the dir stack to 135559243Sobrien * its state before exit/logout. remember that the order 135659243Sobrien * is reversed in the file because we are pushing. 135759243Sobrien * -strike 135859243Sobrien */ 135959243Sobrienvoid 1360167465Smprecdirs(Char *fname, int def) 136159243Sobrien{ 136259243Sobrien int fp, ftmp, oldidfds; 136359243Sobrien int cdflag = 0; 136459243Sobrien struct directory *dp; 136559243Sobrien unsigned int num; 136659243Sobrien Char *snum; 1367167465Smp struct Strbuf qname = Strbuf_INIT; 136859243Sobrien 136959243Sobrien if (fname == NULL && !def) 137059243Sobrien return; 137159243Sobrien 137259243Sobrien if (fname == NULL) { 137359243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 137459243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 137559243Sobrien else 137659243Sobrien fname = Strsave(fname); 137759243Sobrien } 137859243Sobrien else 137959243Sobrien fname = globone(fname, G_ERROR); 1380167465Smp cleanup_push(fname, xfree); 1381167465Smp 1382167465Smp if ((fp = xcreat(short2str(fname), 0600)) == -1) { 1383167465Smp cleanup_until(fname); 138459243Sobrien return; 138559243Sobrien } 138659243Sobrien 138783098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 138859243Sobrien num = (unsigned int) ~0; 138959243Sobrien else 139059243Sobrien num = (unsigned int) atoi(short2str(snum)); 139159243Sobrien 139259243Sobrien oldidfds = didfds; 139359243Sobrien didfds = 0; 139459243Sobrien ftmp = SHOUT; 139559243Sobrien SHOUT = fp; 139659243Sobrien 1397167465Smp cleanup_push(&qname, Strbuf_cleanup); 139859243Sobrien dp = dcwd->di_next; 139959243Sobrien do { 140059243Sobrien if (dp == &dhead) 140159243Sobrien continue; 140259243Sobrien 140359243Sobrien if (cdflag == 0) { 140459243Sobrien cdflag = 1; 1405167465Smp xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 140659243Sobrien } 140759243Sobrien else 1408167465Smp xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 140959243Sobrien 141059243Sobrien if (num-- == 0) 141159243Sobrien break; 141259243Sobrien 141359243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 141459243Sobrien 1415167465Smp xclose(fp); 141659243Sobrien SHOUT = ftmp; 141759243Sobrien didfds = oldidfds; 1418167465Smp cleanup_until(fname); 141959243Sobrien} 1420