cd.c revision 230095
1168404Spjd/*- 2168404Spjd * Copyright (c) 1991, 1993 3168404Spjd * The Regents of the University of California. All rights reserved. 4168404Spjd * 5168404Spjd * This code is derived from software contributed to Berkeley by 6168404Spjd * Kenneth Almquist. 7168404Spjd * 8168404Spjd * Redistribution and use in source and binary forms, with or without 9168404Spjd * modification, are permitted provided that the following conditions 10168404Spjd * are met: 11168404Spjd * 1. Redistributions of source code must retain the above copyright 12168404Spjd * notice, this list of conditions and the following disclaimer. 13168404Spjd * 2. Redistributions in binary form must reproduce the above copyright 14168404Spjd * notice, this list of conditions and the following disclaimer in the 15168404Spjd * documentation and/or other materials provided with the distribution. 16168404Spjd * 4. Neither the name of the University nor the names of its contributors 17168404Spjd * may be used to endorse or promote products derived from this software 18168404Spjd * without specific prior written permission. 19168404Spjd * 20168404Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21236884Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23219089Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24265740Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30168404Spjd * SUCH DAMAGE. 31168404Spjd */ 32168404Spjd 33168404Spjd#ifndef lint 34168404Spjd#if 0 35265740Sdelphijstatic char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 36263397Sdelphij#endif 37168404Spjd#endif /* not lint */ 38168404Spjd#include <sys/cdefs.h> 39168404Spjd__FBSDID("$FreeBSD: head/bin/sh/cd.c 230095 2012-01-13 23:32:27Z jilles $"); 40168404Spjd 41168404Spjd#include <sys/types.h> 42168404Spjd#include <sys/stat.h> 43168404Spjd#include <stdlib.h> 44168404Spjd#include <string.h> 45168404Spjd#include <unistd.h> 46168404Spjd#include <errno.h> 47168404Spjd#include <limits.h> 48168404Spjd 49168404Spjd/* 50168404Spjd * The cd and pwd commands. 51168404Spjd */ 52168404Spjd 53168404Spjd#include "shell.h" 54168404Spjd#include "var.h" 55168404Spjd#include "nodes.h" /* for jobs.h */ 56168404Spjd#include "jobs.h" 57168404Spjd#include "options.h" 58168404Spjd#include "output.h" 59185029Spjd#include "memalloc.h" 60185029Spjd#include "error.h" 61168404Spjd#include "exec.h" 62168404Spjd#include "redir.h" 63168404Spjd#include "mystring.h" 64168404Spjd#include "show.h" 65168404Spjd#include "cd.h" 66168404Spjd#include "builtins.h" 67168404Spjd 68168404Spjdstatic int cdlogical(char *); 69168404Spjdstatic int cdphysical(char *); 70168404Spjdstatic int docd(char *, int, int); 71168404Spjdstatic char *getcomponent(void); 72168404Spjdstatic char *findcwd(char *); 73168404Spjdstatic void updatepwd(char *); 74168404Spjdstatic char *getpwd(void); 75168404Spjdstatic char *getpwd2(void); 76168404Spjd 77168404Spjdstatic char *curdir = NULL; /* current working directory */ 78168404Spjdstatic char *prevdir; /* previous working directory */ 79168404Spjdstatic char *cdcomppath; 80168404Spjd 81168404Spjdint 82168404Spjdcdcmd(int argc, char **argv) 83219089Spjd{ 84219089Spjd const char *dest; 85219089Spjd const char *path; 86219089Spjd char *p; 87219089Spjd struct stat statb; 88168404Spjd int ch, phys, print = 0, getcwderr = 0; 89168404Spjd int rc; 90168404Spjd int errno1 = ENOENT; 91168404Spjd 92168404Spjd optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 93168404Spjd phys = Pflag; 94168404Spjd while ((ch = getopt(argc, argv, "eLP")) != -1) { 95168404Spjd switch (ch) { 96168404Spjd case 'e': 97168404Spjd getcwderr = 1; 98168404Spjd break; 99168404Spjd case 'L': 100168404Spjd phys = 0; 101168404Spjd break; 102168404Spjd case 'P': 103168404Spjd phys = 1; 104168404Spjd break; 105168404Spjd default: 106168404Spjd error("unknown option: -%c", optopt); 107168404Spjd break; 108168404Spjd } 109168404Spjd } 110168404Spjd argc -= optind; 111168404Spjd argv += optind; 112168404Spjd 113168404Spjd if (argc > 1) 114168404Spjd error("too many arguments"); 115168404Spjd 116168404Spjd if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 117263397Sdelphij error("HOME not set"); 118168404Spjd if (*dest == '\0') 119168404Spjd dest = "."; 120185029Spjd if (dest[0] == '-' && dest[1] == '\0') { 121168404Spjd dest = prevdir ? prevdir : curdir; 122168404Spjd if (dest) 123185029Spjd print = 1; 124168404Spjd else 125263397Sdelphij dest = "."; 126168404Spjd } 127168404Spjd if (dest[0] == '/' || 128168404Spjd (dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) || 129219089Spjd (dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) || 130168404Spjd (path = bltinlookup("CDPATH", 1)) == NULL) 131263397Sdelphij path = nullstr; 132263397Sdelphij while ((p = padvance(&path, dest)) != NULL) { 133263397Sdelphij if (stat(p, &statb) < 0) { 134263397Sdelphij if (errno != ENOENT) 135263397Sdelphij errno1 = errno; 136263397Sdelphij } else if (!S_ISDIR(statb.st_mode)) 137263397Sdelphij errno1 = ENOTDIR; 138263397Sdelphij else { 139263397Sdelphij if (!print) { 140263397Sdelphij /* 141263397Sdelphij * XXX - rethink 142263397Sdelphij */ 143263397Sdelphij if (p[0] == '.' && p[1] == '/' && p[2] != '\0') 144168404Spjd print = strcmp(p + 2, dest); 145263397Sdelphij else 146263397Sdelphij print = strcmp(p, dest); 147263397Sdelphij } 148263397Sdelphij rc = docd(p, print, phys); 149263397Sdelphij if (rc >= 0) 150263397Sdelphij return getcwderr ? rc : 0; 151263397Sdelphij if (errno != ENOENT) 152263397Sdelphij errno1 = errno; 153168404Spjd } 154168404Spjd } 155168404Spjd error("%s: %s", dest, strerror(errno1)); 156168404Spjd /*NOTREACHED*/ 157168404Spjd return 0; 158168404Spjd} 159168404Spjd 160168404Spjd 161168404Spjd/* 162168404Spjd * Actually change the directory. In an interactive shell, print the 163168404Spjd * directory name if "print" is nonzero. 164219089Spjd */ 165168404Spjdstatic int 166219089Spjddocd(char *dest, int print, int phys) 167219089Spjd{ 168219089Spjd int rc; 169168404Spjd 170168404Spjd TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); 171168404Spjd 172168404Spjd /* If logical cd fails, fall back to physical. */ 173168404Spjd if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0) 174168404Spjd return (-1); 175219089Spjd 176168404Spjd if (print && iflag && curdir) 177168404Spjd out1fmt("%s\n", curdir); 178168404Spjd 179168404Spjd return (rc); 180168404Spjd} 181168404Spjd 182168404Spjdstatic int 183168404Spjdcdlogical(char *dest) 184168404Spjd{ 185168404Spjd char *p; 186168404Spjd char *q; 187219089Spjd char *component; 188219089Spjd struct stat statb; 189185029Spjd int first; 190219089Spjd int badstat; 191168404Spjd 192168404Spjd /* 193168404Spjd * Check each component of the path. If we find a symlink or 194168404Spjd * something we can't stat, clear curdir to force a getcwd() 195168404Spjd * next time we get the value of the current directory. 196168404Spjd */ 197168404Spjd badstat = 0; 198168404Spjd cdcomppath = stalloc(strlen(dest) + 1); 199168404Spjd scopy(dest, cdcomppath); 200168404Spjd STARTSTACKSTR(p); 201168404Spjd if (*dest == '/') { 202168404Spjd STPUTC('/', p); 203168404Spjd cdcomppath++; 204168404Spjd } 205168404Spjd first = 1; 206168404Spjd while ((q = getcomponent()) != NULL) { 207185029Spjd if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 208168404Spjd continue; 209168404Spjd if (! first) 210168404Spjd STPUTC('/', p); 211168404Spjd first = 0; 212168404Spjd component = q; 213168404Spjd STPUTS(q, p); 214168404Spjd if (equal(component, "..")) 215168404Spjd continue; 216168404Spjd STACKSTRNUL(p); 217168404Spjd if (lstat(stackblock(), &statb) < 0) { 218168404Spjd badstat = 1; 219168404Spjd break; 220168404Spjd } 221168404Spjd } 222168404Spjd 223168404Spjd INTOFF; 224185029Spjd if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) { 225168404Spjd INTON; 226168404Spjd return (-1); 227168404Spjd } 228168404Spjd updatepwd(p); 229168404Spjd INTON; 230168404Spjd return (0); 231168404Spjd} 232219089Spjd 233168404Spjdstatic int 234168404Spjdcdphysical(char *dest) 235168404Spjd{ 236263397Sdelphij char *p; 237263397Sdelphij int rc = 0; 238168404Spjd 239168404Spjd INTOFF; 240219089Spjd if (chdir(dest) < 0) { 241168404Spjd INTON; 242168404Spjd return (-1); 243168404Spjd } 244263397Sdelphij p = findcwd(NULL); 245168404Spjd if (p == NULL) { 246185029Spjd warning("warning: failed to get name of current directory"); 247185029Spjd rc = 1; 248185029Spjd } 249263397Sdelphij updatepwd(p); 250263397Sdelphij INTON; 251185029Spjd return (rc); 252185029Spjd} 253185029Spjd 254185029Spjd/* 255219089Spjd * Get the next component of the path name pointed to by cdcomppath. 256263397Sdelphij * This routine overwrites the string pointed to by cdcomppath. 257168404Spjd */ 258219089Spjdstatic char * 259219089Spjdgetcomponent(void) 260219089Spjd{ 261168404Spjd char *p; 262168404Spjd char *start; 263168404Spjd 264168404Spjd if ((p = cdcomppath) == NULL) 265168404Spjd return NULL; 266168404Spjd start = cdcomppath; 267168404Spjd while (*p != '/' && *p != '\0') 268168404Spjd p++; 269168404Spjd if (*p == '\0') { 270168404Spjd cdcomppath = NULL; 271168404Spjd } else { 272168404Spjd *p++ = '\0'; 273263397Sdelphij cdcomppath = p; 274168404Spjd } 275168404Spjd return start; 276168404Spjd} 277168404Spjd 278263397Sdelphij 279263397Sdelphijstatic char * 280263397Sdelphijfindcwd(char *dir) 281263397Sdelphij{ 282263397Sdelphij char *new; 283263397Sdelphij char *p; 284263397Sdelphij 285263397Sdelphij /* 286263397Sdelphij * If our argument is NULL, we don't know the current directory 287263397Sdelphij * any more because we traversed a symbolic link or something 288263397Sdelphij * we couldn't stat(). 289263397Sdelphij */ 290263397Sdelphij if (dir == NULL || curdir == NULL) 291263397Sdelphij return getpwd2(); 292168404Spjd cdcomppath = stalloc(strlen(dir) + 1); 293168404Spjd scopy(dir, cdcomppath); 294263397Sdelphij STARTSTACKSTR(new); 295263397Sdelphij if (*dir != '/') { 296263397Sdelphij STPUTS(curdir, new); 297263397Sdelphij if (STTOPC(new) == '/') 298263397Sdelphij STUNPUTC(new); 299263397Sdelphij } 300263397Sdelphij while ((p = getcomponent()) != NULL) { 301263397Sdelphij if (equal(p, "..")) { 302263397Sdelphij while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 303263397Sdelphij } else if (*p != '\0' && ! equal(p, ".")) { 304263397Sdelphij STPUTC('/', new); 305263397Sdelphij STPUTS(p, new); 306263397Sdelphij } 307263397Sdelphij } 308263397Sdelphij if (new == stackblock()) 309263397Sdelphij STPUTC('/', new); 310168404Spjd STACKSTRNUL(new); 311219089Spjd return stackblock(); 312168404Spjd} 313168404Spjd 314168404Spjd/* 315168404Spjd * Update curdir (the name of the current directory) in response to a 316251631Sdelphij * cd command. We also call hashcd to let the routines in exec.c know 317168404Spjd * that the current directory has changed. 318168404Spjd */ 319168404Spjdstatic void 320265740Sdelphijupdatepwd(char *dir) 321263397Sdelphij{ 322168404Spjd hashcd(); /* update command hash table */ 323168404Spjd 324168404Spjd if (prevdir) 325263397Sdelphij ckfree(prevdir); 326168404Spjd prevdir = curdir; 327168404Spjd curdir = dir ? savestr(dir) : NULL; 328168404Spjd setvar("PWD", curdir, VEXPORT); 329168404Spjd setvar("OLDPWD", prevdir, VEXPORT); 330168404Spjd} 331263397Sdelphij 332168404Spjdint 333263397Sdelphijpwdcmd(int argc, char **argv) 334263397Sdelphij{ 335168404Spjd char *p; 336168404Spjd int ch, phys; 337168404Spjd 338168404Spjd optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 339168404Spjd phys = Pflag; 340168404Spjd while ((ch = getopt(argc, argv, "LP")) != -1) { 341168404Spjd switch (ch) { 342168404Spjd case 'L': 343263397Sdelphij phys = 0; 344263397Sdelphij break; 345263397Sdelphij case 'P': 346263397Sdelphij phys = 1; 347263397Sdelphij break; 348263397Sdelphij default: 349263397Sdelphij error("unknown option: -%c", optopt); 350168404Spjd break; 351263397Sdelphij } 352263397Sdelphij } 353263397Sdelphij argc -= optind; 354263397Sdelphij argv += optind; 355263397Sdelphij 356263397Sdelphij if (argc != 0) 357263397Sdelphij error("too many arguments"); 358263397Sdelphij 359263397Sdelphij if (!phys && getpwd()) { 360168404Spjd out1str(curdir); 361263397Sdelphij out1c('\n'); 362263397Sdelphij } else { 363168404Spjd if ((p = getpwd2()) == NULL) 364168404Spjd error(".: %s", strerror(errno)); 365263397Sdelphij out1str(p); 366168404Spjd out1c('\n'); 367263397Sdelphij } 368263397Sdelphij 369168404Spjd return 0; 370168404Spjd} 371168404Spjd 372168404Spjd/* 373185029Spjd * Get the current directory and cache the result in curdir. 374168404Spjd */ 375168404Spjdstatic char * 376168404Spjdgetpwd(void) 377265740Sdelphij{ 378265740Sdelphij char *p; 379265740Sdelphij 380265740Sdelphij if (curdir) 381265740Sdelphij return curdir; 382265740Sdelphij 383265740Sdelphij p = getpwd2(); 384265740Sdelphij if (p != NULL) 385265740Sdelphij curdir = savestr(p); 386265740Sdelphij 387265740Sdelphij return curdir; 388265740Sdelphij} 389265740Sdelphij 390265740Sdelphij#define MAXPWD 256 391265740Sdelphij 392265740Sdelphij/* 393168404Spjd * Return the current directory. 394251631Sdelphij */ 395168404Spjdstatic char * 396185029Spjdgetpwd2(void) 397185029Spjd{ 398168404Spjd char *pwd; 399168404Spjd int i; 400168404Spjd 401168404Spjd for (i = MAXPWD;; i *= 2) { 402168404Spjd pwd = stalloc(i); 403269845Sdelphij if (getcwd(pwd, i) != NULL) 404168404Spjd return pwd; 405168404Spjd stunalloc(pwd); 406168404Spjd if (errno != ERANGE) 407168404Spjd break; 408269845Sdelphij } 409269845Sdelphij 410219089Spjd return NULL; 411219089Spjd} 412219089Spjd 413219089Spjd/* 414219089Spjd * Initialize PWD in a new shell. 415168404Spjd * If the shell is interactive, we need to warn if this fails. 416168404Spjd */ 417168404Spjdvoid 418168404Spjdpwd_init(int warn) 419168404Spjd{ 420168404Spjd char *pwd; 421168404Spjd struct stat stdot, stpwd; 422168404Spjd 423168404Spjd pwd = lookupvar("PWD"); 424168404Spjd if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && 425168404Spjd stat(pwd, &stpwd) != -1 && 426168404Spjd stdot.st_dev == stpwd.st_dev && 427168404Spjd stdot.st_ino == stpwd.st_ino) { 428168404Spjd if (curdir) 429168404Spjd ckfree(curdir); 430168404Spjd curdir = savestr(pwd); 431168404Spjd } 432168404Spjd if (getpwd() == NULL && warn) 433168404Spjd out2fmt_flush("sh: cannot determine working directory\n"); 434168404Spjd setvar("PWD", curdir, VEXPORT); 435168404Spjd} 436168404Spjd