cd.c revision 20425
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. 353044Sdg * 3620425Ssteve * $Id: cd.c,v 1.6 1996/09/01 10:19:47 peter Exp $ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 4020425Sstevestatic char const sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 4317987Speter#include <sys/types.h> 4417987Speter#include <sys/stat.h> 4517987Speter#include <stdlib.h> 4620425Ssteve#include <string.h> 4717987Speter#include <unistd.h> 4817987Speter#include <errno.h> 4917987Speter 501556Srgrimes/* 511556Srgrimes * The cd and pwd commands. 521556Srgrimes */ 531556Srgrimes 541556Srgrimes#include "shell.h" 551556Srgrimes#include "var.h" 561556Srgrimes#include "nodes.h" /* for jobs.h */ 571556Srgrimes#include "jobs.h" 581556Srgrimes#include "options.h" 591556Srgrimes#include "output.h" 601556Srgrimes#include "memalloc.h" 611556Srgrimes#include "error.h" 6220425Ssteve#include "exec.h" 6317987Speter#include "redir.h" 641556Srgrimes#include "mystring.h" 6517987Speter#include "show.h" 6620425Ssteve#include "cd.h" 671556Srgrimes 6817987SpeterSTATIC int docd __P((char *, int)); 6917987SpeterSTATIC char *getcomponent __P((void)); 7017987SpeterSTATIC void updatepwd __P((char *)); 711556Srgrimes 7220425Sstevechar *curdir = NULL; /* current working directory */ 731556Srgrimeschar *prevdir; /* previous working directory */ 741556SrgrimesSTATIC char *cdcomppath; 751556Srgrimes 761556Srgrimesint 7717987Spetercdcmd(argc, argv) 7817987Speter int argc; 7920425Ssteve char **argv; 8017987Speter{ 811556Srgrimes char *dest; 821556Srgrimes char *path; 831556Srgrimes char *p; 841556Srgrimes struct stat statb; 851556Srgrimes int print = 0; 861556Srgrimes 871556Srgrimes nextopt(nullstr); 881556Srgrimes if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 891556Srgrimes error("HOME not set"); 905234Sbde if (*dest == '\0') 915234Sbde dest = "."; 921556Srgrimes if (dest[0] == '-' && dest[1] == '\0') { 931556Srgrimes dest = prevdir ? prevdir : curdir; 9412273Speter if (dest) 9512273Speter print = 1; 9612273Speter else 9712273Speter dest = "."; 981556Srgrimes } 991556Srgrimes if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 1001556Srgrimes path = nullstr; 1011556Srgrimes while ((p = padvance(&path, dest)) != NULL) { 10217987Speter if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 1031556Srgrimes if (!print) { 1041556Srgrimes /* 1051556Srgrimes * XXX - rethink 1061556Srgrimes */ 1071556Srgrimes if (p[0] == '.' && p[1] == '/') 1081556Srgrimes p += 2; 1091556Srgrimes print = strcmp(p, dest); 1101556Srgrimes } 1111556Srgrimes if (docd(p, print) >= 0) 1121556Srgrimes return 0; 1131556Srgrimes 1141556Srgrimes } 1151556Srgrimes } 1161556Srgrimes error("can't cd to %s", dest); 11717987Speter /*NOTREACHED*/ 11817987Speter return 0; 1191556Srgrimes} 1201556Srgrimes 1211556Srgrimes 1221556Srgrimes/* 1231556Srgrimes * Actually do the chdir. If the name refers to symbolic links, we 1241556Srgrimes * compute the actual directory name before doing the cd. In an 1251556Srgrimes * interactive shell, print the directory name if "print" is nonzero 1261556Srgrimes * or if the name refers to a symbolic link. We also print the name 1271556Srgrimes * if "/u/logname" was expanded in it, since this is similar to a 1281556Srgrimes * symbolic link. (The check for this breaks if the user gives the 1291556Srgrimes * cd command some additional, unused arguments.) 1301556Srgrimes */ 1311556Srgrimes 1321556Srgrimes#if SYMLINKS == 0 1331556SrgrimesSTATIC int 1341556Srgrimesdocd(dest, print) 1351556Srgrimes char *dest; 13617987Speter int print; 13720425Ssteve{ 13820425Ssteve 13920425Ssteve TRACE(("docd(\"%s\", %d) called\n", dest, print)); 1401556Srgrimes INTOFF; 1411556Srgrimes if (chdir(dest) < 0) { 1421556Srgrimes INTON; 1431556Srgrimes return -1; 1441556Srgrimes } 1451556Srgrimes updatepwd(dest); 1461556Srgrimes INTON; 1471556Srgrimes if (print && iflag) 1481556Srgrimes out1fmt("%s\n", stackblock()); 1491556Srgrimes return 0; 1501556Srgrimes} 1511556Srgrimes 1521556Srgrimes#else 1531556Srgrimes 1541556Srgrimes 1551556Srgrimes 1561556SrgrimesSTATIC int 1571556Srgrimesdocd(dest, print) 1581556Srgrimes char *dest; 15917987Speter int print; 16017987Speter{ 1611556Srgrimes register char *p; 1621556Srgrimes register char *q; 1631556Srgrimes char *symlink; 1641556Srgrimes char *component; 1651556Srgrimes struct stat statb; 1661556Srgrimes int first; 1671556Srgrimes int i; 1681556Srgrimes 1691556Srgrimes TRACE(("docd(\"%s\", %d) called\n", dest, print)); 1701556Srgrimes 1711556Srgrimestop: 1721556Srgrimes cdcomppath = dest; 1731556Srgrimes STARTSTACKSTR(p); 1741556Srgrimes if (*dest == '/') { 1751556Srgrimes STPUTC('/', p); 1761556Srgrimes cdcomppath++; 1771556Srgrimes } 1781556Srgrimes first = 1; 1791556Srgrimes while ((q = getcomponent()) != NULL) { 18017987Speter if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 1811556Srgrimes continue; 1821556Srgrimes if (! first) 1831556Srgrimes STPUTC('/', p); 1841556Srgrimes first = 0; 1851556Srgrimes component = q; 1861556Srgrimes while (*q) 1871556Srgrimes STPUTC(*q++, p); 1881556Srgrimes if (equal(component, "..")) 1891556Srgrimes continue; 1901556Srgrimes STACKSTRNUL(p); 1911556Srgrimes if (lstat(stackblock(), &statb) < 0) 1921556Srgrimes error("lstat %s failed", stackblock()); 19317987Speter if (!S_ISLNK(statb.st_mode)) 1941556Srgrimes continue; 1951556Srgrimes 1961556Srgrimes /* Hit a symbolic link. We have to start all over again. */ 1971556Srgrimes print = 1; 1981556Srgrimes STPUTC('\0', p); 1991556Srgrimes symlink = grabstackstr(p); 2001556Srgrimes i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ 2011556Srgrimes if (cdcomppath != NULL) 2021556Srgrimes i += strlen(cdcomppath); 2031556Srgrimes p = stalloc(i); 2041556Srgrimes if (readlink(symlink, p, (int)statb.st_size) < 0) { 2051556Srgrimes error("readlink %s failed", stackblock()); 2061556Srgrimes } 2071556Srgrimes if (cdcomppath != NULL) { 2081556Srgrimes p[(int)statb.st_size] = '/'; 2091556Srgrimes scopy(cdcomppath, p + (int)statb.st_size + 1); 2101556Srgrimes } else { 2111556Srgrimes p[(int)statb.st_size] = '\0'; 2121556Srgrimes } 2131556Srgrimes if (p[0] != '/') { /* relative path name */ 2141556Srgrimes char *r; 2151556Srgrimes q = r = symlink; 2161556Srgrimes while (*q) { 2171556Srgrimes if (*q++ == '/') 2181556Srgrimes r = q; 2191556Srgrimes } 2201556Srgrimes *r = '\0'; 2211556Srgrimes dest = stalloc(strlen(symlink) + strlen(p) + 1); 2221556Srgrimes scopy(symlink, dest); 2231556Srgrimes strcat(dest, p); 2241556Srgrimes } else { 2251556Srgrimes dest = p; 2261556Srgrimes } 2271556Srgrimes goto top; 2281556Srgrimes } 2291556Srgrimes STPUTC('\0', p); 2301556Srgrimes p = grabstackstr(p); 2311556Srgrimes INTOFF; 2325234Sbde if (chdir(*p ? p : ".") < 0) { 2331556Srgrimes INTON; 2341556Srgrimes return -1; 2351556Srgrimes } 2361556Srgrimes updatepwd(p); 2371556Srgrimes INTON; 2381556Srgrimes if (print && iflag) 2391556Srgrimes out1fmt("%s\n", p); 2401556Srgrimes return 0; 2411556Srgrimes} 2421556Srgrimes#endif /* SYMLINKS */ 2431556Srgrimes 2441556Srgrimes 2451556Srgrimes 2461556Srgrimes/* 2471556Srgrimes * Get the next component of the path name pointed to by cdcomppath. 2481556Srgrimes * This routine overwrites the string pointed to by cdcomppath. 2491556Srgrimes */ 2501556Srgrimes 2511556SrgrimesSTATIC char * 2521556Srgrimesgetcomponent() { 2531556Srgrimes register char *p; 2541556Srgrimes char *start; 2551556Srgrimes 2561556Srgrimes if ((p = cdcomppath) == NULL) 2571556Srgrimes return NULL; 2581556Srgrimes start = cdcomppath; 2591556Srgrimes while (*p != '/' && *p != '\0') 2601556Srgrimes p++; 2611556Srgrimes if (*p == '\0') { 2621556Srgrimes cdcomppath = NULL; 2631556Srgrimes } else { 2641556Srgrimes *p++ = '\0'; 2651556Srgrimes cdcomppath = p; 2661556Srgrimes } 2671556Srgrimes return start; 2681556Srgrimes} 2691556Srgrimes 2701556Srgrimes 2711556Srgrimes 2721556Srgrimes/* 2731556Srgrimes * Update curdir (the name of the current directory) in response to a 2741556Srgrimes * cd command. We also call hashcd to let the routines in exec.c know 2751556Srgrimes * that the current directory has changed. 2761556Srgrimes */ 2771556Srgrimes 2781556Srgrimesvoid hashcd(); 2791556Srgrimes 2801556SrgrimesSTATIC void 2811556Srgrimesupdatepwd(dir) 2821556Srgrimes char *dir; 2831556Srgrimes { 2841556Srgrimes char *new; 2851556Srgrimes char *p; 2861556Srgrimes 2871556Srgrimes hashcd(); /* update command hash table */ 2881556Srgrimes cdcomppath = stalloc(strlen(dir) + 1); 2891556Srgrimes scopy(dir, cdcomppath); 2901556Srgrimes STARTSTACKSTR(new); 2911556Srgrimes if (*dir != '/') { 2921556Srgrimes if (curdir == NULL) 2931556Srgrimes return; 2941556Srgrimes p = curdir; 2951556Srgrimes while (*p) 2961556Srgrimes STPUTC(*p++, new); 2971556Srgrimes if (p[-1] == '/') 2981556Srgrimes STUNPUTC(new); 2991556Srgrimes } 3001556Srgrimes while ((p = getcomponent()) != NULL) { 3011556Srgrimes if (equal(p, "..")) { 3021556Srgrimes while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 3031556Srgrimes } else if (*p != '\0' && ! equal(p, ".")) { 3041556Srgrimes STPUTC('/', new); 3051556Srgrimes while (*p) 3061556Srgrimes STPUTC(*p++, new); 3071556Srgrimes } 3081556Srgrimes } 3091556Srgrimes if (new == stackblock()) 3101556Srgrimes STPUTC('/', new); 3111556Srgrimes STACKSTRNUL(new); 3121556Srgrimes INTOFF; 3131556Srgrimes if (prevdir) 3141556Srgrimes ckfree(prevdir); 3151556Srgrimes prevdir = curdir; 3161556Srgrimes curdir = savestr(stackblock()); 3171556Srgrimes INTON; 3181556Srgrimes} 3191556Srgrimes 3201556Srgrimes 3211556Srgrimes 3221556Srgrimesint 32317987Speterpwdcmd(argc, argv) 32417987Speter int argc; 32520425Ssteve char **argv; 32617987Speter{ 3271556Srgrimes getpwd(); 3281556Srgrimes out1str(curdir); 3291556Srgrimes out1c('\n'); 3301556Srgrimes return 0; 3311556Srgrimes} 3321556Srgrimes 3331556Srgrimes 3341556Srgrimes 33520425Ssteve 33620425Ssteve#define MAXPWD 256 33720425Ssteve 3381556Srgrimes/* 3391556Srgrimes * Run /bin/pwd to find out what the current directory is. We suppress 3401556Srgrimes * interrupts throughout most of this, but the user can still break out 3411556Srgrimes * of it by killing the pwd program. If we already know the current 3421556Srgrimes * directory, this routine returns immediately. 3431556Srgrimes */ 34420425Sstevevoid 34520425Sstevegetpwd() 34620425Ssteve{ 3471556Srgrimes char buf[MAXPWD]; 3481556Srgrimes char *p; 3491556Srgrimes int i; 3501556Srgrimes int status; 3511556Srgrimes struct job *jp; 3521556Srgrimes int pip[2]; 3534192Sjkh char *pwd_bin = "/bin/pwd"; 3541556Srgrimes 3551556Srgrimes if (curdir) 3561556Srgrimes return; 3571556Srgrimes INTOFF; 3581556Srgrimes if (pipe(pip) < 0) 3591556Srgrimes error("Pipe call failed"); 3604192Sjkh /* make a fall-back guess, otherwise we're simply screwed */ 3614192Sjkh if (access(pwd_bin, X_OK) == -1) 3624192Sjkh pwd_bin = "/stand/pwd"; 3631556Srgrimes jp = makejob((union node *)NULL, 1); 3641556Srgrimes if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 3651556Srgrimes close(pip[0]); 3661556Srgrimes if (pip[1] != 1) { 3671556Srgrimes close(1); 3681556Srgrimes copyfd(pip[1], 1); 3691556Srgrimes close(pip[1]); 3701556Srgrimes } 37120425Ssteve (void) execl(pwd_bin, "pwd", (char *)0); 3724192Sjkh error("Cannot exec %s", pwd_bin); 3731556Srgrimes } 37420425Ssteve (void) close(pip[1]); 3751556Srgrimes pip[1] = -1; 3761556Srgrimes p = buf; 3771556Srgrimes while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 37817987Speter || (i == -1 && errno == EINTR)) { 3791556Srgrimes if (i > 0) 3801556Srgrimes p += i; 3811556Srgrimes } 38220425Ssteve (void) close(pip[0]); 3831556Srgrimes pip[0] = -1; 3841556Srgrimes status = waitforjob(jp); 3851556Srgrimes if (status != 0) 3861556Srgrimes error((char *)0); 3871556Srgrimes if (i < 0 || p == buf || p[-1] != '\n') 3881556Srgrimes error("pwd command failed"); 3891556Srgrimes p[-1] = '\0'; 3901556Srgrimes curdir = savestr(buf); 3911556Srgrimes INTON; 3921556Srgrimes} 393