xref: /netbsd-src/bin/sh/cd.c (revision 05e80ac3ee72c9f7c886ba0431c45e6118890bed)
1*05e80ac3Skre /*	$NetBSD: cd.c,v 1.53 2022/01/31 16:54:28 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cbf48b75Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*05e80ac3Skre __RCSID("$NetBSD: cd.c,v 1.53 2022/01/31 16:54:28 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
445dad1439Scgd #include <sys/types.h>
455dad1439Scgd #include <sys/stat.h>
4682386044Skre #include <stdbool.h>
4707bae7edSchristos #include <stdlib.h>
4847f18130Sthorpej #include <string.h>
495dad1439Scgd #include <unistd.h>
505dad1439Scgd #include <errno.h>
515dad1439Scgd 
5261f28255Scgd /*
5361f28255Scgd  * The cd and pwd commands.
5461f28255Scgd  */
5561f28255Scgd 
5661f28255Scgd #include "shell.h"
5761f28255Scgd #include "var.h"
5861f28255Scgd #include "nodes.h"	/* for jobs.h */
5961f28255Scgd #include "jobs.h"
6061f28255Scgd #include "options.h"
614fc4fe2eSchristos #include "builtins.h"
6261f28255Scgd #include "output.h"
6361f28255Scgd #include "memalloc.h"
6461f28255Scgd #include "error.h"
6570ad20e9Schristos #include "exec.h"
6607bae7edSchristos #include "redir.h"
6761f28255Scgd #include "mystring.h"
6807bae7edSchristos #include "show.h"
69ff008dabSchristos #include "cd.h"
7061f28255Scgd 
7182386044Skre STATIC int docd(const char *, bool, bool);
72c02b3bbdSchristos STATIC char *getcomponent(void);
7382386044Skre STATIC bool updatepwd(const char *);
74296844feSdsl STATIC void find_curdir(int noerror);
7582386044Skre STATIC bool is_curdir(const char *);
7661f28255Scgd 
77ff008dabSchristos char *curdir = NULL;		/* current working directory */
7837ed7877Sjtc char *prevdir;			/* previous working directory */
7961f28255Scgd STATIC char *cdcomppath;
8061f28255Scgd 
8161f28255Scgd int
cdcmd(int argc,char ** argv)82c02b3bbdSchristos cdcmd(int argc, char **argv)
834ce0d34aScgd {
843d424690Schristos 	const char *dest;
851676135eSkre 	const char *path, *cp;
861676135eSkre 	char *p;
878c772bc7Sdsl 	char *d;
8861f28255Scgd 	struct stat statb;
8982386044Skre 	char opt;
9082386044Skre 	bool eopt = false;
911676135eSkre 	int print = cdprint;	/* set -o cdprint to enable */
9261f28255Scgd 
9382386044Skre 	while ((opt = nextopt("Pe")) != '\0')
9482386044Skre 		if (opt == 'e')
9582386044Skre 			eopt = true;
96c02b3bbdSchristos 
97296844feSdsl 	/*
98296844feSdsl 	 * Try (quite hard) to have 'curdir' defined, nothing has set
99296844feSdsl 	 * it on entry to the shell, but we want 'cd fred; cd -' to work.
100296844feSdsl 	 */
101c02b3bbdSchristos 	getpwd(1);
102c02b3bbdSchristos 	dest = *argptr;
103c02b3bbdSchristos 	if (dest == NULL) {
104c02b3bbdSchristos 		dest = bltinlookup("HOME", 1);
105c02b3bbdSchristos 		if (dest == NULL)
10661f28255Scgd 			error("HOME not set");
1071676135eSkre 	} else if (argptr[1]) {
108c02b3bbdSchristos 		/* Do 'ksh' style substitution */
109c02b3bbdSchristos 		if (!curdir)
110c02b3bbdSchristos 			error("PWD not set");
111c02b3bbdSchristos 		p = strstr(curdir, dest);
112c02b3bbdSchristos 		if (!p)
113c02b3bbdSchristos 			error("bad substitution");
114c02b3bbdSchristos 		d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1);
115c02b3bbdSchristos 		memcpy(d, curdir, p - curdir);
116c02b3bbdSchristos 		strcpy(d + (p - curdir), argptr[1]);
117c02b3bbdSchristos 		strcat(d, p + strlen(dest));
118c02b3bbdSchristos 		dest = d;
119c02b3bbdSchristos 		print = 1;
1201676135eSkre 	} else if (dest[0] == '-' && dest[1] == '\0') {
12137ed7877Sjtc 		dest = prevdir ? prevdir : curdir;
12237ed7877Sjtc 		print = 1;
12337ed7877Sjtc 	}
124296844feSdsl 	if (*dest == '\0')
125296844feSdsl 	        dest = ".";
1261676135eSkre 
1271676135eSkre 	cp = dest;
1281676135eSkre 	if (*cp == '.' && *++cp == '.')
1291676135eSkre 	    cp++;
1301676135eSkre 	if (*cp == 0 || *cp == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
13161f28255Scgd 		path = nullstr;
1321676135eSkre 	while ((p = padvance(&path, dest, 0)) != NULL) {
1331676135eSkre 		stunalloc(p);
134f5ad44b6Smycroft 		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1351676135eSkre 			int dopr = print;
13682386044Skre 			int x;
1371676135eSkre 
13882386044Skre 			if (!print)
1391676135eSkre 				dopr = strcmp(p, dest);
14037ed7877Sjtc 
14182386044Skre 			if ((x = docd(p, dopr != 0, eopt)) >= 0)
14282386044Skre 				return x;
14337ed7877Sjtc 		}
14461f28255Scgd 	}
14561f28255Scgd 	error("can't cd to %s", dest);
14607bae7edSchristos 	/* NOTREACHED */
14761f28255Scgd }
14861f28255Scgd 
14961f28255Scgd 
15061f28255Scgd /*
15116f5230cSjtc  * Actually do the chdir.  In an interactive shell, print the
15216f5230cSjtc  * directory name if "print" is nonzero.
15361f28255Scgd  */
15461f28255Scgd 
15561f28255Scgd STATIC int
docd(const char * dest,bool print,bool eopt)15682386044Skre docd(const char *dest, bool print, bool eopt)
15761f28255Scgd {
15882386044Skre 	bool gotpwd;
15982386044Skre 
1601676135eSkre #if 0		/* no "cd -L" (ever) so all this is just a waste of time ... */
16148250187Stls 	char *p;
16248250187Stls 	char *q;
1635678a13fScjs 	char *component;
1645678a13fScjs 	struct stat statb;
1655678a13fScjs 	int first;
1665678a13fScjs 	int badstat;
16716f5230cSjtc 
1685678a13fScjs 	/*
1695678a13fScjs 	 *  Check each component of the path. If we find a symlink or
1705678a13fScjs 	 *  something we can't stat, clear curdir to force a getcwd()
1715678a13fScjs 	 *  next time we get the value of the current directory.
1725678a13fScjs 	 */
1735678a13fScjs 	badstat = 0;
1745678a13fScjs 	cdcomppath = stalloc(strlen(dest) + 1);
1755678a13fScjs 	scopy(dest, cdcomppath);
1765678a13fScjs 	STARTSTACKSTR(p);
1775678a13fScjs 	if (*dest == '/') {
1785678a13fScjs 		STPUTC('/', p);
1795678a13fScjs 		cdcomppath++;
1805678a13fScjs 	}
1815678a13fScjs 	first = 1;
1825678a13fScjs 	while ((q = getcomponent()) != NULL) {
1835678a13fScjs 		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
1845678a13fScjs 			continue;
1855678a13fScjs 		if (! first)
1865678a13fScjs 			STPUTC('/', p);
1875678a13fScjs 		first = 0;
1885678a13fScjs 		component = q;
1895678a13fScjs 		while (*q)
1905678a13fScjs 			STPUTC(*q++, p);
1915678a13fScjs 		if (equal(component, ".."))
1925678a13fScjs 			continue;
1935678a13fScjs 		STACKSTRNUL(p);
19456b5e396Schristos 		if (lstat(stackblock(), &statb) < 0) {
1955678a13fScjs 			badstat = 1;
1965678a13fScjs 			break;
1975678a13fScjs 		}
1985678a13fScjs 	}
1991676135eSkre #endif
2005678a13fScjs 
20182386044Skre 	CTRACE(DBG_CMDS, ("docd(\"%s\", %s, %s) called\n", dest,
20282386044Skre 	    print ? "true" : "false", eopt ? "true" : "false"));
20382386044Skre 
20461f28255Scgd 	INTOFF;
20561f28255Scgd 	if (chdir(dest) < 0) {
20661f28255Scgd 		INTON;
20761f28255Scgd 		return -1;
20861f28255Scgd 	}
20982386044Skre 	gotpwd = updatepwd(NULL);   /* only do cd -P, no "pretend" -L mode */
21061f28255Scgd 	INTON;
21182386044Skre 	if (print && (iflag || posix))
21282386044Skre 		out1fmt("%s\n", gotpwd ? curdir : dest);
21382386044Skre 	return gotpwd || !eopt ? 0 : 1;
21461f28255Scgd }
21561f28255Scgd 
21661f28255Scgd 
21761f28255Scgd /*
21861f28255Scgd  * Get the next component of the path name pointed to by cdcomppath.
21961f28255Scgd  * This routine overwrites the string pointed to by cdcomppath.
22061f28255Scgd  */
22161f28255Scgd 
22261f28255Scgd STATIC char *
getcomponent(void)2238c772bc7Sdsl getcomponent(void)
224c02b3bbdSchristos {
22548250187Stls 	char *p;
22661f28255Scgd 	char *start;
22761f28255Scgd 
22861f28255Scgd 	if ((p = cdcomppath) == NULL)
22961f28255Scgd 		return NULL;
23061f28255Scgd 	start = cdcomppath;
23161f28255Scgd 	while (*p != '/' && *p != '\0')
23261f28255Scgd 		p++;
23361f28255Scgd 	if (*p == '\0') {
23461f28255Scgd 		cdcomppath = NULL;
23561f28255Scgd 	} else {
23661f28255Scgd 		*p++ = '\0';
23761f28255Scgd 		cdcomppath = p;
23861f28255Scgd 	}
23961f28255Scgd 	return start;
24061f28255Scgd }
24161f28255Scgd 
24261f28255Scgd 
24361f28255Scgd 
24461f28255Scgd /*
24561f28255Scgd  * Update curdir (the name of the current directory) in response to a
24661f28255Scgd  * cd command.  We also call hashcd to let the routines in exec.c know
24761f28255Scgd  * that the current directory has changed.
24861f28255Scgd  */
24961f28255Scgd 
25082386044Skre STATIC bool
updatepwd(const char * dir)2518c772bc7Sdsl updatepwd(const char *dir)
25261f28255Scgd {
25361f28255Scgd 	char *new;
25461f28255Scgd 	char *p;
25561f28255Scgd 
25661f28255Scgd 	hashcd();				/* update command hash table */
2575678a13fScjs 
2585678a13fScjs 	/*
2595678a13fScjs 	 * If our argument is NULL, we don't know the current directory
2605678a13fScjs 	 * any more because we traversed a symbolic link or something
26182386044Skre 	 * we couldn't stat().   Or we simply don't trust what we had.
2625678a13fScjs 	 */
263ac875ddaSross 	if (dir == NULL || curdir == NULL)  {
2645678a13fScjs 		if (prevdir)
2655678a13fScjs 			ckfree(prevdir);
2665678a13fScjs 		INTOFF;
2675678a13fScjs 		prevdir = curdir;
2685678a13fScjs 		curdir = NULL;
269c02b3bbdSchristos 		getpwd(1);
2705678a13fScjs 		INTON;
27182e9aeceSuebayasi 		if (curdir) {
27282e9aeceSuebayasi 			setvar("OLDPWD", prevdir, VEXPORT);
273c02b3bbdSchristos 			setvar("PWD", curdir, VEXPORT);
27482386044Skre 			return true;
27582e9aeceSuebayasi 		} else
276c02b3bbdSchristos 			unsetvar("PWD", 0);
27782386044Skre 		return false;
2785678a13fScjs 	}
27982386044Skre 
28082386044Skre 	/* XXX none of the following code is ever executed any more */
28182386044Skre 
28261f28255Scgd 	cdcomppath = stalloc(strlen(dir) + 1);
28361f28255Scgd 	scopy(dir, cdcomppath);
28461f28255Scgd 	STARTSTACKSTR(new);
28561f28255Scgd 	if (*dir != '/') {
28661f28255Scgd 		p = curdir;
28761f28255Scgd 		while (*p)
28861f28255Scgd 			STPUTC(*p++, new);
28961f28255Scgd 		if (p[-1] == '/')
29061f28255Scgd 			STUNPUTC(new);
29161f28255Scgd 	}
29261f28255Scgd 	while ((p = getcomponent()) != NULL) {
29361f28255Scgd 		if (equal(p, "..")) {
29461f28255Scgd 			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
29561f28255Scgd 		} else if (*p != '\0' && ! equal(p, ".")) {
29661f28255Scgd 			STPUTC('/', new);
29761f28255Scgd 			while (*p)
29861f28255Scgd 				STPUTC(*p++, new);
29961f28255Scgd 		}
30061f28255Scgd 	}
30161f28255Scgd 	if (new == stackblock())
30261f28255Scgd 		STPUTC('/', new);
30361f28255Scgd 	STACKSTRNUL(new);
30437ed7877Sjtc 	INTOFF;
30537ed7877Sjtc 	if (prevdir)
30637ed7877Sjtc 		ckfree(prevdir);
30737ed7877Sjtc 	prevdir = curdir;
30861f28255Scgd 	curdir = savestr(stackblock());
30982e9aeceSuebayasi 	setvar("OLDPWD", prevdir, VEXPORT);
310ed00fe0aShe 	setvar("PWD", curdir, VEXPORT);
31137ed7877Sjtc 	INTON;
31282386044Skre 	return true;
31382386044Skre }
31482386044Skre 
31582386044Skre /*
31682386044Skre  * Test whether we are currently in the direcory given
31782386044Skre  * (provided it is given, and is absolute)
31882386044Skre  * ie: determine if path is fully qualified pathname of "."
31982386044Skre  */
32082386044Skre STATIC bool
is_curdir(const char * path)32182386044Skre is_curdir(const char *path)
32282386044Skre {
32382386044Skre 	struct stat stdot, stpath;
32482386044Skre 
32582386044Skre 	return	path != NULL &&
32682386044Skre 		*path == '/' &&
32782386044Skre 		stat(".", &stdot) != -1 &&
32882386044Skre 		stat(path, &stpath) != -1 &&
32982386044Skre 		stdot.st_dev == stpath.st_dev &&
33082386044Skre 		stdot.st_ino == stpath.st_ino;
33161f28255Scgd }
33261f28255Scgd 
333296844feSdsl /*
334296844feSdsl  * Posix says the default should be 'pwd -L' (as below), however
335296844feSdsl  * the 'cd' command (above) does something much nearer to the
336296844feSdsl  * posix 'cd -P' (not the posix default of 'cd -L').
337296844feSdsl  * If 'cd' is changed to support -P/L then the default here
338296844feSdsl  * needs to be revisited if the historic behaviour is to be kept.
339296844feSdsl  */
34061f28255Scgd 
34161f28255Scgd int
pwdcmd(int argc,char ** argv)342c02b3bbdSchristos pwdcmd(int argc, char **argv)
3434ce0d34aScgd {
344df5bd11fSdsl 	int i;
345df5bd11fSdsl 	char opt = 'L';
346df5bd11fSdsl 
347df5bd11fSdsl 	while ((i = nextopt("LP")) != '\0')
348df5bd11fSdsl 		opt = i;
349df5bd11fSdsl 	if (*argptr)
350df5bd11fSdsl 		error("unexpected argument");
351df5bd11fSdsl 
352296844feSdsl 	if (opt == 'L')
353c02b3bbdSchristos 		getpwd(0);
354296844feSdsl 	else
355296844feSdsl 		find_curdir(0);
356df5bd11fSdsl 
35782386044Skre #if 0	/* posix has been changed to forbid this */
35882e9aeceSuebayasi 	setvar("OLDPWD", prevdir, VEXPORT);
359296844feSdsl 	setvar("PWD", curdir, VEXPORT);
36082386044Skre #endif
36182386044Skre 
36282386044Skre 	if (!is_curdir(curdir)) {
36382386044Skre 		find_curdir(1);
36482386044Skre 		if (curdir == NULL)
36582386044Skre 			error("Unable to find current directory");
36682386044Skre 	}
367b6dd340dSkre 
368b6dd340dSkre 	flushout(out1);		/* make sure buffer is empty */
369b6dd340dSkre 	clr_err(out1);		/* and forget any earlier errors */
370296844feSdsl 	out1str(curdir);
37161f28255Scgd 	out1c('\n');
372b6dd340dSkre 	flushout(out1);
373b6dd340dSkre 	if (io_err(out1))
374b6dd340dSkre 		error("stdout: %s", strerror(errno));
375b6dd340dSkre 
37661f28255Scgd 	return 0;
37761f28255Scgd }
37861f28255Scgd 
37961f28255Scgd 
38061f28255Scgd 
3818d6ffce2Schristos void
initpwd(void)3828d6ffce2Schristos initpwd(void)
3838d6ffce2Schristos {
38415dc8572Ssimonb 	getpwd(1);
38515dc8572Ssimonb 	if (curdir)
3868d6ffce2Schristos 		setvar("PWD", curdir, VEXPORT);
38715dc8572Ssimonb 	else
38815dc8572Ssimonb 		sh_warnx("Cannot determine current working directory");
3898d6ffce2Schristos }
39061f28255Scgd 
39161f28255Scgd #define MAXPWD 256
39261f28255Scgd 
393ff008dabSchristos /*
394ff008dabSchristos  * Find out what the current directory is. If we already know the current
395ff008dabSchristos  * directory, this routine returns immediately.
396ff008dabSchristos  */
397ff008dabSchristos void
getpwd(int noerror)398c02b3bbdSchristos getpwd(int noerror)
399ff008dabSchristos {
400c02b3bbdSchristos 	char *pwd;
401c02b3bbdSchristos 	static int first = 1;
402ff008dabSchristos 
403ff008dabSchristos 	if (curdir)
404ff008dabSchristos 		return;
405c02b3bbdSchristos 
406c02b3bbdSchristos 	if (first) {
407*05e80ac3Skre 		/*
408*05e80ac3Skre 		 * Note that this happens via the call from initpwd()
409*05e80ac3Skre 		 * just above, which is called early from main() during
410*05e80ac3Skre 		 * sh startup, so fetching PWD from the entry environment
411*05e80ac3Skre 		 * (which is what getenv() does) is acceptable.   Here we
412*05e80ac3Skre 		 * could use normal sh var lookup functions instead, as
413*05e80ac3Skre 		 * the arriving environment has already been imported before
414*05e80ac3Skre 		 * we get here, but it makes little difference.
415*05e80ac3Skre 		 *
416*05e80ac3Skre 		 * XXX What would be better perhaps would be to move all of
417*05e80ac3Skre 		 * this into initpwd() instead of here, so we could get rid of
418*05e80ac3Skre 		 * this "first" static - that function is only ever called once.
419*05e80ac3Skre 		 * XXX Some other day.
420*05e80ac3Skre 		 */
421df5bd11fSdsl 		first = 0;
422c02b3bbdSchristos 		pwd = getenv("PWD");
42382386044Skre 		if (is_curdir(pwd)) {
424c02b3bbdSchristos 			curdir = savestr(pwd);
425c02b3bbdSchristos 			return;
426c02b3bbdSchristos 		}
427c02b3bbdSchristos 	}
428df5bd11fSdsl 
429296844feSdsl 	find_curdir(noerror);
430df5bd11fSdsl 
431df5bd11fSdsl 	return;
432df5bd11fSdsl }
433df5bd11fSdsl 
434296844feSdsl STATIC void
find_curdir(int noerror)435296844feSdsl find_curdir(int noerror)
436df5bd11fSdsl {
437df5bd11fSdsl 	int i;
438df5bd11fSdsl 	char *pwd;
439c02b3bbdSchristos 
440ff008dabSchristos 	/*
441ff008dabSchristos 	 * Things are a bit complicated here; we could have just used
442ff008dabSchristos 	 * getcwd, but traditionally getcwd is implemented using popen
443ff008dabSchristos 	 * to /bin/pwd. This creates a problem for us, since we cannot
444ff008dabSchristos 	 * keep track of the job if it is being ran behind our backs.
44582386044Skre 	 * XXX That's not actually the problem, a process created and
44682386044Skre 	 * XXX destroyed that we know nothing about is harmless.  The
44782386044Skre 	 * XXX problem is that old popen() implementations would use
44882386044Skre 	 * XXX wait(2) to await completion of the command, and that might
44982386044Skre 	 * XXX collect (and ignore) our children.   As long as we are
45082386044Skre 	 * XXX confident that popen() uses waitpid() (or the equv) there
45182386044Skre 	 * XXX would not be a problem.   But how do we know that?
452ff008dabSchristos 	 * So we re-implement getcwd(), and we suppress interrupts
453ff008dabSchristos 	 * throughout the process. This is not completely safe, since
454ff008dabSchristos 	 * the user can still break out of it by killing the pwd program.
455ff008dabSchristos 	 * We still try to use getcwd for systems that we know have a
456ff008dabSchristos 	 * c implementation of getcwd, that does not open a pipe to
457ff008dabSchristos 	 * /bin/pwd.
458ff008dabSchristos 	 */
4592c8f740eSchristos #if defined(__NetBSD__) || defined(__SVR4)
4606b7623a6Schristos 
461c02b3bbdSchristos 	for (i = MAXPWD;; i *= 2) {
462c02b3bbdSchristos 		pwd = stalloc(i);
463296844feSdsl 		if (getcwd(pwd, i) != NULL) {
464296844feSdsl 			curdir = savestr(pwd);
4659038bdcaSkre 			stunalloc(pwd);
466296844feSdsl 			return;
467296844feSdsl 		}
468c02b3bbdSchristos 		stunalloc(pwd);
469c02b3bbdSchristos 		if (errno == ERANGE)
470c02b3bbdSchristos 			continue;
471c02b3bbdSchristos 		if (!noerror)
47247f18130Sthorpej 			error("getcwd() failed: %s", strerror(errno));
473296844feSdsl 		return;
4746b7623a6Schristos 	}
475ff008dabSchristos #else
476ff008dabSchristos 	{
47761f28255Scgd 		char *p;
47861f28255Scgd 		int status;
47961f28255Scgd 		struct job *jp;
48061f28255Scgd 		int pip[2];
48161f28255Scgd 
482296844feSdsl 		pwd = stalloc(MAXPWD);
48361f28255Scgd 		INTOFF;
48461f28255Scgd 		if (pipe(pip) < 0)
48561f28255Scgd 			error("Pipe call failed");
4869f61b804Splunky 		jp = makejob(NULL, 1);
4879f61b804Splunky 		if (forkshell(jp, NULL, FORK_NOJOB) == 0) {
488ff008dabSchristos 			(void) close(pip[0]);
4891fad4bb6Schristos 			movefd(pip[1], 1);
490ff008dabSchristos 			(void) execl("/bin/pwd", "pwd", (char *)0);
49137ed7877Sjtc 			error("Cannot exec /bin/pwd");
49261f28255Scgd 		}
493ff008dabSchristos 		(void) close(pip[1]);
49461f28255Scgd 		pip[1] = -1;
495296844feSdsl 		p = pwd;
496296844feSdsl 		while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0
49707bae7edSchristos 		     || (i == -1 && errno == EINTR)) {
49861f28255Scgd 			if (i > 0)
49961f28255Scgd 				p += i;
50061f28255Scgd 		}
501ff008dabSchristos 		(void) close(pip[0]);
50261f28255Scgd 		pip[0] = -1;
50361f28255Scgd 		status = waitforjob(jp);
50461f28255Scgd 		if (status != 0)
50561f28255Scgd 			error((char *)0);
506296844feSdsl 		if (i < 0 || p == pwd || p[-1] != '\n') {
507c02b3bbdSchristos 			if (noerror) {
508c02b3bbdSchristos 				INTON;
509296844feSdsl 				return;
510c02b3bbdSchristos 			}
51161f28255Scgd 			error("pwd command failed");
512c02b3bbdSchristos 		}
51361f28255Scgd 		p[-1] = '\0';
51461f28255Scgd 		INTON;
515296844feSdsl 		curdir = savestr(pwd);
5169038bdcaSkre 		stunalloc(pwd);
517296844feSdsl 		return;
518df5bd11fSdsl 	}
51916f5230cSjtc #endif
52061f28255Scgd }
521