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