1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.82 2011/10/16 16:25:05 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 36232633SmpRCSID("$tcsh: sh.dir.c,v 3.82 2011/10/16 16:25:05 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; 207232633Smp else if ((*n)[1] == '-' && (*n)[2] == '\0') { /* test for -- */ 208232633Smp n++; 209232633Smp break; 210232633Smp } else { 21159243Sobrien char *p; 212232633Smp 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) { 50459243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 50559243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 50659243Sobrien if (chdir(short2str(cp)) < 0) 50759243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 50859243Sobrien cp = Strsave(cp); 50959243Sobrien } 51059243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 51159243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 51259243Sobrien /* NOTREACHED */ 51359243Sobrien return; 51459243Sobrien } 51559243Sobrien else if ((dp = dfind(cp)) != 0) { 51659243Sobrien char *tmp; 51759243Sobrien 51859243Sobrien printd = 1; 51959243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 52059243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 52159243Sobrien dcwd->di_prev->di_next = dcwd->di_next; 52259243Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 52359243Sobrien dfree(dcwd); 52459243Sobrien dnewcwd(dp, dflag); 52559243Sobrien return; 52659243Sobrien } 52759243Sobrien else 528195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 52959243Sobrien return; 530167465Smp dp = xcalloc(sizeof(struct directory), 1); 53159243Sobrien dp->di_name = cp; 53259243Sobrien dp->di_count = 0; 53359243Sobrien dp->di_next = dcwd->di_next; 53459243Sobrien dp->di_prev = dcwd->di_prev; 53559243Sobrien dp->di_prev->di_next = dp; 53659243Sobrien dp->di_next->di_prev = dp; 53759243Sobrien dfree(dcwd); 53859243Sobrien dnewcwd(dp, dflag); 53959243Sobrien} 54059243Sobrien 54159243Sobrienstatic Char * 542167465Smpdgoto(Char *cp) 54359243Sobrien{ 544167465Smp Char *dp, *ret; 54559243Sobrien 54659243Sobrien if (!ABSOLUTEP(cp)) 54759243Sobrien { 548145479Smp Char *p, *q; 549167465Smp size_t cwdlen; 55059243Sobrien 551167465Smp cwdlen = Strlen(dcwd->di_name); 552167465Smp if (cwdlen == 1) /* root */ 55359243Sobrien cwdlen = 0; 554167465Smp dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); 55559243Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 55659243Sobrien continue; 55759243Sobrien if (cwdlen) 55859243Sobrien p[-1] = '/'; 55959243Sobrien else 56059243Sobrien p--; /* don't add a / after root */ 561167465Smp Strcpy(p, cp); 562167465Smp xfree(cp); 56359243Sobrien cp = dp; 56459243Sobrien dp += cwdlen; 56559243Sobrien } 56659243Sobrien else 56759243Sobrien dp = cp; 56859243Sobrien 569131962Smp#if defined(WINNT_NATIVE) 570167465Smp return agetcwd(); 571131962Smp#elif defined(__CYGWIN__) 572167465Smp if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ 573167465Smp return agetcwd(); 574167465Smp } else { 575167465Smp cleanup_push(cp, xfree); 576167465Smp ret = dcanon(cp, dp); 577167465Smp cleanup_ignore(cp); 578167465Smp cleanup_until(cp); 579167465Smp } 58069408Sache#else /* !WINNT_NATIVE */ 581167465Smp cleanup_push(cp, xfree); 582167465Smp ret = dcanon(cp, dp); 583167465Smp cleanup_ignore(cp); 584167465Smp cleanup_until(cp); 58569408Sache#endif /* WINNT_NATIVE */ 586167465Smp return ret; 58759243Sobrien} 58859243Sobrien 58959243Sobrien/* 59059243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 59159243Sobrien */ 59259243Sobrienstatic Char * 593195609Smpdfollow(Char *cp, int old) 59459243Sobrien{ 595145479Smp Char *dp; 59659243Sobrien struct varent *c; 59759243Sobrien int serrno; 59859243Sobrien 599195609Smp cp = old ? Strsave(cp) : globone(cp, G_ERROR); 600167465Smp cleanup_push(cp, xfree); 60159243Sobrien#ifdef apollo 60259243Sobrien if (Strchr(cp, '`')) { 603167465Smp char *dptr; 60459243Sobrien if (chdir(dptr = short2str(cp)) < 0) 60559243Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 606167465Smp dp = agetcwd(); 607167465Smp cleanup_push(dp, xfree); 608167465Smp if (dp != NULL) { 609167465Smp cleanup_until(cp); 610167465Smp return dgoto(dp); 61159243Sobrien } 612167465Smp else 613167465Smp stderror(ERR_SYSTEM, dptr, strerror(errno)); 61459243Sobrien } 61559243Sobrien#endif /* apollo */ 616167465Smp 61759243Sobrien /* 61859243Sobrien * if we are ignoring symlinks, try to fix relatives now. 61959243Sobrien * if we are expading symlinks, it should be done by now. 62059243Sobrien */ 62159243Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 62259243Sobrien if (chdir(short2str(dp)) >= 0) { 623167465Smp cleanup_until(cp); 62459243Sobrien return dgoto(dp); 62559243Sobrien } 62659243Sobrien else { 627167465Smp xfree(dp); 628167465Smp if (chdir(short2str(cp)) >= 0) { 629167465Smp cleanup_ignore(cp); 630167465Smp cleanup_until(cp); 63159243Sobrien return dgoto(cp); 632167465Smp } 633167465Smp else if (errno != ENOENT && errno != ENOTDIR) { 634167465Smp int err; 635167465Smp 636167465Smp err = errno; 637167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(err)); 638167465Smp } 63959243Sobrien serrno = errno; 64059243Sobrien } 64159243Sobrien 64259243Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 643100616Smp && (c = adrof(STRcdpath)) && c->vec != NULL) { 644167465Smp struct Strbuf buf = Strbuf_INIT; 64559243Sobrien Char **cdp; 64659243Sobrien 64759243Sobrien for (cdp = c->vec; *cdp; cdp++) { 648232633Smp size_t len = Strlen(*cdp); 649167465Smp buf.len = 0; 650232633Smp if (len > 0) { 651232633Smp Strbuf_append(&buf, *cdp); 652232633Smp if ((*cdp)[len - 1] != '/') 653232633Smp Strbuf_append1(&buf, '/'); 654232633Smp } 655167465Smp Strbuf_append(&buf, cp); 656167465Smp Strbuf_terminate(&buf); 65759243Sobrien /* 65859243Sobrien * We always want to fix the directory here 65959243Sobrien * If we are normalizing symlinks 66059243Sobrien */ 661167465Smp dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 662167465Smp symlinks == SYM_EXPAND); 66359243Sobrien if (chdir(short2str(dp)) >= 0) { 66459243Sobrien printd = 1; 665167465Smp xfree(buf.s); 666167465Smp cleanup_until(cp); 66759243Sobrien return dgoto(dp); 66859243Sobrien } 66959243Sobrien else if (chdir(short2str(cp)) >= 0) { 67059243Sobrien printd = 1; 671167465Smp xfree(dp); 672167465Smp xfree(buf.s); 673167465Smp cleanup_ignore(cp); 674167465Smp cleanup_until(cp); 67559243Sobrien return dgoto(cp); 67659243Sobrien } 67759243Sobrien } 678167465Smp xfree(buf.s); 67959243Sobrien } 68059243Sobrien dp = varval(cp); 68159243Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 682167465Smp cleanup_until(cp); 68359243Sobrien cp = Strsave(dp); 68459243Sobrien printd = 1; 68559243Sobrien return dgoto(cp); 68659243Sobrien } 68759243Sobrien /* 68859243Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 68959243Sobrien * directories we could get to. 69059243Sobrien */ 691167465Smp if (!bequiet) 692167465Smp stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 693167465Smp cleanup_until(cp); 694167465Smp return (NULL); 69559243Sobrien} 69659243Sobrien 69759243Sobrien 69859243Sobrien/* 69959243Sobrien * dopushd - push new directory onto directory stack. 70059243Sobrien * with no arguments exchange top and second. 70159243Sobrien * with numeric argument (+n) bring it to top. 70259243Sobrien */ 70359243Sobrien/*ARGSUSED*/ 70459243Sobrienvoid 705167465Smpdopushd(Char **v, struct command *c) 70659243Sobrien{ 707145479Smp struct directory *dp; 708145479Smp Char *cp; 70959243Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 71059243Sobrien 71159243Sobrien USE(c); 71259243Sobrien printd = 1; 71359243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 71459243Sobrien 71559243Sobrien if (cp == NULL) { 71659243Sobrien if (adrof(STRpushdtohome)) { 71759243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 71859243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 71959243Sobrien if (chdir(short2str(cp)) < 0) 72059243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 721195609Smp if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) 72259243Sobrien return; 723167465Smp dp = xcalloc(sizeof(struct directory), 1); 72459243Sobrien dp->di_name = cp; 72559243Sobrien dp->di_count = 0; 72659243Sobrien dp->di_prev = dcwd; 72759243Sobrien dp->di_next = dcwd->di_next; 72859243Sobrien dcwd->di_next = dp; 72959243Sobrien dp->di_next->di_prev = dp; 73059243Sobrien } 73159243Sobrien else { 73259243Sobrien char *tmp; 73359243Sobrien 73459243Sobrien if ((dp = dcwd->di_prev) == &dhead) 73559243Sobrien dp = dhead.di_prev; 73659243Sobrien if (dp == dcwd) 73759243Sobrien stderror(ERR_NAME | ERR_NODIR); 73859243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 73959243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 74059243Sobrien dp->di_prev->di_next = dp->di_next; 74159243Sobrien dp->di_next->di_prev = dp->di_prev; 74259243Sobrien dp->di_next = dcwd->di_next; 74359243Sobrien dp->di_prev = dcwd; 74459243Sobrien dcwd->di_next->di_prev = dp; 74559243Sobrien dcwd->di_next = dp; 74659243Sobrien } 74759243Sobrien } 74859243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 74959243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 75059243Sobrien /* NOTREACHED */ 75159243Sobrien return; 75259243Sobrien } 75359243Sobrien else if ((dp = dfind(cp)) != NULL) { 75459243Sobrien char *tmp; 75559243Sobrien 75659243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 75759243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 75859243Sobrien /* 75959243Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 76059243Sobrien */ 76159243Sobrien if (adrof(STRdextract)) 76259243Sobrien dextract(dp); 76359243Sobrien } 76459243Sobrien else { 765145479Smp Char *ccp; 76659243Sobrien 767195609Smp if ((ccp = dfollow(cp, dflag & DIR_OLD)) == NULL) 76859243Sobrien return; 769167465Smp dp = xcalloc(sizeof(struct directory), 1); 77059243Sobrien dp->di_name = ccp; 77159243Sobrien dp->di_count = 0; 77259243Sobrien dp->di_prev = dcwd; 77359243Sobrien dp->di_next = dcwd->di_next; 77459243Sobrien dcwd->di_next = dp; 77559243Sobrien dp->di_next->di_prev = dp; 77659243Sobrien } 77759243Sobrien dnewcwd(dp, dflag); 77859243Sobrien} 77959243Sobrien 78059243Sobrien/* 78159243Sobrien * dfind - find a directory if specified by numeric (+n) argument 78259243Sobrien */ 78359243Sobrienstatic struct directory * 784167465Smpdfind(Char *cp) 78559243Sobrien{ 786145479Smp struct directory *dp; 787145479Smp int i; 788145479Smp Char *ep; 78959243Sobrien 79059243Sobrien if (*cp++ != '+') 79159243Sobrien return (0); 79259243Sobrien for (ep = cp; Isdigit(*ep); ep++) 79359243Sobrien continue; 79459243Sobrien if (*ep) 79559243Sobrien return (0); 79659243Sobrien i = getn(cp); 79759243Sobrien if (i <= 0) 79859243Sobrien return (0); 79959243Sobrien for (dp = dcwd; i != 0; i--) { 80059243Sobrien if ((dp = dp->di_prev) == &dhead) 80159243Sobrien dp = dp->di_prev; 80259243Sobrien if (dp == dcwd) 80359243Sobrien stderror(ERR_NAME | ERR_DEEP); 80459243Sobrien } 80559243Sobrien return (dp); 80659243Sobrien} 80759243Sobrien 80859243Sobrien/* 80959243Sobrien * dopopd - pop a directory out of the directory stack 81059243Sobrien * with a numeric argument just discard it. 81159243Sobrien */ 81259243Sobrien/*ARGSUSED*/ 81359243Sobrienvoid 814167465Smpdopopd(Char **v, struct command *c) 81559243Sobrien{ 81659243Sobrien Char *cp; 817145479Smp struct directory *dp, *p = NULL; 81859243Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 81959243Sobrien 82059243Sobrien USE(c); 82159243Sobrien printd = 1; 82259243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 82359243Sobrien 82459243Sobrien if (cp == NULL) 82559243Sobrien dp = dcwd; 82659243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 82759243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 82859243Sobrien /* NOTREACHED */ 82959243Sobrien return; 83059243Sobrien } 83159243Sobrien else if ((dp = dfind(cp)) == 0) 83259243Sobrien stderror(ERR_NAME | ERR_BADDIR); 83359243Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 83459243Sobrien stderror(ERR_NAME | ERR_EMPTY); 83559243Sobrien if (dp == dcwd) { 83659243Sobrien char *tmp; 83759243Sobrien 83859243Sobrien if ((p = dp->di_prev) == &dhead) 83959243Sobrien p = dhead.di_prev; 84059243Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 84159243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 84259243Sobrien } 84359243Sobrien dp->di_prev->di_next = dp->di_next; 84459243Sobrien dp->di_next->di_prev = dp->di_prev; 845167465Smp dfree(dp); 84659243Sobrien if (dp == dcwd) { 847167465Smp dnewcwd(p, dflag); 84859243Sobrien } 84959243Sobrien else { 85059243Sobrien printdirs(dflag); 85159243Sobrien } 85259243Sobrien} 85359243Sobrien 85459243Sobrien/* 85559243Sobrien * dfree - free the directory (or keep it if it still has ref count) 85659243Sobrien */ 85759243Sobrienvoid 858167465Smpdfree(struct directory *dp) 85959243Sobrien{ 86059243Sobrien 86159243Sobrien if (dp->di_count != 0) { 86259243Sobrien dp->di_next = dp->di_prev = 0; 86359243Sobrien } 86459243Sobrien else { 865167465Smp xfree(dp->di_name); 866167465Smp xfree(dp); 86759243Sobrien } 86859243Sobrien} 86959243Sobrien 87059243Sobrien/* 87159243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 87259243Sobrien * we are of course assuming that the file system is standardly 87359243Sobrien * constructed (always have ..'s, directories have links) 87459243Sobrien */ 87559243SobrienChar * 876167465Smpdcanon(Char *cp, Char *p) 87759243Sobrien{ 878145479Smp Char *sp; 879145479Smp Char *p1, *p2; /* general purpose */ 880145479Smp int slash; 881167465Smp#ifdef HAVE_SLASHSLASH 882145479Smp int slashslash; 883167465Smp#endif /* HAVE_SLASHSLASH */ 88473393Skris size_t clen; 88559243Sobrien 88659243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 887167465Smp Char *mlink, *newcp; 888167465Smp char *tlink; 889167465Smp size_t cc; 89059243Sobrien#endif /* S_IFLNK */ 89159243Sobrien 892167465Smp clen = Strlen(cp); 89359243Sobrien 89459243Sobrien /* 89559243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 89673393Skris * cwd does not start with a slash or the result would be too long try to 89773393Skris * correct it. 89859243Sobrien */ 89959243Sobrien if (!ABSOLUTEP(cp)) { 900167465Smp Char *tmpdir; 90173393Skris size_t len; 90259243Sobrien 90359243Sobrien p1 = varval(STRcwd); 90473393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 905167465Smp Char *new_cwd = agetcwd(); 906167465Smp 907167465Smp if (new_cwd == NULL) { 90873393Skris xprintf("%s: %s\n", progname, strerror(errno)); 909167465Smp setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 91073393Skris } 911167465Smp else 912167465Smp setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 91373393Skris p1 = varval(STRcwd); 91473393Skris } 91573393Skris len = Strlen(p1); 916167465Smp tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 91759243Sobrien (void) Strcpy(tmpdir, p1); 91859243Sobrien (void) Strcat(tmpdir, STRslash); 91959243Sobrien (void) Strcat(tmpdir, cp); 920167465Smp xfree(cp); 921167465Smp cp = p = tmpdir; 92259243Sobrien } 92359243Sobrien 924167465Smp#ifdef HAVE_SLASHSLASH 92559243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 926167465Smp#endif /* HAVE_SLASHSLASH */ 92759243Sobrien 92859243Sobrien while (*p) { /* for each component */ 92959243Sobrien sp = p; /* save slash address */ 93059243Sobrien while (*++p == '/') /* flush extra slashes */ 93159243Sobrien continue; 93259243Sobrien if (p != ++sp) 93359243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 93459243Sobrien continue; 93559243Sobrien p = sp; /* save start of component */ 93659243Sobrien slash = 0; 93759243Sobrien if (*p) 93859243Sobrien while (*++p) /* find next slash or end of path */ 93959243Sobrien if (*p == '/') { 94059243Sobrien slash = 1; 94159243Sobrien *p = 0; 94259243Sobrien break; 94359243Sobrien } 94459243Sobrien 945167465Smp#ifdef HAVE_SLASHSLASH 94659243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 94759243Sobrien slashslash = 1; 948167465Smp#endif /* HAVE_SLASHSLASH */ 94959243Sobrien if (*sp == '\0') { /* if component is null */ 95059243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 95159243Sobrien break; 95259243Sobrien else 95359243Sobrien *sp = '\0'; 95459243Sobrien } 95559243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 95659243Sobrien if (slash) { 95759243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 95859243Sobrien continue; 95959243Sobrien p = --sp; 96059243Sobrien } 96159243Sobrien else if (--sp != cp) 96259243Sobrien *sp = '\0'; 96359243Sobrien else 96459243Sobrien sp[1] = '\0'; 96559243Sobrien } 96659243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 96759243Sobrien /* 96859243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 96959243Sobrien * a path starting at /, and "xxx" is a single component. Before 97059243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 97159243Sobrien * symbolic link. 97259243Sobrien */ 97359243Sobrien *--sp = 0; /* form the pathname for readlink */ 97459243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 97559243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 976167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 977167465Smp mlink = str2short(tlink); 978167465Smp xfree(tlink); 97959243Sobrien 98059243Sobrien if (slash) 98159243Sobrien *p = '/'; 98259243Sobrien /* 98359243Sobrien * Point p to the '/' in "/..", and restore the '/'. 98459243Sobrien */ 98559243Sobrien *(p = sp) = '/'; 986145479Smp if (*mlink != '/') { 98759243Sobrien /* 98859243Sobrien * Relative path, expand it between the "yyy/" and the 98959243Sobrien * "/..". First, back sp up to the character past "yyy/". 99059243Sobrien */ 99159243Sobrien while (*--sp != '/') 99259243Sobrien continue; 99359243Sobrien sp++; 99459243Sobrien *sp = 0; 99559243Sobrien /* 996145479Smp * New length is "yyy/" + mlink + "/.." and rest 99759243Sobrien */ 998167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 999167465Smp Strlen(p) + 1) * sizeof(Char)); 100059243Sobrien /* 100159243Sobrien * Copy new path into newcp 100259243Sobrien */ 100359243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 100459243Sobrien continue; 1005145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 100659243Sobrien continue; 100759243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 100859243Sobrien continue; 100959243Sobrien /* 101059243Sobrien * Restart canonicalization at expanded "/xxx". 101159243Sobrien */ 101259243Sobrien p = sp - cp - 1 + newcp; 101359243Sobrien } 101459243Sobrien else { 1015167465Smp newcp = Strspl(mlink, p); 101659243Sobrien /* 101759243Sobrien * Restart canonicalization at beginning 101859243Sobrien */ 101959243Sobrien p = newcp; 102059243Sobrien } 1021167465Smp xfree(cp); 102259243Sobrien cp = newcp; 1023167465Smp#ifdef HAVE_SLASHSLASH 102459243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1025167465Smp#endif /* HAVE_SLASHSLASH */ 102659243Sobrien continue; /* canonicalize the link */ 102759243Sobrien } 102859243Sobrien#endif /* S_IFLNK */ 102959243Sobrien *sp = '/'; 103059243Sobrien if (sp != cp) 103159243Sobrien while (*--sp != '/') 103259243Sobrien continue; 103359243Sobrien if (slash) { 103459243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 103559243Sobrien continue; 103659243Sobrien p = sp; 103759243Sobrien } 103859243Sobrien else if (cp == sp) 103959243Sobrien *++sp = '\0'; 104059243Sobrien else 104159243Sobrien *sp = '\0'; 104259243Sobrien } 104359243Sobrien else { /* normal dir name (not . or .. or nothing) */ 104459243Sobrien 104559243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 104659243Sobrien if (sp != cp && symlinks == SYM_CHASE && 1047167465Smp (tlink = areadlink(short2str(cp))) != NULL) { 1048167465Smp mlink = str2short(tlink); 1049167465Smp xfree(tlink); 105059243Sobrien 105159243Sobrien /* 105259243Sobrien * restore the '/'. 105359243Sobrien */ 105459243Sobrien if (slash) 105559243Sobrien *p = '/'; 105659243Sobrien 105759243Sobrien /* 105859243Sobrien * point sp to p (rather than backing up). 105959243Sobrien */ 106059243Sobrien sp = p; 106159243Sobrien 1062145479Smp if (*mlink != '/') { 106359243Sobrien /* 106459243Sobrien * Relative path, expand it between the "yyy/" and the 106559243Sobrien * remainder. First, back sp up to the character past 106659243Sobrien * "yyy/". 106759243Sobrien */ 106859243Sobrien while (*--sp != '/') 106959243Sobrien continue; 107059243Sobrien sp++; 107159243Sobrien *sp = 0; 107259243Sobrien /* 1073145479Smp * New length is "yyy/" + mlink + "/.." and rest 107459243Sobrien */ 1075167465Smp p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 1076167465Smp Strlen(p) + 1) * sizeof(Char)); 107759243Sobrien /* 107859243Sobrien * Copy new path into newcp 107959243Sobrien */ 108059243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 108159243Sobrien continue; 1082145479Smp for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 108359243Sobrien continue; 108459243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 108559243Sobrien continue; 108659243Sobrien /* 108759243Sobrien * Restart canonicalization at expanded "/xxx". 108859243Sobrien */ 108959243Sobrien p = sp - cp - 1 + newcp; 109059243Sobrien } 109159243Sobrien else { 1092167465Smp newcp = Strspl(mlink, p); 109359243Sobrien /* 109459243Sobrien * Restart canonicalization at beginning 109559243Sobrien */ 109659243Sobrien p = newcp; 109759243Sobrien } 1098167465Smp xfree(cp); 109959243Sobrien cp = newcp; 1100167465Smp#ifdef HAVE_SLASHSLASH 110159243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 1102167465Smp#endif /* HAVE_SLASHSLASH */ 1103145479Smp continue; /* canonicalize the mlink */ 110459243Sobrien } 110559243Sobrien#endif /* S_IFLNK */ 110659243Sobrien if (slash) 110759243Sobrien *p = '/'; 110859243Sobrien } 110959243Sobrien } 111059243Sobrien 111159243Sobrien /* 111259243Sobrien * fix home... 111359243Sobrien */ 111459243Sobrien#ifdef S_IFLNK 111559243Sobrien p1 = varval(STRhome); 1116167465Smp cc = Strlen(p1); 111759243Sobrien /* 111859243Sobrien * See if we're not in a subdir of STRhome 111959243Sobrien */ 1120167465Smp if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 112159243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 112259243Sobrien static ino_t home_ino = (ino_t) -1; 112359243Sobrien static dev_t home_dev = (dev_t) -1; 112459243Sobrien static Char *home_ptr = NULL; 112559243Sobrien struct stat statbuf; 112659243Sobrien int found; 1127167465Smp Char *copy; 112859243Sobrien 112959243Sobrien /* 113059243Sobrien * Get dev and ino of STRhome 113159243Sobrien */ 113259243Sobrien if (home_ptr != p1 && 113359243Sobrien stat(short2str(p1), &statbuf) != -1) { 113459243Sobrien home_dev = statbuf.st_dev; 113559243Sobrien home_ino = statbuf.st_ino; 113659243Sobrien home_ptr = p1; 113759243Sobrien } 113859243Sobrien /* 113959243Sobrien * Start comparing dev & ino backwards 114059243Sobrien */ 1141167465Smp p2 = copy = Strsave(cp); 114259243Sobrien found = 0; 114359243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 114459243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 114559243Sobrien statbuf.st_ino == home_ino) { 114659243Sobrien found = 1; 114759243Sobrien break; 114859243Sobrien } 114959243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 115059243Sobrien *sp = '\0'; 115159243Sobrien } 115259243Sobrien /* 115359243Sobrien * See if we found it 115459243Sobrien */ 115559243Sobrien if (*p2 && found) { 115659243Sobrien /* 115759243Sobrien * Use STRhome to make '~' work 115859243Sobrien */ 115959243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 1160167465Smp xfree(cp); 116159243Sobrien cp = newcp; 116259243Sobrien } 1163167465Smp xfree(copy); 116459243Sobrien } 116559243Sobrien#endif /* S_IFLNK */ 116659243Sobrien 1167167465Smp#ifdef HAVE_SLASHSLASH 116859243Sobrien if (slashslash) { 116959243Sobrien if (cp[1] != '/') { 1170167465Smp p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 117159243Sobrien *p = '/'; 117259243Sobrien (void) Strcpy(&p[1], cp); 1173167465Smp xfree(cp); 117459243Sobrien cp = p; 117559243Sobrien } 117659243Sobrien } 1177167465Smp if (cp[1] == '/' && cp[2] == '/') { 1178167465Smp for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 1179167465Smp continue; 1180167465Smp } 1181167465Smp#endif /* HAVE_SLASHSLASH */ 118259243Sobrien return cp; 118359243Sobrien} 118459243Sobrien 118559243Sobrien 118659243Sobrien/* 118759243Sobrien * dnewcwd - make a new directory in the loop the current one 118859243Sobrien */ 118959243Sobrienstatic void 1190167465Smpdnewcwd(struct directory *dp, int dflag) 119159243Sobrien{ 119259243Sobrien int print; 119359243Sobrien 119459243Sobrien if (adrof(STRdunique)) { 119559243Sobrien struct directory *dn; 119659243Sobrien 119759243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 119859243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 119959243Sobrien dn->di_next->di_prev = dn->di_prev; 120059243Sobrien dn->di_prev->di_next = dn->di_next; 120159243Sobrien dfree(dn); 120259243Sobrien break; 120359243Sobrien } 120459243Sobrien } 120559243Sobrien dcwd = dp; 120659243Sobrien dset(dcwd->di_name); 120759243Sobrien dgetstack(); 120859243Sobrien print = printd; /* if printd is set, print dirstack... */ 120959243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 121059243Sobrien print = 0; 121159243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 121259243Sobrien print = 1; 121359243Sobrien if (bequiet) /* and bequiet overrides everything */ 121459243Sobrien print = 0; 121559243Sobrien if (print) 121659243Sobrien printdirs(dflag); 121759243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 121859243Sobrien} 121959243Sobrien 122059243Sobrienvoid 1221167465Smpdsetstack(void) 122259243Sobrien{ 122359243Sobrien Char **cp; 122459243Sobrien struct varent *vp; 122559243Sobrien struct directory *dn, *dp; 122659243Sobrien 1227100616Smp if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 122859243Sobrien return; 122959243Sobrien 123059243Sobrien /* Free the whole stack */ 123159243Sobrien while ((dn = dhead.di_prev) != &dhead) { 123259243Sobrien dn->di_next->di_prev = dn->di_prev; 123359243Sobrien dn->di_prev->di_next = dn->di_next; 123459243Sobrien if (dn != dcwd) 123559243Sobrien dfree(dn); 123659243Sobrien } 123759243Sobrien 123859243Sobrien /* thread the current working directory */ 123959243Sobrien dhead.di_prev = dhead.di_next = dcwd; 124059243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 124159243Sobrien 124259243Sobrien /* put back the stack */ 124359243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 1244167465Smp dp = xcalloc(sizeof(struct directory), 1); 124559243Sobrien dp->di_name = Strsave(*cp); 124659243Sobrien dp->di_count = 0; 124759243Sobrien dp->di_prev = dcwd; 124859243Sobrien dp->di_next = dcwd->di_next; 124959243Sobrien dcwd->di_next = dp; 125059243Sobrien dp->di_next->di_prev = dp; 125159243Sobrien } 125259243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 125359243Sobrien} 125459243Sobrien 125559243Sobrienstatic void 1256167465Smpdgetstack(void) 125759243Sobrien{ 125859243Sobrien int i = 0; 125959243Sobrien Char **dblk, **dbp; 126059243Sobrien struct directory *dn; 126159243Sobrien 126259243Sobrien if (adrof(STRdirstack) == NULL) 126359243Sobrien return; 126459243Sobrien 1265167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126659243Sobrien continue; 1267167465Smp dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 1268167465Smp for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 126959243Sobrien *dbp = Strsave(dn->di_name); 127059243Sobrien *dbp = NULL; 1271167465Smp cleanup_push(dblk, blk_cleanup); 127259243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 1273167465Smp cleanup_ignore(dblk); 1274167465Smp cleanup_until(dblk); 127559243Sobrien} 127659243Sobrien 127759243Sobrien/* 127859243Sobrien * getstakd - added by kfk 17 Jan 1984 127959243Sobrien * Support routine for the stack hack. Finds nth directory in 128059243Sobrien * the directory stack, or finds last directory in stack. 128159243Sobrien */ 1282167465Smpconst Char * 1283167465Smpgetstakd(int cnt) 128459243Sobrien{ 128559243Sobrien struct directory *dp; 128659243Sobrien 128759243Sobrien dp = dcwd; 128859243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 128959243Sobrien dp = dp->di_next; 129059243Sobrien if (dp == &dhead) 129159243Sobrien dp = dp->di_next; 129259243Sobrien } 129359243Sobrien else { 129459243Sobrien while (cnt-- > 0) { 129559243Sobrien dp = dp->di_prev; 129659243Sobrien if (dp == &dhead) 129759243Sobrien dp = dp->di_prev; 129859243Sobrien if (dp == dcwd) 1299167465Smp return NULL; 130059243Sobrien } 130159243Sobrien } 1302167465Smp return dp->di_name; 130359243Sobrien} 130459243Sobrien 130559243Sobrien/* 130659243Sobrien * Karl Kleinpaste - 10 Feb 1984 130759243Sobrien * Added dextract(), which is used in pushd +n. 130859243Sobrien * Instead of just rotating the entire stack around, dextract() 130959243Sobrien * lets the user have the nth dir extracted from its current 131059243Sobrien * position, and pushes it onto the top. 131159243Sobrien */ 131259243Sobrienstatic void 1313167465Smpdextract(struct directory *dp) 131459243Sobrien{ 131559243Sobrien if (dp == dcwd) 131659243Sobrien return; 131759243Sobrien dp->di_next->di_prev = dp->di_prev; 131859243Sobrien dp->di_prev->di_next = dp->di_next; 131959243Sobrien dp->di_next = dcwd->di_next; 132059243Sobrien dp->di_prev = dcwd; 132159243Sobrien dp->di_next->di_prev = dp; 132259243Sobrien dcwd->di_next = dp; 132359243Sobrien} 132459243Sobrien 1325167465Smpstatic void 1326167465Smpbequiet_cleanup(void *dummy) 1327167465Smp{ 1328167465Smp USE(dummy); 1329167465Smp bequiet = 0; 1330167465Smp} 1331167465Smp 133259243Sobrienvoid 1333167465Smploaddirs(Char *fname) 133459243Sobrien{ 133559243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133659243Sobrien 133759243Sobrien bequiet = 1; 1338167465Smp cleanup_push(&bequiet, bequiet_cleanup); 1339167465Smp if (fname) 134059243Sobrien loaddirs_cmd[1] = fname; 134159243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 134259243Sobrien loaddirs_cmd[1] = fname; 134359243Sobrien else 134459243Sobrien loaddirs_cmd[1] = STRtildotdirs; 1345167465Smp dosource(loaddirs_cmd, NULL); 1346167465Smp cleanup_until(&bequiet); 134759243Sobrien} 134859243Sobrien 134959243Sobrien/* 135059243Sobrien * create a file called ~/.cshdirs which has a sequence 135159243Sobrien * of pushd commands which will restore the dir stack to 135259243Sobrien * its state before exit/logout. remember that the order 135359243Sobrien * is reversed in the file because we are pushing. 135459243Sobrien * -strike 135559243Sobrien */ 135659243Sobrienvoid 1357167465Smprecdirs(Char *fname, int def) 135859243Sobrien{ 135959243Sobrien int fp, ftmp, oldidfds; 136059243Sobrien int cdflag = 0; 136159243Sobrien struct directory *dp; 136259243Sobrien unsigned int num; 136359243Sobrien Char *snum; 1364167465Smp struct Strbuf qname = Strbuf_INIT; 136559243Sobrien 136659243Sobrien if (fname == NULL && !def) 136759243Sobrien return; 136859243Sobrien 136959243Sobrien if (fname == NULL) { 137059243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 137159243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 137259243Sobrien else 137359243Sobrien fname = Strsave(fname); 137459243Sobrien } 137559243Sobrien else 137659243Sobrien fname = globone(fname, G_ERROR); 1377167465Smp cleanup_push(fname, xfree); 1378167465Smp 1379167465Smp if ((fp = xcreat(short2str(fname), 0600)) == -1) { 1380167465Smp cleanup_until(fname); 138159243Sobrien return; 138259243Sobrien } 138359243Sobrien 138483098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 138559243Sobrien num = (unsigned int) ~0; 138659243Sobrien else 138759243Sobrien num = (unsigned int) atoi(short2str(snum)); 138859243Sobrien 138959243Sobrien oldidfds = didfds; 139059243Sobrien didfds = 0; 139159243Sobrien ftmp = SHOUT; 139259243Sobrien SHOUT = fp; 139359243Sobrien 1394167465Smp cleanup_push(&qname, Strbuf_cleanup); 139559243Sobrien dp = dcwd->di_next; 139659243Sobrien do { 139759243Sobrien if (dp == &dhead) 139859243Sobrien continue; 139959243Sobrien 140059243Sobrien if (cdflag == 0) { 140159243Sobrien cdflag = 1; 1402167465Smp xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 140359243Sobrien } 140459243Sobrien else 1405167465Smp xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 140659243Sobrien 140759243Sobrien if (num-- == 0) 140859243Sobrien break; 140959243Sobrien 141059243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 141159243Sobrien 1412167465Smp xclose(fp); 141359243Sobrien SHOUT = ftmp; 141459243Sobrien didfds = oldidfds; 1415167465Smp cleanup_until(fname); 141659243Sobrien} 1417