cd.c revision 4192
1/*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $Id: cd.c,v 1.2 1994/09/24 02:57:24 davidg Exp $ 37 */ 38 39#ifndef lint 40static char sccsid[] = "@(#)cd.c 8.1 (Berkeley) 5/31/93"; 41#endif /* not lint */ 42 43/* 44 * The cd and pwd commands. 45 */ 46 47#include "shell.h" 48#include "var.h" 49#include "nodes.h" /* for jobs.h */ 50#include "jobs.h" 51#include "options.h" 52#include "output.h" 53#include "memalloc.h" 54#include "error.h" 55#include "mystring.h" 56#include <sys/types.h> 57#include <sys/stat.h> 58#include <sys/unistd.h> 59#include <errno.h> 60 61 62#ifdef __STDC__ 63STATIC int docd(char *, int); 64STATIC void updatepwd(char *); 65STATIC void getpwd(void); 66STATIC char *getcomponent(void); 67#else 68STATIC int docd(); 69STATIC void updatepwd(); 70STATIC void getpwd(); 71STATIC char *getcomponent(); 72#endif 73 74 75char *curdir; /* current working directory */ 76char *prevdir; /* previous working directory */ 77STATIC char *cdcomppath; 78 79int 80cdcmd(argc, argv) char **argv; { 81 char *dest; 82 char *path; 83 char *p; 84 struct stat statb; 85 char *padvance(); 86 int print = 0; 87 88 nextopt(nullstr); 89 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 90 error("HOME not set"); 91 if (dest[0] == '-' && dest[1] == '\0') { 92 dest = prevdir ? prevdir : curdir; 93 print = 1; 94 } 95 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 96 path = nullstr; 97 while ((p = padvance(&path, dest)) != NULL) { 98 if (stat(p, &statb) >= 0 99 && (statb.st_mode & S_IFMT) == S_IFDIR) { 100 if (!print) { 101 /* 102 * XXX - rethink 103 */ 104 if (p[0] == '.' && p[1] == '/') 105 p += 2; 106 print = strcmp(p, dest); 107 } 108 if (docd(p, print) >= 0) 109 return 0; 110 111 } 112 } 113 error("can't cd to %s", dest); 114} 115 116 117/* 118 * Actually do the chdir. If the name refers to symbolic links, we 119 * compute the actual directory name before doing the cd. In an 120 * interactive shell, print the directory name if "print" is nonzero 121 * or if the name refers to a symbolic link. We also print the name 122 * if "/u/logname" was expanded in it, since this is similar to a 123 * symbolic link. (The check for this breaks if the user gives the 124 * cd command some additional, unused arguments.) 125 */ 126 127#if SYMLINKS == 0 128STATIC int 129docd(dest, print) 130 char *dest; 131 { 132 INTOFF; 133 if (chdir(dest) < 0) { 134 INTON; 135 return -1; 136 } 137 updatepwd(dest); 138 INTON; 139 if (print && iflag) 140 out1fmt("%s\n", stackblock()); 141 return 0; 142} 143 144#else 145 146 147 148STATIC int 149docd(dest, print) 150 char *dest; 151 { 152 register char *p; 153 register char *q; 154 char *symlink; 155 char *component; 156 struct stat statb; 157 int first; 158 int i; 159 160 TRACE(("docd(\"%s\", %d) called\n", dest, print)); 161 162top: 163 cdcomppath = dest; 164 STARTSTACKSTR(p); 165 if (*dest == '/') { 166 STPUTC('/', p); 167 cdcomppath++; 168 } 169 first = 1; 170 while ((q = getcomponent()) != NULL) { 171 if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') 172 continue; 173 if (! first) 174 STPUTC('/', p); 175 first = 0; 176 component = q; 177 while (*q) 178 STPUTC(*q++, p); 179 if (equal(component, "..")) 180 continue; 181 STACKSTRNUL(p); 182 if (lstat(stackblock(), &statb) < 0) 183 error("lstat %s failed", stackblock()); 184 if ((statb.st_mode & S_IFMT) != S_IFLNK) 185 continue; 186 187 /* Hit a symbolic link. We have to start all over again. */ 188 print = 1; 189 STPUTC('\0', p); 190 symlink = grabstackstr(p); 191 i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ 192 if (cdcomppath != NULL) 193 i += strlen(cdcomppath); 194 p = stalloc(i); 195 if (readlink(symlink, p, (int)statb.st_size) < 0) { 196 error("readlink %s failed", stackblock()); 197 } 198 if (cdcomppath != NULL) { 199 p[(int)statb.st_size] = '/'; 200 scopy(cdcomppath, p + (int)statb.st_size + 1); 201 } else { 202 p[(int)statb.st_size] = '\0'; 203 } 204 if (p[0] != '/') { /* relative path name */ 205 char *r; 206 q = r = symlink; 207 while (*q) { 208 if (*q++ == '/') 209 r = q; 210 } 211 *r = '\0'; 212 dest = stalloc(strlen(symlink) + strlen(p) + 1); 213 scopy(symlink, dest); 214 strcat(dest, p); 215 } else { 216 dest = p; 217 } 218 goto top; 219 } 220 STPUTC('\0', p); 221 p = grabstackstr(p); 222 INTOFF; 223 if (chdir(p) < 0) { 224 INTON; 225 return -1; 226 } 227 updatepwd(p); 228 INTON; 229 if (print && iflag) 230 out1fmt("%s\n", p); 231 return 0; 232} 233#endif /* SYMLINKS */ 234 235 236 237/* 238 * Get the next component of the path name pointed to by cdcomppath. 239 * This routine overwrites the string pointed to by cdcomppath. 240 */ 241 242STATIC char * 243getcomponent() { 244 register char *p; 245 char *start; 246 247 if ((p = cdcomppath) == NULL) 248 return NULL; 249 start = cdcomppath; 250 while (*p != '/' && *p != '\0') 251 p++; 252 if (*p == '\0') { 253 cdcomppath = NULL; 254 } else { 255 *p++ = '\0'; 256 cdcomppath = p; 257 } 258 return start; 259} 260 261 262 263/* 264 * Update curdir (the name of the current directory) in response to a 265 * cd command. We also call hashcd to let the routines in exec.c know 266 * that the current directory has changed. 267 */ 268 269void hashcd(); 270 271STATIC void 272updatepwd(dir) 273 char *dir; 274 { 275 char *new; 276 char *p; 277 278 hashcd(); /* update command hash table */ 279 cdcomppath = stalloc(strlen(dir) + 1); 280 scopy(dir, cdcomppath); 281 STARTSTACKSTR(new); 282 if (*dir != '/') { 283 if (curdir == NULL) 284 return; 285 p = curdir; 286 while (*p) 287 STPUTC(*p++, new); 288 if (p[-1] == '/') 289 STUNPUTC(new); 290 } 291 while ((p = getcomponent()) != NULL) { 292 if (equal(p, "..")) { 293 while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 294 } else if (*p != '\0' && ! equal(p, ".")) { 295 STPUTC('/', new); 296 while (*p) 297 STPUTC(*p++, new); 298 } 299 } 300 if (new == stackblock()) 301 STPUTC('/', new); 302 STACKSTRNUL(new); 303 INTOFF; 304 if (prevdir) 305 ckfree(prevdir); 306 prevdir = curdir; 307 curdir = savestr(stackblock()); 308 INTON; 309} 310 311 312 313int 314pwdcmd(argc, argv) char **argv; { 315 getpwd(); 316 out1str(curdir); 317 out1c('\n'); 318 return 0; 319} 320 321 322 323/* 324 * Run /bin/pwd to find out what the current directory is. We suppress 325 * interrupts throughout most of this, but the user can still break out 326 * of it by killing the pwd program. If we already know the current 327 * directory, this routine returns immediately. 328 */ 329 330#define MAXPWD 256 331 332STATIC void 333getpwd() { 334 char buf[MAXPWD]; 335 char *p; 336 int i; 337 int status; 338 struct job *jp; 339 int pip[2]; 340 char *pwd_bin = "/bin/pwd"; 341 342 if (curdir) 343 return; 344 INTOFF; 345 if (pipe(pip) < 0) 346 error("Pipe call failed"); 347 /* make a fall-back guess, otherwise we're simply screwed */ 348 if (access(pwd_bin, X_OK) == -1) 349 pwd_bin = "/stand/pwd"; 350 jp = makejob((union node *)NULL, 1); 351 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 352 close(pip[0]); 353 if (pip[1] != 1) { 354 close(1); 355 copyfd(pip[1], 1); 356 close(pip[1]); 357 } 358 execl(pwd_bin, "pwd", (char *)0); 359 error("Cannot exec %s", pwd_bin); 360 } 361 close(pip[1]); 362 pip[1] = -1; 363 p = buf; 364 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 365 || i == -1 && errno == EINTR) { 366 if (i > 0) 367 p += i; 368 } 369 close(pip[0]); 370 pip[0] = -1; 371 status = waitforjob(jp); 372 if (status != 0) 373 error((char *)0); 374 if (i < 0 || p == buf || p[-1] != '\n') 375 error("pwd command failed"); 376 p[-1] = '\0'; 377 curdir = savestr(buf); 378 INTON; 379} 380