cd.c revision 99110
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836150Scharnier#if 0 3936150Scharnierstatic char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 4036150Scharnier#endif 411556Srgrimes#endif /* not lint */ 4299110Sobrien#include <sys/cdefs.h> 4399110Sobrien__FBSDID("$FreeBSD: head/bin/sh/cd.c 99110 2002-06-30 05:15:05Z obrien $"); 441556Srgrimes 4517987Speter#include <sys/types.h> 4617987Speter#include <sys/stat.h> 4717987Speter#include <stdlib.h> 4820425Ssteve#include <string.h> 4917987Speter#include <unistd.h> 5017987Speter#include <errno.h> 5117987Speter 521556Srgrimes/* 531556Srgrimes * The cd and pwd commands. 541556Srgrimes */ 551556Srgrimes 561556Srgrimes#include "shell.h" 571556Srgrimes#include "var.h" 581556Srgrimes#include "nodes.h" /* for jobs.h */ 591556Srgrimes#include "jobs.h" 601556Srgrimes#include "options.h" 611556Srgrimes#include "output.h" 621556Srgrimes#include "memalloc.h" 631556Srgrimes#include "error.h" 6420425Ssteve#include "exec.h" 6517987Speter#include "redir.h" 661556Srgrimes#include "mystring.h" 6717987Speter#include "show.h" 6820425Ssteve#include "cd.h" 691556Srgrimes 7097092StjrSTATIC int cdlogical(char *); 7197092StjrSTATIC int cdphysical(char *); 7297092StjrSTATIC int docd(char *, int, int); 7390111SimpSTATIC char *getcomponent(void); 7497092StjrSTATIC int updatepwd(char *); 751556Srgrimes 7620425Sstevechar *curdir = NULL; /* current working directory */ 771556Srgrimeschar *prevdir; /* previous working directory */ 781556SrgrimesSTATIC char *cdcomppath; 791556Srgrimes 801556Srgrimesint 8197092Stjrcdcmd(int argc, char **argv) 8217987Speter{ 831556Srgrimes char *dest; 841556Srgrimes char *path; 851556Srgrimes char *p; 861556Srgrimes struct stat statb; 8797092Stjr int ch, phys, print = 0; 881556Srgrimes 8997092Stjr optreset = 1; optind = 1; /* initialize getopt */ 9097092Stjr phys = 0; 9197092Stjr while ((ch = getopt(argc, argv, "LP")) != -1) { 9297092Stjr switch (ch) { 9397092Stjr case 'L': 9497092Stjr phys = 0; 9597092Stjr break; 9697092Stjr case 'P': 9797092Stjr phys = 1; 9897092Stjr break; 9997092Stjr default: 10097092Stjr error("unknown option: -%c", optopt); 10197092Stjr break; 10297092Stjr } 10397092Stjr } 10497092Stjr argc -= optind; 10597092Stjr argv += optind; 10697092Stjr 10797092Stjr if (argc > 1) 10897092Stjr error("too many arguments"); 10997092Stjr 11097092Stjr if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 1111556Srgrimes error("HOME not set"); 1125234Sbde if (*dest == '\0') 1135234Sbde dest = "."; 1141556Srgrimes if (dest[0] == '-' && dest[1] == '\0') { 1151556Srgrimes dest = prevdir ? prevdir : curdir; 11612273Speter if (dest) 11712273Speter print = 1; 11812273Speter else 11912273Speter dest = "."; 1201556Srgrimes } 1211556Srgrimes if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 1221556Srgrimes path = nullstr; 1231556Srgrimes while ((p = padvance(&path, dest)) != NULL) { 12417987Speter if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 1251556Srgrimes if (!print) { 1261556Srgrimes /* 1271556Srgrimes * XXX - rethink 1281556Srgrimes */ 12938886Stegge if (p[0] == '.' && p[1] == '/' && p[2] != '\0') 1301556Srgrimes p += 2; 1311556Srgrimes print = strcmp(p, dest); 1321556Srgrimes } 13397092Stjr if (docd(p, print, phys) >= 0) 1341556Srgrimes return 0; 1351556Srgrimes } 1361556Srgrimes } 1371556Srgrimes error("can't cd to %s", dest); 13817987Speter /*NOTREACHED*/ 13917987Speter return 0; 1401556Srgrimes} 1411556Srgrimes 1421556Srgrimes 1431556Srgrimes/* 14497092Stjr * Actually change the directory. In an interactive shell, print the 14520774Ssteve * directory name if "print" is nonzero. 1461556Srgrimes */ 1471556SrgrimesSTATIC int 14897092Stjrdocd(char *dest, int print, int phys) 14920425Ssteve{ 15097092Stjr 15197092Stjr TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); 15297092Stjr 15397092Stjr /* If logical cd fails, fall back to physical. */ 15497092Stjr if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0) 15597092Stjr return (-1); 15697092Stjr 15797092Stjr if (print && iflag && curdir) 15897092Stjr out1fmt("%s\n", curdir); 15997092Stjr 16097092Stjr return 0; 16197092Stjr} 16297092Stjr 16397092StjrSTATIC int 16497092Stjrcdlogical(char *dest) 16597092Stjr{ 16638886Stegge char *p; 16738886Stegge char *q; 16838886Stegge char *component; 16938886Stegge struct stat statb; 17038886Stegge int first; 17138886Stegge int badstat; 17220425Ssteve 17338886Stegge /* 17438886Stegge * Check each component of the path. If we find a symlink or 17538886Stegge * something we can't stat, clear curdir to force a getcwd() 17638886Stegge * next time we get the value of the current directory. 17738886Stegge */ 17838886Stegge badstat = 0; 17938886Stegge cdcomppath = stalloc(strlen(dest) + 1); 18038886Stegge scopy(dest, cdcomppath); 18138886Stegge STARTSTACKSTR(p); 18238886Stegge if (*dest == '/') { 18338886Stegge STPUTC('/', p); 18438886Stegge cdcomppath++; 18538886Stegge } 18638886Stegge first = 1; 18738886Stegge while ((q = getcomponent()) != NULL) { 18838886Stegge if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 18938886Stegge continue; 19038886Stegge if (! first) 19138886Stegge STPUTC('/', p); 19238886Stegge first = 0; 19338886Stegge component = q; 19438886Stegge while (*q) 19538886Stegge STPUTC(*q++, p); 19638886Stegge if (equal(component, "..")) 19738886Stegge continue; 19838886Stegge STACKSTRNUL(p); 19997092Stjr if (lstat(stackblock(), &statb) < 0) { 20038886Stegge badstat = 1; 20138886Stegge break; 20238886Stegge } 20338886Stegge } 20438886Stegge 2051556Srgrimes INTOFF; 20697092Stjr if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) { 2071556Srgrimes INTON; 20897092Stjr return (-1); 2091556Srgrimes } 2101556Srgrimes INTON; 21197092Stjr return (0); 2121556Srgrimes} 2131556Srgrimes 21497092StjrSTATIC int 21597092Stjrcdphysical(char *dest) 21697092Stjr{ 2171556Srgrimes 21897092Stjr INTOFF; 21997092Stjr if (chdir(dest) < 0 || updatepwd(NULL) < 0) { 22097092Stjr INTON; 22197092Stjr return (-1); 22297092Stjr } 22397092Stjr INTON; 22497092Stjr return (0); 22597092Stjr} 22697092Stjr 2271556Srgrimes/* 2281556Srgrimes * Get the next component of the path name pointed to by cdcomppath. 2291556Srgrimes * This routine overwrites the string pointed to by cdcomppath. 2301556Srgrimes */ 2311556SrgrimesSTATIC char * 23290111Simpgetcomponent(void) 23320774Ssteve{ 23425222Ssteve char *p; 2351556Srgrimes char *start; 2361556Srgrimes 2371556Srgrimes if ((p = cdcomppath) == NULL) 2381556Srgrimes return NULL; 2391556Srgrimes start = cdcomppath; 2401556Srgrimes while (*p != '/' && *p != '\0') 2411556Srgrimes p++; 2421556Srgrimes if (*p == '\0') { 2431556Srgrimes cdcomppath = NULL; 2441556Srgrimes } else { 2451556Srgrimes *p++ = '\0'; 2461556Srgrimes cdcomppath = p; 2471556Srgrimes } 2481556Srgrimes return start; 2491556Srgrimes} 2501556Srgrimes 2511556Srgrimes 2521556Srgrimes/* 25338886Stegge * Update curdir (the name of the current directory) in response to a 25438886Stegge * cd command. We also call hashcd to let the routines in exec.c know 25538886Stegge * that the current directory has changed. 2561556Srgrimes */ 25797092StjrSTATIC int 25890111Simpupdatepwd(char *dir) 25920774Ssteve{ 2601556Srgrimes char *new; 2611556Srgrimes char *p; 2621556Srgrimes 26338886Stegge hashcd(); /* update command hash table */ 26438886Stegge 26538886Stegge /* 26638886Stegge * If our argument is NULL, we don't know the current directory 26738886Stegge * any more because we traversed a symbolic link or something 26838886Stegge * we couldn't stat(). 26938886Stegge */ 27038886Stegge if (dir == NULL || curdir == NULL) { 27138886Stegge if (prevdir) 27238886Stegge ckfree(prevdir); 27338886Stegge INTOFF; 27438886Stegge prevdir = curdir; 27538886Stegge curdir = NULL; 27697092Stjr if (getpwd() == NULL) { 27797092Stjr INTON; 27897092Stjr return (-1); 27997092Stjr } 28086176Stegge setvar("PWD", curdir, VEXPORT); 28186176Stegge setvar("OLDPWD", prevdir, VEXPORT); 28238886Stegge INTON; 28397092Stjr return (0); 28438886Stegge } 2851556Srgrimes cdcomppath = stalloc(strlen(dir) + 1); 2861556Srgrimes scopy(dir, cdcomppath); 2871556Srgrimes STARTSTACKSTR(new); 2881556Srgrimes if (*dir != '/') { 2891556Srgrimes p = curdir; 2901556Srgrimes while (*p) 2911556Srgrimes STPUTC(*p++, new); 2921556Srgrimes if (p[-1] == '/') 2931556Srgrimes STUNPUTC(new); 2941556Srgrimes } 2951556Srgrimes while ((p = getcomponent()) != NULL) { 2961556Srgrimes if (equal(p, "..")) { 2971556Srgrimes while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 2981556Srgrimes } else if (*p != '\0' && ! equal(p, ".")) { 2991556Srgrimes STPUTC('/', new); 3001556Srgrimes while (*p) 3011556Srgrimes STPUTC(*p++, new); 3021556Srgrimes } 3031556Srgrimes } 3041556Srgrimes if (new == stackblock()) 3051556Srgrimes STPUTC('/', new); 3061556Srgrimes STACKSTRNUL(new); 30738886Stegge INTOFF; 30838886Stegge if (prevdir) 30938886Stegge ckfree(prevdir); 31038886Stegge prevdir = curdir; 31138886Stegge curdir = savestr(stackblock()); 31286176Stegge setvar("PWD", curdir, VEXPORT); 31386176Stegge setvar("OLDPWD", prevdir, VEXPORT); 31438886Stegge INTON; 31597092Stjr 31697092Stjr return (0); 3171556Srgrimes} 3181556Srgrimes 31997092Stjr#define MAXPWD 256 3201556Srgrimes 3211556Srgrimesint 32290111Simppwdcmd(int argc __unused, char **argv __unused) 32317987Speter{ 32497092Stjr char buf[MAXPWD]; 32597092Stjr int ch, phys; 3261556Srgrimes 32797092Stjr optreset = 1; optind = 1; /* initialize getopt */ 32897092Stjr phys = 0; 32997092Stjr while ((ch = getopt(argc, argv, "LP")) != -1) { 33097092Stjr switch (ch) { 33197092Stjr case 'L': 33297092Stjr phys = 0; 33397092Stjr break; 33497092Stjr case 'P': 33597092Stjr phys = 1; 33697092Stjr break; 33797092Stjr default: 33897092Stjr error("unknown option: -%c", optopt); 33997092Stjr break; 34097092Stjr } 34197092Stjr } 34297092Stjr argc -= optind; 34397092Stjr argv += optind; 3441556Srgrimes 34597092Stjr if (argc != 0) 34697092Stjr error("too many arguments"); 34738886Stegge 34897092Stjr if (!phys && getpwd()) { 34997092Stjr out1str(curdir); 35097092Stjr out1c('\n'); 35197092Stjr } else { 35297092Stjr if (getcwd(buf, sizeof(buf)) == NULL) 35397092Stjr error(".: %s", strerror(errno)); 35497092Stjr out1str(buf); 35597092Stjr out1c('\n'); 35697092Stjr } 35738886Stegge 35897092Stjr return 0; 35997092Stjr} 36038886Stegge 3611556Srgrimes/* 36220774Ssteve * Find out what the current directory is. If we already know the current 3631556Srgrimes * directory, this routine returns immediately. 3641556Srgrimes */ 36520774Sstevechar * 36690111Simpgetpwd(void) 36720425Ssteve{ 36838886Stegge char buf[MAXPWD]; 36938886Stegge 3701556Srgrimes if (curdir) 37138886Stegge return curdir; 37238886Stegge /* 37338886Stegge * Things are a bit complicated here; we could have just used 37438886Stegge * getcwd, but traditionally getcwd is implemented using popen 37538886Stegge * to /bin/pwd. This creates a problem for us, since we cannot 37638886Stegge * keep track of the job if it is being ran behind our backs. 37738886Stegge * So we re-implement getcwd(), and we suppress interrupts 37838886Stegge * throughout the process. This is not completely safe, since 37938886Stegge * the user can still break out of it by killing the pwd program. 38038886Stegge * We still try to use getcwd for systems that we know have a 38138886Stegge * c implementation of getcwd, that does not open a pipe to 38238886Stegge * /bin/pwd. 38338886Stegge */ 38438886Stegge#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__SVR4) 38596948Sjmallett 38638886Stegge if (getcwd(buf, sizeof(buf)) == NULL) { 38738886Stegge char *pwd = getenv("PWD"); 38838886Stegge struct stat stdot, stpwd; 38938886Stegge 39038886Stegge if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && 39138886Stegge stat(pwd, &stpwd) != -1 && 39238886Stegge stdot.st_dev == stpwd.st_dev && 39338886Stegge stdot.st_ino == stpwd.st_ino) { 39438886Stegge curdir = savestr(pwd); 39538886Stegge return curdir; 39638886Stegge } 39738886Stegge return NULL; 39838886Stegge } 39938886Stegge curdir = savestr(buf); 40038886Stegge#else 40138886Stegge { 40238886Stegge char *p; 40338886Stegge int i; 40438886Stegge int status; 40538886Stegge struct job *jp; 40638886Stegge int pip[2]; 40738886Stegge 40838886Stegge INTOFF; 40938886Stegge if (pipe(pip) < 0) 41053891Scracauer error("Pipe call failed: %s", strerror(errno)); 41138886Stegge jp = makejob((union node *)NULL, 1); 41238886Stegge if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 41338886Stegge (void) close(pip[0]); 41438886Stegge if (pip[1] != 1) { 41538886Stegge close(1); 41638886Stegge copyfd(pip[1], 1); 41738886Stegge close(pip[1]); 41838886Stegge } 41938886Stegge (void) execl("/bin/pwd", "pwd", (char *)0); 42038886Stegge error("Cannot exec /bin/pwd"); 42138886Stegge } 42238886Stegge (void) close(pip[1]); 42338886Stegge pip[1] = -1; 42438886Stegge p = buf; 42538886Stegge while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 42638886Stegge || (i == -1 && errno == EINTR)) { 42738886Stegge if (i > 0) 42838886Stegge p += i; 42938886Stegge } 43038886Stegge (void) close(pip[0]); 43138886Stegge pip[0] = -1; 43238886Stegge status = waitforjob(jp); 43338886Stegge if (status != 0) 43438886Stegge error((char *)0); 43538886Stegge if (i < 0 || p == buf || p[-1] != '\n') 43638886Stegge error("pwd command failed"); 43738886Stegge p[-1] = '\0'; 43838886Stegge } 43938886Stegge curdir = savestr(buf); 44038886Stegge INTON; 44138886Stegge#endif 44238886Stegge return curdir; 4431556Srgrimes} 444