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[] = "from: @(#)cd.c 8.1 (Berkeley) 5/31/93";*/ 39 static char *rcsid = "$Id: cd.c,v 1.10 1994/12/05 19:07:32 cgd Exp $"; 40 #endif /* not lint */ 41 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <unistd.h> 45 #include <errno.h> 46 47 /* 48 * The cd and pwd commands. 49 */ 50 51 #include "shell.h" 52 #include "var.h" 53 #include "nodes.h" /* for jobs.h */ 54 #include "jobs.h" 55 #include "options.h" 56 #include "output.h" 57 #include "memalloc.h" 58 #include "error.h" 59 #include "mystring.h" 60 #include "extern.h" 61 62 63 #ifdef __STDC__ 64 STATIC int docd(char *, int); 65 STATIC void updatepwd(char *); 66 STATIC void getpwd(void); 67 STATIC char *getcomponent(void); 68 #else 69 STATIC int docd(); 70 STATIC void updatepwd(); 71 STATIC void getpwd(); 72 STATIC char *getcomponent(); 73 #endif 74 75 76 char *curdir; /* current working directory */ 77 char *prevdir; /* previous working directory */ 78 STATIC char *cdcomppath; 79 80 int 81 cdcmd(argc, argv) 82 int argc; 83 char **argv; 84 { 85 char *dest; 86 char *path; 87 char *p; 88 struct stat statb; 89 char *padvance(); 90 int print = 0; 91 92 nextopt(nullstr); 93 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 94 error("HOME not set"); 95 if (dest[0] == '-' && dest[1] == '\0') { 96 dest = prevdir ? prevdir : curdir; 97 print = 1; 98 } 99 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 100 path = nullstr; 101 while ((p = padvance(&path, dest)) != NULL) { 102 if (stat(p, &statb) >= 0 103 && (statb.st_mode & S_IFMT) == S_IFDIR) { 104 if (!print) { 105 /* 106 * XXX - rethink 107 */ 108 if (p[0] == '.' && p[1] == '/') 109 p += 2; 110 print = strcmp(p, dest); 111 } 112 if (docd(p, print) >= 0) 113 return 0; 114 115 } 116 } 117 error("can't cd to %s", dest); 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 { 136 INTOFF; 137 if (chdir(dest) < 0) { 138 INTON; 139 return -1; 140 } 141 updatepwd(dest); 142 INTON; 143 if (print && iflag) 144 out1fmt("%s\n", stackblock()); 145 return 0; 146 } 147 148 #else 149 150 151 152 STATIC int 153 docd(dest, print) 154 char *dest; 155 int print; 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) < 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) 320 int argc; 321 char **argv; 322 { 323 getpwd(); 324 out1str(curdir); 325 out1c('\n'); 326 return 0; 327 } 328 329 330 331 /* 332 * Run /bin/pwd to find out what the current directory is. We suppress 333 * interrupts throughout most of this, but the user can still break out 334 * of it by killing the pwd program. If we already know the current 335 * directory, this routine returns immediately. 336 */ 337 338 #define MAXPWD 256 339 340 STATIC void 341 getpwd() { 342 char buf[MAXPWD]; 343 char *p; 344 int i; 345 int status; 346 struct job *jp; 347 int pip[2]; 348 349 if (curdir) 350 return; 351 INTOFF; 352 if (pipe(pip) < 0) 353 error("Pipe call failed"); 354 jp = makejob((union node *)NULL, 1); 355 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 356 close(pip[0]); 357 if (pip[1] != 1) { 358 close(1); 359 copyfd(pip[1], 1); 360 close(pip[1]); 361 } 362 execl("/bin/pwd", "pwd", (char *)0); 363 error("Cannot exec /bin/pwd"); 364 } 365 close(pip[1]); 366 pip[1] = -1; 367 p = buf; 368 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 369 || i == -1 && errno == EINTR) { 370 if (i > 0) 371 p += i; 372 } 373 close(pip[0]); 374 pip[0] = -1; 375 status = waitforjob(jp); 376 if (status != 0) 377 error((char *)0); 378 if (i < 0 || p == buf || p[-1] != '\n') 379 error("pwd command failed"); 380 p[-1] = '\0'; 381 curdir = savestr(buf); 382 INTON; 383 } 384