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