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