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