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