cd.c revision 12273
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	$Id: cd.c,v 1.4 1994/12/26 13:02:05 bde Exp $
37 */
38
39#ifndef lint
40static char sccsid[] = "@(#)cd.c	8.1 (Berkeley) 5/31/93";
41#endif /* not lint */
42
43/*
44 * The cd and pwd commands.
45 */
46
47#include "shell.h"
48#include "var.h"
49#include "nodes.h"	/* for jobs.h */
50#include "jobs.h"
51#include "options.h"
52#include "output.h"
53#include "memalloc.h"
54#include "error.h"
55#include "mystring.h"
56#include <sys/types.h>
57#include <sys/stat.h>
58#include <sys/unistd.h>
59#include <errno.h>
60
61
62#ifdef __STDC__
63STATIC int docd(char *, int);
64STATIC void updatepwd(char *);
65STATIC void getpwd(void);
66STATIC char *getcomponent(void);
67#else
68STATIC int docd();
69STATIC void updatepwd();
70STATIC void getpwd();
71STATIC char *getcomponent();
72#endif
73
74
75char *curdir;			/* current working directory */
76char *prevdir;			/* previous working directory */
77STATIC char *cdcomppath;
78
79int
80cdcmd(argc, argv)  char **argv; {
81	char *dest;
82	char *path;
83	char *p;
84	struct stat statb;
85	char *padvance();
86	int print = 0;
87
88	nextopt(nullstr);
89	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
90		error("HOME not set");
91	if (*dest == '\0')
92		dest = ".";
93	if (dest[0] == '-' && dest[1] == '\0') {
94		dest = prevdir ? prevdir : curdir;
95		if (dest)
96			print = 1;
97		else
98			dest = ".";
99	}
100	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
101		path = nullstr;
102	while ((p = padvance(&path, dest)) != NULL) {
103		if (stat(p, &statb) >= 0
104		 && (statb.st_mode & S_IFMT) == S_IFDIR) {
105			if (!print) {
106				/*
107				 * XXX - rethink
108				 */
109				if (p[0] == '.' && p[1] == '/')
110					p += 2;
111				print = strcmp(p, dest);
112			}
113			if (docd(p, print) >= 0)
114				return 0;
115
116		}
117	}
118	error("can't cd to %s", dest);
119}
120
121
122/*
123 * Actually do the chdir.  If the name refers to symbolic links, we
124 * compute the actual directory name before doing the cd.  In an
125 * interactive shell, print the directory name if "print" is nonzero
126 * or if the name refers to a symbolic link.  We also print the name
127 * if "/u/logname" was expanded in it, since this is similar to a
128 * symbolic link.  (The check for this breaks if the user gives the
129 * cd command some additional, unused arguments.)
130 */
131
132#if SYMLINKS == 0
133STATIC int
134docd(dest, print)
135	char *dest;
136	{
137	INTOFF;
138	if (chdir(dest) < 0) {
139		INTON;
140		return -1;
141	}
142	updatepwd(dest);
143	INTON;
144	if (print && iflag)
145		out1fmt("%s\n", stackblock());
146	return 0;
147}
148
149#else
150
151
152
153STATIC int
154docd(dest, print)
155	char *dest;
156	{
157	register char *p;
158	register char *q;
159	char *symlink;
160	char *component;
161	struct stat statb;
162	int first;
163	int i;
164
165	TRACE(("docd(\"%s\", %d) called\n", dest, print));
166
167top:
168	cdcomppath = dest;
169	STARTSTACKSTR(p);
170	if (*dest == '/') {
171		STPUTC('/', p);
172		cdcomppath++;
173	}
174	first = 1;
175	while ((q = getcomponent()) != NULL) {
176		if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
177			continue;
178		if (! first)
179			STPUTC('/', p);
180		first = 0;
181		component = q;
182		while (*q)
183			STPUTC(*q++, p);
184		if (equal(component, ".."))
185			continue;
186		STACKSTRNUL(p);
187		if (lstat(stackblock(), &statb) < 0)
188			error("lstat %s failed", stackblock());
189		if ((statb.st_mode & S_IFMT) != S_IFLNK)
190			continue;
191
192		/* Hit a symbolic link.  We have to start all over again. */
193		print = 1;
194		STPUTC('\0', p);
195		symlink = grabstackstr(p);
196		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
197		if (cdcomppath != NULL)
198			i += strlen(cdcomppath);
199		p = stalloc(i);
200		if (readlink(symlink, p, (int)statb.st_size) < 0) {
201			error("readlink %s failed", stackblock());
202		}
203		if (cdcomppath != NULL) {
204			p[(int)statb.st_size] = '/';
205			scopy(cdcomppath, p + (int)statb.st_size + 1);
206		} else {
207			p[(int)statb.st_size] = '\0';
208		}
209		if (p[0] != '/') {	/* relative path name */
210			char *r;
211			q = r = symlink;
212			while (*q) {
213				if (*q++ == '/')
214					r = q;
215			}
216			*r = '\0';
217			dest = stalloc(strlen(symlink) + strlen(p) + 1);
218			scopy(symlink, dest);
219			strcat(dest, p);
220		} else {
221			dest = p;
222		}
223		goto top;
224	}
225	STPUTC('\0', p);
226	p = grabstackstr(p);
227	INTOFF;
228	if (chdir(*p ? p : ".") < 0) {
229		INTON;
230		return -1;
231	}
232	updatepwd(p);
233	INTON;
234	if (print && iflag)
235		out1fmt("%s\n", p);
236	return 0;
237}
238#endif /* SYMLINKS */
239
240
241
242/*
243 * Get the next component of the path name pointed to by cdcomppath.
244 * This routine overwrites the string pointed to by cdcomppath.
245 */
246
247STATIC char *
248getcomponent() {
249	register char *p;
250	char *start;
251
252	if ((p = cdcomppath) == NULL)
253		return NULL;
254	start = cdcomppath;
255	while (*p != '/' && *p != '\0')
256		p++;
257	if (*p == '\0') {
258		cdcomppath = NULL;
259	} else {
260		*p++ = '\0';
261		cdcomppath = p;
262	}
263	return start;
264}
265
266
267
268/*
269 * Update curdir (the name of the current directory) in response to a
270 * cd command.  We also call hashcd to let the routines in exec.c know
271 * that the current directory has changed.
272 */
273
274void hashcd();
275
276STATIC void
277updatepwd(dir)
278	char *dir;
279	{
280	char *new;
281	char *p;
282
283	hashcd();				/* update command hash table */
284	cdcomppath = stalloc(strlen(dir) + 1);
285	scopy(dir, cdcomppath);
286	STARTSTACKSTR(new);
287	if (*dir != '/') {
288		if (curdir == NULL)
289			return;
290		p = curdir;
291		while (*p)
292			STPUTC(*p++, new);
293		if (p[-1] == '/')
294			STUNPUTC(new);
295	}
296	while ((p = getcomponent()) != NULL) {
297		if (equal(p, "..")) {
298			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
299		} else if (*p != '\0' && ! equal(p, ".")) {
300			STPUTC('/', new);
301			while (*p)
302				STPUTC(*p++, new);
303		}
304	}
305	if (new == stackblock())
306		STPUTC('/', new);
307	STACKSTRNUL(new);
308	INTOFF;
309	if (prevdir)
310		ckfree(prevdir);
311	prevdir = curdir;
312	curdir = savestr(stackblock());
313	INTON;
314}
315
316
317
318int
319pwdcmd(argc, argv)  char **argv; {
320	getpwd();
321	out1str(curdir);
322	out1c('\n');
323	return 0;
324}
325
326
327
328/*
329 * Run /bin/pwd to find out what the current directory is.  We suppress
330 * interrupts throughout most of this, but the user can still break out
331 * of it by killing the pwd program.  If we already know the current
332 * directory, this routine returns immediately.
333 */
334
335#define MAXPWD 256
336
337STATIC void
338getpwd() {
339	char buf[MAXPWD];
340	char *p;
341	int i;
342	int status;
343	struct job *jp;
344	int pip[2];
345	char *pwd_bin = "/bin/pwd";
346
347	if (curdir)
348		return;
349	INTOFF;
350	if (pipe(pip) < 0)
351		error("Pipe call failed");
352	/* make a fall-back guess, otherwise we're simply screwed */
353	if (access(pwd_bin, X_OK) == -1)
354		pwd_bin = "/stand/pwd";
355	jp = makejob((union node *)NULL, 1);
356	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
357		close(pip[0]);
358		if (pip[1] != 1) {
359			close(1);
360			copyfd(pip[1], 1);
361			close(pip[1]);
362		}
363		execl(pwd_bin, "pwd", (char *)0);
364		error("Cannot exec %s", pwd_bin);
365	}
366	close(pip[1]);
367	pip[1] = -1;
368	p = buf;
369	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
370	     || i == -1 && errno == EINTR) {
371		if (i > 0)
372			p += i;
373	}
374	close(pip[0]);
375	pip[0] = -1;
376	status = waitforjob(jp);
377	if (status != 0)
378		error((char *)0);
379	if (i < 0 || p == buf || p[-1] != '\n')
380		error("pwd command failed");
381	p[-1] = '\0';
382	curdir = savestr(buf);
383	INTON;
384}
385