1 1.56 kre /* $NetBSD: cd.c,v 1.56 2025/08/18 18:58:59 kre Exp $ */ 2 1.12 cgd 3 1.1 cgd /*- 4 1.6 jtc * Copyright (c) 1991, 1993 5 1.6 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Kenneth Almquist. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.31 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.23 christos #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.12 cgd #if 0 38 1.13 christos static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 39 1.12 cgd #else 40 1.56 kre __RCSID("$NetBSD: cd.c,v 1.56 2025/08/18 18:58:59 kre Exp $"); 41 1.12 cgd #endif 42 1.1 cgd #endif /* not lint */ 43 1.1 cgd 44 1.9 cgd #include <sys/types.h> 45 1.9 cgd #include <sys/stat.h> 46 1.51 kre #include <stdbool.h> 47 1.13 christos #include <stdlib.h> 48 1.17 thorpej #include <string.h> 49 1.9 cgd #include <unistd.h> 50 1.9 cgd #include <errno.h> 51 1.9 cgd 52 1.1 cgd /* 53 1.1 cgd * The cd and pwd commands. 54 1.1 cgd */ 55 1.1 cgd 56 1.1 cgd #include "shell.h" 57 1.1 cgd #include "var.h" 58 1.1 cgd #include "nodes.h" /* for jobs.h */ 59 1.1 cgd #include "jobs.h" 60 1.1 cgd #include "options.h" 61 1.43 christos #include "builtins.h" 62 1.1 cgd #include "output.h" 63 1.1 cgd #include "memalloc.h" 64 1.1 cgd #include "error.h" 65 1.16 christos #include "exec.h" 66 1.13 christos #include "redir.h" 67 1.1 cgd #include "mystring.h" 68 1.13 christos #include "show.h" 69 1.14 christos #include "cd.h" 70 1.1 cgd 71 1.51 kre STATIC int docd(const char *, bool, bool); 72 1.29 christos STATIC char *getcomponent(void); 73 1.51 kre STATIC bool updatepwd(const char *); 74 1.34 dsl STATIC void find_curdir(int noerror); 75 1.51 kre STATIC bool is_curdir(const char *); 76 1.1 cgd 77 1.14 christos char *curdir = NULL; /* current working directory */ 78 1.6 jtc char *prevdir; /* previous working directory */ 79 1.1 cgd STATIC char *cdcomppath; 80 1.1 cgd 81 1.1 cgd int 82 1.29 christos cdcmd(int argc, char **argv) 83 1.10 cgd { 84 1.27 christos const char *dest; 85 1.48 kre const char *path, *cp; 86 1.48 kre char *p; 87 1.36 dsl char *d; 88 1.1 cgd struct stat statb; 89 1.55 kre int print = cdprint; /* set -o cdprint to enable */ 90 1.55 kre int err = ENOENT; 91 1.55 kre int err_set = -1; 92 1.55 kre bool eopt = false; 93 1.51 kre char opt; 94 1.1 cgd 95 1.51 kre while ((opt = nextopt("Pe")) != '\0') 96 1.51 kre if (opt == 'e') 97 1.51 kre eopt = true; 98 1.29 christos 99 1.34 dsl /* 100 1.34 dsl * Try (quite hard) to have 'curdir' defined, nothing has set 101 1.34 dsl * it on entry to the shell, but we want 'cd fred; cd -' to work. 102 1.34 dsl */ 103 1.29 christos getpwd(1); 104 1.29 christos dest = *argptr; 105 1.29 christos if (dest == NULL) { 106 1.29 christos dest = bltinlookup("HOME", 1); 107 1.29 christos if (dest == NULL) 108 1.29 christos error("HOME not set"); 109 1.48 kre } else if (argptr[1]) { 110 1.48 kre /* Do 'ksh' style substitution */ 111 1.48 kre if (!curdir) 112 1.48 kre error("PWD not set"); 113 1.48 kre p = strstr(curdir, dest); 114 1.48 kre if (!p) 115 1.55 kre error("bad substitution (no \"%s\" in \"%s\")", 116 1.55 kre dest, curdir); 117 1.48 kre d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1); 118 1.48 kre memcpy(d, curdir, p - curdir); 119 1.48 kre strcpy(d + (p - curdir), argptr[1]); 120 1.48 kre strcat(d, p + strlen(dest)); 121 1.48 kre dest = d; 122 1.48 kre print = 1; 123 1.48 kre } else if (dest[0] == '-' && dest[1] == '\0') { 124 1.6 jtc dest = prevdir ? prevdir : curdir; 125 1.6 jtc print = 1; 126 1.6 jtc } 127 1.54 kre #if 0 /* POSIX 2024 says this must be an error */ 128 1.34 dsl if (*dest == '\0') 129 1.34 dsl dest = "."; 130 1.54 kre #endif 131 1.48 kre 132 1.48 kre cp = dest; 133 1.48 kre if (*cp == '.' && *++cp == '.') 134 1.48 kre cp++; 135 1.48 kre if (*cp == 0 || *cp == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 136 1.1 cgd path = nullstr; 137 1.55 kre 138 1.55 kre cp = dest; 139 1.48 kre while ((p = padvance(&path, dest, 0)) != NULL) { 140 1.55 kre int dopr = print; 141 1.55 kre int x; 142 1.55 kre 143 1.48 kre stunalloc(p); 144 1.55 kre /* until the next stack write, p remains valid */ 145 1.48 kre 146 1.55 kre if (stat(p, &statb) < 0) { 147 1.55 kre if (err_set < 0 && errno != ENOENT) { 148 1.55 kre err = errno; 149 1.55 kre cp = stalloc((int)strlen(p) + 1); 150 1.55 kre err_set = 0; 151 1.55 kre } 152 1.55 kre continue; 153 1.55 kre } 154 1.55 kre if (!S_ISDIR(statb.st_mode)) { 155 1.55 kre if (err_set <= 0) { 156 1.55 kre err = ENOTDIR; 157 1.55 kre cp = stalloc((int)strlen(p) + 1); 158 1.55 kre err_set = 1; 159 1.55 kre } 160 1.55 kre continue; 161 1.55 kre } 162 1.6 jtc 163 1.55 kre if (!print) 164 1.55 kre dopr = strcmp(p, dest); 165 1.55 kre 166 1.55 kre if ((x = docd(p, dopr != 0, eopt)) >= 0) 167 1.55 kre return x; 168 1.55 kre 169 1.55 kre if (err_set <= 0 && errno != ENOENT) { 170 1.55 kre err = errno; 171 1.55 kre cp = stalloc(strlen(p) + 1); 172 1.55 kre err_set = 1; 173 1.6 jtc } 174 1.1 cgd } 175 1.55 kre error("Can't cd to \"%s\": %s", cp, strerror(err)); 176 1.26 mycroft /* NOTREACHED */ 177 1.1 cgd } 178 1.1 cgd 179 1.1 cgd 180 1.1 cgd /* 181 1.15 jtc * Actually do the chdir. In an interactive shell, print the 182 1.15 jtc * directory name if "print" is nonzero. 183 1.1 cgd */ 184 1.1 cgd 185 1.1 cgd STATIC int 186 1.51 kre docd(const char *dest, bool print, bool eopt) 187 1.15 jtc { 188 1.51 kre bool gotpwd; 189 1.51 kre 190 1.48 kre #if 0 /* no "cd -L" (ever) so all this is just a waste of time ... */ 191 1.20 tls char *p; 192 1.20 tls char *q; 193 1.19 cjs char *component; 194 1.19 cjs struct stat statb; 195 1.19 cjs int first; 196 1.19 cjs int badstat; 197 1.15 jtc 198 1.19 cjs /* 199 1.19 cjs * Check each component of the path. If we find a symlink or 200 1.19 cjs * something we can't stat, clear curdir to force a getcwd() 201 1.19 cjs * next time we get the value of the current directory. 202 1.19 cjs */ 203 1.19 cjs badstat = 0; 204 1.19 cjs cdcomppath = stalloc(strlen(dest) + 1); 205 1.19 cjs scopy(dest, cdcomppath); 206 1.19 cjs STARTSTACKSTR(p); 207 1.19 cjs if (*dest == '/') { 208 1.19 cjs STPUTC('/', p); 209 1.19 cjs cdcomppath++; 210 1.19 cjs } 211 1.19 cjs first = 1; 212 1.19 cjs while ((q = getcomponent()) != NULL) { 213 1.19 cjs if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) 214 1.19 cjs continue; 215 1.19 cjs if (! first) 216 1.19 cjs STPUTC('/', p); 217 1.19 cjs first = 0; 218 1.19 cjs component = q; 219 1.19 cjs while (*q) 220 1.19 cjs STPUTC(*q++, p); 221 1.19 cjs if (equal(component, "..")) 222 1.19 cjs continue; 223 1.19 cjs STACKSTRNUL(p); 224 1.47 christos if (lstat(stackblock(), &statb) < 0) { 225 1.19 cjs badstat = 1; 226 1.19 cjs break; 227 1.19 cjs } 228 1.19 cjs } 229 1.48 kre #endif 230 1.19 cjs 231 1.51 kre CTRACE(DBG_CMDS, ("docd(\"%s\", %s, %s) called\n", dest, 232 1.51 kre print ? "true" : "false", eopt ? "true" : "false")); 233 1.51 kre 234 1.55 kre 235 1.1 cgd INTOFF; 236 1.1 cgd if (chdir(dest) < 0) { 237 1.55 kre INTON; /* If int was pending, this does not return */ 238 1.1 cgd return -1; 239 1.1 cgd } 240 1.51 kre gotpwd = updatepwd(NULL); /* only do cd -P, no "pretend" -L mode */ 241 1.1 cgd INTON; 242 1.56 kre if (!gotpwd && eopt) 243 1.55 kre sh_warnx("Unable to determine new working directory"); 244 1.55 kre else if (print && (iflag || posix)) 245 1.55 kre out1fmt("%s\n", curdir); 246 1.51 kre return gotpwd || !eopt ? 0 : 1; 247 1.1 cgd } 248 1.1 cgd 249 1.1 cgd 250 1.1 cgd /* 251 1.1 cgd * Get the next component of the path name pointed to by cdcomppath. 252 1.1 cgd * This routine overwrites the string pointed to by cdcomppath. 253 1.1 cgd */ 254 1.1 cgd 255 1.1 cgd STATIC char * 256 1.36 dsl getcomponent(void) 257 1.29 christos { 258 1.20 tls char *p; 259 1.1 cgd char *start; 260 1.1 cgd 261 1.1 cgd if ((p = cdcomppath) == NULL) 262 1.1 cgd return NULL; 263 1.1 cgd start = cdcomppath; 264 1.1 cgd while (*p != '/' && *p != '\0') 265 1.1 cgd p++; 266 1.1 cgd if (*p == '\0') { 267 1.1 cgd cdcomppath = NULL; 268 1.1 cgd } else { 269 1.1 cgd *p++ = '\0'; 270 1.1 cgd cdcomppath = p; 271 1.1 cgd } 272 1.1 cgd return start; 273 1.1 cgd } 274 1.1 cgd 275 1.1 cgd 276 1.1 cgd 277 1.1 cgd /* 278 1.1 cgd * Update curdir (the name of the current directory) in response to a 279 1.1 cgd * cd command. We also call hashcd to let the routines in exec.c know 280 1.1 cgd * that the current directory has changed. 281 1.1 cgd */ 282 1.1 cgd 283 1.51 kre STATIC bool 284 1.36 dsl updatepwd(const char *dir) 285 1.29 christos { 286 1.1 cgd char *new; 287 1.1 cgd char *p; 288 1.1 cgd 289 1.1 cgd hashcd(); /* update command hash table */ 290 1.19 cjs 291 1.19 cjs /* 292 1.19 cjs * If our argument is NULL, we don't know the current directory 293 1.19 cjs * any more because we traversed a symbolic link or something 294 1.51 kre * we couldn't stat(). Or we simply don't trust what we had. 295 1.19 cjs */ 296 1.25 ross if (dir == NULL || curdir == NULL) { 297 1.19 cjs if (prevdir) 298 1.19 cjs ckfree(prevdir); 299 1.19 cjs INTOFF; 300 1.19 cjs prevdir = curdir; 301 1.19 cjs curdir = NULL; 302 1.29 christos getpwd(1); 303 1.19 cjs INTON; 304 1.42 uebayasi if (curdir) { 305 1.42 uebayasi setvar("OLDPWD", prevdir, VEXPORT); 306 1.29 christos setvar("PWD", curdir, VEXPORT); 307 1.51 kre return true; 308 1.42 uebayasi } else 309 1.29 christos unsetvar("PWD", 0); 310 1.51 kre return false; 311 1.19 cjs } 312 1.51 kre 313 1.51 kre /* XXX none of the following code is ever executed any more */ 314 1.51 kre 315 1.1 cgd cdcomppath = stalloc(strlen(dir) + 1); 316 1.1 cgd scopy(dir, cdcomppath); 317 1.1 cgd STARTSTACKSTR(new); 318 1.1 cgd if (*dir != '/') { 319 1.1 cgd p = curdir; 320 1.1 cgd while (*p) 321 1.1 cgd STPUTC(*p++, new); 322 1.1 cgd if (p[-1] == '/') 323 1.1 cgd STUNPUTC(new); 324 1.1 cgd } 325 1.1 cgd while ((p = getcomponent()) != NULL) { 326 1.1 cgd if (equal(p, "..")) { 327 1.1 cgd while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 328 1.1 cgd } else if (*p != '\0' && ! equal(p, ".")) { 329 1.1 cgd STPUTC('/', new); 330 1.1 cgd while (*p) 331 1.1 cgd STPUTC(*p++, new); 332 1.1 cgd } 333 1.1 cgd } 334 1.1 cgd if (new == stackblock()) 335 1.1 cgd STPUTC('/', new); 336 1.1 cgd STACKSTRNUL(new); 337 1.6 jtc INTOFF; 338 1.6 jtc if (prevdir) 339 1.6 jtc ckfree(prevdir); 340 1.6 jtc prevdir = curdir; 341 1.1 cgd curdir = savestr(stackblock()); 342 1.42 uebayasi setvar("OLDPWD", prevdir, VEXPORT); 343 1.28 he setvar("PWD", curdir, VEXPORT); 344 1.6 jtc INTON; 345 1.51 kre return true; 346 1.51 kre } 347 1.51 kre 348 1.51 kre /* 349 1.51 kre * Test whether we are currently in the direcory given 350 1.51 kre * (provided it is given, and is absolute) 351 1.51 kre * ie: determine if path is fully qualified pathname of "." 352 1.51 kre */ 353 1.51 kre STATIC bool 354 1.51 kre is_curdir(const char *path) 355 1.51 kre { 356 1.51 kre struct stat stdot, stpath; 357 1.51 kre 358 1.51 kre return path != NULL && 359 1.51 kre *path == '/' && 360 1.51 kre stat(".", &stdot) != -1 && 361 1.51 kre stat(path, &stpath) != -1 && 362 1.51 kre stdot.st_dev == stpath.st_dev && 363 1.51 kre stdot.st_ino == stpath.st_ino; 364 1.1 cgd } 365 1.1 cgd 366 1.34 dsl /* 367 1.34 dsl * Posix says the default should be 'pwd -L' (as below), however 368 1.34 dsl * the 'cd' command (above) does something much nearer to the 369 1.34 dsl * posix 'cd -P' (not the posix default of 'cd -L'). 370 1.34 dsl * If 'cd' is changed to support -P/L then the default here 371 1.34 dsl * needs to be revisited if the historic behaviour is to be kept. 372 1.34 dsl */ 373 1.1 cgd 374 1.1 cgd int 375 1.29 christos pwdcmd(int argc, char **argv) 376 1.10 cgd { 377 1.33 dsl int i; 378 1.33 dsl char opt = 'L'; 379 1.33 dsl 380 1.33 dsl while ((i = nextopt("LP")) != '\0') 381 1.33 dsl opt = i; 382 1.33 dsl if (*argptr) 383 1.33 dsl error("unexpected argument"); 384 1.33 dsl 385 1.34 dsl if (opt == 'L') 386 1.33 dsl getpwd(0); 387 1.34 dsl else 388 1.34 dsl find_curdir(0); 389 1.33 dsl 390 1.51 kre #if 0 /* posix has been changed to forbid this */ 391 1.42 uebayasi setvar("OLDPWD", prevdir, VEXPORT); 392 1.34 dsl setvar("PWD", curdir, VEXPORT); 393 1.51 kre #endif 394 1.51 kre 395 1.51 kre if (!is_curdir(curdir)) { 396 1.51 kre find_curdir(1); 397 1.51 kre if (curdir == NULL) 398 1.51 kre error("Unable to find current directory"); 399 1.51 kre } 400 1.52 kre 401 1.52 kre flushout(out1); /* make sure buffer is empty */ 402 1.52 kre clr_err(out1); /* and forget any earlier errors */ 403 1.34 dsl out1str(curdir); 404 1.1 cgd out1c('\n'); 405 1.52 kre flushout(out1); 406 1.52 kre if (io_err(out1)) 407 1.52 kre error("stdout: %s", strerror(errno)); 408 1.52 kre 409 1.1 cgd return 0; 410 1.1 cgd } 411 1.1 cgd 412 1.1 cgd 413 1.1 cgd 414 1.37 christos void 415 1.37 christos initpwd(void) 416 1.37 christos { 417 1.39 simonb getpwd(1); 418 1.39 simonb if (curdir) 419 1.39 simonb setvar("PWD", curdir, VEXPORT); 420 1.39 simonb else 421 1.39 simonb sh_warnx("Cannot determine current working directory"); 422 1.37 christos } 423 1.14 christos 424 1.14 christos #define MAXPWD 256 425 1.14 christos 426 1.1 cgd /* 427 1.14 christos * Find out what the current directory is. If we already know the current 428 1.1 cgd * directory, this routine returns immediately. 429 1.1 cgd */ 430 1.14 christos void 431 1.29 christos getpwd(int noerror) 432 1.14 christos { 433 1.29 christos char *pwd; 434 1.29 christos static int first = 1; 435 1.1 cgd 436 1.1 cgd if (curdir) 437 1.1 cgd return; 438 1.29 christos 439 1.29 christos if (first) { 440 1.53 kre /* 441 1.53 kre * Note that this happens via the call from initpwd() 442 1.53 kre * just above, which is called early from main() during 443 1.53 kre * sh startup, so fetching PWD from the entry environment 444 1.53 kre * (which is what getenv() does) is acceptable. Here we 445 1.53 kre * could use normal sh var lookup functions instead, as 446 1.53 kre * the arriving environment has already been imported before 447 1.53 kre * we get here, but it makes little difference. 448 1.53 kre * 449 1.53 kre * XXX What would be better perhaps would be to move all of 450 1.53 kre * this into initpwd() instead of here, so we could get rid of 451 1.53 kre * this "first" static - that function is only ever called once. 452 1.53 kre * XXX Some other day. 453 1.53 kre */ 454 1.33 dsl first = 0; 455 1.29 christos pwd = getenv("PWD"); 456 1.51 kre if (is_curdir(pwd)) { 457 1.29 christos curdir = savestr(pwd); 458 1.29 christos return; 459 1.29 christos } 460 1.29 christos } 461 1.33 dsl 462 1.34 dsl find_curdir(noerror); 463 1.33 dsl 464 1.33 dsl return; 465 1.33 dsl } 466 1.33 dsl 467 1.34 dsl STATIC void 468 1.34 dsl find_curdir(int noerror) 469 1.33 dsl { 470 1.33 dsl int i; 471 1.33 dsl char *pwd; 472 1.29 christos 473 1.14 christos /* 474 1.14 christos * Things are a bit complicated here; we could have just used 475 1.14 christos * getcwd, but traditionally getcwd is implemented using popen 476 1.14 christos * to /bin/pwd. This creates a problem for us, since we cannot 477 1.14 christos * keep track of the job if it is being ran behind our backs. 478 1.51 kre * XXX That's not actually the problem, a process created and 479 1.51 kre * XXX destroyed that we know nothing about is harmless. The 480 1.51 kre * XXX problem is that old popen() implementations would use 481 1.51 kre * XXX wait(2) to await completion of the command, and that might 482 1.51 kre * XXX collect (and ignore) our children. As long as we are 483 1.51 kre * XXX confident that popen() uses waitpid() (or the equv) there 484 1.51 kre * XXX would not be a problem. But how do we know that? 485 1.14 christos * So we re-implement getcwd(), and we suppress interrupts 486 1.14 christos * throughout the process. This is not completely safe, since 487 1.14 christos * the user can still break out of it by killing the pwd program. 488 1.14 christos * We still try to use getcwd for systems that we know have a 489 1.14 christos * c implementation of getcwd, that does not open a pipe to 490 1.14 christos * /bin/pwd. 491 1.14 christos */ 492 1.22 christos #if defined(__NetBSD__) || defined(__SVR4) 493 1.21 christos 494 1.29 christos for (i = MAXPWD;; i *= 2) { 495 1.29 christos pwd = stalloc(i); 496 1.34 dsl if (getcwd(pwd, i) != NULL) { 497 1.34 dsl curdir = savestr(pwd); 498 1.49 kre stunalloc(pwd); 499 1.34 dsl return; 500 1.34 dsl } 501 1.29 christos stunalloc(pwd); 502 1.29 christos if (errno == ERANGE) 503 1.29 christos continue; 504 1.29 christos if (!noerror) 505 1.29 christos error("getcwd() failed: %s", strerror(errno)); 506 1.34 dsl return; 507 1.21 christos } 508 1.14 christos #else 509 1.14 christos { 510 1.14 christos char *p; 511 1.14 christos int status; 512 1.14 christos struct job *jp; 513 1.14 christos int pip[2]; 514 1.14 christos 515 1.34 dsl pwd = stalloc(MAXPWD); 516 1.14 christos INTOFF; 517 1.14 christos if (pipe(pip) < 0) 518 1.14 christos error("Pipe call failed"); 519 1.44 plunky jp = makejob(NULL, 1); 520 1.44 plunky if (forkshell(jp, NULL, FORK_NOJOB) == 0) { 521 1.14 christos (void) close(pip[0]); 522 1.46 christos movefd(pip[1], 1); 523 1.14 christos (void) execl("/bin/pwd", "pwd", (char *)0); 524 1.14 christos error("Cannot exec /bin/pwd"); 525 1.14 christos } 526 1.14 christos (void) close(pip[1]); 527 1.14 christos pip[1] = -1; 528 1.34 dsl p = pwd; 529 1.34 dsl while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0 530 1.14 christos || (i == -1 && errno == EINTR)) { 531 1.14 christos if (i > 0) 532 1.14 christos p += i; 533 1.1 cgd } 534 1.14 christos (void) close(pip[0]); 535 1.14 christos pip[0] = -1; 536 1.14 christos status = waitforjob(jp); 537 1.14 christos if (status != 0) 538 1.14 christos error((char *)0); 539 1.34 dsl if (i < 0 || p == pwd || p[-1] != '\n') { 540 1.29 christos if (noerror) { 541 1.29 christos INTON; 542 1.34 dsl return; 543 1.29 christos } 544 1.14 christos error("pwd command failed"); 545 1.29 christos } 546 1.14 christos p[-1] = '\0'; 547 1.33 dsl INTON; 548 1.34 dsl curdir = savestr(pwd); 549 1.49 kre stunalloc(pwd); 550 1.34 dsl return; 551 1.1 cgd } 552 1.15 jtc #endif 553 1.1 cgd } 554