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