cd.c revision 201053
160894Smsmith/*- 260894Smsmith * Copyright (c) 1991, 1993 3123103Sps * The Regents of the University of California. All rights reserved. 4123103Sps * 560894Smsmith * This code is derived from software contributed to Berkeley by 660894Smsmith * Kenneth Almquist. 760894Smsmith * 860894Smsmith * Redistribution and use in source and binary forms, with or without 960894Smsmith * modification, are permitted provided that the following conditions 1060894Smsmith * are met: 1160894Smsmith * 1. Redistributions of source code must retain the above copyright 1260894Smsmith * notice, this list of conditions and the following disclaimer. 1360894Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1460894Smsmith * notice, this list of conditions and the following disclaimer in the 1560894Smsmith * documentation and/or other materials provided with the distribution. 1660894Smsmith * 4. Neither the name of the University nor the names of its contributors 1760894Smsmith * may be used to endorse or promote products derived from this software 1860894Smsmith * without specific prior written permission. 1960894Smsmith * 2060894Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2160894Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2260894Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2360894Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2460894Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2560894Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2660894Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2760894Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2860894Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2960894Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3060894Smsmith * SUCH DAMAGE. 3160894Smsmith */ 3260894Smsmith 3360894Smsmith#ifndef lint 3460894Smsmith#if 0 3560894Smsmithstatic char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 3667555Smsmith#endif 3760894Smsmith#endif /* not lint */ 3867555Smsmith#include <sys/cdefs.h> 3960894Smsmith__FBSDID("$FreeBSD: head/bin/sh/cd.c 201053 2009-12-27 18:04:05Z jilles $"); 4067555Smsmith 4167555Smsmith#include <sys/types.h> 4260894Smsmith#include <sys/stat.h> 4360894Smsmith#include <stdlib.h> 4460894Smsmith#include <string.h> 4560894Smsmith#include <unistd.h> 4667555Smsmith#include <errno.h> 4767555Smsmith#include <limits.h> 4867555Smsmith 4967555Smsmith/* 5060894Smsmith * The cd and pwd commands. 5191449Speter */ 5267555Smsmith 5391449Speter#include "shell.h" 5491449Speter#include "var.h" 5567555Smsmith#include "nodes.h" /* for jobs.h */ 5667555Smsmith#include "jobs.h" 5791449Speter#include "options.h" 5867555Smsmith#include "output.h" 5967555Smsmith#include "memalloc.h" 6067555Smsmith#include "error.h" 6167555Smsmith#include "exec.h" 62141492Sscottl#include "redir.h" 6367555Smsmith#include "mystring.h" 6467555Smsmith#include "show.h" 65123103Sps#include "cd.h" 66123103Sps 6760894SmsmithSTATIC int cdlogical(char *); 6860894SmsmithSTATIC int cdphysical(char *); 6960894SmsmithSTATIC int docd(char *, int, int); 7060894SmsmithSTATIC char *getcomponent(void); 71240137SjhbSTATIC char *findcwd(char *); 7267555SmsmithSTATIC void updatepwd(char *); 7367555SmsmithSTATIC char *getpwd2(void); 7467555Smsmith 7567555SmsmithSTATIC char *curdir = NULL; /* current working directory */ 7667555SmsmithSTATIC char *prevdir; /* previous working directory */ 7760894SmsmithSTATIC char *cdcomppath; 7860894Smsmith 7960894Smsmithint 8060894Smsmithcdcmd(int argc, char **argv) 8167555Smsmith{ 8267555Smsmith const char *dest; 8367555Smsmith const char *path; 8460894Smsmith char *p; 8560894Smsmith struct stat statb; 8660894Smsmith int ch, phys, print = 0; 8760894Smsmith 8867555Smsmith optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 8967555Smsmith phys = Pflag; 9067555Smsmith while ((ch = getopt(argc, argv, "LP")) != -1) { 91123103Sps switch (ch) { 9267555Smsmith case 'L': 9367555Smsmith phys = 0; 9460894Smsmith break; 9560894Smsmith case 'P': 9660894Smsmith phys = 1; 9760894Smsmith break; 9867555Smsmith default: 9967555Smsmith error("unknown option: -%c", optopt); 10060894Smsmith break; 10160894Smsmith } 10260894Smsmith } 10360894Smsmith argc -= optind; 10467555Smsmith argv += optind; 10569543Smsmith 10667555Smsmith if (argc > 1) 10760894Smsmith error("too many arguments"); 10860894Smsmith 10960894Smsmith if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 11060894Smsmith error("HOME not set"); 11160894Smsmith if (*dest == '\0') 11260894Smsmith dest = "."; 11360894Smsmith if (dest[0] == '-' && dest[1] == '\0') { 11460894Smsmith dest = prevdir ? prevdir : curdir; 11567555Smsmith if (dest) 11660894Smsmith print = 1; 11767555Smsmith else 11867555Smsmith dest = "."; 11960894Smsmith } 12060894Smsmith if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 121118816Sps path = nullstr; 12291790Smsmith while ((p = padvance(&path, dest)) != NULL) { 12367555Smsmith if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 12460894Smsmith if (!print) { 12560894Smsmith /* 12660894Smsmith * XXX - rethink 12760894Smsmith */ 12867555Smsmith if (p[0] == '.' && p[1] == '/' && p[2] != '\0') 12960894Smsmith print = strcmp(p + 2, dest); 13067555Smsmith else 13167555Smsmith print = strcmp(p, dest); 13267555Smsmith } 13367555Smsmith if (docd(p, print, phys) >= 0) 13467555Smsmith return 0; 13567555Smsmith } 13660894Smsmith } 13760894Smsmith error("can't cd to %s", dest); 13867555Smsmith /*NOTREACHED*/ 13960894Smsmith return 0; 14067555Smsmith} 141118816Sps 14267555Smsmith 14367555Smsmith/* 14467555Smsmith * Actually change the directory. In an interactive shell, print the 14567555Smsmith * directory name if "print" is nonzero. 146118816Sps */ 147118816SpsSTATIC int 148118816Spsdocd(char *dest, int print, int phys) 14967555Smsmith{ 15060894Smsmith 15167555Smsmith TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); 15267555Smsmith 15367555Smsmith /* If logical cd fails, fall back to physical. */ 154239244Sjhb if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0) 15567555Smsmith return (-1); 156239244Sjhb 15760894Smsmith if (print && iflag && curdir) 158239244Sjhb out1fmt("%s\n", curdir); 15960894Smsmith 16060894Smsmith return 0; 16191790Smsmith} 16291790Smsmith 16391790SmsmithSTATIC int 16491790Smsmithcdlogical(char *dest) 16591790Smsmith{ 16691790Smsmith char *p; 16767555Smsmith char *q; 16860894Smsmith char *component; 16967555Smsmith struct stat statb; 170239244Sjhb int first; 17167555Smsmith int badstat; 17260894Smsmith 17360894Smsmith /* 17460894Smsmith * Check each component of the path. If we find a symlink or 17560894Smsmith * something we can't stat, clear curdir to force a getcwd() 17667555Smsmith * next time we get the value of the current directory. 17760894Smsmith */ 17867555Smsmith badstat = 0; 17960894Smsmith cdcomppath = stalloc(strlen(dest) + 1); 18060894Smsmith scopy(dest, cdcomppath); 18167555Smsmith STARTSTACKSTR(p); 18267555Smsmith if (*dest == '/') { 18360894Smsmith STPUTC('/', p); 18467555Smsmith cdcomppath++; 18560894Smsmith } 18667555Smsmith first = 1; 18767555Smsmith while ((q = getcomponent()) != NULL) { 18867555Smsmith if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 18967555Smsmith continue; 19067555Smsmith if (! first) 19167555Smsmith STPUTC('/', p); 192239244Sjhb first = 0; 19367555Smsmith component = q; 19467555Smsmith while (*q) 19567555Smsmith STPUTC(*q++, p); 19660894Smsmith if (equal(component, "..")) 19760894Smsmith continue; 19860894Smsmith STACKSTRNUL(p); 19960894Smsmith if (lstat(stackblock(), &statb) < 0) { 20060894Smsmith badstat = 1; 20160894Smsmith break; 202123103Sps } 203118508Sps } 20460894Smsmith 20560894Smsmith INTOFF; 206123103Sps if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) { 20767555Smsmith INTON; 208118508Sps return (-1); 209200991Smav } 21060894Smsmith updatepwd(p); 211239244Sjhb INTON; 212118508Sps return (0); 213123103Sps} 21460894Smsmith 21560894SmsmithSTATIC int 21667555Smsmithcdphysical(char *dest) 21760894Smsmith{ 218239244Sjhb char *p; 21967555Smsmith 22067555Smsmith INTOFF; 221239244Sjhb if (chdir(dest) < 0 || (p = findcwd(NULL)) == NULL) { 22267555Smsmith INTON; 223123103Sps return (-1); 22460894Smsmith } 22560894Smsmith updatepwd(p); 226118508Sps INTON; 227118508Sps return (0); 228123103Sps} 229239244Sjhb 230123103Sps/* 231118508Sps * Get the next component of the path name pointed to by cdcomppath. 232123103Sps * This routine overwrites the string pointed to by cdcomppath. 23360894Smsmith */ 234118508SpsSTATIC char * 23560894Smsmithgetcomponent(void) 236118508Sps{ 237239244Sjhb char *p; 238118508Sps char *start; 239123103Sps 240118508Sps if ((p = cdcomppath) == NULL) 241118508Sps return NULL; 242118508Sps start = cdcomppath; 243239244Sjhb while (*p != '/' && *p != '\0') 244118508Sps p++; 245123103Sps if (*p == '\0') { 246118508Sps cdcomppath = NULL; 247118508Sps } else { 248118508Sps *p++ = '\0'; 249239244Sjhb cdcomppath = p; 250118508Sps } 251123103Sps return start; 252118508Sps} 253118508Sps 254118508Sps 255239244SjhbSTATIC char * 256118508Spsfindcwd(char *dir) 257123103Sps{ 258118508Sps char *new; 259118508Sps char *p; 260200991Smav 261118508Sps /* 262200991Smav * If our argument is NULL, we don't know the current directory 263118508Sps * any more because we traversed a symbolic link or something 264118508Sps * we couldn't stat(). 265118508Sps */ 266118508Sps if (dir == NULL || curdir == NULL) 267118508Sps return getpwd2(); 268118508Sps cdcomppath = stalloc(strlen(dir) + 1); 269118508Sps scopy(dir, cdcomppath); 270118508Sps STARTSTACKSTR(new); 271118508Sps if (*dir != '/') { 272118508Sps p = curdir; 273123103Sps while (*p) 274239244Sjhb STPUTC(*p++, new); 275118508Sps if (p[-1] == '/') 276123103Sps STUNPUTC(new); 277118508Sps } 278118508Sps while ((p = getcomponent()) != NULL) { 279118508Sps if (equal(p, "..")) { 28067555Smsmith while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 281118508Sps } else if (*p != '\0' && ! equal(p, ".")) { 282118508Sps STPUTC('/', new); 283123103Sps while (*p) 284118508Sps STPUTC(*p++, new); 28560894Smsmith } 286123103Sps } 287118508Sps if (new == stackblock()) 288118508Sps STPUTC('/', new); 289123103Sps STACKSTRNUL(new); 29060894Smsmith return stackblock(); 291239244Sjhb} 292126099Scperciva 293123103Sps/* 29460894Smsmith * Update curdir (the name of the current directory) in response to a 295123103Sps * cd command. We also call hashcd to let the routines in exec.c know 296123103Sps * that the current directory has changed. 297123103Sps */ 298123103SpsSTATIC void 299123103Spsupdatepwd(char *dir) 300118508Sps{ 301118508Sps hashcd(); /* update command hash table */ 302118508Sps 303118508Sps if (prevdir) 304118508Sps ckfree(prevdir); 305118508Sps prevdir = curdir; 306118508Sps curdir = savestr(dir); 307118508Sps setvar("PWD", curdir, VEXPORT); 308118508Sps setvar("OLDPWD", prevdir, VEXPORT); 309118508Sps} 31060894Smsmith 311118508Spsint 312118508Spspwdcmd(int argc, char **argv) 313239244Sjhb{ 314118508Sps char *p; 315118508Sps int ch, phys; 316239244Sjhb 317118508Sps optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 318118508Sps phys = Pflag; 31960894Smsmith while ((ch = getopt(argc, argv, "LP")) != -1) { 32060894Smsmith switch (ch) { 321239244Sjhb case 'L': 32267555Smsmith phys = 0; 32360894Smsmith break; 32467555Smsmith case 'P': 32567555Smsmith phys = 1; 32667555Smsmith break; 32767555Smsmith default: 32867555Smsmith error("unknown option: -%c", optopt); 32967555Smsmith break; 33067555Smsmith } 33160894Smsmith } 33260894Smsmith argc -= optind; 33360894Smsmith argv += optind; 33460894Smsmith 33560894Smsmith if (argc != 0) 33660894Smsmith error("too many arguments"); 33767555Smsmith 33860894Smsmith if (!phys && getpwd()) { 33960894Smsmith out1str(curdir); 340239244Sjhb out1c('\n'); 34160894Smsmith } else { 34260894Smsmith if ((p = getpwd2()) == NULL) 34360894Smsmith error(".: %s", strerror(errno)); 34467555Smsmith out1str(p); 34560894Smsmith out1c('\n'); 34667555Smsmith } 34767555Smsmith 34860894Smsmith return 0; 34960894Smsmith} 35060894Smsmith 35160894Smsmith/* 352239244Sjhb * Get the current directory and cache the result in curdir. 35360894Smsmith */ 35460894Smsmithchar * 35560894Smsmithgetpwd(void) 35667555Smsmith{ 35767555Smsmith char *p; 35867555Smsmith 35960894Smsmith if (curdir) 36067555Smsmith return curdir; 36167555Smsmith 36260894Smsmith p = getpwd2(); 36360894Smsmith if (p != NULL) 36460894Smsmith curdir = savestr(p); 36560894Smsmith 36660894Smsmith return curdir; 36760894Smsmith} 36867555Smsmith 36967555Smsmith#define MAXPWD 256 37060894Smsmith 37160894Smsmith/* 37260894Smsmith * Return the current directory. 37360894Smsmith */ 37460894SmsmithSTATIC char * 37560894Smsmithgetpwd2(void) 37660894Smsmith{ 37760894Smsmith struct stat stdot, stpwd; 37860894Smsmith char *pwd; 37960894Smsmith int i; 38060894Smsmith 38160894Smsmith for (i = MAXPWD;; i *= 2) { 38260894Smsmith pwd = stalloc(i); 38360894Smsmith if (getcwd(pwd, i) != NULL) 38460894Smsmith return pwd; 38560894Smsmith stunalloc(pwd); 38660894Smsmith if (errno != ERANGE) 38760894Smsmith break; 38860894Smsmith } 38960894Smsmith 39073104Smsmith pwd = getenv("PWD"); 391240137Sjhb if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && 39260894Smsmith stat(pwd, &stpwd) != -1 && 39360894Smsmith stdot.st_dev == stpwd.st_dev && 39469543Smsmith stdot.st_ino == stpwd.st_ino) { 39569543Smsmith return pwd; 39669543Smsmith } 39760894Smsmith return NULL; 39869543Smsmith} 39969543Smsmith