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