cd.c revision 201053
160894Smsmith/*-
260894Smsmith * Copyright (c) 1991, 1993
3123103Sps *	The Regents of the University of California.  All rights reserved.
4123103Sps *
560894Smsmith * This code is derived from software contributed to Berkeley by
660894Smsmith * Kenneth Almquist.
760894Smsmith *
860894Smsmith * Redistribution and use in source and binary forms, with or without
960894Smsmith * modification, are permitted provided that the following conditions
1060894Smsmith * are met:
1160894Smsmith * 1. Redistributions of source code must retain the above copyright
1260894Smsmith *    notice, this list of conditions and the following disclaimer.
1360894Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1460894Smsmith *    notice, this list of conditions and the following disclaimer in the
1560894Smsmith *    documentation and/or other materials provided with the distribution.
1660894Smsmith * 4. Neither the name of the University nor the names of its contributors
1760894Smsmith *    may be used to endorse or promote products derived from this software
1860894Smsmith *    without specific prior written permission.
1960894Smsmith *
2060894Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2160894Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2260894Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2360894Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2460894Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2560894Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2660894Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2760894Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2860894Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2960894Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3060894Smsmith * SUCH DAMAGE.
3160894Smsmith */
3260894Smsmith
3360894Smsmith#ifndef lint
3460894Smsmith#if 0
3560894Smsmithstatic char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
3667555Smsmith#endif
3760894Smsmith#endif /* not lint */
3867555Smsmith#include <sys/cdefs.h>
3960894Smsmith__FBSDID("$FreeBSD: head/bin/sh/cd.c 201053 2009-12-27 18:04:05Z jilles $");
4067555Smsmith
4167555Smsmith#include <sys/types.h>
4260894Smsmith#include <sys/stat.h>
4360894Smsmith#include <stdlib.h>
4460894Smsmith#include <string.h>
4560894Smsmith#include <unistd.h>
4667555Smsmith#include <errno.h>
4767555Smsmith#include <limits.h>
4867555Smsmith
4967555Smsmith/*
5060894Smsmith * The cd and pwd commands.
5191449Speter */
5267555Smsmith
5391449Speter#include "shell.h"
5491449Speter#include "var.h"
5567555Smsmith#include "nodes.h"	/* for jobs.h */
5667555Smsmith#include "jobs.h"
5791449Speter#include "options.h"
5867555Smsmith#include "output.h"
5967555Smsmith#include "memalloc.h"
6067555Smsmith#include "error.h"
6167555Smsmith#include "exec.h"
62141492Sscottl#include "redir.h"
6367555Smsmith#include "mystring.h"
6467555Smsmith#include "show.h"
65123103Sps#include "cd.h"
66123103Sps
6760894SmsmithSTATIC int cdlogical(char *);
6860894SmsmithSTATIC int cdphysical(char *);
6960894SmsmithSTATIC int docd(char *, int, int);
7060894SmsmithSTATIC char *getcomponent(void);
71240137SjhbSTATIC char *findcwd(char *);
7267555SmsmithSTATIC void updatepwd(char *);
7367555SmsmithSTATIC char *getpwd2(void);
7467555Smsmith
7567555SmsmithSTATIC char *curdir = NULL;	/* current working directory */
7667555SmsmithSTATIC char *prevdir;		/* previous working directory */
7760894SmsmithSTATIC char *cdcomppath;
7860894Smsmith
7960894Smsmithint
8060894Smsmithcdcmd(int argc, char **argv)
8167555Smsmith{
8267555Smsmith	const char *dest;
8367555Smsmith	const char *path;
8460894Smsmith	char *p;
8560894Smsmith	struct stat statb;
8660894Smsmith	int ch, phys, print = 0;
8760894Smsmith
8867555Smsmith	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
8967555Smsmith	phys = Pflag;
9067555Smsmith	while ((ch = getopt(argc, argv, "LP")) != -1) {
91123103Sps		switch (ch) {
9267555Smsmith		case 'L':
9367555Smsmith			phys = 0;
9460894Smsmith			break;
9560894Smsmith		case 'P':
9660894Smsmith			phys = 1;
9760894Smsmith			break;
9867555Smsmith		default:
9967555Smsmith			error("unknown option: -%c", optopt);
10060894Smsmith			break;
10160894Smsmith		}
10260894Smsmith	}
10360894Smsmith	argc -= optind;
10467555Smsmith	argv += optind;
10569543Smsmith
10667555Smsmith	if (argc > 1)
10760894Smsmith		error("too many arguments");
10860894Smsmith
10960894Smsmith	if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
11060894Smsmith		error("HOME not set");
11160894Smsmith	if (*dest == '\0')
11260894Smsmith		dest = ".";
11360894Smsmith	if (dest[0] == '-' && dest[1] == '\0') {
11460894Smsmith		dest = prevdir ? prevdir : curdir;
11567555Smsmith		if (dest)
11660894Smsmith			print = 1;
11767555Smsmith		else
11867555Smsmith			dest = ".";
11960894Smsmith	}
12060894Smsmith	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
121118816Sps		path = nullstr;
12291790Smsmith	while ((p = padvance(&path, dest)) != NULL) {
12367555Smsmith		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
12460894Smsmith			if (!print) {
12560894Smsmith				/*
12660894Smsmith				 * XXX - rethink
12760894Smsmith				 */
12867555Smsmith				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
12960894Smsmith					print = strcmp(p + 2, dest);
13067555Smsmith				else
13167555Smsmith					print = strcmp(p, dest);
13267555Smsmith			}
13367555Smsmith			if (docd(p, print, phys) >= 0)
13467555Smsmith				return 0;
13567555Smsmith		}
13660894Smsmith	}
13760894Smsmith	error("can't cd to %s", dest);
13867555Smsmith	/*NOTREACHED*/
13960894Smsmith	return 0;
14067555Smsmith}
141118816Sps
14267555Smsmith
14367555Smsmith/*
14467555Smsmith * Actually change the directory.  In an interactive shell, print the
14567555Smsmith * directory name if "print" is nonzero.
146118816Sps */
147118816SpsSTATIC int
148118816Spsdocd(char *dest, int print, int phys)
14967555Smsmith{
15060894Smsmith
15167555Smsmith	TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
15267555Smsmith
15367555Smsmith	/* If logical cd fails, fall back to physical. */
154239244Sjhb	if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0)
15567555Smsmith		return (-1);
156239244Sjhb
15760894Smsmith	if (print && iflag && curdir)
158239244Sjhb		out1fmt("%s\n", curdir);
15960894Smsmith
16060894Smsmith	return 0;
16191790Smsmith}
16291790Smsmith
16391790SmsmithSTATIC int
16491790Smsmithcdlogical(char *dest)
16591790Smsmith{
16691790Smsmith	char *p;
16767555Smsmith	char *q;
16860894Smsmith	char *component;
16967555Smsmith	struct stat statb;
170239244Sjhb	int first;
17167555Smsmith	int badstat;
17260894Smsmith
17360894Smsmith	/*
17460894Smsmith	 *  Check each component of the path. If we find a symlink or
17560894Smsmith	 *  something we can't stat, clear curdir to force a getcwd()
17667555Smsmith	 *  next time we get the value of the current directory.
17760894Smsmith	 */
17867555Smsmith	badstat = 0;
17960894Smsmith	cdcomppath = stalloc(strlen(dest) + 1);
18060894Smsmith	scopy(dest, cdcomppath);
18167555Smsmith	STARTSTACKSTR(p);
18267555Smsmith	if (*dest == '/') {
18360894Smsmith		STPUTC('/', p);
18467555Smsmith		cdcomppath++;
18560894Smsmith	}
18667555Smsmith	first = 1;
18767555Smsmith	while ((q = getcomponent()) != NULL) {
18867555Smsmith		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
18967555Smsmith			continue;
19067555Smsmith		if (! first)
19167555Smsmith			STPUTC('/', p);
192239244Sjhb		first = 0;
19367555Smsmith		component = q;
19467555Smsmith		while (*q)
19567555Smsmith			STPUTC(*q++, p);
19660894Smsmith		if (equal(component, ".."))
19760894Smsmith			continue;
19860894Smsmith		STACKSTRNUL(p);
19960894Smsmith		if (lstat(stackblock(), &statb) < 0) {
20060894Smsmith			badstat = 1;
20160894Smsmith			break;
202123103Sps		}
203118508Sps	}
20460894Smsmith
20560894Smsmith	INTOFF;
206123103Sps	if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
20767555Smsmith		INTON;
208118508Sps		return (-1);
209200991Smav	}
21060894Smsmith	updatepwd(p);
211239244Sjhb	INTON;
212118508Sps	return (0);
213123103Sps}
21460894Smsmith
21560894SmsmithSTATIC int
21667555Smsmithcdphysical(char *dest)
21760894Smsmith{
218239244Sjhb	char *p;
21967555Smsmith
22067555Smsmith	INTOFF;
221239244Sjhb	if (chdir(dest) < 0 || (p = findcwd(NULL)) == NULL) {
22267555Smsmith		INTON;
223123103Sps		return (-1);
22460894Smsmith	}
22560894Smsmith	updatepwd(p);
226118508Sps	INTON;
227118508Sps	return (0);
228123103Sps}
229239244Sjhb
230123103Sps/*
231118508Sps * Get the next component of the path name pointed to by cdcomppath.
232123103Sps * This routine overwrites the string pointed to by cdcomppath.
23360894Smsmith */
234118508SpsSTATIC char *
23560894Smsmithgetcomponent(void)
236118508Sps{
237239244Sjhb	char *p;
238118508Sps	char *start;
239123103Sps
240118508Sps	if ((p = cdcomppath) == NULL)
241118508Sps		return NULL;
242118508Sps	start = cdcomppath;
243239244Sjhb	while (*p != '/' && *p != '\0')
244118508Sps		p++;
245123103Sps	if (*p == '\0') {
246118508Sps		cdcomppath = NULL;
247118508Sps	} else {
248118508Sps		*p++ = '\0';
249239244Sjhb		cdcomppath = p;
250118508Sps	}
251123103Sps	return start;
252118508Sps}
253118508Sps
254118508Sps
255239244SjhbSTATIC char *
256118508Spsfindcwd(char *dir)
257123103Sps{
258118508Sps	char *new;
259118508Sps	char *p;
260200991Smav
261118508Sps	/*
262200991Smav	 * If our argument is NULL, we don't know the current directory
263118508Sps	 * any more because we traversed a symbolic link or something
264118508Sps	 * we couldn't stat().
265118508Sps	 */
266118508Sps	if (dir == NULL || curdir == NULL)
267118508Sps		return getpwd2();
268118508Sps	cdcomppath = stalloc(strlen(dir) + 1);
269118508Sps	scopy(dir, cdcomppath);
270118508Sps	STARTSTACKSTR(new);
271118508Sps	if (*dir != '/') {
272118508Sps		p = curdir;
273123103Sps		while (*p)
274239244Sjhb			STPUTC(*p++, new);
275118508Sps		if (p[-1] == '/')
276123103Sps			STUNPUTC(new);
277118508Sps	}
278118508Sps	while ((p = getcomponent()) != NULL) {
279118508Sps		if (equal(p, "..")) {
28067555Smsmith			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
281118508Sps		} else if (*p != '\0' && ! equal(p, ".")) {
282118508Sps			STPUTC('/', new);
283123103Sps			while (*p)
284118508Sps				STPUTC(*p++, new);
28560894Smsmith		}
286123103Sps	}
287118508Sps	if (new == stackblock())
288118508Sps		STPUTC('/', new);
289123103Sps	STACKSTRNUL(new);
29060894Smsmith	return stackblock();
291239244Sjhb}
292126099Scperciva
293123103Sps/*
29460894Smsmith * Update curdir (the name of the current directory) in response to a
295123103Sps * cd command.  We also call hashcd to let the routines in exec.c know
296123103Sps * that the current directory has changed.
297123103Sps */
298123103SpsSTATIC void
299123103Spsupdatepwd(char *dir)
300118508Sps{
301118508Sps	hashcd();				/* update command hash table */
302118508Sps
303118508Sps	if (prevdir)
304118508Sps		ckfree(prevdir);
305118508Sps	prevdir = curdir;
306118508Sps	curdir = savestr(dir);
307118508Sps	setvar("PWD", curdir, VEXPORT);
308118508Sps	setvar("OLDPWD", prevdir, VEXPORT);
309118508Sps}
31060894Smsmith
311118508Spsint
312118508Spspwdcmd(int argc, char **argv)
313239244Sjhb{
314118508Sps	char *p;
315118508Sps	int ch, phys;
316239244Sjhb
317118508Sps	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
318118508Sps	phys = Pflag;
31960894Smsmith	while ((ch = getopt(argc, argv, "LP")) != -1) {
32060894Smsmith		switch (ch) {
321239244Sjhb		case 'L':
32267555Smsmith			phys = 0;
32360894Smsmith			break;
32467555Smsmith		case 'P':
32567555Smsmith			phys = 1;
32667555Smsmith			break;
32767555Smsmith		default:
32867555Smsmith			error("unknown option: -%c", optopt);
32967555Smsmith			break;
33067555Smsmith		}
33160894Smsmith	}
33260894Smsmith	argc -= optind;
33360894Smsmith	argv += optind;
33460894Smsmith
33560894Smsmith	if (argc != 0)
33660894Smsmith		error("too many arguments");
33767555Smsmith
33860894Smsmith	if (!phys && getpwd()) {
33960894Smsmith		out1str(curdir);
340239244Sjhb		out1c('\n');
34160894Smsmith	} else {
34260894Smsmith		if ((p = getpwd2()) == NULL)
34360894Smsmith			error(".: %s", strerror(errno));
34467555Smsmith		out1str(p);
34560894Smsmith		out1c('\n');
34667555Smsmith	}
34767555Smsmith
34860894Smsmith	return 0;
34960894Smsmith}
35060894Smsmith
35160894Smsmith/*
352239244Sjhb * Get the current directory and cache the result in curdir.
35360894Smsmith */
35460894Smsmithchar *
35560894Smsmithgetpwd(void)
35667555Smsmith{
35767555Smsmith	char *p;
35867555Smsmith
35960894Smsmith	if (curdir)
36067555Smsmith		return curdir;
36167555Smsmith
36260894Smsmith	p = getpwd2();
36360894Smsmith	if (p != NULL)
36460894Smsmith		curdir = savestr(p);
36560894Smsmith
36660894Smsmith	return curdir;
36760894Smsmith}
36867555Smsmith
36967555Smsmith#define MAXPWD 256
37060894Smsmith
37160894Smsmith/*
37260894Smsmith * Return the current directory.
37360894Smsmith */
37460894SmsmithSTATIC char *
37560894Smsmithgetpwd2(void)
37660894Smsmith{
37760894Smsmith	struct stat stdot, stpwd;
37860894Smsmith	char *pwd;
37960894Smsmith	int i;
38060894Smsmith
38160894Smsmith	for (i = MAXPWD;; i *= 2) {
38260894Smsmith		pwd = stalloc(i);
38360894Smsmith		if (getcwd(pwd, i) != NULL)
38460894Smsmith			return pwd;
38560894Smsmith		stunalloc(pwd);
38660894Smsmith		if (errno != ERANGE)
38760894Smsmith			break;
38860894Smsmith	}
38960894Smsmith
39073104Smsmith	pwd = getenv("PWD");
391240137Sjhb	if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
39260894Smsmith	    stat(pwd, &stpwd) != -1 &&
39360894Smsmith	    stdot.st_dev == stpwd.st_dev &&
39469543Smsmith	    stdot.st_ino == stpwd.st_ino) {
39569543Smsmith		return pwd;
39669543Smsmith	}
39760894Smsmith	return NULL;
39869543Smsmith}
39969543Smsmith