sh.dir.c revision 167466
189857Sobrien/* $Header: /p/tcsh/cvsroot/tcsh/sh.dir.c,v 3.79 2006/09/25 18:17:26 christos Exp $ */ 289857Sobrien/* 389857Sobrien * sh.dir.c: Directory manipulation functions 489857Sobrien */ 589857Sobrien/*- 689857Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 777298Sobrien * All rights reserved. 877298Sobrien * 977298Sobrien * Redistribution and use in source and binary forms, with or without 1077298Sobrien * modification, are permitted provided that the following conditions 1177298Sobrien * are met: 1277298Sobrien * 1. Redistributions of source code must retain the above copyright 1377298Sobrien * notice, this list of conditions and the following disclaimer. 1477298Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1577298Sobrien * notice, this list of conditions and the following disclaimer in the 1677298Sobrien * documentation and/or other materials provided with the distribution. 1777298Sobrien * 3. Neither the name of the University nor the names of its contributors 1877298Sobrien * may be used to endorse or promote products derived from this software 1977298Sobrien * without specific prior written permission. 2077298Sobrien * 2177298Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2277298Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2377298Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2477298Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2577298Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2677298Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2777298Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2877298Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2977298Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3077298Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3177298Sobrien * SUCH DAMAGE. 3277298Sobrien */ 3377298Sobrien#include "sh.h" 3477298Sobrien#include "ed.h" 3577298Sobrien 3677298SobrienRCSID("$tcsh: sh.dir.c,v 3.79 2006/09/25 18:17:26 christos Exp $") 3777298Sobrien 3877298Sobrien/* 3977298Sobrien * C Shell - directory management 4077298Sobrien */ 4177298Sobrien 4277298Sobrienstatic Char *agetcwd (void); 4377298Sobrienstatic void dstart (const char *); 4477298Sobrienstatic struct directory *dfind (Char *); 4577298Sobrienstatic Char *dfollow (Char *); 4677298Sobrienstatic void printdirs (int); 4777298Sobrienstatic Char *dgoto (Char *); 4877298Sobrienstatic void dnewcwd (struct directory *, int); 4977298Sobrienstatic void dset (Char *); 5077298Sobrienstatic void dextract (struct directory *); 5177298Sobrienstatic int skipargs (Char ***, const char *, 5277298Sobrien const char *); 5377298Sobrienstatic void dgetstack (void); 5477298Sobrien 5577298Sobrienstatic struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 5677298Sobrienstatic int printd; /* force name to be printed */ 5777298Sobrien 5877298Sobrienint bequiet = 0; /* do not print dir stack -strike */ 5977298Sobrien 6077298Sobrienstatic Char * 6177298Sobrienagetcwd(void) 6277298Sobrien{ 6377298Sobrien char *buf; 6477298Sobrien Char *cwd; 6577298Sobrien size_t len; 6677298Sobrien 6777298Sobrien len = MAXPATHLEN; 6877298Sobrien buf = xmalloc(len); 6977298Sobrien while (getcwd(buf, len) == NULL) { 7077298Sobrien int err; 7177298Sobrien 7277298Sobrien err = errno; 7377298Sobrien if (err != ERANGE) { 7477298Sobrien xfree(buf); 7577298Sobrien errno = err; 7677298Sobrien return NULL; 7777298Sobrien } 7877298Sobrien len *= 2; 7977298Sobrien buf = xrealloc(buf, len); 8077298Sobrien } 8177298Sobrien if (*buf == '\0') { 8277298Sobrien xfree(buf); 8377298Sobrien return NULL; 8477298Sobrien } 8577298Sobrien cwd = SAVE(buf); 8677298Sobrien xfree(buf); 8777298Sobrien return cwd; 8877298Sobrien} 8977298Sobrien 9077298Sobrienstatic void 9177298Sobriendstart(const char *from) 9277298Sobrien{ 9377298Sobrien xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 9477298Sobrien} 9577298Sobrien 9677298Sobrien/* 9777298Sobrien * dinit - initialize current working directory 9877298Sobrien */ 9977298Sobrienvoid 10077298Sobriendinit(Char *hp) 10177298Sobrien{ 10277298Sobrien Char *cp, *tcp; 10377298Sobrien struct directory *dp; 10477298Sobrien 10577298Sobrien /* Don't believe the login shell home, because it may be a symlink */ 10677298Sobrien tcp = agetcwd(); 10777298Sobrien if (tcp == NULL) { 10877298Sobrien xprintf("%s: %s\n", progname, strerror(errno)); 10977298Sobrien if (hp && *hp) { 11077298Sobrien char *xcp = short2str(hp); 11177298Sobrien dstart(xcp); 11277298Sobrien if (chdir(xcp) == -1) 11377298Sobrien cp = NULL; 11477298Sobrien else 11577298Sobrien cp = Strsave(hp); 11677298Sobrien } 11777298Sobrien else 11877298Sobrien cp = NULL; 11977298Sobrien if (cp == NULL) { 12077298Sobrien dstart("/"); 12177298Sobrien if (chdir("/") == -1) 12277298Sobrien /* I am not even try to print an error message! */ 12377298Sobrien xexit(1); 12477298Sobrien cp = SAVE("/"); 12577298Sobrien } 12677298Sobrien } 12777298Sobrien else { 12877298Sobrien#ifdef S_IFLNK 12977298Sobrien struct stat swd, shp; 13077298Sobrien int swd_ok; 13177298Sobrien 13277298Sobrien swd_ok = stat(short2str(tcp), &swd) == 0; 13377298Sobrien /* 13477298Sobrien * See if $HOME is the working directory we got and use that 13577298Sobrien */ 13677298Sobrien if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 && 13777298Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 13877298Sobrien swd.st_ino == shp.st_ino) 13977298Sobrien cp = Strsave(hp); 14077298Sobrien else { 14177298Sobrien char *cwd; 14277298Sobrien 14377298Sobrien /* 14477298Sobrien * use PWD if we have it (for subshells) 14577298Sobrien */ 14677298Sobrien if (swd_ok && (cwd = getenv("PWD")) != NULL) { 14777298Sobrien if (stat(cwd, &shp) != -1 && 14877298Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 14977298Sobrien swd.st_ino == shp.st_ino) { 15077298Sobrien tcp = SAVE(cwd); 15177298Sobrien cleanup_push(tcp, xfree); 15277298Sobrien } 15377298Sobrien } 15477298Sobrien cleanup_push(tcp, xfree); 15577298Sobrien cp = dcanon(tcp, STRNULL); 15677298Sobrien cleanup_ignore(tcp); 15777298Sobrien cleanup_until(tcp); 15877298Sobrien } 15977298Sobrien#else /* S_IFLNK */ 16077298Sobrien cleanup_push(tcp, xfree); 16177298Sobrien cp = dcanon(tcp, STRNULL); 16277298Sobrien cleanup_ignore(tcp); 16377298Sobrien cleanup_until(tcp); 16477298Sobrien#endif /* S_IFLNK */ 16577298Sobrien } 16677298Sobrien 16777298Sobrien dp = xcalloc(sizeof(struct directory), 1); 16877298Sobrien dp->di_name = cp; 16977298Sobrien dp->di_count = 0; 17077298Sobrien dhead.di_next = dhead.di_prev = dp; 17177298Sobrien dp->di_next = dp->di_prev = &dhead; 17277298Sobrien printd = 0; 17377298Sobrien dnewcwd(dp, 0); 17477298Sobrien setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB); 17577298Sobrien} 17677298Sobrien 17777298Sobrienstatic void 17877298Sobriendset(Char *dp) 17977298Sobrien{ 18077298Sobrien /* 18177298Sobrien * Don't call set() directly cause if the directory contains ` or 18277298Sobrien * other junk characters glob will fail. 18389857Sobrien */ 18489857Sobrien setcopy(STRowd, varval(STRcwd), VAR_READWRITE|VAR_NOGLOB); 18577298Sobrien setcopy(STRcwd, dp, VAR_READWRITE|VAR_NOGLOB); 18677298Sobrien tsetenv(STRPWD, dp); 18777298Sobrien} 18877298Sobrien 18977298Sobrien#define DIR_PRINT 0x01 /* -p */ 19077298Sobrien#define DIR_LONG 0x02 /* -l */ 19177298Sobrien#define DIR_VERT 0x04 /* -v */ 19277298Sobrien#define DIR_LINE 0x08 /* -n */ 19377298Sobrien#define DIR_SAVE 0x10 /* -S */ 19477298Sobrien#define DIR_LOAD 0x20 /* -L */ 19577298Sobrien#define DIR_CLEAR 0x40 /* -c */ 19677298Sobrien#define DIR_OLD 0x80 /* - */ 19777298Sobrien 19877298Sobrienstatic int 19977298Sobrienskipargs(Char ***v, const char *dstr, const char *str) 20077298Sobrien{ 20177298Sobrien Char **n = *v, *s; 20277298Sobrien 20377298Sobrien int dflag = 0, loop = 1; 20477298Sobrien for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 20577298Sobrien if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 20677298Sobrien dflag |= DIR_OLD; 20777298Sobrien else { 20877298Sobrien char *p; 20977298Sobrien while (*s != '\0') /* examine flags */ 21077298Sobrien { 21177298Sobrien if ((p = strchr(dstr, *s++)) != NULL) 21277298Sobrien dflag |= (1 << (p - dstr)); 21377298Sobrien else 21477298Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21577298Sobrien } 21677298Sobrien } 21777298Sobrien if (*n && (dflag & DIR_OLD)) 21877298Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 21977298Sobrien *v = n; 22077298Sobrien /* make -l, -v, and -n imply -p */ 22177298Sobrien if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 22277298Sobrien dflag |= DIR_PRINT; 22377298Sobrien return dflag; 22477298Sobrien} 22577298Sobrien 22677298Sobrien/* 22777298Sobrien * dodirs - list all directories in directory loop 22877298Sobrien */ 22977298Sobrien/*ARGSUSED*/ 23077298Sobrienvoid 23177298Sobriendodirs(Char **v, struct command *c) 23277298Sobrien{ 23377298Sobrien static const char flags[] = "plvnSLc"; 23477298Sobrien int dflag = skipargs(&v, flags, ""); 23577298Sobrien 23677298Sobrien USE(c); 23777298Sobrien if ((dflag & DIR_CLEAR) != 0) { 23877298Sobrien struct directory *dp, *fdp; 23977298Sobrien for (dp = dcwd->di_next; dp != dcwd; ) { 24077298Sobrien fdp = dp; 24177298Sobrien dp = dp->di_next; 24277298Sobrien if (fdp != &dhead) 24377298Sobrien dfree(fdp); 24477298Sobrien } 24577298Sobrien dhead.di_next = dhead.di_prev = dp; 24677298Sobrien dp->di_next = dp->di_prev = &dhead; 24777298Sobrien } 24877298Sobrien if ((dflag & DIR_LOAD) != 0) 24977298Sobrien loaddirs(*v); 25077298Sobrien else if ((dflag & DIR_SAVE) != 0) 25177298Sobrien recdirs(*v, 1); 25277298Sobrien 25377298Sobrien if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 25477298Sobrien v++; 25577298Sobrien 25677298Sobrien if (*v != NULL || (dflag & DIR_OLD)) 25777298Sobrien stderror(ERR_DIRUS, "dirs", flags, ""); 25877298Sobrien if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 25977298Sobrien printdirs(dflag); 26077298Sobrien} 26177298Sobrien 26277298Sobrienstatic void 26377298Sobrienprintdirs(int dflag) 26477298Sobrien{ 26577298Sobrien struct directory *dp; 26677298Sobrien Char *s, *user; 26777298Sobrien int idx, len, cur; 26877298Sobrien 26977298Sobrien dp = dcwd; 27077298Sobrien idx = 0; 27177298Sobrien cur = 0; 27277298Sobrien do { 27377298Sobrien if (dp == &dhead) 27477298Sobrien continue; 27577298Sobrien if (dflag & DIR_VERT) { 27677298Sobrien xprintf("%d\t", idx++); 27777298Sobrien cur = 0; 27877298Sobrien } 27977298Sobrien s = dp->di_name; 28077298Sobrien user = NULL; 28177298Sobrien if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 28277298Sobrien len = (int) (Strlen(user) + Strlen(s) + 2); 28377298Sobrien else 28477298Sobrien len = (int) (Strlen(s) + 1); 28577298Sobrien 28677298Sobrien cur += len; 28777298Sobrien if ((dflag & DIR_LINE) && cur >= TermH - 1 && len < TermH) { 28877298Sobrien xputchar('\n'); 28977298Sobrien cur = len; 29077298Sobrien } 29177298Sobrien if (user) 29277298Sobrien xprintf("~%S", user); 29377298Sobrien xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 29477298Sobrien } while ((dp = dp->di_prev) != dcwd); 29577298Sobrien if (!(dflag & DIR_VERT)) 29677298Sobrien xputchar('\n'); 29777298Sobrien} 29877298Sobrien 29977298Sobrienvoid 30077298Sobriendtildepr(Char *dir) 30177298Sobrien{ 30277298Sobrien Char* user; 30377298Sobrien if ((user = getusername(&dir)) != NULL) 30477298Sobrien xprintf("~%-S%S", user, dir); 30577298Sobrien else 30677298Sobrien xprintf("%S", dir); 30777298Sobrien} 30877298Sobrien 30977298Sobrienvoid 31077298Sobriendtilde(void) 31177298Sobrien{ 31277298Sobrien struct directory *d = dcwd; 31377298Sobrien 31477298Sobrien do { 31577298Sobrien if (d == &dhead) 31677298Sobrien continue; 31777298Sobrien d->di_name = dcanon(d->di_name, STRNULL); 31877298Sobrien } while ((d = d->di_prev) != dcwd); 31977298Sobrien 32077298Sobrien dset(dcwd->di_name); 32177298Sobrien} 32277298Sobrien 32377298Sobrien 32477298Sobrien/* dnormalize(): 32577298Sobrien * The path will be normalized if it 32677298Sobrien * 1) is "..", 32777298Sobrien * 2) or starts with "../", 32877298Sobrien * 3) or ends with "/..", 32977298Sobrien * 4) or contains the string "/../", 33077298Sobrien * then it will be normalized, unless those strings are quoted. 33177298Sobrien * Otherwise, a copy is made and sent back. 33277298Sobrien */ 33377298SobrienChar * 33477298Sobriendnormalize(const Char *cp, int expnd) 33577298Sobrien{ 33677298Sobrien 33777298Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */ 33877298Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 33977298Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 34077298Sobrien 34177298Sobrien#ifdef S_IFLNK 34277298Sobrien if (expnd) { 34377298Sobrien struct Strbuf buf = Strbuf_INIT; 34477298Sobrien int dotdot = 0; 34577298Sobrien Char *dp, *cwd; 34677298Sobrien const Char *start = cp; 34777298Sobrien# ifdef HAVE_SLASHSLASH 34877298Sobrien int slashslash; 34977298Sobrien# endif /* HAVE_SLASHSLASH */ 35077298Sobrien 35177298Sobrien /* 35277298Sobrien * count the number of "../xxx" or "xxx/../xxx" in the path 35377298Sobrien */ 35477298Sobrien for ( ; *cp && *(cp + 1); cp++) 35577298Sobrien if (IS_DOTDOT(start, cp)) 35677298Sobrien dotdot++; 35777298Sobrien 35877298Sobrien /* 35977298Sobrien * if none, we are done. 36077298Sobrien */ 36177298Sobrien if (dotdot == 0) 36277298Sobrien return (Strsave(start)); 36377298Sobrien 36477298Sobrien# ifdef notdef 36577298Sobrien struct stat sb; 36677298Sobrien /* 36777298Sobrien * We disable this test because: 36877298Sobrien * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1; 36977298Sobrien * echo ../../dir1 does not expand. We had enabled this before 37077298Sobrien * because it was bothering people with expansions in compilation 37177298Sobrien * lines like -I../../foo. Maybe we need some kind of finer grain 37277298Sobrien * control? 37377298Sobrien * 37477298Sobrien * If the path doesn't exist, we are done too. 37577298Sobrien */ 37677298Sobrien if (lstat(short2str(start), &sb) != 0 && errno == ENOENT) 37777298Sobrien return (Strsave(start)); 37877298Sobrien# endif 37977298Sobrien 38077298Sobrien cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char)); 38177298Sobrien (void) Strcpy(cwd, dcwd->di_name); 38277298Sobrien 38377298Sobrien /* 38477298Sobrien * If the path starts with a slash, we are not relative to 38577298Sobrien * the current working directory. 38677298Sobrien */ 38777298Sobrien if (ABSOLUTEP(start)) 38889857Sobrien *cwd = '\0'; 38977298Sobrien# ifdef HAVE_SLASHSLASH 39077298Sobrien slashslash = cwd[0] == '/' && cwd[1] == '/'; 39177298Sobrien# endif /* HAVE_SLASHSLASH */ 39277298Sobrien 39377298Sobrien /* 39477298Sobrien * Ignore . and count ..'s 39577298Sobrien */ 39677298Sobrien cp = start; 39777298Sobrien do { 39877298Sobrien dotdot = 0; 39977298Sobrien buf.len = 0; 40077298Sobrien while (*cp) 40177298Sobrien if (IS_DOT(start, cp)) { 40277298Sobrien if (*++cp) 40377298Sobrien cp++; 40477298Sobrien } 40577298Sobrien else if (IS_DOTDOT(start, cp)) { 40677298Sobrien if (buf.len != 0) 40777298Sobrien break; /* finish analyzing .././../xxx/[..] */ 40877298Sobrien dotdot++; 40977298Sobrien cp += 2; 41077298Sobrien if (*cp) 41177298Sobrien cp++; 41277298Sobrien } 41377298Sobrien else 41477298Sobrien Strbuf_append1(&buf, *cp++); 41577298Sobrien 41677298Sobrien Strbuf_terminate(&buf); 41777298Sobrien while (dotdot > 0) 41877298Sobrien if ((dp = Strrchr(cwd, '/')) != NULL) { 41977298Sobrien# ifdef HAVE_SLASHSLASH 42077298Sobrien if (dp == &cwd[1]) 42177298Sobrien slashslash = 1; 42277298Sobrien# endif /* HAVE_SLASHSLASH */ 42377298Sobrien *dp = '\0'; 42477298Sobrien dotdot--; 42577298Sobrien } 42677298Sobrien else 42777298Sobrien break; 42877298Sobrien 42977298Sobrien if (!*cwd) { /* too many ..'s, starts with "/" */ 43077298Sobrien cwd[0] = '/'; 43177298Sobrien# ifdef HAVE_SLASHSLASH 43277298Sobrien /* 43377298Sobrien * Only append another slash, if already the former cwd 43477298Sobrien * was in a double-slash path. 43577298Sobrien */ 43677298Sobrien cwd[1] = slashslash ? '/' : '\0'; 43777298Sobrien cwd[2] = '\0'; 43877298Sobrien# else /* !HAVE_SLASHSLASH */ 43977298Sobrien cwd[1] = '\0'; 44077298Sobrien# endif /* HAVE_SLASHSLASH */ 44177298Sobrien } 44277298Sobrien# ifdef HAVE_SLASHSLASH 44377298Sobrien else if (slashslash && cwd[1] == '\0') { 44477298Sobrien cwd[1] = '/'; 44577298Sobrien cwd[2] = '\0'; 44677298Sobrien } 44777298Sobrien# endif /* HAVE_SLASHSLASH */ 44877298Sobrien 44977298Sobrien if (buf.len != 0) { 45077298Sobrien size_t i; 45177298Sobrien 45277298Sobrien i = Strlen(cwd); 45377298Sobrien if (TRM(cwd[i - 1]) != '/') { 45477298Sobrien cwd[i++] = '/'; 45577298Sobrien cwd[i] = '\0'; 45677298Sobrien } 45777298Sobrien dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s); 45877298Sobrien xfree(cwd); 45977298Sobrien cwd = dp; 46077298Sobrien i = Strlen(cwd) - 1; 46177298Sobrien if (TRM(cwd[i]) == '/') 46277298Sobrien cwd[i] = '\0'; 46377298Sobrien } 46477298Sobrien /* Reduction of ".." following the stuff we collected in buf 46577298Sobrien * only makes sense if the directory item in buf really exists. 46677298Sobrien * Avoid reduction of "-I../.." (typical compiler call) to "" 46777298Sobrien * or "/usr/nonexistant/../bin" to "/usr/bin": 46877298Sobrien */ 46977298Sobrien if (cwd[0]) { 47077298Sobrien struct stat exists; 47177298Sobrien if (0 != stat(short2str(cwd), &exists)) { 47277298Sobrien xfree(buf.s); 47377298Sobrien xfree(cwd); 47477298Sobrien return Strsave(start); 47577298Sobrien } 47677298Sobrien } 47777298Sobrien } while (*cp != '\0'); 47877298Sobrien xfree(buf.s); 47977298Sobrien return cwd; 48077298Sobrien } 48177298Sobrien#endif /* S_IFLNK */ 48277298Sobrien return Strsave(cp); 48377298Sobrien} 48477298Sobrien 48577298Sobrien 48677298Sobrien/* 48777298Sobrien * dochngd - implement chdir command. 48877298Sobrien */ 48977298Sobrien/*ARGSUSED*/ 49077298Sobrienvoid 49177298Sobriendochngd(Char **v, struct command *c) 49277298Sobrien{ 49377298Sobrien Char *cp; 49477298Sobrien struct directory *dp; 49577298Sobrien int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 49677298Sobrien 49777298Sobrien USE(c); 49877298Sobrien printd = 0; 49977298Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 50077298Sobrien 50177298Sobrien if (cp == NULL) { 50277298Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 50377298Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 50477298Sobrien if (chdir(short2str(cp)) < 0) 50577298Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 50677298Sobrien cp = Strsave(cp); 50777298Sobrien } 50877298Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 50977298Sobrien stderror(ERR_NAME | ERR_TOOMANY); 51077298Sobrien /* NOTREACHED */ 51177298Sobrien return; 51277298Sobrien } 51377298Sobrien else if ((dp = dfind(cp)) != 0) { 51477298Sobrien char *tmp; 51577298Sobrien 51677298Sobrien printd = 1; 51777298Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 51877298Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 51977298Sobrien dcwd->di_prev->di_next = dcwd->di_next; 52077298Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 52177298Sobrien dfree(dcwd); 52277298Sobrien dnewcwd(dp, dflag); 52377298Sobrien return; 52477298Sobrien } 52577298Sobrien else 52677298Sobrien if ((cp = dfollow(cp)) == NULL) 52777298Sobrien return; 52877298Sobrien dp = xcalloc(sizeof(struct directory), 1); 52977298Sobrien dp->di_name = cp; 53077298Sobrien dp->di_count = 0; 53177298Sobrien dp->di_next = dcwd->di_next; 53277298Sobrien dp->di_prev = dcwd->di_prev; 53377298Sobrien dp->di_prev->di_next = dp; 53477298Sobrien dp->di_next->di_prev = dp; 53577298Sobrien dfree(dcwd); 53677298Sobrien dnewcwd(dp, dflag); 53777298Sobrien} 53877298Sobrien 53977298Sobrienstatic Char * 54077298Sobriendgoto(Char *cp) 54177298Sobrien{ 54277298Sobrien Char *dp, *ret; 54377298Sobrien 54477298Sobrien if (!ABSOLUTEP(cp)) 54577298Sobrien { 54677298Sobrien Char *p, *q; 54777298Sobrien size_t cwdlen; 54877298Sobrien 54977298Sobrien cwdlen = Strlen(dcwd->di_name); 55077298Sobrien if (cwdlen == 1) /* root */ 55177298Sobrien cwdlen = 0; 55277298Sobrien dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); 55377298Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 55477298Sobrien continue; 55577298Sobrien if (cwdlen) 55677298Sobrien p[-1] = '/'; 55777298Sobrien else 55877298Sobrien p--; /* don't add a / after root */ 55977298Sobrien Strcpy(p, cp); 56077298Sobrien xfree(cp); 56177298Sobrien cp = dp; 56277298Sobrien dp += cwdlen; 56377298Sobrien } 56477298Sobrien else 56577298Sobrien dp = cp; 56677298Sobrien 56777298Sobrien#if defined(WINNT_NATIVE) 56877298Sobrien return agetcwd(); 56977298Sobrien#elif defined(__CYGWIN__) 57077298Sobrien if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ 57177298Sobrien return agetcwd(); 57277298Sobrien } else { 57377298Sobrien cleanup_push(cp, xfree); 57477298Sobrien ret = dcanon(cp, dp); 57577298Sobrien cleanup_ignore(cp); 57677298Sobrien cleanup_until(cp); 57777298Sobrien } 57877298Sobrien#else /* !WINNT_NATIVE */ 57977298Sobrien cleanup_push(cp, xfree); 58077298Sobrien ret = dcanon(cp, dp); 58177298Sobrien cleanup_ignore(cp); 58277298Sobrien cleanup_until(cp); 58377298Sobrien#endif /* WINNT_NATIVE */ 58477298Sobrien return ret; 58577298Sobrien} 58677298Sobrien 58777298Sobrien/* 58877298Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 58977298Sobrien */ 59077298Sobrienstatic Char * 59177298Sobriendfollow(Char *cp) 59277298Sobrien{ 59377298Sobrien Char *dp; 59477298Sobrien struct varent *c; 59577298Sobrien int serrno; 59677298Sobrien 59777298Sobrien cp = globone(cp, G_ERROR); 59877298Sobrien cleanup_push(cp, xfree); 59977298Sobrien#ifdef apollo 60077298Sobrien if (Strchr(cp, '`')) { 60177298Sobrien char *dptr; 60277298Sobrien if (chdir(dptr = short2str(cp)) < 0) 60377298Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 60477298Sobrien dp = agetcwd(); 60577298Sobrien cleanup_push(dp, xfree); 60677298Sobrien if (dp != NULL) { 60777298Sobrien cleanup_until(cp); 60877298Sobrien return dgoto(dp); 60977298Sobrien } 61077298Sobrien else 61177298Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 61277298Sobrien } 61377298Sobrien#endif /* apollo */ 61477298Sobrien 61577298Sobrien /* 61677298Sobrien * if we are ignoring symlinks, try to fix relatives now. 61777298Sobrien * if we are expading symlinks, it should be done by now. 61877298Sobrien */ 61977298Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 62077298Sobrien if (chdir(short2str(dp)) >= 0) { 62177298Sobrien cleanup_until(cp); 62277298Sobrien return dgoto(dp); 62377298Sobrien } 62477298Sobrien else { 62577298Sobrien xfree(dp); 62677298Sobrien if (chdir(short2str(cp)) >= 0) { 62777298Sobrien cleanup_ignore(cp); 62877298Sobrien cleanup_until(cp); 62977298Sobrien return dgoto(cp); 63077298Sobrien } 63177298Sobrien else if (errno != ENOENT && errno != ENOTDIR) { 63277298Sobrien int err; 63377298Sobrien 63477298Sobrien err = errno; 63577298Sobrien stderror(ERR_SYSTEM, short2str(cp), strerror(err)); 63677298Sobrien } 63777298Sobrien serrno = errno; 63877298Sobrien } 63977298Sobrien 64077298Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 64177298Sobrien && (c = adrof(STRcdpath)) && c->vec != NULL) { 64277298Sobrien struct Strbuf buf = Strbuf_INIT; 64377298Sobrien Char **cdp; 64477298Sobrien 64577298Sobrien for (cdp = c->vec; *cdp; cdp++) { 64677298Sobrien buf.len = 0; 64777298Sobrien Strbuf_append(&buf, *cdp); 64877298Sobrien Strbuf_append1(&buf, '/'); 64977298Sobrien Strbuf_append(&buf, cp); 65077298Sobrien Strbuf_terminate(&buf); 65177298Sobrien /* 65277298Sobrien * We always want to fix the directory here 65377298Sobrien * If we are normalizing symlinks 65477298Sobrien */ 65577298Sobrien dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 65677298Sobrien symlinks == SYM_EXPAND); 65777298Sobrien if (chdir(short2str(dp)) >= 0) { 65877298Sobrien printd = 1; 65977298Sobrien xfree(buf.s); 66077298Sobrien cleanup_until(cp); 66177298Sobrien return dgoto(dp); 66277298Sobrien } 66377298Sobrien else if (chdir(short2str(cp)) >= 0) { 66477298Sobrien printd = 1; 66577298Sobrien xfree(dp); 66677298Sobrien xfree(buf.s); 66777298Sobrien cleanup_ignore(cp); 66877298Sobrien cleanup_until(cp); 66977298Sobrien return dgoto(cp); 67077298Sobrien } 67177298Sobrien } 67277298Sobrien xfree(buf.s); 67377298Sobrien } 67477298Sobrien dp = varval(cp); 67577298Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 67677298Sobrien cleanup_until(cp); 67777298Sobrien cp = Strsave(dp); 67877298Sobrien printd = 1; 67977298Sobrien return dgoto(cp); 68077298Sobrien } 68177298Sobrien /* 68277298Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 68377298Sobrien * directories we could get to. 68477298Sobrien */ 68577298Sobrien if (!bequiet) 68677298Sobrien stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); 68777298Sobrien cleanup_until(cp); 68877298Sobrien return (NULL); 68977298Sobrien} 69077298Sobrien 69177298Sobrien 69277298Sobrien/* 69377298Sobrien * dopushd - push new directory onto directory stack. 69477298Sobrien * with no arguments exchange top and second. 69577298Sobrien * with numeric argument (+n) bring it to top. 69677298Sobrien */ 69777298Sobrien/*ARGSUSED*/ 69877298Sobrienvoid 69977298Sobriendopushd(Char **v, struct command *c) 70077298Sobrien{ 70177298Sobrien struct directory *dp; 70277298Sobrien Char *cp; 70377298Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 70477298Sobrien 70577298Sobrien USE(c); 70677298Sobrien printd = 1; 70777298Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 70877298Sobrien 70977298Sobrien if (cp == NULL) { 71077298Sobrien if (adrof(STRpushdtohome)) { 71177298Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 71277298Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 71377298Sobrien if (chdir(short2str(cp)) < 0) 71477298Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 71577298Sobrien if ((cp = dfollow(cp)) == NULL) 71677298Sobrien return; 71777298Sobrien dp = xcalloc(sizeof(struct directory), 1); 71877298Sobrien dp->di_name = cp; 71977298Sobrien dp->di_count = 0; 72077298Sobrien dp->di_prev = dcwd; 72177298Sobrien dp->di_next = dcwd->di_next; 72277298Sobrien dcwd->di_next = dp; 72377298Sobrien dp->di_next->di_prev = dp; 72477298Sobrien } 72577298Sobrien else { 72677298Sobrien char *tmp; 72777298Sobrien 72877298Sobrien if ((dp = dcwd->di_prev) == &dhead) 72977298Sobrien dp = dhead.di_prev; 73077298Sobrien if (dp == dcwd) 73177298Sobrien stderror(ERR_NAME | ERR_NODIR); 73277298Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 73377298Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 73477298Sobrien dp->di_prev->di_next = dp->di_next; 73577298Sobrien dp->di_next->di_prev = dp->di_prev; 73677298Sobrien dp->di_next = dcwd->di_next; 73777298Sobrien dp->di_prev = dcwd; 73877298Sobrien dcwd->di_next->di_prev = dp; 73977298Sobrien dcwd->di_next = dp; 74077298Sobrien } 74177298Sobrien } 74277298Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 74377298Sobrien stderror(ERR_NAME | ERR_TOOMANY); 74477298Sobrien /* NOTREACHED */ 74577298Sobrien return; 74677298Sobrien } 74777298Sobrien else if ((dp = dfind(cp)) != NULL) { 74877298Sobrien char *tmp; 74977298Sobrien 75077298Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 75177298Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 75277298Sobrien /* 75377298Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 75477298Sobrien */ 75577298Sobrien if (adrof(STRdextract)) 75677298Sobrien dextract(dp); 75777298Sobrien } 75877298Sobrien else { 75977298Sobrien Char *ccp; 76077298Sobrien 76177298Sobrien if ((ccp = dfollow(cp)) == NULL) 76277298Sobrien return; 76377298Sobrien dp = xcalloc(sizeof(struct directory), 1); 76477298Sobrien dp->di_name = ccp; 76577298Sobrien dp->di_count = 0; 76677298Sobrien dp->di_prev = dcwd; 76777298Sobrien dp->di_next = dcwd->di_next; 76877298Sobrien dcwd->di_next = dp; 76977298Sobrien dp->di_next->di_prev = dp; 77077298Sobrien } 77177298Sobrien dnewcwd(dp, dflag); 77277298Sobrien} 77377298Sobrien 77477298Sobrien/* 77577298Sobrien * dfind - find a directory if specified by numeric (+n) argument 77677298Sobrien */ 77777298Sobrienstatic struct directory * 77877298Sobriendfind(Char *cp) 77977298Sobrien{ 78077298Sobrien struct directory *dp; 78177298Sobrien int i; 78277298Sobrien Char *ep; 78377298Sobrien 78477298Sobrien if (*cp++ != '+') 78577298Sobrien return (0); 78677298Sobrien for (ep = cp; Isdigit(*ep); ep++) 78777298Sobrien continue; 78877298Sobrien if (*ep) 78977298Sobrien return (0); 79077298Sobrien i = getn(cp); 79177298Sobrien if (i <= 0) 79277298Sobrien return (0); 79377298Sobrien for (dp = dcwd; i != 0; i--) { 79477298Sobrien if ((dp = dp->di_prev) == &dhead) 79577298Sobrien dp = dp->di_prev; 79677298Sobrien if (dp == dcwd) 79777298Sobrien stderror(ERR_NAME | ERR_DEEP); 79877298Sobrien } 79977298Sobrien return (dp); 80077298Sobrien} 80177298Sobrien 80277298Sobrien/* 80377298Sobrien * dopopd - pop a directory out of the directory stack 80477298Sobrien * with a numeric argument just discard it. 80577298Sobrien */ 80677298Sobrien/*ARGSUSED*/ 80777298Sobrienvoid 80877298Sobriendopopd(Char **v, struct command *c) 80977298Sobrien{ 81077298Sobrien Char *cp; 81177298Sobrien struct directory *dp, *p = NULL; 81277298Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 81377298Sobrien 81477298Sobrien USE(c); 81577298Sobrien printd = 1; 81677298Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 81777298Sobrien 81877298Sobrien if (cp == NULL) 81977298Sobrien dp = dcwd; 82077298Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 82177298Sobrien stderror(ERR_NAME | ERR_TOOMANY); 82277298Sobrien /* NOTREACHED */ 82377298Sobrien return; 82477298Sobrien } 82577298Sobrien else if ((dp = dfind(cp)) == 0) 82677298Sobrien stderror(ERR_NAME | ERR_BADDIR); 82777298Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 82877298Sobrien stderror(ERR_NAME | ERR_EMPTY); 82977298Sobrien if (dp == dcwd) { 83077298Sobrien char *tmp; 83177298Sobrien 83277298Sobrien if ((p = dp->di_prev) == &dhead) 83377298Sobrien p = dhead.di_prev; 83477298Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 83577298Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 83677298Sobrien } 83777298Sobrien dp->di_prev->di_next = dp->di_next; 83877298Sobrien dp->di_next->di_prev = dp->di_prev; 83977298Sobrien dfree(dp); 84077298Sobrien if (dp == dcwd) { 84177298Sobrien dnewcwd(p, dflag); 84277298Sobrien } 84377298Sobrien else { 84477298Sobrien printdirs(dflag); 84577298Sobrien } 84677298Sobrien} 84777298Sobrien 84877298Sobrien/* 84977298Sobrien * dfree - free the directory (or keep it if it still has ref count) 85077298Sobrien */ 85177298Sobrienvoid 85277298Sobriendfree(struct directory *dp) 85377298Sobrien{ 85477298Sobrien 85577298Sobrien if (dp->di_count != 0) { 85677298Sobrien dp->di_next = dp->di_prev = 0; 85777298Sobrien } 85877298Sobrien else { 85977298Sobrien xfree(dp->di_name); 86077298Sobrien xfree(dp); 86177298Sobrien } 86277298Sobrien} 86377298Sobrien 86477298Sobrien/* 86577298Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 86677298Sobrien * we are of course assuming that the file system is standardly 86777298Sobrien * constructed (always have ..'s, directories have links) 86877298Sobrien */ 86977298SobrienChar * 87077298Sobriendcanon(Char *cp, Char *p) 87177298Sobrien{ 87277298Sobrien Char *sp; 87377298Sobrien Char *p1, *p2; /* general purpose */ 87477298Sobrien int slash; 87577298Sobrien#ifdef HAVE_SLASHSLASH 87677298Sobrien int slashslash; 87777298Sobrien#endif /* HAVE_SLASHSLASH */ 87877298Sobrien size_t clen; 87977298Sobrien 88077298Sobrien#ifdef S_IFLNK /* if we have symlinks */ 88177298Sobrien Char *mlink, *newcp; 88277298Sobrien char *tlink; 88377298Sobrien size_t cc; 88477298Sobrien#endif /* S_IFLNK */ 88577298Sobrien 88677298Sobrien clen = Strlen(cp); 88777298Sobrien 88877298Sobrien /* 88977298Sobrien * christos: if the path given does not start with a slash prepend cwd. If 89077298Sobrien * cwd does not start with a slash or the result would be too long try to 89177298Sobrien * correct it. 89277298Sobrien */ 89377298Sobrien if (!ABSOLUTEP(cp)) { 89477298Sobrien Char *tmpdir; 89577298Sobrien size_t len; 89677298Sobrien 89777298Sobrien p1 = varval(STRcwd); 89877298Sobrien if (p1 == STRNULL || !ABSOLUTEP(p1)) { 89977298Sobrien Char *new_cwd = agetcwd(); 90077298Sobrien 90177298Sobrien if (new_cwd == NULL) { 90277298Sobrien xprintf("%s: %s\n", progname, strerror(errno)); 90377298Sobrien setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); 90477298Sobrien } 90577298Sobrien else 90677298Sobrien setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); 90777298Sobrien p1 = varval(STRcwd); 90877298Sobrien } 90977298Sobrien len = Strlen(p1); 91077298Sobrien tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); 91177298Sobrien (void) Strcpy(tmpdir, p1); 91277298Sobrien (void) Strcat(tmpdir, STRslash); 91377298Sobrien (void) Strcat(tmpdir, cp); 91477298Sobrien xfree(cp); 91577298Sobrien cp = p = tmpdir; 91677298Sobrien } 91777298Sobrien 91877298Sobrien#ifdef HAVE_SLASHSLASH 91977298Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 92077298Sobrien#endif /* HAVE_SLASHSLASH */ 92177298Sobrien 92277298Sobrien while (*p) { /* for each component */ 92377298Sobrien sp = p; /* save slash address */ 92477298Sobrien while (*++p == '/') /* flush extra slashes */ 92577298Sobrien continue; 92677298Sobrien if (p != ++sp) 92777298Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 92877298Sobrien continue; 92977298Sobrien p = sp; /* save start of component */ 93077298Sobrien slash = 0; 93177298Sobrien if (*p) 93277298Sobrien while (*++p) /* find next slash or end of path */ 93377298Sobrien if (*p == '/') { 93477298Sobrien slash = 1; 93577298Sobrien *p = 0; 93677298Sobrien break; 93777298Sobrien } 93877298Sobrien 93977298Sobrien#ifdef HAVE_SLASHSLASH 94077298Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 94177298Sobrien slashslash = 1; 94277298Sobrien#endif /* HAVE_SLASHSLASH */ 94377298Sobrien if (*sp == '\0') { /* if component is null */ 94477298Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 94577298Sobrien break; 94677298Sobrien else 94777298Sobrien *sp = '\0'; 94877298Sobrien } 94977298Sobrien else if (sp[0] == '.' && sp[1] == 0) { 95077298Sobrien if (slash) { 95177298Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 95277298Sobrien continue; 95377298Sobrien p = --sp; 95477298Sobrien } 95577298Sobrien else if (--sp != cp) 95677298Sobrien *sp = '\0'; 95777298Sobrien else 95877298Sobrien sp[1] = '\0'; 95977298Sobrien } 96077298Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 96177298Sobrien /* 96277298Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 96377298Sobrien * a path starting at /, and "xxx" is a single component. Before 96477298Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 96577298Sobrien * symbolic link. 96677298Sobrien */ 96777298Sobrien *--sp = 0; /* form the pathname for readlink */ 96877298Sobrien#ifdef S_IFLNK /* if we have symlinks */ 96977298Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 97077298Sobrien (tlink = areadlink(short2str(cp))) != NULL) { 97177298Sobrien mlink = str2short(tlink); 97277298Sobrien xfree(tlink); 97377298Sobrien 97477298Sobrien if (slash) 97577298Sobrien *p = '/'; 97677298Sobrien /* 97777298Sobrien * Point p to the '/' in "/..", and restore the '/'. 97877298Sobrien */ 97977298Sobrien *(p = sp) = '/'; 98077298Sobrien if (*mlink != '/') { 98177298Sobrien /* 98277298Sobrien * Relative path, expand it between the "yyy/" and the 98377298Sobrien * "/..". First, back sp up to the character past "yyy/". 98477298Sobrien */ 98577298Sobrien while (*--sp != '/') 98677298Sobrien continue; 98777298Sobrien sp++; 98877298Sobrien *sp = 0; 98977298Sobrien /* 99077298Sobrien * New length is "yyy/" + mlink + "/.." and rest 99177298Sobrien */ 99277298Sobrien p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 99377298Sobrien Strlen(p) + 1) * sizeof(Char)); 99477298Sobrien /* 99577298Sobrien * Copy new path into newcp 99677298Sobrien */ 99777298Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 99877298Sobrien continue; 99977298Sobrien for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 100077298Sobrien continue; 100177298Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 100277298Sobrien continue; 100377298Sobrien /* 100477298Sobrien * Restart canonicalization at expanded "/xxx". 100577298Sobrien */ 100677298Sobrien p = sp - cp - 1 + newcp; 100777298Sobrien } 100877298Sobrien else { 100977298Sobrien newcp = Strspl(mlink, p); 101077298Sobrien /* 101177298Sobrien * Restart canonicalization at beginning 101277298Sobrien */ 101377298Sobrien p = newcp; 101477298Sobrien } 101577298Sobrien xfree(cp); 101677298Sobrien cp = newcp; 101777298Sobrien#ifdef HAVE_SLASHSLASH 101877298Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 101977298Sobrien#endif /* HAVE_SLASHSLASH */ 102077298Sobrien continue; /* canonicalize the link */ 102177298Sobrien } 102277298Sobrien#endif /* S_IFLNK */ 102377298Sobrien *sp = '/'; 102477298Sobrien if (sp != cp) 102577298Sobrien while (*--sp != '/') 102677298Sobrien continue; 102777298Sobrien if (slash) { 102877298Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 102977298Sobrien continue; 103077298Sobrien p = sp; 103177298Sobrien } 103277298Sobrien else if (cp == sp) 103377298Sobrien *++sp = '\0'; 103477298Sobrien else 103577298Sobrien *sp = '\0'; 103677298Sobrien } 103777298Sobrien else { /* normal dir name (not . or .. or nothing) */ 103877298Sobrien 103977298Sobrien#ifdef S_IFLNK /* if we have symlinks */ 104077298Sobrien if (sp != cp && symlinks == SYM_CHASE && 104177298Sobrien (tlink = areadlink(short2str(cp))) != NULL) { 104277298Sobrien mlink = str2short(tlink); 104377298Sobrien xfree(tlink); 104477298Sobrien 104577298Sobrien /* 104677298Sobrien * restore the '/'. 104777298Sobrien */ 104877298Sobrien if (slash) 104977298Sobrien *p = '/'; 105077298Sobrien 105177298Sobrien /* 105277298Sobrien * point sp to p (rather than backing up). 105377298Sobrien */ 105477298Sobrien sp = p; 105577298Sobrien 105677298Sobrien if (*mlink != '/') { 105777298Sobrien /* 105877298Sobrien * Relative path, expand it between the "yyy/" and the 105977298Sobrien * remainder. First, back sp up to the character past 106077298Sobrien * "yyy/". 106177298Sobrien */ 106277298Sobrien while (*--sp != '/') 106377298Sobrien continue; 106477298Sobrien sp++; 106577298Sobrien *sp = 0; 106677298Sobrien /* 106777298Sobrien * New length is "yyy/" + mlink + "/.." and rest 106877298Sobrien */ 106977298Sobrien p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + 107077298Sobrien Strlen(p) + 1) * sizeof(Char)); 107177298Sobrien /* 107277298Sobrien * Copy new path into newcp 107377298Sobrien */ 107477298Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 107577298Sobrien continue; 107677298Sobrien for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) 107777298Sobrien continue; 107877298Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 107977298Sobrien continue; 108077298Sobrien /* 108177298Sobrien * Restart canonicalization at expanded "/xxx". 108277298Sobrien */ 108389857Sobrien p = sp - cp - 1 + newcp; 108477298Sobrien } 108577298Sobrien else { 108689857Sobrien newcp = Strspl(mlink, p); 108777298Sobrien /* 108877298Sobrien * Restart canonicalization at beginning 108977298Sobrien */ 109077298Sobrien p = newcp; 109177298Sobrien } 109277298Sobrien xfree(cp); 109377298Sobrien cp = newcp; 109477298Sobrien#ifdef HAVE_SLASHSLASH 109577298Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 109677298Sobrien#endif /* HAVE_SLASHSLASH */ 109777298Sobrien continue; /* canonicalize the mlink */ 109877298Sobrien } 109977298Sobrien#endif /* S_IFLNK */ 110077298Sobrien if (slash) 110177298Sobrien *p = '/'; 110277298Sobrien } 110377298Sobrien } 110477298Sobrien 110577298Sobrien /* 110677298Sobrien * fix home... 110777298Sobrien */ 110889857Sobrien#ifdef S_IFLNK 110977298Sobrien p1 = varval(STRhome); 111077298Sobrien cc = Strlen(p1); 111177298Sobrien /* 111277298Sobrien * See if we're not in a subdir of STRhome 111377298Sobrien */ 111477298Sobrien if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || 111577298Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 111677298Sobrien static ino_t home_ino = (ino_t) -1; 111777298Sobrien static dev_t home_dev = (dev_t) -1; 111877298Sobrien static Char *home_ptr = NULL; 111977298Sobrien struct stat statbuf; 112077298Sobrien int found; 112177298Sobrien Char *copy; 112277298Sobrien 112377298Sobrien /* 112477298Sobrien * Get dev and ino of STRhome 112577298Sobrien */ 112677298Sobrien if (home_ptr != p1 && 112777298Sobrien stat(short2str(p1), &statbuf) != -1) { 112877298Sobrien home_dev = statbuf.st_dev; 112977298Sobrien home_ino = statbuf.st_ino; 113077298Sobrien home_ptr = p1; 113177298Sobrien } 113277298Sobrien /* 113377298Sobrien * Start comparing dev & ino backwards 113477298Sobrien */ 113577298Sobrien p2 = copy = Strsave(cp); 113677298Sobrien found = 0; 113777298Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 113877298Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 113977298Sobrien statbuf.st_ino == home_ino) { 114077298Sobrien found = 1; 114177298Sobrien break; 114277298Sobrien } 114377298Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 114477298Sobrien *sp = '\0'; 114577298Sobrien } 114677298Sobrien /* 114777298Sobrien * See if we found it 114877298Sobrien */ 114977298Sobrien if (*p2 && found) { 115077298Sobrien /* 115177298Sobrien * Use STRhome to make '~' work 115277298Sobrien */ 115377298Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 115477298Sobrien xfree(cp); 115577298Sobrien cp = newcp; 115677298Sobrien } 115777298Sobrien xfree(copy); 115877298Sobrien } 115977298Sobrien#endif /* S_IFLNK */ 116077298Sobrien 116177298Sobrien#ifdef HAVE_SLASHSLASH 116277298Sobrien if (slashslash) { 116377298Sobrien if (cp[1] != '/') { 116477298Sobrien p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); 116577298Sobrien *p = '/'; 116677298Sobrien (void) Strcpy(&p[1], cp); 116777298Sobrien xfree(cp); 116877298Sobrien cp = p; 116977298Sobrien } 117077298Sobrien } 117177298Sobrien if (cp[1] == '/' && cp[2] == '/') { 117277298Sobrien for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) 117377298Sobrien continue; 117477298Sobrien } 117577298Sobrien#endif /* HAVE_SLASHSLASH */ 117677298Sobrien return cp; 117777298Sobrien} 117877298Sobrien 117977298Sobrien 118077298Sobrien/* 118177298Sobrien * dnewcwd - make a new directory in the loop the current one 118277298Sobrien */ 118377298Sobrienstatic void 118477298Sobriendnewcwd(struct directory *dp, int dflag) 118577298Sobrien{ 118677298Sobrien int print; 118777298Sobrien 118877298Sobrien if (adrof(STRdunique)) { 118977298Sobrien struct directory *dn; 119077298Sobrien 119177298Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 119277298Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 119377298Sobrien dn->di_next->di_prev = dn->di_prev; 119477298Sobrien dn->di_prev->di_next = dn->di_next; 119577298Sobrien dfree(dn); 119677298Sobrien break; 119777298Sobrien } 119877298Sobrien } 119977298Sobrien dcwd = dp; 120077298Sobrien dset(dcwd->di_name); 120177298Sobrien dgetstack(); 120277298Sobrien print = printd; /* if printd is set, print dirstack... */ 120377298Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 120477298Sobrien print = 0; 120577298Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 120677298Sobrien print = 1; 120777298Sobrien if (bequiet) /* and bequiet overrides everything */ 120877298Sobrien print = 0; 120977298Sobrien if (print) 121077298Sobrien printdirs(dflag); 121177298Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 121277298Sobrien} 121377298Sobrien 121477298Sobrienvoid 121577298Sobriendsetstack(void) 121677298Sobrien{ 121777298Sobrien Char **cp; 121877298Sobrien struct varent *vp; 121977298Sobrien struct directory *dn, *dp; 122077298Sobrien 122177298Sobrien if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 122277298Sobrien return; 122377298Sobrien 122477298Sobrien /* Free the whole stack */ 122577298Sobrien while ((dn = dhead.di_prev) != &dhead) { 122677298Sobrien dn->di_next->di_prev = dn->di_prev; 122777298Sobrien dn->di_prev->di_next = dn->di_next; 122877298Sobrien if (dn != dcwd) 122977298Sobrien dfree(dn); 123077298Sobrien } 123177298Sobrien 123277298Sobrien /* thread the current working directory */ 123377298Sobrien dhead.di_prev = dhead.di_next = dcwd; 123477298Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 123577298Sobrien 123677298Sobrien /* put back the stack */ 123777298Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 123877298Sobrien dp = xcalloc(sizeof(struct directory), 1); 123977298Sobrien dp->di_name = Strsave(*cp); 124077298Sobrien dp->di_count = 0; 124177298Sobrien dp->di_prev = dcwd; 124277298Sobrien dp->di_next = dcwd->di_next; 124377298Sobrien dcwd->di_next = dp; 124477298Sobrien dp->di_next->di_prev = dp; 124577298Sobrien } 124677298Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 124777298Sobrien} 124877298Sobrien 124977298Sobrienstatic void 125077298Sobriendgetstack(void) 125177298Sobrien{ 125277298Sobrien int i = 0; 125377298Sobrien Char **dblk, **dbp; 125477298Sobrien struct directory *dn; 125577298Sobrien 125677298Sobrien if (adrof(STRdirstack) == NULL) 125777298Sobrien return; 125877298Sobrien 125977298Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126077298Sobrien continue; 126177298Sobrien dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); 126277298Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 126377298Sobrien *dbp = Strsave(dn->di_name); 126477298Sobrien *dbp = NULL; 126577298Sobrien cleanup_push(dblk, blk_cleanup); 126677298Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 126777298Sobrien cleanup_ignore(dblk); 126877298Sobrien cleanup_until(dblk); 126977298Sobrien} 127077298Sobrien 127177298Sobrien/* 127277298Sobrien * getstakd - added by kfk 17 Jan 1984 127377298Sobrien * Support routine for the stack hack. Finds nth directory in 127477298Sobrien * the directory stack, or finds last directory in stack. 127577298Sobrien */ 127677298Sobrienconst Char * 127777298Sobriengetstakd(int cnt) 127877298Sobrien{ 127977298Sobrien struct directory *dp; 128077298Sobrien 128177298Sobrien dp = dcwd; 128277298Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 128377298Sobrien dp = dp->di_next; 128477298Sobrien if (dp == &dhead) 128577298Sobrien dp = dp->di_next; 128677298Sobrien } 128777298Sobrien else { 128877298Sobrien while (cnt-- > 0) { 128977298Sobrien dp = dp->di_prev; 129077298Sobrien if (dp == &dhead) 129177298Sobrien dp = dp->di_prev; 129277298Sobrien if (dp == dcwd) 129377298Sobrien return NULL; 129477298Sobrien } 129577298Sobrien } 129677298Sobrien return dp->di_name; 129777298Sobrien} 129877298Sobrien 129977298Sobrien/* 130077298Sobrien * Karl Kleinpaste - 10 Feb 1984 130177298Sobrien * Added dextract(), which is used in pushd +n. 130277298Sobrien * Instead of just rotating the entire stack around, dextract() 130377298Sobrien * lets the user have the nth dir extracted from its current 130477298Sobrien * position, and pushes it onto the top. 130577298Sobrien */ 130677298Sobrienstatic void 130777298Sobriendextract(struct directory *dp) 130877298Sobrien{ 130977298Sobrien if (dp == dcwd) 131077298Sobrien return; 131177298Sobrien dp->di_next->di_prev = dp->di_prev; 131277298Sobrien dp->di_prev->di_next = dp->di_next; 131377298Sobrien dp->di_next = dcwd->di_next; 131477298Sobrien dp->di_prev = dcwd; 131577298Sobrien dp->di_next->di_prev = dp; 131677298Sobrien dcwd->di_next = dp; 131777298Sobrien} 131877298Sobrien 131977298Sobrienstatic void 132077298Sobrienbequiet_cleanup(void *dummy) 132177298Sobrien{ 132277298Sobrien USE(dummy); 132377298Sobrien bequiet = 0; 132477298Sobrien} 132577298Sobrien 132677298Sobrienvoid 132777298Sobrienloaddirs(Char *fname) 132877298Sobrien{ 132977298Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133077298Sobrien 133177298Sobrien bequiet = 1; 133277298Sobrien cleanup_push(&bequiet, bequiet_cleanup); 133377298Sobrien if (fname) 133477298Sobrien loaddirs_cmd[1] = fname; 133577298Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 133677298Sobrien loaddirs_cmd[1] = fname; 133777298Sobrien else 133877298Sobrien loaddirs_cmd[1] = STRtildotdirs; 133977298Sobrien dosource(loaddirs_cmd, NULL); 134077298Sobrien cleanup_until(&bequiet); 134177298Sobrien} 134277298Sobrien 134377298Sobrien/* 134477298Sobrien * create a file called ~/.cshdirs which has a sequence 134577298Sobrien * of pushd commands which will restore the dir stack to 134677298Sobrien * its state before exit/logout. remember that the order 134777298Sobrien * is reversed in the file because we are pushing. 134877298Sobrien * -strike 134977298Sobrien */ 135077298Sobrienvoid 135177298Sobrienrecdirs(Char *fname, int def) 135277298Sobrien{ 135377298Sobrien int fp, ftmp, oldidfds; 135477298Sobrien int cdflag = 0; 135577298Sobrien struct directory *dp; 135677298Sobrien unsigned int num; 135777298Sobrien Char *snum; 135877298Sobrien struct Strbuf qname = Strbuf_INIT; 135977298Sobrien 136077298Sobrien if (fname == NULL && !def) 136177298Sobrien return; 136277298Sobrien 136377298Sobrien if (fname == NULL) { 136477298Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 136577298Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 136677298Sobrien else 136777298Sobrien fname = Strsave(fname); 136877298Sobrien } 136977298Sobrien else 137077298Sobrien fname = globone(fname, G_ERROR); 137177298Sobrien cleanup_push(fname, xfree); 137277298Sobrien 137377298Sobrien if ((fp = xcreat(short2str(fname), 0600)) == -1) { 137477298Sobrien cleanup_until(fname); 137577298Sobrien return; 137677298Sobrien } 137777298Sobrien 137877298Sobrien if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 137977298Sobrien num = (unsigned int) ~0; 138077298Sobrien else 138177298Sobrien num = (unsigned int) atoi(short2str(snum)); 138277298Sobrien 138377298Sobrien oldidfds = didfds; 138477298Sobrien didfds = 0; 138577298Sobrien ftmp = SHOUT; 138677298Sobrien SHOUT = fp; 138777298Sobrien 138877298Sobrien cleanup_push(&qname, Strbuf_cleanup); 138977298Sobrien dp = dcwd->di_next; 139077298Sobrien do { 139177298Sobrien if (dp == &dhead) 139277298Sobrien continue; 139377298Sobrien 139477298Sobrien if (cdflag == 0) { 139577298Sobrien cdflag = 1; 139677298Sobrien xprintf("cd %S\n", quote_meta(&qname, dp->di_name)); 139777298Sobrien } 139877298Sobrien else 139977298Sobrien xprintf("pushd %S\n", quote_meta(&qname, dp->di_name)); 140077298Sobrien 140177298Sobrien if (num-- == 0) 140277298Sobrien break; 140377298Sobrien 140477298Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 140577298Sobrien 140677298Sobrien xclose(fp); 140777298Sobrien SHOUT = ftmp; 140877298Sobrien didfds = oldidfds; 140977298Sobrien cleanup_until(fname); 141077298Sobrien} 141177298Sobrien