cd.c revision 230095
1168404Spjd/*-
2168404Spjd * Copyright (c) 1991, 1993
3168404Spjd *	The Regents of the University of California.  All rights reserved.
4168404Spjd *
5168404Spjd * This code is derived from software contributed to Berkeley by
6168404Spjd * Kenneth Almquist.
7168404Spjd *
8168404Spjd * Redistribution and use in source and binary forms, with or without
9168404Spjd * modification, are permitted provided that the following conditions
10168404Spjd * are met:
11168404Spjd * 1. Redistributions of source code must retain the above copyright
12168404Spjd *    notice, this list of conditions and the following disclaimer.
13168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
14168404Spjd *    notice, this list of conditions and the following disclaimer in the
15168404Spjd *    documentation and/or other materials provided with the distribution.
16168404Spjd * 4. Neither the name of the University nor the names of its contributors
17168404Spjd *    may be used to endorse or promote products derived from this software
18168404Spjd *    without specific prior written permission.
19168404Spjd *
20168404Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21236884Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23219089Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24265740Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30168404Spjd * SUCH DAMAGE.
31168404Spjd */
32168404Spjd
33168404Spjd#ifndef lint
34168404Spjd#if 0
35265740Sdelphijstatic char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
36263397Sdelphij#endif
37168404Spjd#endif /* not lint */
38168404Spjd#include <sys/cdefs.h>
39168404Spjd__FBSDID("$FreeBSD: head/bin/sh/cd.c 230095 2012-01-13 23:32:27Z jilles $");
40168404Spjd
41168404Spjd#include <sys/types.h>
42168404Spjd#include <sys/stat.h>
43168404Spjd#include <stdlib.h>
44168404Spjd#include <string.h>
45168404Spjd#include <unistd.h>
46168404Spjd#include <errno.h>
47168404Spjd#include <limits.h>
48168404Spjd
49168404Spjd/*
50168404Spjd * The cd and pwd commands.
51168404Spjd */
52168404Spjd
53168404Spjd#include "shell.h"
54168404Spjd#include "var.h"
55168404Spjd#include "nodes.h"	/* for jobs.h */
56168404Spjd#include "jobs.h"
57168404Spjd#include "options.h"
58168404Spjd#include "output.h"
59185029Spjd#include "memalloc.h"
60185029Spjd#include "error.h"
61168404Spjd#include "exec.h"
62168404Spjd#include "redir.h"
63168404Spjd#include "mystring.h"
64168404Spjd#include "show.h"
65168404Spjd#include "cd.h"
66168404Spjd#include "builtins.h"
67168404Spjd
68168404Spjdstatic int cdlogical(char *);
69168404Spjdstatic int cdphysical(char *);
70168404Spjdstatic int docd(char *, int, int);
71168404Spjdstatic char *getcomponent(void);
72168404Spjdstatic char *findcwd(char *);
73168404Spjdstatic void updatepwd(char *);
74168404Spjdstatic char *getpwd(void);
75168404Spjdstatic char *getpwd2(void);
76168404Spjd
77168404Spjdstatic char *curdir = NULL;	/* current working directory */
78168404Spjdstatic char *prevdir;		/* previous working directory */
79168404Spjdstatic char *cdcomppath;
80168404Spjd
81168404Spjdint
82168404Spjdcdcmd(int argc, char **argv)
83219089Spjd{
84219089Spjd	const char *dest;
85219089Spjd	const char *path;
86219089Spjd	char *p;
87219089Spjd	struct stat statb;
88168404Spjd	int ch, phys, print = 0, getcwderr = 0;
89168404Spjd	int rc;
90168404Spjd	int errno1 = ENOENT;
91168404Spjd
92168404Spjd	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
93168404Spjd	phys = Pflag;
94168404Spjd	while ((ch = getopt(argc, argv, "eLP")) != -1) {
95168404Spjd		switch (ch) {
96168404Spjd		case 'e':
97168404Spjd			getcwderr = 1;
98168404Spjd			break;
99168404Spjd		case 'L':
100168404Spjd			phys = 0;
101168404Spjd			break;
102168404Spjd		case 'P':
103168404Spjd			phys = 1;
104168404Spjd			break;
105168404Spjd		default:
106168404Spjd			error("unknown option: -%c", optopt);
107168404Spjd			break;
108168404Spjd		}
109168404Spjd	}
110168404Spjd	argc -= optind;
111168404Spjd	argv += optind;
112168404Spjd
113168404Spjd	if (argc > 1)
114168404Spjd		error("too many arguments");
115168404Spjd
116168404Spjd	if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
117263397Sdelphij		error("HOME not set");
118168404Spjd	if (*dest == '\0')
119168404Spjd		dest = ".";
120185029Spjd	if (dest[0] == '-' && dest[1] == '\0') {
121168404Spjd		dest = prevdir ? prevdir : curdir;
122168404Spjd		if (dest)
123185029Spjd			print = 1;
124168404Spjd		else
125263397Sdelphij			dest = ".";
126168404Spjd	}
127168404Spjd	if (dest[0] == '/' ||
128168404Spjd	    (dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) ||
129219089Spjd	    (dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) ||
130168404Spjd	    (path = bltinlookup("CDPATH", 1)) == NULL)
131263397Sdelphij		path = nullstr;
132263397Sdelphij	while ((p = padvance(&path, dest)) != NULL) {
133263397Sdelphij		if (stat(p, &statb) < 0) {
134263397Sdelphij			if (errno != ENOENT)
135263397Sdelphij				errno1 = errno;
136263397Sdelphij		} else if (!S_ISDIR(statb.st_mode))
137263397Sdelphij			errno1 = ENOTDIR;
138263397Sdelphij		else {
139263397Sdelphij			if (!print) {
140263397Sdelphij				/*
141263397Sdelphij				 * XXX - rethink
142263397Sdelphij				 */
143263397Sdelphij				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
144168404Spjd					print = strcmp(p + 2, dest);
145263397Sdelphij				else
146263397Sdelphij					print = strcmp(p, dest);
147263397Sdelphij			}
148263397Sdelphij			rc = docd(p, print, phys);
149263397Sdelphij			if (rc >= 0)
150263397Sdelphij				return getcwderr ? rc : 0;
151263397Sdelphij			if (errno != ENOENT)
152263397Sdelphij				errno1 = errno;
153168404Spjd		}
154168404Spjd	}
155168404Spjd	error("%s: %s", dest, strerror(errno1));
156168404Spjd	/*NOTREACHED*/
157168404Spjd	return 0;
158168404Spjd}
159168404Spjd
160168404Spjd
161168404Spjd/*
162168404Spjd * Actually change the directory.  In an interactive shell, print the
163168404Spjd * directory name if "print" is nonzero.
164219089Spjd */
165168404Spjdstatic int
166219089Spjddocd(char *dest, int print, int phys)
167219089Spjd{
168219089Spjd	int rc;
169168404Spjd
170168404Spjd	TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
171168404Spjd
172168404Spjd	/* If logical cd fails, fall back to physical. */
173168404Spjd	if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0)
174168404Spjd		return (-1);
175219089Spjd
176168404Spjd	if (print && iflag && curdir)
177168404Spjd		out1fmt("%s\n", curdir);
178168404Spjd
179168404Spjd	return (rc);
180168404Spjd}
181168404Spjd
182168404Spjdstatic int
183168404Spjdcdlogical(char *dest)
184168404Spjd{
185168404Spjd	char *p;
186168404Spjd	char *q;
187219089Spjd	char *component;
188219089Spjd	struct stat statb;
189185029Spjd	int first;
190219089Spjd	int badstat;
191168404Spjd
192168404Spjd	/*
193168404Spjd	 *  Check each component of the path. If we find a symlink or
194168404Spjd	 *  something we can't stat, clear curdir to force a getcwd()
195168404Spjd	 *  next time we get the value of the current directory.
196168404Spjd	 */
197168404Spjd	badstat = 0;
198168404Spjd	cdcomppath = stalloc(strlen(dest) + 1);
199168404Spjd	scopy(dest, cdcomppath);
200168404Spjd	STARTSTACKSTR(p);
201168404Spjd	if (*dest == '/') {
202168404Spjd		STPUTC('/', p);
203168404Spjd		cdcomppath++;
204168404Spjd	}
205168404Spjd	first = 1;
206168404Spjd	while ((q = getcomponent()) != NULL) {
207185029Spjd		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
208168404Spjd			continue;
209168404Spjd		if (! first)
210168404Spjd			STPUTC('/', p);
211168404Spjd		first = 0;
212168404Spjd		component = q;
213168404Spjd		STPUTS(q, p);
214168404Spjd		if (equal(component, ".."))
215168404Spjd			continue;
216168404Spjd		STACKSTRNUL(p);
217168404Spjd		if (lstat(stackblock(), &statb) < 0) {
218168404Spjd			badstat = 1;
219168404Spjd			break;
220168404Spjd		}
221168404Spjd	}
222168404Spjd
223168404Spjd	INTOFF;
224185029Spjd	if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
225168404Spjd		INTON;
226168404Spjd		return (-1);
227168404Spjd	}
228168404Spjd	updatepwd(p);
229168404Spjd	INTON;
230168404Spjd	return (0);
231168404Spjd}
232219089Spjd
233168404Spjdstatic int
234168404Spjdcdphysical(char *dest)
235168404Spjd{
236263397Sdelphij	char *p;
237263397Sdelphij	int rc = 0;
238168404Spjd
239168404Spjd	INTOFF;
240219089Spjd	if (chdir(dest) < 0) {
241168404Spjd		INTON;
242168404Spjd		return (-1);
243168404Spjd	}
244263397Sdelphij	p = findcwd(NULL);
245168404Spjd	if (p == NULL) {
246185029Spjd		warning("warning: failed to get name of current directory");
247185029Spjd		rc = 1;
248185029Spjd	}
249263397Sdelphij	updatepwd(p);
250263397Sdelphij	INTON;
251185029Spjd	return (rc);
252185029Spjd}
253185029Spjd
254185029Spjd/*
255219089Spjd * Get the next component of the path name pointed to by cdcomppath.
256263397Sdelphij * This routine overwrites the string pointed to by cdcomppath.
257168404Spjd */
258219089Spjdstatic char *
259219089Spjdgetcomponent(void)
260219089Spjd{
261168404Spjd	char *p;
262168404Spjd	char *start;
263168404Spjd
264168404Spjd	if ((p = cdcomppath) == NULL)
265168404Spjd		return NULL;
266168404Spjd	start = cdcomppath;
267168404Spjd	while (*p != '/' && *p != '\0')
268168404Spjd		p++;
269168404Spjd	if (*p == '\0') {
270168404Spjd		cdcomppath = NULL;
271168404Spjd	} else {
272168404Spjd		*p++ = '\0';
273263397Sdelphij		cdcomppath = p;
274168404Spjd	}
275168404Spjd	return start;
276168404Spjd}
277168404Spjd
278263397Sdelphij
279263397Sdelphijstatic char *
280263397Sdelphijfindcwd(char *dir)
281263397Sdelphij{
282263397Sdelphij	char *new;
283263397Sdelphij	char *p;
284263397Sdelphij
285263397Sdelphij	/*
286263397Sdelphij	 * If our argument is NULL, we don't know the current directory
287263397Sdelphij	 * any more because we traversed a symbolic link or something
288263397Sdelphij	 * we couldn't stat().
289263397Sdelphij	 */
290263397Sdelphij	if (dir == NULL || curdir == NULL)
291263397Sdelphij		return getpwd2();
292168404Spjd	cdcomppath = stalloc(strlen(dir) + 1);
293168404Spjd	scopy(dir, cdcomppath);
294263397Sdelphij	STARTSTACKSTR(new);
295263397Sdelphij	if (*dir != '/') {
296263397Sdelphij		STPUTS(curdir, new);
297263397Sdelphij		if (STTOPC(new) == '/')
298263397Sdelphij			STUNPUTC(new);
299263397Sdelphij	}
300263397Sdelphij	while ((p = getcomponent()) != NULL) {
301263397Sdelphij		if (equal(p, "..")) {
302263397Sdelphij			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
303263397Sdelphij		} else if (*p != '\0' && ! equal(p, ".")) {
304263397Sdelphij			STPUTC('/', new);
305263397Sdelphij			STPUTS(p, new);
306263397Sdelphij		}
307263397Sdelphij	}
308263397Sdelphij	if (new == stackblock())
309263397Sdelphij		STPUTC('/', new);
310168404Spjd	STACKSTRNUL(new);
311219089Spjd	return stackblock();
312168404Spjd}
313168404Spjd
314168404Spjd/*
315168404Spjd * Update curdir (the name of the current directory) in response to a
316251631Sdelphij * cd command.  We also call hashcd to let the routines in exec.c know
317168404Spjd * that the current directory has changed.
318168404Spjd */
319168404Spjdstatic void
320265740Sdelphijupdatepwd(char *dir)
321263397Sdelphij{
322168404Spjd	hashcd();				/* update command hash table */
323168404Spjd
324168404Spjd	if (prevdir)
325263397Sdelphij		ckfree(prevdir);
326168404Spjd	prevdir = curdir;
327168404Spjd	curdir = dir ? savestr(dir) : NULL;
328168404Spjd	setvar("PWD", curdir, VEXPORT);
329168404Spjd	setvar("OLDPWD", prevdir, VEXPORT);
330168404Spjd}
331263397Sdelphij
332168404Spjdint
333263397Sdelphijpwdcmd(int argc, char **argv)
334263397Sdelphij{
335168404Spjd	char *p;
336168404Spjd	int ch, phys;
337168404Spjd
338168404Spjd	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
339168404Spjd	phys = Pflag;
340168404Spjd	while ((ch = getopt(argc, argv, "LP")) != -1) {
341168404Spjd		switch (ch) {
342168404Spjd		case 'L':
343263397Sdelphij			phys = 0;
344263397Sdelphij			break;
345263397Sdelphij		case 'P':
346263397Sdelphij			phys = 1;
347263397Sdelphij			break;
348263397Sdelphij		default:
349263397Sdelphij			error("unknown option: -%c", optopt);
350168404Spjd			break;
351263397Sdelphij		}
352263397Sdelphij	}
353263397Sdelphij	argc -= optind;
354263397Sdelphij	argv += optind;
355263397Sdelphij
356263397Sdelphij	if (argc != 0)
357263397Sdelphij		error("too many arguments");
358263397Sdelphij
359263397Sdelphij	if (!phys && getpwd()) {
360168404Spjd		out1str(curdir);
361263397Sdelphij		out1c('\n');
362263397Sdelphij	} else {
363168404Spjd		if ((p = getpwd2()) == NULL)
364168404Spjd			error(".: %s", strerror(errno));
365263397Sdelphij		out1str(p);
366168404Spjd		out1c('\n');
367263397Sdelphij	}
368263397Sdelphij
369168404Spjd	return 0;
370168404Spjd}
371168404Spjd
372168404Spjd/*
373185029Spjd * Get the current directory and cache the result in curdir.
374168404Spjd */
375168404Spjdstatic char *
376168404Spjdgetpwd(void)
377265740Sdelphij{
378265740Sdelphij	char *p;
379265740Sdelphij
380265740Sdelphij	if (curdir)
381265740Sdelphij		return curdir;
382265740Sdelphij
383265740Sdelphij	p = getpwd2();
384265740Sdelphij	if (p != NULL)
385265740Sdelphij		curdir = savestr(p);
386265740Sdelphij
387265740Sdelphij	return curdir;
388265740Sdelphij}
389265740Sdelphij
390265740Sdelphij#define MAXPWD 256
391265740Sdelphij
392265740Sdelphij/*
393168404Spjd * Return the current directory.
394251631Sdelphij */
395168404Spjdstatic char *
396185029Spjdgetpwd2(void)
397185029Spjd{
398168404Spjd	char *pwd;
399168404Spjd	int i;
400168404Spjd
401168404Spjd	for (i = MAXPWD;; i *= 2) {
402168404Spjd		pwd = stalloc(i);
403269845Sdelphij		if (getcwd(pwd, i) != NULL)
404168404Spjd			return pwd;
405168404Spjd		stunalloc(pwd);
406168404Spjd		if (errno != ERANGE)
407168404Spjd			break;
408269845Sdelphij	}
409269845Sdelphij
410219089Spjd	return NULL;
411219089Spjd}
412219089Spjd
413219089Spjd/*
414219089Spjd * Initialize PWD in a new shell.
415168404Spjd * If the shell is interactive, we need to warn if this fails.
416168404Spjd */
417168404Spjdvoid
418168404Spjdpwd_init(int warn)
419168404Spjd{
420168404Spjd	char *pwd;
421168404Spjd	struct stat stdot, stpwd;
422168404Spjd
423168404Spjd	pwd = lookupvar("PWD");
424168404Spjd	if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
425168404Spjd	    stat(pwd, &stpwd) != -1 &&
426168404Spjd	    stdot.st_dev == stpwd.st_dev &&
427168404Spjd	    stdot.st_ino == stpwd.st_ino) {
428168404Spjd		if (curdir)
429168404Spjd			ckfree(curdir);
430168404Spjd		curdir = savestr(pwd);
431168404Spjd	}
432168404Spjd	if (getpwd() == NULL && warn)
433168404Spjd		out2fmt_flush("sh: cannot determine working directory\n");
434168404Spjd	setvar("PWD", curdir, VEXPORT);
435168404Spjd}
436168404Spjd