| /*- |
| * Copyright (c) 1991 The Regents of the University of California. |
| * All rights reserved. |
| * |
| * This code is derived from software contributed to Berkeley by |
| * Kenneth Almquist. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by the University of |
| * California, Berkeley and its contributors. |
| * 4. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #ifndef lint |
| /*static char sccsid[] = "from: @(#)cd.c 5.2 (Berkeley) 3/13/91";*/ |
| static char rcsid[] = "cd.c,v 1.5 1993/08/01 18:58:22 mycroft Exp"; |
| #endif /* not lint */ |
| |
| /* |
| * The cd and pwd commands. |
| */ |
| |
| #include "shell.h" |
| #include "var.h" |
| #include "nodes.h" /* for jobs.h */ |
| #include "jobs.h" |
| #include "options.h" |
| #include "output.h" |
| #include "memalloc.h" |
| #include "error.h" |
| #include "mystring.h" |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| |
| |
| #ifdef __STDC__ |
| STATIC int docd(char *, int); |
| STATIC void updatepwd(char *); |
| STATIC void getpwd(void); |
| STATIC char *getcomponent(void); |
| #else |
| STATIC int docd(); |
| STATIC void updatepwd(); |
| STATIC void getpwd(); |
| STATIC char *getcomponent(); |
| #endif |
| |
| |
| char *curdir; /* current working directory */ |
| STATIC char *cdcomppath; |
| |
| #if UDIR |
| extern int didudir; /* set if /u/logname expanded */ |
| #endif |
| |
| |
| int |
| cdcmd(argc, argv) char **argv; { |
| char *dest; |
| char *path; |
| char *p; |
| struct stat statb; |
| char *padvance(); |
| |
| nextopt(nullstr); |
| if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) |
| error("HOME not set"); |
| if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) |
| path = nullstr; |
| while ((p = padvance(&path, dest)) != NULL) { |
| if (stat(p, &statb) >= 0 |
| && (statb.st_mode & S_IFMT) == S_IFDIR |
| && docd(p, strcmp(p, dest)) >= 0) |
| return 0; |
| } |
| error("can't cd to %s", dest); |
| } |
| |
| |
| /* |
| * Actually do the chdir. If the name refers to symbolic links, we |
| * compute the actual directory name before doing the cd. In an |
| * interactive shell, print the directory name if "print" is nonzero |
| * or if the name refers to a symbolic link. We also print the name |
| * if "/u/logname" was expanded in it, since this is similar to a |
| * symbolic link. (The check for this breaks if the user gives the |
| * cd command some additional, unused arguments.) |
| */ |
| |
| #if SYMLINKS == 0 |
| STATIC int |
| docd(dest, print) |
| char *dest; |
| { |
| #if UDIR |
| if (didudir) |
| print = 1; |
| #endif |
| INTOFF; |
| if (dest[0] != '\0' && chdir(dest) < 0) { |
| INTON; |
| return -1; |
| } |
| updatepwd(dest); |
| INTON; |
| #ifdef not |
| if (print && iflag) |
| out1fmt("%s\n", stackblock()); |
| #endif |
| return 0; |
| } |
| |
| #else |
| |
| |
| |
| STATIC int |
| docd(dest, print) |
| char *dest; |
| { |
| register char *p; |
| register char *q; |
| char *symlink; |
| char *component; |
| struct stat statb; |
| int first; |
| int i; |
| |
| TRACE(("docd(\"%s\", %d) called\n", dest, print)); |
| #if UDIR |
| if (didudir) |
| print = 1; |
| #endif |
| |
| top: |
| cdcomppath = dest; |
| STARTSTACKSTR(p); |
| if (*dest == '/') { |
| STPUTC('/', p); |
| cdcomppath++; |
| } |
| first = 1; |
| while ((q = getcomponent()) != NULL) { |
| if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') |
| continue; |
| if (! first) |
| STPUTC('/', p); |
| first = 0; |
| component = q; |
| while (*q) |
| STPUTC(*q++, p); |
| if (equal(component, "..")) |
| continue; |
| STACKSTRNUL(p); |
| if (lstat(stackblock(), &statb) < 0) |
| error("lstat %s failed", stackblock()); |
| if ((statb.st_mode & S_IFMT) != S_IFLNK) |
| continue; |
| |
| /* Hit a symbolic link. We have to start all over again. */ |
| print = 1; |
| STPUTC('\0', p); |
| symlink = grabstackstr(p); |
| i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ |
| if (cdcomppath != NULL) |
| i += strlen(cdcomppath); |
| p = stalloc(i); |
| if (readlink(symlink, p, (int)statb.st_size) < 0) { |
| error("readlink %s failed", stackblock()); |
| } |
| if (cdcomppath != NULL) { |
| p[(int)statb.st_size] = '/'; |
| scopy(cdcomppath, p + (int)statb.st_size + 1); |
| } else { |
| p[(int)statb.st_size] = '\0'; |
| } |
| if (p[0] != '/') { /* relative path name */ |
| char *r; |
| q = r = symlink; |
| while (*q) { |
| if (*q++ == '/') |
| r = q; |
| } |
| *r = '\0'; |
| dest = stalloc(strlen(symlink) + strlen(p) + 1); |
| scopy(symlink, dest); |
| strcat(dest, p); |
| } else { |
| dest = p; |
| } |
| goto top; |
| } |
| STPUTC('\0', p); |
| p = grabstackstr(p); |
| INTOFF; |
| if (p[0] != '\0' && chdir(p) < 0) { |
| INTON; |
| return -1; |
| } |
| updatepwd(p); |
| INTON; |
| #ifdef not |
| if (print && iflag) |
| out1fmt("%s\n", p); |
| #endif |
| return 0; |
| } |
| #endif /* SYMLINKS */ |
| |
| |
| |
| /* |
| * Get the next component of the path name pointed to by cdcomppath. |
| * This routine overwrites the string pointed to by cdcomppath. |
| */ |
| |
| STATIC char * |
| getcomponent() { |
| register char *p; |
| char *start; |
| |
| if ((p = cdcomppath) == NULL) |
| return NULL; |
| start = cdcomppath; |
| while (*p != '/' && *p != '\0') |
| p++; |
| if (*p == '\0') { |
| cdcomppath = NULL; |
| } else { |
| *p++ = '\0'; |
| cdcomppath = p; |
| } |
| return start; |
| } |
| |
| |
| |
| /* |
| * Update curdir (the name of the current directory) in response to a |
| * cd command. We also call hashcd to let the routines in exec.c know |
| * that the current directory has changed. |
| */ |
| |
| void hashcd(); |
| |
| STATIC void |
| updatepwd(dir) |
| char *dir; |
| { |
| char *new; |
| char *p; |
| |
| hashcd(); /* update command hash table */ |
| cdcomppath = stalloc(strlen(dir) + 1); |
| scopy(dir, cdcomppath); |
| STARTSTACKSTR(new); |
| if (*dir != '/') { |
| if (curdir == NULL) |
| return; |
| p = curdir; |
| while (*p) |
| STPUTC(*p++, new); |
| if (p[-1] == '/') |
| STUNPUTC(new); |
| } |
| while ((p = getcomponent()) != NULL) { |
| if (equal(p, "..")) { |
| while (new > stackblock() && (STUNPUTC(new), *new) != '/'); |
| } else if (*p != '\0' && ! equal(p, ".")) { |
| STPUTC('/', new); |
| while (*p) |
| STPUTC(*p++, new); |
| } |
| } |
| if (new == stackblock()) |
| STPUTC('/', new); |
| STACKSTRNUL(new); |
| if (curdir) |
| ckfree(curdir); |
| curdir = savestr(stackblock()); |
| } |
| |
| |
| |
| int |
| pwdcmd(argc, argv) char **argv; { |
| getpwd(); |
| out1str(curdir); |
| out1c('\n'); |
| return 0; |
| } |
| |
| |
| |
| /* |
| * Run /bin/pwd to find out what the current directory is. We suppress |
| * interrupts throughout most of this, but the user can still break out |
| * of it by killing the pwd program. If we already know the current |
| * directory, this routine returns immediately. |
| */ |
| |
| #define MAXPWD 256 |
| |
| STATIC void |
| getpwd() { |
| char buf[MAXPWD]; |
| char *p; |
| int i; |
| int status; |
| struct job *jp; |
| int pip[2]; |
| |
| if (curdir) |
| return; |
| INTOFF; |
| if (pipe(pip) < 0) |
| error("Pipe call failed"); |
| jp = makejob((union node *)NULL, 1); |
| if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { |
| close(pip[0]); |
| if (pip[1] != 1) { |
| close(1); |
| copyfd(pip[1], 1); |
| close(pip[1]); |
| } |
| execl("/bin/pwd", "pwd", (char *)0); |
| /* error("Cannot exec /bin/pwd");*/ |
| out2str("Cannot exec /bin/pwd\n"); /* 22 Aug 92*/ |
| flushall(); |
| _exit(1); |
| } |
| close(pip[1]); |
| pip[1] = -1; |
| p = buf; |
| while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 |
| || i == -1 && errno == EINTR) { |
| if (i > 0) |
| p += i; |
| } |
| close(pip[0]); |
| pip[0] = -1; |
| status = waitforjob(jp); |
| if (status != 0) |
| error((char *)0); |
| if (i < 0 || p == buf || p[-1] != '\n') |
| error("pwd command failed"); |
| p[-1] = '\0'; |
| curdir = savestr(buf); |
| INTON; |
| } |