sh.dir.c revision 83098
183098Smp/* $Header: /src/pub/tcsh/sh.dir.c,v 3.56 2001/01/29 01:28:02 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 3983098SmpRCSID("$Id: sh.dir.c,v 3.56 2001/01/29 01:28:02 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 */ 81773393Skris size_t clen; 81859243Sobrien 81959243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 82059243Sobrien Char link[MAXPATHLEN]; 82159243Sobrien char tlink[MAXPATHLEN]; 82259243Sobrien int cc; 82359243Sobrien Char *newcp; 82459243Sobrien#endif /* S_IFLNK */ 82559243Sobrien 82659243Sobrien /* 82773393Skris * if the path given is too long truncate it! 82859243Sobrien */ 82973393Skris if ((clen = Strlen(cp)) >= MAXPATHLEN) 83073393Skris cp[clen = MAXPATHLEN - 1] = '\0'; 83159243Sobrien 83259243Sobrien /* 83359243Sobrien * christos: if the path given does not start with a slash prepend cwd. If 83473393Skris * cwd does not start with a slash or the result would be too long try to 83573393Skris * correct it. 83659243Sobrien */ 83759243Sobrien if (!ABSOLUTEP(cp)) { 83859243Sobrien Char tmpdir[MAXPATHLEN]; 83973393Skris size_t len; 84059243Sobrien 84159243Sobrien p1 = varval(STRcwd); 84273393Skris if (p1 == STRNULL || !ABSOLUTEP(p1)) { 84373393Skris char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir)); 84473393Skris if (tmp == NULL || *tmp == '\0') { 84573393Skris xprintf("%s: %s\n", progname, strerror(errno)); 84673393Skris set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB); 84773393Skris } else { 84873393Skris set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB); 84973393Skris } 85073393Skris p1 = varval(STRcwd); 85173393Skris } 85273393Skris len = Strlen(p1); 85373393Skris if (len + clen + 1 >= MAXPATHLEN) 85473393Skris cp[MAXPATHLEN - (len + 1)] = '\0'; 85559243Sobrien (void) Strcpy(tmpdir, p1); 85659243Sobrien (void) Strcat(tmpdir, STRslash); 85759243Sobrien (void) Strcat(tmpdir, cp); 85859243Sobrien xfree((ptr_t) cp); 85959243Sobrien cp = p = Strsave(tmpdir); 86059243Sobrien } 86159243Sobrien 86259243Sobrien#ifdef apollo 86359243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 86459243Sobrien#endif /* apollo */ 86559243Sobrien 86659243Sobrien while (*p) { /* for each component */ 86759243Sobrien sp = p; /* save slash address */ 86859243Sobrien while (*++p == '/') /* flush extra slashes */ 86959243Sobrien continue; 87059243Sobrien if (p != ++sp) 87159243Sobrien for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 87259243Sobrien continue; 87359243Sobrien p = sp; /* save start of component */ 87459243Sobrien slash = 0; 87559243Sobrien if (*p) 87659243Sobrien while (*++p) /* find next slash or end of path */ 87759243Sobrien if (*p == '/') { 87859243Sobrien slash = 1; 87959243Sobrien *p = 0; 88059243Sobrien break; 88159243Sobrien } 88259243Sobrien 88359243Sobrien#ifdef apollo 88459243Sobrien if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') 88559243Sobrien slashslash = 1; 88659243Sobrien#endif /* apollo */ 88759243Sobrien if (*sp == '\0') { /* if component is null */ 88859243Sobrien if (--sp == cp) /* if path is one char (i.e. /) */ 88959243Sobrien break; 89059243Sobrien else 89159243Sobrien *sp = '\0'; 89259243Sobrien } 89359243Sobrien else if (sp[0] == '.' && sp[1] == 0) { 89459243Sobrien if (slash) { 89559243Sobrien for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 89659243Sobrien continue; 89759243Sobrien p = --sp; 89859243Sobrien } 89959243Sobrien else if (--sp != cp) 90059243Sobrien *sp = '\0'; 90159243Sobrien else 90259243Sobrien sp[1] = '\0'; 90359243Sobrien } 90459243Sobrien else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 90559243Sobrien /* 90659243Sobrien * We have something like "yyy/xxx/..", where "yyy" can be null or 90759243Sobrien * a path starting at /, and "xxx" is a single component. Before 90859243Sobrien * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 90959243Sobrien * symbolic link. 91059243Sobrien */ 91159243Sobrien *--sp = 0; /* form the pathname for readlink */ 91259243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 91359243Sobrien if (sp != cp && /* symlinks != SYM_IGNORE && */ 91459243Sobrien (cc = readlink(short2str(cp), tlink, 91559243Sobrien sizeof tlink)) >= 0) { 91659243Sobrien tlink[cc] = '\0'; 91759243Sobrien (void) Strncpy(link, str2short(tlink), 91859243Sobrien sizeof(link) / sizeof(Char)); 91959243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 92059243Sobrien 92159243Sobrien if (slash) 92259243Sobrien *p = '/'; 92359243Sobrien /* 92459243Sobrien * Point p to the '/' in "/..", and restore the '/'. 92559243Sobrien */ 92659243Sobrien *(p = sp) = '/'; 92759243Sobrien /* 92859243Sobrien * find length of p 92959243Sobrien */ 93059243Sobrien for (p1 = p; *p1++;) 93159243Sobrien continue; 93259243Sobrien if (*link != '/') { 93359243Sobrien /* 93459243Sobrien * Relative path, expand it between the "yyy/" and the 93559243Sobrien * "/..". First, back sp up to the character past "yyy/". 93659243Sobrien */ 93759243Sobrien while (*--sp != '/') 93859243Sobrien continue; 93959243Sobrien sp++; 94059243Sobrien *sp = 0; 94159243Sobrien /* 94259243Sobrien * New length is "yyy/" + link + "/.." and rest 94359243Sobrien */ 94459243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 94559243Sobrien (((sp - cp) + cc + (p1 - p)) * 94659243Sobrien sizeof(Char))); 94759243Sobrien /* 94859243Sobrien * Copy new path into newcp 94959243Sobrien */ 95059243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 95159243Sobrien continue; 95259243Sobrien for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 95359243Sobrien continue; 95459243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 95559243Sobrien continue; 95659243Sobrien /* 95759243Sobrien * Restart canonicalization at expanded "/xxx". 95859243Sobrien */ 95959243Sobrien p = sp - cp - 1 + newcp; 96059243Sobrien } 96159243Sobrien else { 96259243Sobrien /* 96359243Sobrien * New length is link + "/.." and rest 96459243Sobrien */ 96559243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 96659243Sobrien ((cc + (p1 - p)) * sizeof(Char))); 96759243Sobrien /* 96859243Sobrien * Copy new path into newcp 96959243Sobrien */ 97059243Sobrien for (p2 = link; (*p1++ = *p2++) != '\0';) 97159243Sobrien continue; 97259243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 97359243Sobrien continue; 97459243Sobrien /* 97559243Sobrien * Restart canonicalization at beginning 97659243Sobrien */ 97759243Sobrien p = newcp; 97859243Sobrien } 97959243Sobrien xfree((ptr_t) cp); 98059243Sobrien cp = newcp; 98159243Sobrien#ifdef apollo 98259243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 98359243Sobrien#endif /* apollo */ 98459243Sobrien continue; /* canonicalize the link */ 98559243Sobrien } 98659243Sobrien#endif /* S_IFLNK */ 98759243Sobrien *sp = '/'; 98859243Sobrien if (sp != cp) 98959243Sobrien while (*--sp != '/') 99059243Sobrien continue; 99159243Sobrien if (slash) { 99259243Sobrien for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 99359243Sobrien continue; 99459243Sobrien p = sp; 99559243Sobrien } 99659243Sobrien else if (cp == sp) 99759243Sobrien *++sp = '\0'; 99859243Sobrien else 99959243Sobrien *sp = '\0'; 100059243Sobrien } 100159243Sobrien else { /* normal dir name (not . or .. or nothing) */ 100259243Sobrien 100359243Sobrien#ifdef S_IFLNK /* if we have symlinks */ 100459243Sobrien if (sp != cp && symlinks == SYM_CHASE && 100559243Sobrien (cc = readlink(short2str(cp), tlink, 100659243Sobrien sizeof tlink)) >= 0) { 100759243Sobrien tlink[cc] = '\0'; 100859243Sobrien (void) Strncpy(link, str2short(tlink), 100959243Sobrien sizeof(link) / sizeof(Char)); 101059243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 101159243Sobrien 101259243Sobrien /* 101359243Sobrien * restore the '/'. 101459243Sobrien */ 101559243Sobrien if (slash) 101659243Sobrien *p = '/'; 101759243Sobrien 101859243Sobrien /* 101959243Sobrien * point sp to p (rather than backing up). 102059243Sobrien */ 102159243Sobrien sp = p; 102259243Sobrien 102359243Sobrien /* 102459243Sobrien * find length of p 102559243Sobrien */ 102659243Sobrien for (p1 = p; *p1++;) 102759243Sobrien continue; 102859243Sobrien if (*link != '/') { 102959243Sobrien /* 103059243Sobrien * Relative path, expand it between the "yyy/" and the 103159243Sobrien * remainder. First, back sp up to the character past 103259243Sobrien * "yyy/". 103359243Sobrien */ 103459243Sobrien while (*--sp != '/') 103559243Sobrien continue; 103659243Sobrien sp++; 103759243Sobrien *sp = 0; 103859243Sobrien /* 103959243Sobrien * New length is "yyy/" + link + "/.." and rest 104059243Sobrien */ 104159243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 104259243Sobrien (((sp - cp) + cc + (p1 - p)) 104359243Sobrien * sizeof(Char))); 104459243Sobrien /* 104559243Sobrien * Copy new path into newcp 104659243Sobrien */ 104759243Sobrien for (p2 = cp; (*p1++ = *p2++) != '\0';) 104859243Sobrien continue; 104959243Sobrien for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) 105059243Sobrien continue; 105159243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 105259243Sobrien continue; 105359243Sobrien /* 105459243Sobrien * Restart canonicalization at expanded "/xxx". 105559243Sobrien */ 105659243Sobrien p = sp - cp - 1 + newcp; 105759243Sobrien } 105859243Sobrien else { 105959243Sobrien /* 106059243Sobrien * New length is link + the rest 106159243Sobrien */ 106259243Sobrien p1 = newcp = (Char *) xmalloc((size_t) 106359243Sobrien ((cc + (p1 - p)) * sizeof(Char))); 106459243Sobrien /* 106559243Sobrien * Copy new path into newcp 106659243Sobrien */ 106759243Sobrien for (p2 = link; (*p1++ = *p2++) != '\0';) 106859243Sobrien continue; 106959243Sobrien for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 107059243Sobrien continue; 107159243Sobrien /* 107259243Sobrien * Restart canonicalization at beginning 107359243Sobrien */ 107459243Sobrien p = newcp; 107559243Sobrien } 107659243Sobrien xfree((ptr_t) cp); 107759243Sobrien cp = newcp; 107859243Sobrien#ifdef apollo 107959243Sobrien slashslash = (cp[0] == '/' && cp[1] == '/'); 108059243Sobrien#endif /* apollo */ 108159243Sobrien continue; /* canonicalize the link */ 108259243Sobrien } 108359243Sobrien#endif /* S_IFLNK */ 108459243Sobrien if (slash) 108559243Sobrien *p = '/'; 108659243Sobrien } 108759243Sobrien } 108859243Sobrien 108959243Sobrien /* 109059243Sobrien * fix home... 109159243Sobrien */ 109259243Sobrien#ifdef S_IFLNK 109359243Sobrien p1 = varval(STRhome); 109459243Sobrien cc = (int) Strlen(p1); 109559243Sobrien /* 109659243Sobrien * See if we're not in a subdir of STRhome 109759243Sobrien */ 109859243Sobrien if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 || 109959243Sobrien (cp[cc] != '/' && cp[cc] != '\0'))) { 110059243Sobrien static ino_t home_ino = (ino_t) -1; 110159243Sobrien static dev_t home_dev = (dev_t) -1; 110259243Sobrien static Char *home_ptr = NULL; 110359243Sobrien struct stat statbuf; 110459243Sobrien int found; 110559243Sobrien 110659243Sobrien /* 110759243Sobrien * Get dev and ino of STRhome 110859243Sobrien */ 110959243Sobrien if (home_ptr != p1 && 111059243Sobrien stat(short2str(p1), &statbuf) != -1) { 111159243Sobrien home_dev = statbuf.st_dev; 111259243Sobrien home_ino = statbuf.st_ino; 111359243Sobrien home_ptr = p1; 111459243Sobrien } 111559243Sobrien /* 111659243Sobrien * Start comparing dev & ino backwards 111759243Sobrien */ 111859243Sobrien p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char)); 111959243Sobrien link[sizeof(link) / sizeof(Char) - 1] = '\0'; 112059243Sobrien found = 0; 112159243Sobrien while (*p2 && stat(short2str(p2), &statbuf) != -1) { 112259243Sobrien if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && 112359243Sobrien statbuf.st_ino == home_ino) { 112459243Sobrien found = 1; 112559243Sobrien break; 112659243Sobrien } 112759243Sobrien if ((sp = Strrchr(p2, '/')) != NULL) 112859243Sobrien *sp = '\0'; 112959243Sobrien } 113059243Sobrien /* 113159243Sobrien * See if we found it 113259243Sobrien */ 113359243Sobrien if (*p2 && found) { 113459243Sobrien /* 113559243Sobrien * Use STRhome to make '~' work 113659243Sobrien */ 113759243Sobrien newcp = Strspl(p1, cp + Strlen(p2)); 113859243Sobrien xfree((ptr_t) cp); 113959243Sobrien cp = newcp; 114059243Sobrien } 114159243Sobrien } 114259243Sobrien#endif /* S_IFLNK */ 114359243Sobrien 114459243Sobrien#ifdef apollo 114559243Sobrien if (slashslash) { 114659243Sobrien if (cp[1] != '/') { 114759243Sobrien p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char)); 114859243Sobrien *p = '/'; 114959243Sobrien (void) Strcpy(&p[1], cp); 115059243Sobrien xfree((ptr_t) cp); 115159243Sobrien cp = p; 115259243Sobrien } 115359243Sobrien } 115459243Sobrien if (cp[1] == '/' && cp[2] == '/') 115559243Sobrien (void) Strcpy(&cp[1], &cp[2]); 115659243Sobrien#endif /* apollo */ 115759243Sobrien return cp; 115859243Sobrien} 115959243Sobrien 116059243Sobrien 116159243Sobrien/* 116259243Sobrien * dnewcwd - make a new directory in the loop the current one 116359243Sobrien */ 116459243Sobrienstatic void 116559243Sobriendnewcwd(dp, dflag) 116659243Sobrien register struct directory *dp; 116759243Sobrien int dflag; 116859243Sobrien{ 116959243Sobrien int print; 117059243Sobrien 117159243Sobrien if (adrof(STRdunique)) { 117259243Sobrien struct directory *dn; 117359243Sobrien 117459243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 117559243Sobrien if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) { 117659243Sobrien dn->di_next->di_prev = dn->di_prev; 117759243Sobrien dn->di_prev->di_next = dn->di_next; 117859243Sobrien dfree(dn); 117959243Sobrien break; 118059243Sobrien } 118159243Sobrien } 118259243Sobrien dcwd = dp; 118359243Sobrien dset(dcwd->di_name); 118459243Sobrien dgetstack(); 118559243Sobrien print = printd; /* if printd is set, print dirstack... */ 118659243Sobrien if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */ 118759243Sobrien print = 0; 118859243Sobrien if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */ 118959243Sobrien print = 1; 119059243Sobrien if (bequiet) /* and bequiet overrides everything */ 119159243Sobrien print = 0; 119259243Sobrien if (print) 119359243Sobrien printdirs(dflag); 119459243Sobrien cwd_cmd(); /* PWP: run the defined cwd command */ 119559243Sobrien} 119659243Sobrien 119759243Sobrienvoid 119859243Sobriendsetstack() 119959243Sobrien{ 120059243Sobrien Char **cp; 120159243Sobrien struct varent *vp; 120259243Sobrien struct directory *dn, *dp; 120359243Sobrien 120459243Sobrien if ((vp = adrof(STRdirstack)) == NULL) 120559243Sobrien return; 120659243Sobrien 120759243Sobrien /* Free the whole stack */ 120859243Sobrien while ((dn = dhead.di_prev) != &dhead) { 120959243Sobrien dn->di_next->di_prev = dn->di_prev; 121059243Sobrien dn->di_prev->di_next = dn->di_next; 121159243Sobrien if (dn != dcwd) 121259243Sobrien dfree(dn); 121359243Sobrien } 121459243Sobrien 121559243Sobrien /* thread the current working directory */ 121659243Sobrien dhead.di_prev = dhead.di_next = dcwd; 121759243Sobrien dcwd->di_next = dcwd->di_prev = &dhead; 121859243Sobrien 121959243Sobrien /* put back the stack */ 122059243Sobrien for (cp = vp->vec; cp && *cp && **cp; cp++) { 122159243Sobrien dp = (struct directory *) xcalloc(sizeof(struct directory), 1); 122259243Sobrien dp->di_name = Strsave(*cp); 122359243Sobrien dp->di_count = 0; 122459243Sobrien dp->di_prev = dcwd; 122559243Sobrien dp->di_next = dcwd->di_next; 122659243Sobrien dcwd->di_next = dp; 122759243Sobrien dp->di_next->di_prev = dp; 122859243Sobrien } 122959243Sobrien dgetstack(); /* Make $dirstack reflect the current state */ 123059243Sobrien} 123159243Sobrien 123259243Sobrienstatic void 123359243Sobriendgetstack() 123459243Sobrien{ 123559243Sobrien int i = 0; 123659243Sobrien Char **dblk, **dbp; 123759243Sobrien struct directory *dn; 123859243Sobrien 123959243Sobrien if (adrof(STRdirstack) == NULL) 124059243Sobrien return; 124159243Sobrien 124259243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 124359243Sobrien continue; 124459243Sobrien dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *)); 124559243Sobrien for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 124659243Sobrien *dbp = Strsave(dn->di_name); 124759243Sobrien *dbp = NULL; 124859243Sobrien setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); 124959243Sobrien} 125059243Sobrien 125159243Sobrien/* 125259243Sobrien * getstakd - added by kfk 17 Jan 1984 125359243Sobrien * Support routine for the stack hack. Finds nth directory in 125459243Sobrien * the directory stack, or finds last directory in stack. 125559243Sobrien */ 125659243Sobrienint 125759243Sobriengetstakd(s, cnt) 125859243Sobrien Char *s; 125959243Sobrien int cnt; 126059243Sobrien{ 126159243Sobrien struct directory *dp; 126259243Sobrien 126359243Sobrien dp = dcwd; 126459243Sobrien if (cnt < 0) { /* < 0 ==> last dir requested. */ 126559243Sobrien dp = dp->di_next; 126659243Sobrien if (dp == &dhead) 126759243Sobrien dp = dp->di_next; 126859243Sobrien } 126959243Sobrien else { 127059243Sobrien while (cnt-- > 0) { 127159243Sobrien dp = dp->di_prev; 127259243Sobrien if (dp == &dhead) 127359243Sobrien dp = dp->di_prev; 127459243Sobrien if (dp == dcwd) 127559243Sobrien return (0); 127659243Sobrien } 127759243Sobrien } 127869408Sache (void) Strncpy(s, dp->di_name, BUFSIZE); 127969408Sache s[BUFSIZE - 1] = '\0'; 128059243Sobrien return (1); 128159243Sobrien} 128259243Sobrien 128359243Sobrien/* 128459243Sobrien * Karl Kleinpaste - 10 Feb 1984 128559243Sobrien * Added dextract(), which is used in pushd +n. 128659243Sobrien * Instead of just rotating the entire stack around, dextract() 128759243Sobrien * lets the user have the nth dir extracted from its current 128859243Sobrien * position, and pushes it onto the top. 128959243Sobrien */ 129059243Sobrienstatic void 129159243Sobriendextract(dp) 129259243Sobrien struct directory *dp; 129359243Sobrien{ 129459243Sobrien if (dp == dcwd) 129559243Sobrien return; 129659243Sobrien dp->di_next->di_prev = dp->di_prev; 129759243Sobrien dp->di_prev->di_next = dp->di_next; 129859243Sobrien dp->di_next = dcwd->di_next; 129959243Sobrien dp->di_prev = dcwd; 130059243Sobrien dp->di_next->di_prev = dp; 130159243Sobrien dcwd->di_next = dp; 130259243Sobrien} 130359243Sobrien 130459243Sobrienvoid 130559243Sobrienloaddirs(fname) 130659243Sobrien Char *fname; 130759243Sobrien{ 130859243Sobrien static Char *loaddirs_cmd[] = { STRsource, NULL, NULL }; 130959243Sobrien 131059243Sobrien bequiet = 1; 131159243Sobrien if (fname) 131259243Sobrien loaddirs_cmd[1] = fname; 131359243Sobrien else if ((fname = varval(STRdirsfile)) != STRNULL) 131459243Sobrien loaddirs_cmd[1] = fname; 131559243Sobrien else 131659243Sobrien loaddirs_cmd[1] = STRtildotdirs; 131759243Sobrien dosource(loaddirs_cmd, (struct command *)0); 131859243Sobrien bequiet = 0; 131959243Sobrien} 132059243Sobrien 132159243Sobrien/* 132259243Sobrien * create a file called ~/.cshdirs which has a sequence 132359243Sobrien * of pushd commands which will restore the dir stack to 132459243Sobrien * its state before exit/logout. remember that the order 132559243Sobrien * is reversed in the file because we are pushing. 132659243Sobrien * -strike 132759243Sobrien */ 132859243Sobrienvoid 132959243Sobrienrecdirs(fname, def) 133059243Sobrien Char *fname; 133159243Sobrien int def; 133259243Sobrien{ 133359243Sobrien int fp, ftmp, oldidfds; 133459243Sobrien int cdflag = 0; 133559243Sobrien extern struct directory *dcwd; 133659243Sobrien struct directory *dp; 133759243Sobrien unsigned int num; 133859243Sobrien Char *snum; 133959243Sobrien Char qname[MAXPATHLEN*2]; 134059243Sobrien 134159243Sobrien if (fname == NULL && !def) 134259243Sobrien return; 134359243Sobrien 134459243Sobrien if (fname == NULL) { 134559243Sobrien if ((fname = varval(STRdirsfile)) == STRNULL) 134659243Sobrien fname = Strspl(varval(STRhome), &STRtildotdirs[1]); 134759243Sobrien else 134859243Sobrien fname = Strsave(fname); 134959243Sobrien } 135059243Sobrien else 135159243Sobrien fname = globone(fname, G_ERROR); 135259243Sobrien 135359243Sobrien if ((fp = creat(short2str(fname), 0600)) == -1) { 135459243Sobrien xfree((ptr_t) fname); 135559243Sobrien return; 135659243Sobrien } 135759243Sobrien 135883098Smp if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0') 135959243Sobrien num = (unsigned int) ~0; 136059243Sobrien else 136159243Sobrien num = (unsigned int) atoi(short2str(snum)); 136259243Sobrien 136359243Sobrien oldidfds = didfds; 136459243Sobrien didfds = 0; 136559243Sobrien ftmp = SHOUT; 136659243Sobrien SHOUT = fp; 136759243Sobrien 136859243Sobrien dp = dcwd->di_next; 136959243Sobrien do { 137059243Sobrien if (dp == &dhead) 137159243Sobrien continue; 137259243Sobrien 137359243Sobrien if (cdflag == 0) { 137459243Sobrien cdflag = 1; 137559243Sobrien xprintf("cd %S\n", quote_meta(qname, dp->di_name)); 137659243Sobrien } 137759243Sobrien else 137859243Sobrien xprintf("pushd %S\n", quote_meta(qname, dp->di_name)); 137959243Sobrien 138059243Sobrien if (num-- == 0) 138159243Sobrien break; 138259243Sobrien 138359243Sobrien } while ((dp = dp->di_next) != dcwd->di_next); 138459243Sobrien 138559243Sobrien (void) close(fp); 138659243Sobrien SHOUT = ftmp; 138759243Sobrien didfds = oldidfds; 138859243Sobrien xfree((ptr_t) fname); 138959243Sobrien} 1390