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