cd.c revision 230095
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 4207944Sjilles * 5207944Sjilles * 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 * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 331556Srgrimes#ifndef lint 341556Srgrimes#if 0 351556Srgrimesstatic char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 3636150Scharnier#endif 3736150Scharnier#endif /* not lint */ 3836150Scharnier#include <sys/cdefs.h> 391556Srgrimes__FBSDID("$FreeBSD: head/bin/sh/cd.c 230095 2012-01-13 23:32:27Z jilles $"); 4099110Sobrien 4199110Sobrien#include <sys/types.h> 421556Srgrimes#include <sys/stat.h> 4317987Speter#include <stdlib.h> 4417987Speter#include <string.h> 4517987Speter#include <unistd.h> 46213775Sjhb#include <errno.h> 4717987Speter#include <limits.h> 48213775Sjhb 49213775Sjhb/* 5017987Speter * The cd and pwd commands. 51213775Sjhb */ 5217987Speter 53108286Stjr#include "shell.h" 54213775Sjhb#include "var.h" 5517987Speter#include "nodes.h" /* for jobs.h */ 561556Srgrimes#include "jobs.h" 571556Srgrimes#include "options.h" 581556Srgrimes#include "output.h" 591556Srgrimes#include "memalloc.h" 601556Srgrimes#include "error.h" 611556Srgrimes#include "exec.h" 621556Srgrimes#include "redir.h" 631556Srgrimes#include "mystring.h" 641556Srgrimes#include "show.h" 651556Srgrimes#include "cd.h" 661556Srgrimes#include "builtins.h" 671556Srgrimes 681556Srgrimesstatic int cdlogical(char *); 691556Srgrimesstatic int cdphysical(char *); 701556Srgrimesstatic int docd(char *, int, int); 711556Srgrimesstatic char *getcomponent(void); 721556Srgrimesstatic char *findcwd(char *); 731556Srgrimesstatic void updatepwd(char *); 741556Srgrimesstatic char *getpwd(void); 751556Srgrimesstatic char *getpwd2(void); 7617987Speter 7717987Speterstatic char *curdir = NULL; /* current working directory */ 781556Srgrimesstatic char *prevdir; /* previous working directory */ 791556Srgrimesstatic char *cdcomppath; 801556Srgrimes 811556Srgrimesint 821556Srgrimescdcmd(int argc, char **argv) 831556Srgrimes{ 841556Srgrimes const char *dest; 851556Srgrimes const char *path; 861556Srgrimes char *p; 871556Srgrimes struct stat statb; 88194975Sjilles int ch, phys, print = 0, getcwderr = 0; 891556Srgrimes int rc; 901556Srgrimes int errno1 = ENOENT; 911556Srgrimes 92213760Sobrien optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 93213760Sobrien phys = Pflag; 94213760Sobrien while ((ch = getopt(argc, argv, "eLP")) != -1) { 95213760Sobrien switch (ch) { 96213760Sobrien case 'e': 971556Srgrimes getcwderr = 1; 98213811Sobrien break; 99213811Sobrien case 'L': 100213811Sobrien phys = 0; 101214524Sjilles break; 102213811Sobrien case 'P': 103213811Sobrien phys = 1; 104213811Sobrien break; 105213811Sobrien default: 106213811Sobrien error("unknown option: -%c", optopt); 107213811Sobrien break; 108213811Sobrien } 109213811Sobrien } 110213811Sobrien argc -= optind; 111213811Sobrien argv += optind; 112213811Sobrien 113213811Sobrien if (argc > 1) 114213811Sobrien error("too many arguments"); 1151556Srgrimes 116213811Sobrien if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 117118374Sache error("HOME not set"); 11819281Sache if (*dest == '\0') 11919281Sache dest = "."; 12019281Sache if (dest[0] == '-' && dest[1] == '\0') { 12119281Sache dest = prevdir ? prevdir : curdir; 12219281Sache if (dest) 123118374Sache print = 1; 12419281Sache else 12519281Sache dest = "."; 1261556Srgrimes } 1271556Srgrimes if (dest[0] == '/' || 12890111Simp (dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) || 12990111Simp (dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) || 1301556Srgrimes (path = bltinlookup("CDPATH", 1)) == NULL) 1311556Srgrimes path = nullstr; 1321556Srgrimes while ((p = padvance(&path, dest)) != NULL) { 13390111Simp if (stat(p, &statb) < 0) { 13490111Simp if (errno != ENOENT) 1351556Srgrimes errno1 = errno; 1361556Srgrimes } else if (!S_ISDIR(statb.st_mode)) 13739137Stegge errno1 = ENOTDIR; 1381556Srgrimes else { 1391556Srgrimes if (!print) { 1401556Srgrimes /* 1411556Srgrimes * XXX - rethink 142212243Sjilles */ 143212243Sjilles if (p[0] == '.' && p[1] == '/' && p[2] != '\0') 144212243Sjilles print = strcmp(p + 2, dest); 145212243Sjilles else 146212243Sjilles print = strcmp(p, dest); 147212243Sjilles } 148212243Sjilles rc = docd(p, print, phys); 149212243Sjilles if (rc >= 0) 150212243Sjilles return getcwderr ? rc : 0; 151212243Sjilles if (errno != ENOENT) 152212243Sjilles errno1 = errno; 1531556Srgrimes } 1541556Srgrimes } 15590111Simp error("%s: %s", dest, strerror(errno1)); 15617987Speter /*NOTREACHED*/ 1571556Srgrimes return 0; 1581556Srgrimes} 1591556Srgrimes 1601556Srgrimes 1611556Srgrimes/* 1621556Srgrimes * Actually change the directory. In an interactive shell, print the 1631556Srgrimes * directory name if "print" is nonzero. 1641556Srgrimes */ 1651556Srgrimesstatic int 1661556Srgrimesdocd(char *dest, int print, int phys) 1671556Srgrimes{ 1681556Srgrimes int rc; 1691556Srgrimes 1701556Srgrimes TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); 1711556Srgrimes 1721556Srgrimes /* If logical cd fails, fall back to physical. */ 1731556Srgrimes if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0) 1741556Srgrimes return (-1); 1751556Srgrimes 1761556Srgrimes if (print && iflag && curdir) 1771556Srgrimes out1fmt("%s\n", curdir); 1781556Srgrimes 1791556Srgrimes return (rc); 1801556Srgrimes} 1811556Srgrimes 1821556Srgrimesstatic int 1831556Srgrimescdlogical(char *dest) 1841556Srgrimes{ 1851556Srgrimes char *p; 1861556Srgrimes char *q; 1871556Srgrimes char *component; 1881556Srgrimes struct stat statb; 1891556Srgrimes int first; 1901556Srgrimes int badstat; 1911556Srgrimes 1921556Srgrimes /* 1931556Srgrimes * Check each component of the path. If we find a symlink or 1941556Srgrimes * something we can't stat, clear curdir to force a getcwd() 1951556Srgrimes * next time we get the value of the current directory. 1961556Srgrimes */ 1971556Srgrimes badstat = 0; 1981556Srgrimes cdcomppath = stalloc(strlen(dest) + 1); 1991556Srgrimes scopy(dest, cdcomppath); 2001556Srgrimes STARTSTACKSTR(p); 2011556Srgrimes if (*dest == '/') { 2021556Srgrimes STPUTC('/', p); 2031556Srgrimes cdcomppath++; 2041556Srgrimes } 205212243Sjilles first = 1; 206212243Sjilles while ((q = getcomponent()) != NULL) { 207212243Sjilles if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 208212243Sjilles continue; 209212243Sjilles if (! first) 210212243Sjilles STPUTC('/', p); 211212243Sjilles first = 0; 2121556Srgrimes component = q; 213213811Sobrien STPUTS(q, p); 21490111Simp if (equal(component, "..")) 21517987Speter continue; 21625233Ssteve STACKSTRNUL(p); 217104672Stjr if (lstat(stackblock(), &statb) < 0) { 2181556Srgrimes badstat = 1; 219214512Sjilles break; 220214512Sjilles } 2211556Srgrimes } 222214512Sjilles 223214512Sjilles INTOFF; 224214512Sjilles if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) { 2251556Srgrimes INTON; 2261556Srgrimes return (-1); 2271556Srgrimes } 228215783Sjilles updatepwd(p); 2291556Srgrimes INTON; 2301556Srgrimes return (0); 231212243Sjilles} 2321556Srgrimes 23338887Steggestatic int 234214512Sjillescdphysical(char *dest) 23538887Stegge{ 23638887Stegge char *p; 23738887Stegge int rc = 0; 23839137Stegge 239215783Sjilles INTOFF; 24038887Stegge if (chdir(dest) < 0) { 241214512Sjilles INTON; 242214512Sjilles return (-1); 243214512Sjilles } 2441556Srgrimes p = findcwd(NULL); 2451556Srgrimes if (p == NULL) { 246215783Sjilles warning("warning: failed to get name of current directory"); 2471556Srgrimes rc = 1; 248215783Sjilles } 249214512Sjilles updatepwd(p); 250214512Sjilles INTON; 251214512Sjilles return (rc); 252214512Sjilles} 2531556Srgrimes 2541556Srgrimes/* 2551556Srgrimes * Get the next component of the path name pointed to by cdcomppath. 2561556Srgrimes * This routine overwrites the string pointed to by cdcomppath. 2571556Srgrimes */ 2581556Srgrimesstatic char * 2591556Srgrimesgetcomponent(void) 2601556Srgrimes{ 2611556Srgrimes char *p; 2621556Srgrimes char *start; 2631556Srgrimes 2641556Srgrimes if ((p = cdcomppath) == NULL) 2651556Srgrimes return NULL; 2661556Srgrimes start = cdcomppath; 2671556Srgrimes while (*p != '/' && *p != '\0') 2681556Srgrimes p++; 2691556Srgrimes if (*p == '\0') { 2701556Srgrimes cdcomppath = NULL; 271215783Sjilles } else { 272214512Sjilles *p++ = '\0'; 273214512Sjilles cdcomppath = p; 274214512Sjilles } 275214512Sjilles return start; 276214512Sjilles} 277214512Sjilles 278214512Sjilles 2791556Srgrimesstatic char * 2801556Srgrimesfindcwd(char *dir) 2811556Srgrimes{ 2821556Srgrimes char *new; 283215783Sjilles char *p; 284214512Sjilles 285214512Sjilles /* 286214512Sjilles * If our argument is NULL, we don't know the current directory 2871556Srgrimes * any more because we traversed a symbolic link or something 2881556Srgrimes * we couldn't stat(). 2891556Srgrimes */ 2901556Srgrimes if (dir == NULL || curdir == NULL) 2911556Srgrimes return getpwd2(); 292212243Sjilles cdcomppath = stalloc(strlen(dir) + 1); 293212243Sjilles scopy(dir, cdcomppath); 294212243Sjilles STARTSTACKSTR(new); 295212243Sjilles if (*dir != '/') { 296213811Sobrien STPUTS(curdir, new); 29790111Simp if (STTOPC(new) == '/') 29817987Speter STUNPUTC(new); 2991556Srgrimes } 3001556Srgrimes while ((p = getcomponent()) != NULL) { 3011556Srgrimes if (equal(p, "..")) { 302108935Stjr while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 3031556Srgrimes } else if (*p != '\0' && ! equal(p, ".")) { 30417987Speter STPUTC('/', new); 3051556Srgrimes STPUTS(p, new); 306200988Sjilles } 307200988Sjilles } 308200988Sjilles if (new == stackblock()) 309200988Sjilles STPUTC('/', new); 310200988Sjilles STACKSTRNUL(new); 311200988Sjilles return stackblock(); 31239137Stegge} 31339137Stegge 3141556Srgrimes/* 3151556Srgrimes * Update curdir (the name of the current directory) in response to a 3161556Srgrimes * cd command. We also call hashcd to let the routines in exec.c know 3171556Srgrimes * that the current directory has changed. 3181556Srgrimes */ 319206150Sjillesstatic void 3201556Srgrimesupdatepwd(char *dir) 3211556Srgrimes{ 3221556Srgrimes hashcd(); /* update command hash table */ 3231556Srgrimes 3241556Srgrimes if (prevdir) 3251556Srgrimes ckfree(prevdir); 3261556Srgrimes prevdir = curdir; 3271556Srgrimes curdir = dir ? savestr(dir) : NULL; 3281556Srgrimes setvar("PWD", curdir, VEXPORT); 3291556Srgrimes setvar("OLDPWD", prevdir, VEXPORT); 3301556Srgrimes} 3311556Srgrimes 3321556Srgrimesint 3331556Srgrimespwdcmd(int argc, char **argv) 3341556Srgrimes{ 3351556Srgrimes char *p; 3361556Srgrimes int ch, phys; 33717987Speter 33883675Stegge optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 3391556Srgrimes phys = Pflag; 3401556Srgrimes while ((ch = getopt(argc, argv, "LP")) != -1) { 3411556Srgrimes switch (ch) { 3421556Srgrimes case 'L': 3431556Srgrimes phys = 0; 3441556Srgrimes break; 3451556Srgrimes case 'P': 3461556Srgrimes phys = 1; 3471556Srgrimes break; 3481556Srgrimes default: 349213811Sobrien error("unknown option: -%c", optopt); 35090111Simp break; 35138887Stegge } 35238887Stegge } 35338887Stegge argc -= optind; 35438887Stegge argv += optind; 35538887Stegge 35638887Stegge if (argc != 0) 35738887Stegge error("too many arguments"); 35838887Stegge 35938887Stegge if (!phys && getpwd()) { 36038887Stegge out1str(curdir); 36138887Stegge out1c('\n'); 36238887Stegge } else { 36338887Stegge if ((p = getpwd2()) == NULL) 36438887Stegge error(".: %s", strerror(errno)); 36538887Stegge out1str(p); 36638887Stegge out1c('\n'); 36738887Stegge } 36838887Stegge 36938887Stegge return 0; 37038887Stegge} 37138887Stegge 372155301Sschweikh/* 37338887Stegge * Get the current directory and cache the result in curdir. 37438887Stegge */ 37538887Steggestatic char * 37638887Steggegetpwd(void) 37738887Stegge{ 37838887Stegge char *p; 37938887Stegge 38038887Stegge if (curdir) 38138887Stegge return curdir; 38238887Stegge 38338887Stegge p = getpwd2(); 38438887Stegge if (p != NULL) 38538887Stegge curdir = savestr(p); 38638887Stegge 38738887Stegge return curdir; 3881556Srgrimes} 3891556Srgrimes 3901556Srgrimes#define MAXPWD 256 3911556Srgrimes 3921556Srgrimes/* 39390111Simp * Return the current directory. 39417987Speter */ 395207206Sjillesstatic char * 396178631Sstefanfgetpwd2(void) 39738887Stegge{ 398108935Stjr char *pwd; 39938887Stegge int i; 4001556Srgrimes 4011556Srgrimes for (i = MAXPWD;; i *= 2) { 40246684Skris pwd = stalloc(i); 4031556Srgrimes if (getcwd(pwd, i) != NULL) 4041556Srgrimes return pwd; 405212243Sjilles stunalloc(pwd); 4061556Srgrimes if (errno != ERANGE) 4071556Srgrimes break; 4081556Srgrimes } 4098855Srgrimes 4101556Srgrimes return NULL; 411178631Sstefanf} 4128855Srgrimes 4131556Srgrimes/* 41483676Stegge * Initialize PWD in a new shell. 41583676Stegge * If the shell is interactive, we need to warn if this fails. 4161556Srgrimes */ 41783676Steggevoid 4181556Srgrimespwd_init(int warn) 41983676Stegge{ 4201556Srgrimes char *pwd; 4211556Srgrimes struct stat stdot, stpwd; 4221556Srgrimes 42338887Stegge pwd = lookupvar("PWD"); 42438887Stegge if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && 42538887Stegge stat(pwd, &stpwd) != -1 && 42638887Stegge stdot.st_dev == stpwd.st_dev && 42738887Stegge stdot.st_ino == stpwd.st_ino) { 42838887Stegge if (curdir) 42938887Stegge ckfree(curdir); 4301556Srgrimes curdir = savestr(pwd); 43138887Stegge } 432207206Sjilles if (getpwd() == NULL && warn) 43338887Stegge out2fmt_flush("sh: cannot determine working directory\n"); 434207206Sjilles setvar("PWD", curdir, VEXPORT); 435178631Sstefanf} 4361556Srgrimes