Home | History | Annotate | Line # | Download | only in sh
cd.c revision 1.53.6.1
      1  1.53.6.1    martin /*	$NetBSD: cd.c,v 1.53.6.1 2025/09/02 17:48:16 martin 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.53.6.1    martin __RCSID("$NetBSD: cd.c,v 1.53.6.1 2025/09/02 17:48:16 martin 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.48       kre 	int print = cdprint;	/* set -o cdprint to enable */
     90  1.53.6.1    martin 	int err = ENOENT;
     91  1.53.6.1    martin 	int err_set = -1;
     92  1.53.6.1    martin 	bool eopt = false;
     93  1.53.6.1    martin 	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.53.6.1    martin 			error("bad substitution (no \"%s\" in \"%s\")",
    116  1.53.6.1    martin 			    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.53.6.1    martin #if 0				/* POSIX 2024 says this must be an error */
    128      1.34       dsl 	if (*dest == '\0')
    129      1.34       dsl 	        dest = ".";
    130  1.53.6.1    martin #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.53.6.1    martin 
    138  1.53.6.1    martin 	cp = dest;
    139      1.48       kre 	while ((p = padvance(&path, dest, 0)) != NULL) {
    140  1.53.6.1    martin 		int dopr = print;
    141  1.53.6.1    martin 		int x;
    142  1.53.6.1    martin 
    143      1.48       kre 		stunalloc(p);
    144  1.53.6.1    martin 		/* until the next stack write, p remains valid */
    145  1.53.6.1    martin 
    146  1.53.6.1    martin 		if (stat(p, &statb) < 0) {
    147  1.53.6.1    martin 			if (err_set < 0 && errno != ENOENT) {
    148  1.53.6.1    martin 				err = errno;
    149  1.53.6.1    martin 				cp = stalloc((int)strlen(p) + 1);
    150  1.53.6.1    martin 				err_set = 0;
    151  1.53.6.1    martin 			}
    152  1.53.6.1    martin 			continue;
    153  1.53.6.1    martin 		}
    154  1.53.6.1    martin 		if (!S_ISDIR(statb.st_mode)) {
    155  1.53.6.1    martin 			if (err_set <= 0) {
    156  1.53.6.1    martin 				err = ENOTDIR;
    157  1.53.6.1    martin 				cp = stalloc((int)strlen(p) + 1);
    158  1.53.6.1    martin 				err_set = 1;
    159  1.53.6.1    martin 			}
    160  1.53.6.1    martin 			continue;
    161  1.53.6.1    martin 		}
    162  1.53.6.1    martin 
    163  1.53.6.1    martin 		if (!print)
    164  1.53.6.1    martin 			dopr = strcmp(p, dest);
    165      1.48       kre 
    166  1.53.6.1    martin 		if ((x = docd(p, dopr != 0, eopt)) >= 0)
    167  1.53.6.1    martin 			return x;
    168       1.6       jtc 
    169  1.53.6.1    martin 		if (err_set <= 0 && errno != ENOENT) {
    170  1.53.6.1    martin 			err = errno;
    171  1.53.6.1    martin 			cp = stalloc(strlen(p) + 1);
    172  1.53.6.1    martin 			err_set = 1;
    173       1.6       jtc 		}
    174       1.1       cgd 	}
    175  1.53.6.1    martin 	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.53.6.1    martin 
    235       1.1       cgd 	INTOFF;
    236       1.1       cgd 	if (chdir(dest) < 0) {
    237  1.53.6.1    martin 		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.53.6.1    martin 	if (!gotpwd && eopt)
    243  1.53.6.1    martin 		sh_warnx("Unable to determine new working directory");
    244  1.53.6.1    martin 	else if (print && (iflag || posix))
    245  1.53.6.1    martin 		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