sh.dir.c revision 131962
1131962Smp/* $Header: /src/pub/tcsh/sh.dir.c,v 3.63 2004/05/10 19:12:37 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" 3459243Sobrien 35131962SmpRCSID("$Id: sh.dir.c,v 3.63 2004/05/10 19:12:37 christos Exp $") 3659243Sobrien 3759243Sobrien/* 3859243Sobrien * C Shell - directory management 3959243Sobrien */ 4059243Sobrien 4159243Sobrienstatic void dstart __P((const char *)); 4259243Sobrienstatic struct directory *dfind __P((Char *)); 4359243Sobrienstatic Char *dfollow __P((Char *)); 4459243Sobrienstatic void printdirs __P((int)); 4559243Sobrienstatic Char *dgoto __P((Char *)); 4659243Sobrienstatic void dnewcwd __P((struct directory *, int)); 4759243Sobrienstatic void dset __P((Char *)); 4859243Sobrienstatic void dextract __P((struct directory *)); 4959243Sobrienstatic int skipargs __P((Char ***, char *, char *)); 5059243Sobrienstatic void dgetstack __P((void)); 5159243Sobrien 5259243Sobrienstatic struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */ 5359243Sobrienstatic int printd; /* force name to be printed */ 5459243Sobrien 5559243Sobrienint bequiet = 0; /* do not print dir stack -strike */ 5659243Sobrien 5759243Sobrienstatic void 5859243Sobriendstart(from) 5959243Sobrien const char *from; 6059243Sobrien{ 6159243Sobrien xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from); 6259243Sobrien} 6359243Sobrien 6459243Sobrien/* 6559243Sobrien * dinit - initialize current working directory 6659243Sobrien */ 6759243Sobrienvoid 6859243Sobriendinit(hp) 6959243Sobrien Char *hp; 7059243Sobrien{ 7159243Sobrien register char *tcp; 7259243Sobrien register Char *cp; 7359243Sobrien register struct directory *dp; 7459243Sobrien char path[MAXPATHLEN]; 7559243Sobrien 7659243Sobrien /* Don't believe the login shell home, because it may be a symlink */ 7759243Sobrien tcp = (char *) getcwd(path, sizeof(path)); 7859243Sobrien if (tcp == NULL || *tcp == '\0') { 7959243Sobrien xprintf("%s: %s\n", progname, strerror(errno)); 8059243Sobrien if (hp && *hp) { 8159243Sobrien tcp = short2str(hp); 8259243Sobrien dstart(tcp); 8359243Sobrien if (chdir(tcp) == -1) 8459243Sobrien cp = NULL; 8559243Sobrien else 8659243Sobrien cp = Strsave(hp); 8759243Sobrien } 8859243Sobrien else 8959243Sobrien cp = NULL; 9059243Sobrien if (cp == NULL) { 9159243Sobrien dstart("/"); 9259243Sobrien if (chdir("/") == -1) 9359243Sobrien /* I am not even try to print an error message! */ 9459243Sobrien xexit(1); 9559243Sobrien cp = SAVE("/"); 9659243Sobrien } 9759243Sobrien } 9859243Sobrien else { 9959243Sobrien#ifdef S_IFLNK 10059243Sobrien struct stat swd, shp; 10159243Sobrien 10259243Sobrien /* 10359243Sobrien * See if $HOME is the working directory we got and use that 10459243Sobrien */ 10559243Sobrien if (hp && *hp && 10659243Sobrien stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 10759243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 10859243Sobrien swd.st_ino == shp.st_ino) 10959243Sobrien cp = Strsave(hp); 11059243Sobrien else { 11159243Sobrien char *cwd; 11259243Sobrien 11359243Sobrien /* 11459243Sobrien * use PWD if we have it (for subshells) 11559243Sobrien */ 11659243Sobrien if ((cwd = getenv("PWD")) != NULL) { 11759243Sobrien if (stat(cwd, &shp) != -1 && 11859243Sobrien DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && 11959243Sobrien swd.st_ino == shp.st_ino) 12059243Sobrien tcp = cwd; 12159243Sobrien } 12259243Sobrien cp = dcanon(SAVE(tcp), STRNULL); 12359243Sobrien } 12459243Sobrien#else /* S_IFLNK */ 12559243Sobrien cp = dcanon(SAVE(tcp), STRNULL); 12659243Sobrien#endif /* S_IFLNK */ 12759243Sobrien } 12859243Sobrien 12959243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 13059243Sobrien dp->di_name = cp; 13159243Sobrien dp->di_count = 0; 13259243Sobrien dhead.di_next = dhead.di_prev = dp; 13359243Sobrien dp->di_next = dp->di_prev = &dhead; 13459243Sobrien printd = 0; 13559243Sobrien dnewcwd(dp, 0); 13659243Sobrien set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB); 13759243Sobrien} 13859243Sobrien 13959243Sobrienstatic void 14059243Sobriendset(dp) 14159243SobrienChar *dp; 14259243Sobrien{ 14359243Sobrien /* 14459243Sobrien * Don't call set() directly cause if the directory contains ` or 14559243Sobrien * other junk characters glob will fail. 14659243Sobrien */ 14759243Sobrien set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB); 14859243Sobrien set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB); 14959243Sobrien 15059243Sobrien tsetenv(STRPWD, dp); 15159243Sobrien} 15259243Sobrien 15359243Sobrien#define DIR_PRINT 0x01 /* -p */ 15459243Sobrien#define DIR_LONG 0x02 /* -l */ 15559243Sobrien#define DIR_VERT 0x04 /* -v */ 15659243Sobrien#define DIR_LINE 0x08 /* -n */ 15759243Sobrien#define DIR_SAVE 0x10 /* -S */ 15859243Sobrien#define DIR_LOAD 0x20 /* -L */ 15959243Sobrien#define DIR_CLEAR 0x40 /* -c */ 16059243Sobrien#define DIR_OLD 0x80 /* - */ 16159243Sobrien 16259243Sobrienstatic int 16359243Sobrienskipargs(v, dstr, str) 16459243Sobrien Char ***v; 16559243Sobrien char *dstr; 16659243Sobrien char *str; 16759243Sobrien{ 16859243Sobrien Char **n = *v, *s; 16959243Sobrien 17059243Sobrien int dflag = 0, loop = 1; 17159243Sobrien for (n++; loop && *n != NULL && (*n)[0] == '-'; n++) 17259243Sobrien if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */ 17359243Sobrien dflag |= DIR_OLD; 17459243Sobrien else { 17559243Sobrien char *p; 17659243Sobrien while (loop && *s != '\0') /* examine flags */ 17759243Sobrien { 17859243Sobrien if ((p = strchr(dstr, *s++)) != NULL) 17959243Sobrien dflag |= (1 << (p - dstr)); 18059243Sobrien else { 18159243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 18259243Sobrien loop = 0; /* break from both loops */ 18359243Sobrien break; 18459243Sobrien } 18559243Sobrien } 18659243Sobrien } 18759243Sobrien if (*n && (dflag & DIR_OLD)) 18859243Sobrien stderror(ERR_DIRUS, short2str(**v), dstr, str); 18959243Sobrien *v = n; 19059243Sobrien /* make -l, -v, and -n imply -p */ 19159243Sobrien if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE)) 19259243Sobrien dflag |= DIR_PRINT; 19359243Sobrien return dflag; 19459243Sobrien} 19559243Sobrien 19659243Sobrien/* 19759243Sobrien * dodirs - list all directories in directory loop 19859243Sobrien */ 19959243Sobrien/*ARGSUSED*/ 20059243Sobrienvoid 20159243Sobriendodirs(v, c) 20259243Sobrien Char **v; 20359243Sobrien struct command *c; 20459243Sobrien{ 20559243Sobrien static char flags[] = "plvnSLc"; 20659243Sobrien int dflag = skipargs(&v, flags, ""); 20759243Sobrien 20859243Sobrien USE(c); 20959243Sobrien if ((dflag & DIR_CLEAR) != 0) { 21059243Sobrien struct directory *dp, *fdp; 21159243Sobrien for (dp = dcwd->di_next; dp != dcwd; ) { 21259243Sobrien fdp = dp; 21359243Sobrien dp = dp->di_next; 21459243Sobrien if (fdp != &dhead) 21559243Sobrien dfree(fdp); 21659243Sobrien } 21759243Sobrien dhead.di_next = dhead.di_prev = dp; 21859243Sobrien dp->di_next = dp->di_prev = &dhead; 21959243Sobrien } 22059243Sobrien if ((dflag & DIR_LOAD) != 0) 22159243Sobrien loaddirs(*v); 22259243Sobrien else if ((dflag & DIR_SAVE) != 0) 22359243Sobrien recdirs(*v, 1); 22459243Sobrien 22559243Sobrien if (*v && (dflag & (DIR_SAVE|DIR_LOAD))) 22659243Sobrien v++; 22759243Sobrien 22859243Sobrien if (*v != NULL || (dflag & DIR_OLD)) 22959243Sobrien stderror(ERR_DIRUS, "dirs", flags, ""); 23059243Sobrien if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT)) 23159243Sobrien printdirs(dflag); 23259243Sobrien} 23359243Sobrien 23459243Sobrienstatic void 23559243Sobrienprintdirs(dflag) 23659243Sobrien int dflag; 23759243Sobrien{ 23859243Sobrien register struct directory *dp; 23959243Sobrien Char *s, *user; 24059243Sobrien int idx, len, cur; 24159243Sobrien extern int T_Cols; 24259243Sobrien 24359243Sobrien dp = dcwd; 24459243Sobrien idx = 0; 24559243Sobrien cur = 0; 24659243Sobrien do { 24759243Sobrien if (dp == &dhead) 24859243Sobrien continue; 24959243Sobrien if (dflag & DIR_VERT) { 25059243Sobrien xprintf("%d\t", idx++); 25159243Sobrien cur = 0; 25259243Sobrien } 25359243Sobrien s = dp->di_name; 25459243Sobrien user = NULL; 25559243Sobrien if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL) 25659243Sobrien len = (int) (Strlen(user) + Strlen(s) + 2); 25759243Sobrien else 25859243Sobrien len = (int) (Strlen(s) + 1); 25959243Sobrien 26059243Sobrien cur += len; 26159243Sobrien if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) { 26259243Sobrien xputchar('\n'); 26359243Sobrien cur = len; 26459243Sobrien } 26559243Sobrien if (user) 26659243Sobrien xprintf("~%S", user); 267131962Smp xprintf("\045S%c", s, (dflag & DIR_VERT) ? '\n' : ' '); 26859243Sobrien } while ((dp = dp->di_prev) != dcwd); 26959243Sobrien if (!(dflag & DIR_VERT)) 27059243Sobrien xputchar('\n'); 27159243Sobrien} 27259243Sobrien 27359243Sobrienvoid 27459243Sobriendtildepr(dir) 27559243Sobrien Char *dir; 27659243Sobrien{ 27759243Sobrien Char* user; 27859243Sobrien if ((user = getusername(&dir)) != NULL) 279131962Smp xprintf("~\045S%S", user, dir); 28059243Sobrien else 28159243Sobrien xprintf("%S", dir); 28259243Sobrien} 28359243Sobrien 28459243Sobrienvoid 28559243Sobriendtilde() 28659243Sobrien{ 28759243Sobrien struct directory *d = dcwd; 28859243Sobrien 28959243Sobrien do { 29059243Sobrien if (d == &dhead) 29159243Sobrien continue; 29259243Sobrien d->di_name = dcanon(d->di_name, STRNULL); 29359243Sobrien } while ((d = d->di_prev) != dcwd); 29459243Sobrien 29559243Sobrien dset(dcwd->di_name); 29659243Sobrien} 29759243Sobrien 29859243Sobrien 29959243Sobrien/* dnormalize(): 30059243Sobrien * The path will be normalized if it 30159243Sobrien * 1) is "..", 30259243Sobrien * 2) or starts with "../", 30359243Sobrien * 3) or ends with "/..", 30459243Sobrien * 4) or contains the string "/../", 30559243Sobrien * then it will be normalized, unless those strings are quoted. 30659243Sobrien * Otherwise, a copy is made and sent back. 30759243Sobrien */ 30859243SobrienChar * 30959243Sobriendnormalize(cp, exp) 31059243Sobrien Char *cp; 31159243Sobrien int exp; 31259243Sobrien{ 31359243Sobrien 31459243Sobrien/* return true if dp is of the form "../xxx" or "/../xxx" */ 31559243Sobrien#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 31659243Sobrien#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/')) 31759243Sobrien 31859243Sobrien#ifdef S_IFLNK 31959243Sobrien if (exp) { 32059243Sobrien int dotdot = 0; 32159243Sobrien Char *dp, *cwd, *start = cp, buf[MAXPATHLEN]; 322100616Smp struct stat sb; 32359243Sobrien# ifdef apollo 32459243Sobrien bool slashslash; 32559243Sobrien# endif /* apollo */ 32659243Sobrien 32759243Sobrien /* 32859243Sobrien * count the number of "../xxx" or "xxx/../xxx" in the path 32959243Sobrien */ 33059243Sobrien for (dp=start; *dp && *(dp+1); dp++) 33159243Sobrien if (IS_DOTDOT(start, dp)) 33259243Sobrien dotdot++; 33359243Sobrien /* 33459243Sobrien * if none, we are done. 33559243Sobrien */ 33659243Sobrien if (dotdot == 0) 33759243Sobrien return (Strsave(cp)); 33859243Sobrien 339100616Smp /* 340100616Smp * If the path doesn't exist, we are done too. 341100616Smp */ 342100616Smp if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT) 343100616Smp return (Strsave(cp)); 344100616Smp 345100616Smp 34659243Sobrien cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) * 34759243Sobrien sizeof(Char))); 34859243Sobrien (void) Strcpy(cwd, dcwd->di_name); 34959243Sobrien 35059243Sobrien /* 35159243Sobrien * If the path starts with a slash, we are not relative to 35259243Sobrien * the current working directory. 35359243Sobrien */ 35459243Sobrien if (ABSOLUTEP(start)) 35559243Sobrien *cwd = '\0'; 35659243Sobrien# ifdef apollo 35759243Sobrien slashslash = cwd[0] == '/' && cwd[1] == '/'; 35859243Sobrien# endif /* apollo */ 35959243Sobrien 36059243Sobrien /* 36159243Sobrien * Ignore . and count ..'s 36259243Sobrien */ 36359243Sobrien for (;;) { 36459243Sobrien dotdot = 0; 36559243Sobrien buf[0] = '\0'; 36659243Sobrien dp = buf; 36759243Sobrien while (*cp) 36859243Sobrien if (IS_DOT(start, cp)) { 36959243Sobrien if (*++cp) 37059243Sobrien cp++; 37159243Sobrien } 37259243Sobrien else if (IS_DOTDOT(start, cp)) { 37359243Sobrien if (buf[0]) 37459243Sobrien break; /* finish analyzing .././../xxx/[..] */ 37559243Sobrien dotdot++; 37659243Sobrien cp += 2; 37759243Sobrien if (*cp) 37859243Sobrien cp++; 37959243Sobrien } 38059243Sobrien else 38159243Sobrien *dp++ = *cp++; 38259243Sobrien 38359243Sobrien *dp = '\0'; 38459243Sobrien while (dotdot > 0) 38559243Sobrien if ((dp = Strrchr(cwd, '/')) != NULL) { 38659243Sobrien# ifdef apollo 38759243Sobrien if (dp == &cwd[1]) 38859243Sobrien slashslash = 1; 38959243Sobrien# endif /* apollo */ 39059243Sobrien *dp = '\0'; 39159243Sobrien dotdot--; 39259243Sobrien } 39359243Sobrien else 39459243Sobrien break; 39559243Sobrien 39659243Sobrien if (!*cwd) { /* too many ..'s, starts with "/" */ 39759243Sobrien cwd[0] = '/'; 39859243Sobrien# ifdef apollo 39959243Sobrien cwd[1] = '/'; 40059243Sobrien cwd[2] = '\0'; 40159243Sobrien# else /* !apollo */ 40259243Sobrien cwd[1] = '\0'; 40359243Sobrien# endif /* apollo */ 40459243Sobrien } 40559243Sobrien# ifdef apollo 40659243Sobrien else if (slashslash && cwd[1] == '\0') { 40759243Sobrien cwd[1] = '/'; 40859243Sobrien cwd[2] = '\0'; 40959243Sobrien } 41059243Sobrien# endif /* apollo */ 41159243Sobrien 41259243Sobrien if (buf[0]) { 41359243Sobrien if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/') 41459243Sobrien cwd[dotdot++] = '/'; 41559243Sobrien cwd[dotdot] = '\0'; 41659243Sobrien dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf); 41759243Sobrien xfree((ptr_t) cwd); 41859243Sobrien cwd = dp; 41959243Sobrien if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/') 42059243Sobrien cwd[--dotdot] = '\0'; 42159243Sobrien } 422100616Smp /* Reduction of ".." following the stuff we collected in buf 423100616Smp * only makes sense if the directory item in buf really exists. 424100616Smp * Avoid reduction of "-I../.." (typical compiler call) to "" 425100616Smp * or "/usr/nonexistant/../bin" to "/usr/bin": 426100616Smp */ 427100616Smp if (cwd[0]) { 428100616Smp struct stat exists; 429100616Smp if (0 != stat(short2str(cwd), &exists)) { 430100616Smp xfree((ptr_t) cwd); 431100616Smp return Strsave(start); 432100616Smp } 433100616Smp } 43459243Sobrien if (!*cp) 43559243Sobrien break; 43659243Sobrien } 43759243Sobrien return cwd; 43859243Sobrien } 43959243Sobrien#endif /* S_IFLNK */ 44059243Sobrien return Strsave(cp); 44159243Sobrien} 44259243Sobrien 44359243Sobrien 44459243Sobrien/* 44559243Sobrien * dochngd - implement chdir command. 44659243Sobrien */ 44759243Sobrien/*ARGSUSED*/ 44859243Sobrienvoid 44959243Sobriendochngd(v, c) 45059243Sobrien Char **v; 45159243Sobrien struct command *c; 45259243Sobrien{ 45359243Sobrien register Char *cp; 45459243Sobrien register struct directory *dp; 45559243Sobrien int dflag = skipargs(&v, "plvn", "[-|<dir>]"); 45659243Sobrien 45759243Sobrien USE(c); 45859243Sobrien printd = 0; 45959243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 46059243Sobrien 46159243Sobrien if (cp == NULL) { 46259243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 46359243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 46459243Sobrien if (chdir(short2str(cp)) < 0) 46559243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 46659243Sobrien cp = Strsave(cp); 46759243Sobrien } 46859243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 46959243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 47059243Sobrien /* NOTREACHED */ 47159243Sobrien return; 47259243Sobrien } 47359243Sobrien else if ((dp = dfind(cp)) != 0) { 47459243Sobrien char *tmp; 47559243Sobrien 47659243Sobrien printd = 1; 47759243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 47859243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 47959243Sobrien dcwd->di_prev->di_next = dcwd->di_next; 48059243Sobrien dcwd->di_next->di_prev = dcwd->di_prev; 48159243Sobrien dfree(dcwd); 48259243Sobrien dnewcwd(dp, dflag); 48359243Sobrien return; 48459243Sobrien } 48559243Sobrien else 48659243Sobrien if ((cp = dfollow(cp)) == NULL) 48759243Sobrien return; 48859243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 48959243Sobrien dp->di_name = cp; 49059243Sobrien dp->di_count = 0; 49159243Sobrien dp->di_next = dcwd->di_next; 49259243Sobrien dp->di_prev = dcwd->di_prev; 49359243Sobrien dp->di_prev->di_next = dp; 49459243Sobrien dp->di_next->di_prev = dp; 49559243Sobrien dfree(dcwd); 49659243Sobrien dnewcwd(dp, dflag); 49759243Sobrien} 49859243Sobrien 49959243Sobrienstatic Char * 50059243Sobriendgoto(cp) 50159243Sobrien Char *cp; 50259243Sobrien{ 50359243Sobrien Char *dp; 50459243Sobrien 50559243Sobrien if (!ABSOLUTEP(cp)) 50659243Sobrien { 50759243Sobrien register Char *p, *q; 50859243Sobrien int cwdlen; 50959243Sobrien 51059243Sobrien for (p = dcwd->di_name; *p++;) 51159243Sobrien continue; 51259243Sobrien if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1) /* root */ 51359243Sobrien cwdlen = 0; 51459243Sobrien for (p = cp; *p++;) 51559243Sobrien continue; 51659243Sobrien dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 51759243Sobrien for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 51859243Sobrien continue; 51959243Sobrien if (cwdlen) 52059243Sobrien p[-1] = '/'; 52159243Sobrien else 52259243Sobrien p--; /* don't add a / after root */ 52359243Sobrien for (q = cp; (*p++ = *q++) != '\0';) 52459243Sobrien continue; 52559243Sobrien xfree((ptr_t) cp); 52659243Sobrien cp = dp; 52759243Sobrien dp += cwdlen; 52859243Sobrien } 52959243Sobrien else 53059243Sobrien dp = cp; 53159243Sobrien 532131962Smp#if defined(WINNT_NATIVE) 53359243Sobrien cp = SAVE(getcwd(NULL, 0)); 534131962Smp#elif defined(__CYGWIN__) 535131962Smp if (ABSOLUTEP(cp) && cp[1] == ':') /* Only DOS paths are treated that way */ 536131962Smp cp = SAVE(getcwd(NULL, 0)); 537131962Smp else 538131962Smp cp = dcanon(cp, dp); 53969408Sache#else /* !WINNT_NATIVE */ 54059243Sobrien cp = dcanon(cp, dp); 54169408Sache#endif /* WINNT_NATIVE */ 54259243Sobrien return cp; 54359243Sobrien} 54459243Sobrien 54559243Sobrien/* 54659243Sobrien * dfollow - change to arg directory; fall back on cdpath if not valid 54759243Sobrien */ 54859243Sobrienstatic Char * 54959243Sobriendfollow(cp) 55059243Sobrien register Char *cp; 55159243Sobrien{ 55259243Sobrien register Char *dp; 55359243Sobrien struct varent *c; 55459243Sobrien char ebuf[MAXPATHLEN]; 55559243Sobrien int serrno; 55659243Sobrien 55759243Sobrien cp = globone(cp, G_ERROR); 55859243Sobrien#ifdef apollo 55959243Sobrien if (Strchr(cp, '`')) { 56059243Sobrien char *dptr, *ptr; 56159243Sobrien if (chdir(dptr = short2str(cp)) < 0) 56259243Sobrien stderror(ERR_SYSTEM, dptr, strerror(errno)); 56359243Sobrien else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') { 56459243Sobrien xfree((ptr_t) cp); 56559243Sobrien cp = Strsave(str2short(ptr)); 56659243Sobrien return dgoto(cp); 56759243Sobrien } 56859243Sobrien else 56959243Sobrien stderror(ERR_SYSTEM, dptr, ebuf); 57059243Sobrien } 57159243Sobrien#endif /* apollo */ 57259243Sobrien 57359243Sobrien (void) strncpy(ebuf, short2str(cp), MAXPATHLEN); 57459243Sobrien ebuf[MAXPATHLEN-1] = '\0'; 57559243Sobrien /* 57659243Sobrien * if we are ignoring symlinks, try to fix relatives now. 57759243Sobrien * if we are expading symlinks, it should be done by now. 57859243Sobrien */ 57959243Sobrien dp = dnormalize(cp, symlinks == SYM_IGNORE); 58059243Sobrien if (chdir(short2str(dp)) >= 0) { 58159243Sobrien xfree((ptr_t) cp); 58259243Sobrien return dgoto(dp); 58359243Sobrien } 58459243Sobrien else { 58559243Sobrien xfree((ptr_t) dp); 58659243Sobrien if (chdir(short2str(cp)) >= 0) 58759243Sobrien return dgoto(cp); 58859243Sobrien else if (errno != ENOENT && errno != ENOTDIR) 58959243Sobrien stderror(ERR_SYSTEM, ebuf, strerror(errno)); 59059243Sobrien serrno = errno; 59159243Sobrien } 59259243Sobrien 59359243Sobrien if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 594100616Smp && (c = adrof(STRcdpath)) && c->vec != NULL) { 59559243Sobrien Char **cdp; 59659243Sobrien register Char *p; 59759243Sobrien Char buf[MAXPATHLEN]; 59859243Sobrien 59959243Sobrien for (cdp = c->vec; *cdp; cdp++) { 60059243Sobrien for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 60159243Sobrien continue; 60259243Sobrien dp[-1] = '/'; 60359243Sobrien for (p = cp; (*dp++ = *p++) != '\0';) 60459243Sobrien continue; 60559243Sobrien /* 60659243Sobrien * We always want to fix the directory here 60759243Sobrien * If we are normalizing symlinks 60859243Sobrien */ 60959243Sobrien dp = dnormalize(buf, symlinks == SYM_IGNORE || 61059243Sobrien symlinks == SYM_EXPAND); 61159243Sobrien if (chdir(short2str(dp)) >= 0) { 61259243Sobrien printd = 1; 61359243Sobrien xfree((ptr_t) cp); 61459243Sobrien return dgoto(dp); 61559243Sobrien } 61659243Sobrien else if (chdir(short2str(cp)) >= 0) { 61759243Sobrien printd = 1; 61859243Sobrien xfree((ptr_t) dp); 61959243Sobrien return dgoto(cp); 62059243Sobrien } 62159243Sobrien } 62259243Sobrien } 62359243Sobrien dp = varval(cp); 62459243Sobrien if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 62559243Sobrien xfree((ptr_t) cp); 62659243Sobrien cp = Strsave(dp); 62759243Sobrien printd = 1; 62859243Sobrien return dgoto(cp); 62959243Sobrien } 63059243Sobrien xfree((ptr_t) cp); 63159243Sobrien /* 63259243Sobrien * on login source of ~/.cshdirs, errors are eaten. the dir stack is all 63359243Sobrien * directories we could get to. 63459243Sobrien */ 63559243Sobrien if (!bequiet) { 63659243Sobrien stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 63759243Sobrien return (NULL); 63859243Sobrien } 63959243Sobrien else 64059243Sobrien return (NULL); 64159243Sobrien} 64259243Sobrien 64359243Sobrien 64459243Sobrien/* 64559243Sobrien * dopushd - push new directory onto directory stack. 64659243Sobrien * with no arguments exchange top and second. 64759243Sobrien * with numeric argument (+n) bring it to top. 64859243Sobrien */ 64959243Sobrien/*ARGSUSED*/ 65059243Sobrienvoid 65159243Sobriendopushd(v, c) 65259243Sobrien Char **v; 65359243Sobrien struct command *c; 65459243Sobrien{ 65559243Sobrien register struct directory *dp; 65659243Sobrien register Char *cp; 65759243Sobrien int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]"); 65859243Sobrien 65959243Sobrien USE(c); 66059243Sobrien printd = 1; 66159243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 66259243Sobrien 66359243Sobrien if (cp == NULL) { 66459243Sobrien if (adrof(STRpushdtohome)) { 66559243Sobrien if ((cp = varval(STRhome)) == STRNULL || *cp == 0) 66659243Sobrien stderror(ERR_NAME | ERR_NOHOMEDIR); 66759243Sobrien if (chdir(short2str(cp)) < 0) 66859243Sobrien stderror(ERR_NAME | ERR_CANTCHANGE); 66959243Sobrien cp = Strsave(cp); /* hmmm... PWP */ 67059243Sobrien if ((cp = dfollow(cp)) == NULL) 67159243Sobrien return; 67259243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 67359243Sobrien dp->di_name = cp; 67459243Sobrien dp->di_count = 0; 67559243Sobrien dp->di_prev = dcwd; 67659243Sobrien dp->di_next = dcwd->di_next; 67759243Sobrien dcwd->di_next = dp; 67859243Sobrien dp->di_next->di_prev = dp; 67959243Sobrien } 68059243Sobrien else { 68159243Sobrien char *tmp; 68259243Sobrien 68359243Sobrien if ((dp = dcwd->di_prev) == &dhead) 68459243Sobrien dp = dhead.di_prev; 68559243Sobrien if (dp == dcwd) 68659243Sobrien stderror(ERR_NAME | ERR_NODIR); 68759243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 68859243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 68959243Sobrien dp->di_prev->di_next = dp->di_next; 69059243Sobrien dp->di_next->di_prev = dp->di_prev; 69159243Sobrien dp->di_next = dcwd->di_next; 69259243Sobrien dp->di_prev = dcwd; 69359243Sobrien dcwd->di_next->di_prev = dp; 69459243Sobrien dcwd->di_next = dp; 69559243Sobrien } 69659243Sobrien } 69759243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 69859243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 69959243Sobrien /* NOTREACHED */ 70059243Sobrien return; 70159243Sobrien } 70259243Sobrien else if ((dp = dfind(cp)) != NULL) { 70359243Sobrien char *tmp; 70459243Sobrien 70559243Sobrien if (chdir(tmp = short2str(dp->di_name)) < 0) 70659243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 70759243Sobrien /* 70859243Sobrien * kfk - 10 Feb 1984 - added new "extraction style" pushd +n 70959243Sobrien */ 71059243Sobrien if (adrof(STRdextract)) 71159243Sobrien dextract(dp); 71259243Sobrien } 71359243Sobrien else { 71459243Sobrien register Char *ccp; 71559243Sobrien 71659243Sobrien if ((ccp = dfollow(cp)) == NULL) 71759243Sobrien return; 71859243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 71959243Sobrien dp->di_name = ccp; 72059243Sobrien dp->di_count = 0; 72159243Sobrien dp->di_prev = dcwd; 72259243Sobrien dp->di_next = dcwd->di_next; 72359243Sobrien dcwd->di_next = dp; 72459243Sobrien dp->di_next->di_prev = dp; 72559243Sobrien } 72659243Sobrien dnewcwd(dp, dflag); 72759243Sobrien} 72859243Sobrien 72959243Sobrien/* 73059243Sobrien * dfind - find a directory if specified by numeric (+n) argument 73159243Sobrien */ 73259243Sobrienstatic struct directory * 73359243Sobriendfind(cp) 73459243Sobrien register Char *cp; 73559243Sobrien{ 73659243Sobrien register struct directory *dp; 73759243Sobrien register int i; 73859243Sobrien register Char *ep; 73959243Sobrien 74059243Sobrien if (*cp++ != '+') 74159243Sobrien return (0); 74259243Sobrien for (ep = cp; Isdigit(*ep); ep++) 74359243Sobrien continue; 74459243Sobrien if (*ep) 74559243Sobrien return (0); 74659243Sobrien i = getn(cp); 74759243Sobrien if (i <= 0) 74859243Sobrien return (0); 74959243Sobrien for (dp = dcwd; i != 0; i--) { 75059243Sobrien if ((dp = dp->di_prev) == &dhead) 75159243Sobrien dp = dp->di_prev; 75259243Sobrien if (dp == dcwd) 75359243Sobrien stderror(ERR_NAME | ERR_DEEP); 75459243Sobrien } 75559243Sobrien return (dp); 75659243Sobrien} 75759243Sobrien 75859243Sobrien/* 75959243Sobrien * dopopd - pop a directory out of the directory stack 76059243Sobrien * with a numeric argument just discard it. 76159243Sobrien */ 76259243Sobrien/*ARGSUSED*/ 76359243Sobrienvoid 76459243Sobriendopopd(v, c) 76559243Sobrien Char **v; 76659243Sobrien struct command *c; 76759243Sobrien{ 76859243Sobrien Char *cp; 76959243Sobrien register struct directory *dp, *p = NULL; 77059243Sobrien int dflag = skipargs(&v, "plvn", " [-|+<n>]"); 77159243Sobrien 77259243Sobrien USE(c); 77359243Sobrien printd = 1; 77459243Sobrien cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; 77559243Sobrien 77659243Sobrien if (cp == NULL) 77759243Sobrien dp = dcwd; 77859243Sobrien else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { 77959243Sobrien stderror(ERR_NAME | ERR_TOOMANY); 78059243Sobrien /* NOTREACHED */ 78159243Sobrien return; 78259243Sobrien } 78359243Sobrien else if ((dp = dfind(cp)) == 0) 78459243Sobrien stderror(ERR_NAME | ERR_BADDIR); 78559243Sobrien if (dp->di_prev == &dhead && dp->di_next == &dhead) 78659243Sobrien stderror(ERR_NAME | ERR_EMPTY); 78759243Sobrien if (dp == dcwd) { 78859243Sobrien char *tmp; 78959243Sobrien 79059243Sobrien if ((p = dp->di_prev) == &dhead) 79159243Sobrien p = dhead.di_prev; 79259243Sobrien if (chdir(tmp = short2str(p->di_name)) < 0) 79359243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 79459243Sobrien } 79559243Sobrien dp->di_prev->di_next = dp->di_next; 79659243Sobrien dp->di_next->di_prev = dp->di_prev; 79759243Sobrien if (dp == dcwd) { 79859243Sobrien dnewcwd(p, dflag); 79959243Sobrien } 80059243Sobrien else { 80159243Sobrien printdirs(dflag); 80259243Sobrien } 80359243Sobrien dfree(dp); 80459243Sobrien} 80559243Sobrien 80659243Sobrien/* 80759243Sobrien * dfree - free the directory (or keep it if it still has ref count) 80859243Sobrien */ 80959243Sobrienvoid 81059243Sobriendfree(dp) 81159243Sobrien register struct directory *dp; 81259243Sobrien{ 81359243Sobrien 81459243Sobrien if (dp->di_count != 0) { 81559243Sobrien dp->di_next = dp->di_prev = 0; 81659243Sobrien } 81759243Sobrien else { 81859243Sobrien xfree((ptr_t) dp->di_name); 81959243Sobrien xfree((ptr_t) dp); 82059243Sobrien } 82159243Sobrien} 82259243Sobrien 82359243Sobrien/* 82459243Sobrien * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 82559243Sobrien * we are of course assuming that the file system is standardly 82659243Sobrien * constructed (always have ..'s, directories have links) 82759243Sobrien */ 82859243SobrienChar * 82959243Sobriendcanon(cp, p) 83059243Sobrien register Char *cp, *p; 83159243Sobrien{ 83259243Sobrien register Char *sp; 83359243Sobrien register Char *p1, *p2; /* general purpose */ 83459243Sobrien bool slash; 83559243Sobrien#ifdef apollo 83659243Sobrien bool slashslash; 83759243Sobrien#endif /* apollo */ 83873393Skris size_t clen; 83959243Sobrien 84059243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 84159243Sobrien Char link[MAXPATHLEN]; 84259243Sobrien char tlink[MAXPATHLEN]; 84359243Sobrien int cc; 84459243Sobrien Char *newcp; 84559243Sobrien#endif /* S_IFLNK */ 84659243Sobrien 84759243Sobrien /* 84873393Skris * if the path given is too long truncate it! 84959243Sobrien */ 85073393Skris if ((clen = Strlen(cp)) >= MAXPATHLEN) 85173393Skris cp[clen = MAXPATHLEN - 1] = '\0'; 85259243Sobrien 85359243Sobrien /* 85459243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 85573393Skris * cwd does not start with a slash or the result would be too long try to 85673393Skris * correct it. 85759243Sobrien */ 85859243Sobrien if (!ABSOLUTEP(cp)) { 85959243Sobrien Char tmpdir[MAXPATHLEN]; 86073393Skris size_t len; 86159243Sobrien 86259243Sobrien p1 = varval(STRcwd); 86373393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 86473393Skris char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir)); 86573393Skris if (tmp == NULL || *tmp == '\0') { 86673393Skris xprintf("%s: %s\n", progname, strerror(errno)); 86773393Skris set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB); 86873393Skris } else { 86973393Skris set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB); 87073393Skris } 87173393Skris p1 = varval(STRcwd); 87273393Skris } 87373393Skris len = Strlen(p1); 87473393Skris if (len + clen + 1 >= MAXPATHLEN) 87573393Skris cp[MAXPATHLEN - (len + 1)] = '\0'; 87659243Sobrien (void) Strcpy(tmpdir, p1); 87759243Sobrien (void) Strcat(tmpdir, STRslash); 87859243Sobrien (void) Strcat(tmpdir, cp); 87959243Sobrien xfree((ptr_t) cp); 88059243Sobrien cp = p = Strsave(tmpdir); 88159243Sobrien } 88259243Sobrien 88359243Sobrien#ifdef apollo 88459243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 88559243Sobrien#endif /* apollo */ 88659243Sobrien 88759243Sobrien while (*p) { /* for each component */ 88859243Sobrien sp = p; /* save slash address */ 88959243Sobrien while (*++p == '/') /* flush extra slashes */ 89059243Sobrien continue; 89159243Sobrien if (p != ++sp) 89259243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 89359243Sobrien continue; 89459243Sobrien p = sp; /* save start of component */ 89559243Sobrien slash = 0; 89659243Sobrien if (*p) 89759243Sobrien while (*++p) /* find next slash or end of path */ 89859243Sobrien if (*p == '/') { 89959243Sobrien slash = 1; 90059243Sobrien *p = 0; 90159243Sobrien break; 90259243Sobrien } 90359243Sobrien 90459243Sobrien#ifdef apollo 90559243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 90659243Sobrien slashslash = 1; 90759243Sobrien#endif /* apollo */ 90859243Sobrien if (*sp == '\0') { /* if component is null */ 90959243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 91059243Sobrien break; 91159243Sobrien else 91259243Sobrien *sp = '\0'; 91359243Sobrien } 91459243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 91559243Sobrien if (slash) { 91659243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 91759243Sobrien continue; 91859243Sobrien p = --sp; 91959243Sobrien } 92059243Sobrien else if (--sp != cp) 92159243Sobrien *sp = '\0'; 92259243Sobrien else 92359243Sobrien sp[1] = '\0'; 92459243Sobrien } 92559243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 92659243Sobrien /* 92759243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 92859243Sobrien * a path starting at /, and "xxx" is a single component. Before 92959243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 93059243Sobrien * symbolic link. 93159243Sobrien */ 93259243Sobrien *--sp = 0; /* form the pathname for readlink */ 93359243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 93459243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 93559243Sobrien (cc = readlink(short2str(cp), tlink, 936131962Smp sizeof(tlink) - 1)) >= 0) { 93759243Sobrien tlink[cc] = '\0'; 93859243Sobrien (void) Strncpy(link, str2short(tlink), 93959243Sobrien sizeof(link) / sizeof(Char)); 94059243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 94159243Sobrien 94259243Sobrien if (slash) 94359243Sobrien *p = '/'; 94459243Sobrien /* 94559243Sobrien * Point p to the '/' in "/..", and restore the '/'. 94659243Sobrien */ 94759243Sobrien *(p = sp) = '/'; 94859243Sobrien /* 94959243Sobrien * find length of p 95059243Sobrien */ 95159243Sobrien for (p1 = p; *p1++;) 95259243Sobrien continue; 95359243Sobrien if (*link != '/') { 95459243Sobrien /* 95559243Sobrien * Relative path, expand it between the "yyy/" and the 95659243Sobrien * "/..". First, back sp up to the character past "yyy/". 95759243Sobrien */ 95859243Sobrien while (*--sp != '/') 95959243Sobrien continue; 96059243Sobrien sp++; 96159243Sobrien *sp = 0; 96259243Sobrien /* 96359243Sobrien * New length is "yyy/" + link + "/.." and rest 96459243Sobrien */ 96559243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 96659243Sobrien (((sp - cp) + cc + (p1 - p)) * 96759243Sobrien sizeof(Char))); 96859243Sobrien /* 96959243Sobrien * Copy new path into newcp 97059243Sobrien */ 97159243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 97259243Sobrien continue; 97359243Sobrien for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 97459243Sobrien continue; 97559243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 97659243Sobrien continue; 97759243Sobrien /* 97859243Sobrien * Restart canonicalization at expanded "/xxx". 97959243Sobrien */ 98059243Sobrien p = sp - cp - 1 + newcp; 98159243Sobrien } 98259243Sobrien else { 98359243Sobrien /* 98459243Sobrien * New length is link + "/.." and rest 98559243Sobrien */ 98659243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 98759243Sobrien ((cc + (p1 - p)) * sizeof(Char))); 98859243Sobrien /* 98959243Sobrien * Copy new path into newcp 99059243Sobrien */ 99159243Sobrien for (p2 = link; (*p1++ = *p2++) != '\0';) 99259243Sobrien continue; 99359243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 99459243Sobrien continue; 99559243Sobrien /* 99659243Sobrien * Restart canonicalization at beginning 99759243Sobrien */ 99859243Sobrien p = newcp; 99959243Sobrien } 100059243Sobrien xfree((ptr_t) cp); 100159243Sobrien cp = newcp; 100259243Sobrien#ifdef apollo 100359243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 100459243Sobrien#endif /* apollo */ 100559243Sobrien continue; /* canonicalize the link */ 100659243Sobrien } 100759243Sobrien#endif /* S_IFLNK */ 100859243Sobrien *sp = '/'; 100959243Sobrien if (sp != cp) 101059243Sobrien while (*--sp != '/') 101159243Sobrien continue; 101259243Sobrien if (slash) { 101359243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 101459243Sobrien continue; 101559243Sobrien p = sp; 101659243Sobrien } 101759243Sobrien else if (cp == sp) 101859243Sobrien *++sp = '\0'; 101959243Sobrien else 102059243Sobrien *sp = '\0'; 102159243Sobrien } 102259243Sobrien else { /* normal dir name (not . or .. or nothing) */ 102359243Sobrien 102459243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 102559243Sobrien if (sp != cp && symlinks == SYM_CHASE && 102659243Sobrien (cc = readlink(short2str(cp), tlink, 1027131962Smp sizeof(tlink) - 1)) >= 0) { 102859243Sobrien tlink[cc] = '\0'; 102959243Sobrien (void) Strncpy(link, str2short(tlink), 103059243Sobrien sizeof(link) / sizeof(Char)); 103159243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 103259243Sobrien 103359243Sobrien /* 103459243Sobrien * restore the '/'. 103559243Sobrien */ 103659243Sobrien if (slash) 103759243Sobrien *p = '/'; 103859243Sobrien 103959243Sobrien /* 104059243Sobrien * point sp to p (rather than backing up). 104159243Sobrien */ 104259243Sobrien sp = p; 104359243Sobrien 104459243Sobrien /* 104559243Sobrien * find length of p 104659243Sobrien */ 104759243Sobrien for (p1 = p; *p1++;) 104859243Sobrien continue; 104959243Sobrien if (*link != '/') { 105059243Sobrien /* 105159243Sobrien * Relative path, expand it between the "yyy/" and the 105259243Sobrien * remainder. First, back sp up to the character past 105359243Sobrien * "yyy/". 105459243Sobrien */ 105559243Sobrien while (*--sp != '/') 105659243Sobrien continue; 105759243Sobrien sp++; 105859243Sobrien *sp = 0; 105959243Sobrien /* 106059243Sobrien * New length is "yyy/" + link + "/.." and rest 106159243Sobrien */ 106259243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 106359243Sobrien (((sp - cp) + cc + (p1 - p)) 106459243Sobrien * sizeof(Char))); 106559243Sobrien /* 106659243Sobrien * Copy new path into newcp 106759243Sobrien */ 106859243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 106959243Sobrien continue; 107059243Sobrien for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 107159243Sobrien continue; 107259243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 107359243Sobrien continue; 107459243Sobrien /* 107559243Sobrien * Restart canonicalization at expanded "/xxx". 107659243Sobrien */ 107759243Sobrien p = sp - cp - 1 + newcp; 107859243Sobrien } 107959243Sobrien else { 108059243Sobrien /* 108159243Sobrien * New length is link + the rest 108259243Sobrien */ 108359243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 108459243Sobrien ((cc + (p1 - p)) * sizeof(Char))); 108559243Sobrien /* 108659243Sobrien * Copy new path into newcp 108759243Sobrien */ 108859243Sobrien for (p2 = link; (*p1++ = *p2++) != '\0';) 108959243Sobrien continue; 109059243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 109159243Sobrien continue; 109259243Sobrien /* 109359243Sobrien * Restart canonicalization at beginning 109459243Sobrien */ 109559243Sobrien p = newcp; 109659243Sobrien } 109759243Sobrien xfree((ptr_t) cp); 109859243Sobrien cp = newcp; 109959243Sobrien#ifdef apollo 110059243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 110159243Sobrien#endif /* apollo */ 110259243Sobrien continue; /* canonicalize the link */ 110359243Sobrien } 110459243Sobrien#endif /* S_IFLNK */ 110559243Sobrien if (slash) 110659243Sobrien *p = '/'; 110759243Sobrien } 110859243Sobrien } 110959243Sobrien 111059243Sobrien /* 111159243Sobrien * fix home... 111259243Sobrien */ 111359243Sobrien#ifdef S_IFLNK 111459243Sobrien p1 = varval(STRhome); 111559243Sobrien cc = (int) Strlen(p1); 111659243Sobrien /* 111759243Sobrien * See if we're not in a subdir of STRhome 111859243Sobrien */ 111959243Sobrien if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 || 112059243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 112159243Sobrien static ino_t home_ino = (ino_t) -1; 112259243Sobrien static dev_t home_dev = (dev_t) -1; 112359243Sobrien static Char *home_ptr = NULL; 112459243Sobrien struct stat statbuf; 112559243Sobrien int found; 112659243Sobrien 112759243Sobrien /* 112859243Sobrien * Get dev and ino of STRhome 112959243Sobrien */ 113059243Sobrien if (home_ptr != p1 && 113159243Sobrien stat(short2str(p1), &statbuf) != -1) { 113259243Sobrien home_dev = statbuf.st_dev; 113359243Sobrien home_ino = statbuf.st_ino; 113459243Sobrien home_ptr = p1; 113559243Sobrien } 113659243Sobrien /* 113759243Sobrien * Start comparing dev & ino backwards 113859243Sobrien */ 113959243Sobrien p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char)); 114059243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 114159243Sobrien found = 0; 114259243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 114359243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 114459243Sobrien statbuf.st_ino == home_ino) { 114559243Sobrien found = 1; 114659243Sobrien break; 114759243Sobrien } 114859243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 114959243Sobrien *sp = '\0'; 115059243Sobrien } 115159243Sobrien /* 115259243Sobrien * See if we found it 115359243Sobrien */ 115459243Sobrien if (*p2 && found) { 115559243Sobrien /* 115659243Sobrien * Use STRhome to make '~' work 115759243Sobrien */ 115859243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 115959243Sobrien xfree((ptr_t) cp); 116059243Sobrien cp = newcp; 116159243Sobrien } 116259243Sobrien } 116359243Sobrien#endif /* S_IFLNK */ 116459243Sobrien 116559243Sobrien#ifdef apollo 116659243Sobrien if (slashslash) { 116759243Sobrien if (cp[1] != '/') { 116859243Sobrien p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char)); 116959243Sobrien *p = '/'; 117059243Sobrien (void) Strcpy(&p[1], cp); 117159243Sobrien xfree((ptr_t) cp); 117259243Sobrien cp = p; 117359243Sobrien } 117459243Sobrien } 117559243Sobrien if (cp[1] == '/' && cp[2] == '/') 117659243Sobrien (void) Strcpy(&cp[1], &cp[2]); 117759243Sobrien#endif /* apollo */ 117859243Sobrien return cp; 117959243Sobrien} 118059243Sobrien 118159243Sobrien 118259243Sobrien/* 118359243Sobrien * dnewcwd - make a new directory in the loop the current one 118459243Sobrien */ 118559243Sobrienstatic void 118659243Sobriendnewcwd(dp, dflag) 118759243Sobrien register struct directory *dp; 118859243Sobrien int dflag; 118959243Sobrien{ 119059243Sobrien int print; 119159243Sobrien 119259243Sobrien if (adrof(STRdunique)) { 119359243Sobrien struct directory *dn; 119459243Sobrien 119559243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 119659243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 119759243Sobrien dn->di_next->di_prev = dn->di_prev; 119859243Sobrien dn->di_prev->di_next = dn->di_next; 119959243Sobrien dfree(dn); 120059243Sobrien break; 120159243Sobrien } 120259243Sobrien } 120359243Sobrien dcwd = dp; 120459243Sobrien dset(dcwd->di_name); 120559243Sobrien dgetstack(); 120659243Sobrien print = printd; /* if printd is set, print dirstack... */ 120759243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 120859243Sobrien print = 0; 120959243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 121059243Sobrien print = 1; 121159243Sobrien if (bequiet) /* and bequiet overrides everything */ 121259243Sobrien print = 0; 121359243Sobrien if (print) 121459243Sobrien printdirs(dflag); 121559243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 121659243Sobrien} 121759243Sobrien 121859243Sobrienvoid 121959243Sobriendsetstack() 122059243Sobrien{ 122159243Sobrien Char **cp; 122259243Sobrien struct varent *vp; 122359243Sobrien struct directory *dn, *dp; 122459243Sobrien 1225100616Smp if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) 122659243Sobrien return; 122759243Sobrien 122859243Sobrien /* Free the whole stack */ 122959243Sobrien while ((dn = dhead.di_prev) != &dhead) { 123059243Sobrien dn->di_next->di_prev = dn->di_prev; 123159243Sobrien dn->di_prev->di_next = dn->di_next; 123259243Sobrien if (dn != dcwd) 123359243Sobrien dfree(dn); 123459243Sobrien } 123559243Sobrien 123659243Sobrien /* thread the current working directory */ 123759243Sobrien dhead.di_prev = dhead.di_next = dcwd; 123859243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 123959243Sobrien 124059243Sobrien /* put back the stack */ 124159243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 124259243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 124359243Sobrien dp->di_name = Strsave(*cp); 124459243Sobrien dp->di_count = 0; 124559243Sobrien dp->di_prev = dcwd; 124659243Sobrien dp->di_next = dcwd->di_next; 124759243Sobrien dcwd->di_next = dp; 124859243Sobrien dp->di_next->di_prev = dp; 124959243Sobrien } 125059243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 125159243Sobrien} 125259243Sobrien 125359243Sobrienstatic void 125459243Sobriendgetstack() 125559243Sobrien{ 125659243Sobrien int i = 0; 125759243Sobrien Char **dblk, **dbp; 125859243Sobrien struct directory *dn; 125959243Sobrien 126059243Sobrien if (adrof(STRdirstack) == NULL) 126159243Sobrien return; 126259243Sobrien 126359243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 126459243Sobrien continue; 126559243Sobrien dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *)); 126659243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 126759243Sobrien *dbp = Strsave(dn->di_name); 126859243Sobrien *dbp = NULL; 126959243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 127059243Sobrien} 127159243Sobrien 127259243Sobrien/* 127359243Sobrien * getstakd - added by kfk 17 Jan 1984 127459243Sobrien * Support routine for the stack hack. Finds nth directory in 127559243Sobrien * the directory stack, or finds last directory in stack. 127659243Sobrien */ 127759243Sobrienint 127859243Sobriengetstakd(s, cnt) 127959243Sobrien Char *s; 128059243Sobrien int cnt; 128159243Sobrien{ 128259243Sobrien struct directory *dp; 128359243Sobrien 128459243Sobrien dp = dcwd; 128559243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 128659243Sobrien dp = dp->di_next; 128759243Sobrien if (dp == &dhead) 128859243Sobrien dp = dp->di_next; 128959243Sobrien } 129059243Sobrien else { 129159243Sobrien while (cnt-- > 0) { 129259243Sobrien dp = dp->di_prev; 129359243Sobrien if (dp == &dhead) 129459243Sobrien dp = dp->di_prev; 129559243Sobrien if (dp == dcwd) 129659243Sobrien return (0); 129759243Sobrien } 129859243Sobrien } 129969408Sache (void) Strncpy(s, dp->di_name, BUFSIZE); 130069408Sache s[BUFSIZE - 1] = '\0'; 130159243Sobrien return (1); 130259243Sobrien} 130359243Sobrien 130459243Sobrien/* 130559243Sobrien * Karl Kleinpaste - 10 Feb 1984 130659243Sobrien * Added dextract(), which is used in pushd +n. 130759243Sobrien * Instead of just rotating the entire stack around, dextract() 130859243Sobrien * lets the user have the nth dir extracted from its current 130959243Sobrien * position, and pushes it onto the top. 131059243Sobrien */ 131159243Sobrienstatic void 131259243Sobriendextract(dp) 131359243Sobrien 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 132559243Sobrienvoid 132659243Sobrienloaddirs(fname) 132759243Sobrien Char *fname; 132859243Sobrien{ 132959243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 133059243Sobrien 133159243Sobrien bequiet = 1; 133259243Sobrien if (fname) 133359243Sobrien loaddirs_cmd[1] = fname; 133459243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 133559243Sobrien loaddirs_cmd[1] = fname; 133659243Sobrien else 133759243Sobrien loaddirs_cmd[1] = STRtildotdirs; 133859243Sobrien dosource(loaddirs_cmd, (struct command *)0); 133959243Sobrien bequiet = 0; 134059243Sobrien} 134159243Sobrien 134259243Sobrien/* 134359243Sobrien * create a file called ~/.cshdirs which has a sequence 134459243Sobrien * of pushd commands which will restore the dir stack to 134559243Sobrien * its state before exit/logout. remember that the order 134659243Sobrien * is reversed in the file because we are pushing. 134759243Sobrien * -strike 134859243Sobrien */ 134959243Sobrienvoid 135059243Sobrienrecdirs(fname, def) 135159243Sobrien Char *fname; 135259243Sobrien int def; 135359243Sobrien{ 135459243Sobrien int fp, ftmp, oldidfds; 135559243Sobrien int cdflag = 0; 135659243Sobrien extern struct directory *dcwd; 135759243Sobrien struct directory *dp; 135859243Sobrien unsigned int num; 135959243Sobrien Char *snum; 136059243Sobrien Char qname[MAXPATHLEN*2]; 136159243Sobrien 136259243Sobrien if (fname == NULL && !def) 136359243Sobrien return; 136459243Sobrien 136559243Sobrien if (fname == NULL) { 136659243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 136759243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 136859243Sobrien else 136959243Sobrien fname = Strsave(fname); 137059243Sobrien } 137159243Sobrien else 137259243Sobrien fname = globone(fname, G_ERROR); 137359243Sobrien 137459243Sobrien if ((fp = creat(short2str(fname), 0600)) == -1) { 137559243Sobrien xfree((ptr_t) fname); 137659243Sobrien return; 137759243Sobrien } 137859243Sobrien 137983098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 138059243Sobrien num = (unsigned int) ~0; 138159243Sobrien else 138259243Sobrien num = (unsigned int) atoi(short2str(snum)); 138359243Sobrien 138459243Sobrien oldidfds = didfds; 138559243Sobrien didfds = 0; 138659243Sobrien ftmp = SHOUT; 138759243Sobrien SHOUT = fp; 138859243Sobrien 138959243Sobrien dp = dcwd->di_next; 139059243Sobrien do { 139159243Sobrien if (dp == &dhead) 139259243Sobrien continue; 139359243Sobrien 139459243Sobrien if (cdflag == 0) { 139559243Sobrien cdflag = 1; 139659243Sobrien xprintf("cd %S\n", quote_meta(qname, dp->di_name)); 139759243Sobrien } 139859243Sobrien else 139959243Sobrien xprintf("pushd %S\n", quote_meta(qname, dp->di_name)); 140059243Sobrien 140159243Sobrien if (num-- == 0) 140259243Sobrien break; 140359243Sobrien 140459243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 140559243Sobrien 140659243Sobrien (void) close(fp); 140759243Sobrien SHOUT = ftmp; 140859243Sobrien didfds = oldidfds; 140959243Sobrien xfree((ptr_t) fname); 141059243Sobrien} 1411