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