cd.c revision 3044
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$ 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 <errno.h> 59 60 61#ifdef __STDC__ 62STATIC int docd(char *, int); 63STATIC void updatepwd(char *); 64STATIC void getpwd(void); 65STATIC char *getcomponent(void); 66#else 67STATIC int docd(); 68STATIC void updatepwd(); 69STATIC void getpwd(); 70STATIC char *getcomponent(); 71#endif 72 73 74char *curdir; /* current working directory */ 75char *prevdir; /* previous working directory */ 76STATIC char *cdcomppath; 77 78int 79cdcmd(argc, argv) char **argv; { 80 char *dest; 81 char *path; 82 char *p; 83 struct stat statb; 84 char *padvance(); 85 int print = 0; 86 87 nextopt(nullstr); 88 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 89 error("HOME not set"); 90 if (dest[0] == '-' && dest[1] == '\0') { 91 dest = prevdir ? prevdir : curdir; 92 print = 1; 93 } 94 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 95 path = nullstr; 96 while ((p = padvance(&path, dest)) != NULL) { 97 if (stat(p, &statb) >= 0 98 && (statb.st_mode & S_IFMT) == S_IFDIR) { 99 if (!print) { 100 /* 101 * XXX - rethink 102 */ 103 if (p[0] == '.' && p[1] == '/') 104 p += 2; 105 print = strcmp(p, dest); 106 } 107 if (docd(p, print) >= 0) 108 return 0; 109 110 } 111 } 112 error("can't cd to %s", dest); 113} 114 115 116/* 117 * Actually do the chdir. If the name refers to symbolic links, we 118 * compute the actual directory name before doing the cd. In an 119 * interactive shell, print the directory name if "print" is nonzero 120 * or if the name refers to a symbolic link. We also print the name 121 * if "/u/logname" was expanded in it, since this is similar to a 122 * symbolic link. (The check for this breaks if the user gives the 123 * cd command some additional, unused arguments.) 124 */ 125 126#if SYMLINKS == 0 127STATIC int 128docd(dest, print) 129 char *dest; 130 { 131 INTOFF; 132 if (chdir(dest) < 0) { 133 INTON; 134 return -1; 135 } 136 updatepwd(dest); 137 INTON; 138 if (print && iflag) 139 out1fmt("%s\n", stackblock()); 140 return 0; 141} 142 143#else 144 145 146 147STATIC int 148docd(dest, print) 149 char *dest; 150 { 151 register char *p; 152 register char *q; 153 char *symlink; 154 char *component; 155 struct stat statb; 156 int first; 157 int i; 158 159 TRACE(("docd(\"%s\", %d) called\n", dest, print)); 160 161top: 162 cdcomppath = dest; 163 STARTSTACKSTR(p); 164 if (*dest == '/') { 165 STPUTC('/', p); 166 cdcomppath++; 167 } 168 first = 1; 169 while ((q = getcomponent()) != NULL) { 170 if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') 171 continue; 172 if (! first) 173 STPUTC('/', p); 174 first = 0; 175 component = q; 176 while (*q) 177 STPUTC(*q++, p); 178 if (equal(component, "..")) 179 continue; 180 STACKSTRNUL(p); 181 if (lstat(stackblock(), &statb) < 0) 182 error("lstat %s failed", stackblock()); 183 if ((statb.st_mode & S_IFMT) != S_IFLNK) 184 continue; 185 186 /* Hit a symbolic link. We have to start all over again. */ 187 print = 1; 188 STPUTC('\0', p); 189 symlink = grabstackstr(p); 190 i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ 191 if (cdcomppath != NULL) 192 i += strlen(cdcomppath); 193 p = stalloc(i); 194 if (readlink(symlink, p, (int)statb.st_size) < 0) { 195 error("readlink %s failed", stackblock()); 196 } 197 if (cdcomppath != NULL) { 198 p[(int)statb.st_size] = '/'; 199 scopy(cdcomppath, p + (int)statb.st_size + 1); 200 } else { 201 p[(int)statb.st_size] = '\0'; 202 } 203 if (p[0] != '/') { /* relative path name */ 204 char *r; 205 q = r = symlink; 206 while (*q) { 207 if (*q++ == '/') 208 r = q; 209 } 210 *r = '\0'; 211 dest = stalloc(strlen(symlink) + strlen(p) + 1); 212 scopy(symlink, dest); 213 strcat(dest, p); 214 } else { 215 dest = p; 216 } 217 goto top; 218 } 219 STPUTC('\0', p); 220 p = grabstackstr(p); 221 INTOFF; 222 if (chdir(p) < 0) { 223 INTON; 224 return -1; 225 } 226 updatepwd(p); 227 INTON; 228 if (print && iflag) 229 out1fmt("%s\n", p); 230 return 0; 231} 232#endif /* SYMLINKS */ 233 234 235 236/* 237 * Get the next component of the path name pointed to by cdcomppath. 238 * This routine overwrites the string pointed to by cdcomppath. 239 */ 240 241STATIC char * 242getcomponent() { 243 register char *p; 244 char *start; 245 246 if ((p = cdcomppath) == NULL) 247 return NULL; 248 start = cdcomppath; 249 while (*p != '/' && *p != '\0') 250 p++; 251 if (*p == '\0') { 252 cdcomppath = NULL; 253 } else { 254 *p++ = '\0'; 255 cdcomppath = p; 256 } 257 return start; 258} 259 260 261 262/* 263 * Update curdir (the name of the current directory) in response to a 264 * cd command. We also call hashcd to let the routines in exec.c know 265 * that the current directory has changed. 266 */ 267 268void hashcd(); 269 270STATIC void 271updatepwd(dir) 272 char *dir; 273 { 274 char *new; 275 char *p; 276 277 hashcd(); /* update command hash table */ 278 cdcomppath = stalloc(strlen(dir) + 1); 279 scopy(dir, cdcomppath); 280 STARTSTACKSTR(new); 281 if (*dir != '/') { 282 if (curdir == NULL) 283 return; 284 p = curdir; 285 while (*p) 286 STPUTC(*p++, new); 287 if (p[-1] == '/') 288 STUNPUTC(new); 289 } 290 while ((p = getcomponent()) != NULL) { 291 if (equal(p, "..")) { 292 while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 293 } else if (*p != '\0' && ! equal(p, ".")) { 294 STPUTC('/', new); 295 while (*p) 296 STPUTC(*p++, new); 297 } 298 } 299 if (new == stackblock()) 300 STPUTC('/', new); 301 STACKSTRNUL(new); 302 INTOFF; 303 if (prevdir) 304 ckfree(prevdir); 305 prevdir = curdir; 306 curdir = savestr(stackblock()); 307 INTON; 308} 309 310 311 312int 313pwdcmd(argc, argv) char **argv; { 314 getpwd(); 315 out1str(curdir); 316 out1c('\n'); 317 return 0; 318} 319 320 321 322/* 323 * Run /bin/pwd to find out what the current directory is. We suppress 324 * interrupts throughout most of this, but the user can still break out 325 * of it by killing the pwd program. If we already know the current 326 * directory, this routine returns immediately. 327 */ 328 329#define MAXPWD 256 330 331STATIC void 332getpwd() { 333 char buf[MAXPWD]; 334 char *p; 335 int i; 336 int status; 337 struct job *jp; 338 int pip[2]; 339 340 if (curdir) 341 return; 342 INTOFF; 343 if (pipe(pip) < 0) 344 error("Pipe call failed"); 345 jp = makejob((union node *)NULL, 1); 346 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 347 close(pip[0]); 348 if (pip[1] != 1) { 349 close(1); 350 copyfd(pip[1], 1); 351 close(pip[1]); 352 } 353 execl("/bin/pwd", "pwd", (char *)0); 354 error("Cannot exec /bin/pwd"); 355 } 356 close(pip[1]); 357 pip[1] = -1; 358 p = buf; 359 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 360 || i == -1 && errno == EINTR) { 361 if (i > 0) 362 p += i; 363 } 364 close(pip[0]); 365 pip[0] = -1; 366 status = waitforjob(jp); 367 if (status != 0) 368 error((char *)0); 369 if (i < 0 || p == buf || p[-1] != '\n') 370 error("pwd command failed"); 371 p[-1] = '\0'; 372 curdir = savestr(buf); 373 INTON; 374} 375