1 /*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * 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 5.2 (Berkeley) 3/13/91";*/ 39 static char rcsid[] = "$Id: cd.c,v 1.5 1993/08/01 18:58:22 mycroft Exp $"; 40 #endif /* not lint */ 41 42 /* 43 * The cd and pwd commands. 44 */ 45 46 #include "shell.h" 47 #include "var.h" 48 #include "nodes.h" /* for jobs.h */ 49 #include "jobs.h" 50 #include "options.h" 51 #include "output.h" 52 #include "memalloc.h" 53 #include "error.h" 54 #include "mystring.h" 55 #include <sys/types.h> 56 #include <sys/stat.h> 57 #include <errno.h> 58 59 60 #ifdef __STDC__ 61 STATIC int docd(char *, int); 62 STATIC void updatepwd(char *); 63 STATIC void getpwd(void); 64 STATIC char *getcomponent(void); 65 #else 66 STATIC int docd(); 67 STATIC void updatepwd(); 68 STATIC void getpwd(); 69 STATIC char *getcomponent(); 70 #endif 71 72 73 char *curdir; /* current working directory */ 74 STATIC char *cdcomppath; 75 76 #if UDIR 77 extern int didudir; /* set if /u/logname expanded */ 78 #endif 79 80 81 int 82 cdcmd(argc, argv) char **argv; { 83 char *dest; 84 char *path; 85 char *p; 86 struct stat statb; 87 char *padvance(); 88 89 nextopt(nullstr); 90 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 91 error("HOME not set"); 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 && docd(p, strcmp(p, dest)) >= 0) 98 return 0; 99 } 100 error("can't cd to %s", dest); 101 } 102 103 104 /* 105 * Actually do the chdir. If the name refers to symbolic links, we 106 * compute the actual directory name before doing the cd. In an 107 * interactive shell, print the directory name if "print" is nonzero 108 * or if the name refers to a symbolic link. We also print the name 109 * if "/u/logname" was expanded in it, since this is similar to a 110 * symbolic link. (The check for this breaks if the user gives the 111 * cd command some additional, unused arguments.) 112 */ 113 114 #if SYMLINKS == 0 115 STATIC int 116 docd(dest, print) 117 char *dest; 118 { 119 #if UDIR 120 if (didudir) 121 print = 1; 122 #endif 123 INTOFF; 124 if (chdir(dest) < 0) { 125 INTON; 126 return -1; 127 } 128 updatepwd(dest); 129 INTON; 130 #ifdef not 131 if (print && iflag) 132 out1fmt("%s\n", stackblock()); 133 #endif 134 return 0; 135 } 136 137 #else 138 139 140 141 STATIC int 142 docd(dest, print) 143 char *dest; 144 { 145 register char *p; 146 register char *q; 147 char *symlink; 148 char *component; 149 struct stat statb; 150 int first; 151 int i; 152 153 TRACE(("docd(\"%s\", %d) called\n", dest, print)); 154 #if UDIR 155 if (didudir) 156 print = 1; 157 #endif 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 #ifdef not 227 if (print && iflag) 228 out1fmt("%s\n", p); 229 #endif 230 return 0; 231 } 232 #endif /* SYMLINKS */ 233 234 235 236 /* 237 * Get the next component of the path name pointed to by cdcomppath. 238 * This routine overwrites the string pointed to by cdcomppath. 239 */ 240 241 STATIC char * 242 getcomponent() { 243 register char *p; 244 char *start; 245 246 if ((p = cdcomppath) == NULL) 247 return NULL; 248 start = cdcomppath; 249 while (*p != '/' && *p != '\0') 250 p++; 251 if (*p == '\0') { 252 cdcomppath = NULL; 253 } else { 254 *p++ = '\0'; 255 cdcomppath = p; 256 } 257 return start; 258 } 259 260 261 262 /* 263 * Update curdir (the name of the current directory) in response to a 264 * cd command. We also call hashcd to let the routines in exec.c know 265 * that the current directory has changed. 266 */ 267 268 void hashcd(); 269 270 STATIC void 271 updatepwd(dir) 272 char *dir; 273 { 274 char *new; 275 char *p; 276 277 hashcd(); /* update command hash table */ 278 cdcomppath = stalloc(strlen(dir) + 1); 279 scopy(dir, cdcomppath); 280 STARTSTACKSTR(new); 281 if (*dir != '/') { 282 if (curdir == NULL) 283 return; 284 p = curdir; 285 while (*p) 286 STPUTC(*p++, new); 287 if (p[-1] == '/') 288 STUNPUTC(new); 289 } 290 while ((p = getcomponent()) != NULL) { 291 if (equal(p, "..")) { 292 while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 293 } else if (*p != '\0' && ! equal(p, ".")) { 294 STPUTC('/', new); 295 while (*p) 296 STPUTC(*p++, new); 297 } 298 } 299 if (new == stackblock()) 300 STPUTC('/', new); 301 STACKSTRNUL(new); 302 if (curdir) 303 ckfree(curdir); 304 curdir = savestr(stackblock()); 305 } 306 307 308 309 int 310 pwdcmd(argc, argv) char **argv; { 311 getpwd(); 312 out1str(curdir); 313 out1c('\n'); 314 return 0; 315 } 316 317 318 319 /* 320 * Run /bin/pwd to find out what the current directory is. We suppress 321 * interrupts throughout most of this, but the user can still break out 322 * of it by killing the pwd program. If we already know the current 323 * directory, this routine returns immediately. 324 */ 325 326 #define MAXPWD 256 327 328 STATIC void 329 getpwd() { 330 char buf[MAXPWD]; 331 char *p; 332 int i; 333 int status; 334 struct job *jp; 335 int pip[2]; 336 337 if (curdir) 338 return; 339 INTOFF; 340 if (pipe(pip) < 0) 341 error("Pipe call failed"); 342 jp = makejob((union node *)NULL, 1); 343 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { 344 close(pip[0]); 345 if (pip[1] != 1) { 346 close(1); 347 copyfd(pip[1], 1); 348 close(pip[1]); 349 } 350 execl("/bin/pwd", "pwd", (char *)0); 351 /* error("Cannot exec /bin/pwd");*/ 352 out2str("Cannot exec /bin/pwd\n"); /* 22 Aug 92*/ 353 flushall(); 354 _exit(1); 355 } 356 close(pip[1]); 357 pip[1] = -1; 358 p = buf; 359 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 360 || i == -1 && errno == EINTR) { 361 if (i > 0) 362 p += i; 363 } 364 close(pip[0]); 365 pip[0] = -1; 366 status = waitforjob(jp); 367 if (status != 0) 368 error((char *)0); 369 if (i < 0 || p == buf || p[-1] != '\n') 370 error("pwd command failed"); 371 p[-1] = '\0'; 372 curdir = savestr(buf); 373 INTON; 374 } 375