cd.c revision 100660
1356843Sdim/*- 2356843Sdim * Copyright (c) 1991, 1993 3356843Sdim * The Regents of the University of California. All rights reserved. 4356843Sdim * 5356843Sdim * This code is derived from software contributed to Berkeley by 6356843Sdim * Kenneth Almquist. 7356843Sdim * 8356843Sdim * Redistribution and use in source and binary forms, with or without 9356843Sdim * modification, are permitted provided that the following conditions 10356843Sdim * are met: 11356843Sdim * 1. Redistributions of source code must retain the above copyright 12356843Sdim * notice, this list of conditions and the following disclaimer. 13356843Sdim * 2. Redistributions in binary form must reproduce the above copyright 14356843Sdim * notice, this list of conditions and the following disclaimer in the 15356843Sdim * documentation and/or other materials provided with the distribution. 16356843Sdim * 3. All advertising materials mentioning features or use of this software 17356843Sdim * must display the following acknowledgement: 18356843Sdim * This product includes software developed by the University of 19356843Sdim * California, Berkeley and its contributors. 20356843Sdim * 4. Neither the name of the University nor the names of its contributors 21356843Sdim * may be used to endorse or promote products derived from this software 22356843Sdim * without specific prior written permission. 23356843Sdim * 24356843Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25356843Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26356843Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27356843Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28356843Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29356843Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30356843Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31356843Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32356843Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33356843Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34356843Sdim * SUCH DAMAGE. 35356843Sdim */ 36356843Sdim 37356843Sdim#ifndef lint 38356843Sdim#if 0 39356843Sdimstatic char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 40356843Sdim#endif 41356843Sdim#endif /* not lint */ 42356843Sdim#include <sys/cdefs.h> 43356843Sdim__FBSDID("$FreeBSD: head/bin/sh/cd.c 100660 2002-07-25 09:46:31Z tjr $"); 44356843Sdim 45356843Sdim#include <sys/types.h> 46356843Sdim#include <sys/stat.h> 47356843Sdim#include <stdlib.h> 48356843Sdim#include <string.h> 49356843Sdim#include <unistd.h> 50356843Sdim#include <errno.h> 51356843Sdim 52356843Sdim/* 53356843Sdim * The cd and pwd commands. 54356843Sdim */ 55356843Sdim 56356843Sdim#include "shell.h" 57356843Sdim#include "var.h" 58356843Sdim#include "nodes.h" /* for jobs.h */ 59356843Sdim#include "jobs.h" 60356843Sdim#include "options.h" 61356843Sdim#include "output.h" 62356843Sdim#include "memalloc.h" 63356843Sdim#include "error.h" 64356843Sdim#include "exec.h" 65356843Sdim#include "redir.h" 66356843Sdim#include "mystring.h" 67356843Sdim#include "show.h" 68356843Sdim#include "cd.h" 69356843Sdim 70356843SdimSTATIC int cdlogical(char *); 71356843SdimSTATIC int cdphysical(char *); 72356843SdimSTATIC int docd(char *, int, int); 73356843SdimSTATIC char *getcomponent(void); 74356843SdimSTATIC int updatepwd(char *); 75356843Sdim 76356843Sdimchar *curdir = NULL; /* current working directory */ 77356843Sdimchar *prevdir; /* previous working directory */ 78356843SdimSTATIC char *cdcomppath; 79356843Sdim 80356843Sdimint 81356843Sdimcdcmd(int argc, char **argv) 82356843Sdim{ 83356843Sdim char *dest; 84356843Sdim char *path; 85356843Sdim char *p; 86356843Sdim struct stat statb; 87356843Sdim int ch, phys, print = 0; 88356843Sdim 89356843Sdim optreset = 1; optind = 1; /* initialize getopt */ 90356843Sdim phys = 0; 91356843Sdim while ((ch = getopt(argc, argv, "LP")) != -1) { 92356843Sdim switch (ch) { 93356843Sdim case 'L': 94356843Sdim phys = 0; 95356843Sdim break; 96356843Sdim case 'P': 97356843Sdim phys = 1; 98356843Sdim break; 99356843Sdim default: 100356843Sdim error("unknown option: -%c", optopt); 101356843Sdim break; 102356843Sdim } 103356843Sdim } 104356843Sdim argc -= optind; 105356843Sdim argv += optind; 106356843Sdim 107356843Sdim if (argc > 1) 108356843Sdim error("too many arguments"); 109356843Sdim 110356843Sdim if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 111356843Sdim error("HOME not set"); 112356843Sdim if (*dest == '\0') 113356843Sdim dest = "."; 114356843Sdim if (dest[0] == '-' && dest[1] == '\0') { 115356843Sdim dest = prevdir ? prevdir : curdir; 116356843Sdim if (dest) 117356843Sdim print = 1; 118356843Sdim else 119356843Sdim dest = "."; 120356843Sdim } 121356843Sdim if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 122356843Sdim path = nullstr; 123356843Sdim while ((p = padvance(&path, dest)) != NULL) { 124356843Sdim if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 125356843Sdim if (!print) { 126356843Sdim /* 127 * XXX - rethink 128 */ 129 if (p[0] == '.' && p[1] == '/' && p[2] != '\0') 130 p += 2; 131 print = strcmp(p, dest); 132 } 133 if (docd(p, print, phys) >= 0) 134 return 0; 135 } 136 } 137 error("can't cd to %s", dest); 138 /*NOTREACHED*/ 139 return 0; 140} 141 142 143/* 144 * Actually change the directory. In an interactive shell, print the 145 * directory name if "print" is nonzero. 146 */ 147STATIC int 148docd(char *dest, int print, int phys) 149{ 150 151 TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); 152 153 /* If logical cd fails, fall back to physical. */ 154 if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0) 155 return (-1); 156 157 if (print && iflag && curdir) 158 out1fmt("%s\n", curdir); 159 160 return 0; 161} 162 163STATIC int 164cdlogical(char *dest) 165{ 166 char *p; 167 char *q; 168 char *component; 169 struct stat statb; 170 int first; 171 int badstat; 172 173 /* 174 * Check each component of the path. If we find a symlink or 175 * something we can't stat, clear curdir to force a getcwd() 176 * next time we get the value of the current directory. 177 */ 178 badstat = 0; 179 cdcomppath = stalloc(strlen(dest) + 1); 180 scopy(dest, cdcomppath); 181 STARTSTACKSTR(p); 182 if (*dest == '/') { 183 STPUTC('/', p); 184 cdcomppath++; 185 } 186 first = 1; 187 while ((q = getcomponent()) != NULL) { 188 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 189 continue; 190 if (! first) 191 STPUTC('/', p); 192 first = 0; 193 component = q; 194 while (*q) 195 STPUTC(*q++, p); 196 if (equal(component, "..")) 197 continue; 198 STACKSTRNUL(p); 199 if (lstat(stackblock(), &statb) < 0) { 200 badstat = 1; 201 break; 202 } 203 } 204 205 INTOFF; 206 if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) { 207 INTON; 208 return (-1); 209 } 210 INTON; 211 return (0); 212} 213 214STATIC int 215cdphysical(char *dest) 216{ 217 218 INTOFF; 219 if (chdir(dest) < 0 || updatepwd(NULL) < 0) { 220 INTON; 221 return (-1); 222 } 223 INTON; 224 return (0); 225} 226 227/* 228 * Get the next component of the path name pointed to by cdcomppath. 229 * This routine overwrites the string pointed to by cdcomppath. 230 */ 231STATIC char * 232getcomponent(void) 233{ 234 char *p; 235 char *start; 236 237 if ((p = cdcomppath) == NULL) 238 return NULL; 239 start = cdcomppath; 240 while (*p != '/' && *p != '\0') 241 p++; 242 if (*p == '\0') { 243 cdcomppath = NULL; 244 } else { 245 *p++ = '\0'; 246 cdcomppath = p; 247 } 248 return start; 249} 250 251 252/* 253 * Update curdir (the name of the current directory) in response to a 254 * cd command. We also call hashcd to let the routines in exec.c know 255 * that the current directory has changed. 256 */ 257STATIC int 258updatepwd(char *dir) 259{ 260 char *new; 261 char *p; 262 263 hashcd(); /* update command hash table */ 264 265 /* 266 * If our argument is NULL, we don't know the current directory 267 * any more because we traversed a symbolic link or something 268 * we couldn't stat(). 269 */ 270 if (dir == NULL || curdir == NULL) { 271 if (prevdir) 272 ckfree(prevdir); 273 INTOFF; 274 prevdir = curdir; 275 curdir = NULL; 276 if (getpwd() == NULL) { 277 INTON; 278 return (-1); 279 } 280 setvar("PWD", curdir, VEXPORT); 281 setvar("OLDPWD", prevdir, VEXPORT); 282 INTON; 283 return (0); 284 } 285 cdcomppath = stalloc(strlen(dir) + 1); 286 scopy(dir, cdcomppath); 287 STARTSTACKSTR(new); 288 if (*dir != '/') { 289 p = curdir; 290 while (*p) 291 STPUTC(*p++, new); 292 if (p[-1] == '/') 293 STUNPUTC(new); 294 } 295 while ((p = getcomponent()) != NULL) { 296 if (equal(p, "..")) { 297 while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 298 } else if (*p != '\0' && ! equal(p, ".")) { 299 STPUTC('/', new); 300 while (*p) 301 STPUTC(*p++, new); 302 } 303 } 304 if (new == stackblock()) 305 STPUTC('/', new); 306 STACKSTRNUL(new); 307 INTOFF; 308 if (prevdir) 309 ckfree(prevdir); 310 prevdir = curdir; 311 curdir = savestr(stackblock()); 312 setvar("PWD", curdir, VEXPORT); 313 setvar("OLDPWD", prevdir, VEXPORT); 314 INTON; 315 316 return (0); 317} 318 319#define MAXPWD 256 320 321int 322pwdcmd(int argc, char **argv) 323{ 324 char buf[MAXPWD]; 325 int ch, phys; 326 327 optreset = 1; optind = 1; /* initialize getopt */ 328 phys = 0; 329 while ((ch = getopt(argc, argv, "LP")) != -1) { 330 switch (ch) { 331 case 'L': 332 phys = 0; 333 break; 334 case 'P': 335 phys = 1; 336 break; 337 default: 338 error("unknown option: -%c", optopt); 339 break; 340 } 341 } 342 argc -= optind; 343 argv += optind; 344 345 if (argc != 0) 346 error("too many arguments"); 347 348 if (!phys && getpwd()) { 349 out1str(curdir); 350 out1c('\n'); 351 } else { 352 if (getcwd(buf, sizeof(buf)) == NULL) 353 error(".: %s", strerror(errno)); 354 out1str(buf); 355 out1c('\n'); 356 } 357 358 return 0; 359} 360 361/* 362 * Find out what the current directory is. If we already know the current 363 * directory, this routine returns immediately. 364 */ 365char * 366getpwd(void) 367{ 368 char buf[MAXPWD]; 369 370 if (curdir) 371 return curdir; 372 if (getcwd(buf, sizeof(buf)) == NULL) { 373 char *pwd = getenv("PWD"); 374 struct stat stdot, stpwd; 375 376 if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && 377 stat(pwd, &stpwd) != -1 && 378 stdot.st_dev == stpwd.st_dev && 379 stdot.st_ino == stpwd.st_ino) { 380 curdir = savestr(pwd); 381 return curdir; 382 } 383 return NULL; 384 } 385 curdir = savestr(buf); 386 387 return curdir; 388} 389