xref: /csrg-svn/bin/sh/cd.c (revision 69272)
147109Sbostic /*-
260698Sbostic  * Copyright (c) 1991, 1993
360698Sbostic  *	The Regents of the University of California.  All rights reserved.
447109Sbostic  *
547109Sbostic  * This code is derived from software contributed to Berkeley by
647109Sbostic  * Kenneth Almquist.
747109Sbostic  *
847109Sbostic  * %sccs.include.redist.c%
947109Sbostic  */
1047109Sbostic 
1147109Sbostic #ifndef lint
12*69272Schristos static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 05/04/95";
1347109Sbostic #endif /* not lint */
1447109Sbostic 
15*69272Schristos #include <sys/types.h>
16*69272Schristos #include <sys/stat.h>
17*69272Schristos #include <stdlib.h>
18*69272Schristos #include <unistd.h>
19*69272Schristos #include <errno.h>
20*69272Schristos 
2147109Sbostic /*
2247109Sbostic  * The cd and pwd commands.
2347109Sbostic  */
2447109Sbostic 
2547109Sbostic #include "shell.h"
2647109Sbostic #include "var.h"
2747109Sbostic #include "nodes.h"	/* for jobs.h */
2847109Sbostic #include "jobs.h"
2947109Sbostic #include "options.h"
3047109Sbostic #include "output.h"
3147109Sbostic #include "memalloc.h"
3247109Sbostic #include "error.h"
33*69272Schristos #include "redir.h"
3447109Sbostic #include "mystring.h"
35*69272Schristos #include "show.h"
3647109Sbostic 
37*69272Schristos STATIC int docd __P((char *, int));
38*69272Schristos STATIC char *getcomponent __P((void));
39*69272Schristos STATIC void updatepwd __P((char *));
40*69272Schristos STATIC void getpwd __P((void));
4147109Sbostic 
4247109Sbostic char *curdir;			/* current working directory */
4355248Smarc char *prevdir;			/* previous working directory */
4447109Sbostic STATIC char *cdcomppath;
4547109Sbostic 
4647109Sbostic int
cdcmd(argc,argv)47*69272Schristos cdcmd(argc, argv)
48*69272Schristos 	int argc;
49*69272Schristos 	char **argv;
50*69272Schristos {
5147109Sbostic 	char *dest;
5247109Sbostic 	char *path;
5347109Sbostic 	char *p;
5447109Sbostic 	struct stat statb;
5547109Sbostic 	char *padvance();
5655248Smarc 	int print = 0;
5747109Sbostic 
5847291Smarc 	nextopt(nullstr);
5947291Smarc 	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
6047109Sbostic 		error("HOME not set");
6155248Smarc 	if (dest[0] == '-' && dest[1] == '\0') {
6255248Smarc 		dest = prevdir ? prevdir : curdir;
6355248Smarc 		print = 1;
6455248Smarc 	}
6547109Sbostic 	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
6647109Sbostic 		path = nullstr;
6747109Sbostic 	while ((p = padvance(&path, dest)) != NULL) {
68*69272Schristos 		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
6955248Smarc 			if (!print) {
7055248Smarc 				/*
7155248Smarc 				 * XXX - rethink
7255248Smarc 				 */
7355248Smarc 				if (p[0] == '.' && p[1] == '/')
7455248Smarc 					p += 2;
7555248Smarc 				print = strcmp(p, dest);
7655248Smarc 			}
7755248Smarc 			if (docd(p, print) >= 0)
7855248Smarc 				return 0;
7955248Smarc 
8055248Smarc 		}
8147109Sbostic 	}
8247109Sbostic 	error("can't cd to %s", dest);
83*69272Schristos 	/*NOTREACHED*/
84*69272Schristos 	return 0;
8547109Sbostic }
8647109Sbostic 
8747109Sbostic 
8847109Sbostic /*
8947109Sbostic  * Actually do the chdir.  If the name refers to symbolic links, we
9047109Sbostic  * compute the actual directory name before doing the cd.  In an
9147109Sbostic  * interactive shell, print the directory name if "print" is nonzero
9247109Sbostic  * or if the name refers to a symbolic link.  We also print the name
9347109Sbostic  * if "/u/logname" was expanded in it, since this is similar to a
9447109Sbostic  * symbolic link.  (The check for this breaks if the user gives the
9547109Sbostic  * cd command some additional, unused arguments.)
9647109Sbostic  */
9747109Sbostic 
9847109Sbostic #if SYMLINKS == 0
9947109Sbostic STATIC int
docd(dest,print)10047109Sbostic docd(dest, print)
10147109Sbostic 	char *dest;
10247109Sbostic 	{
10347109Sbostic 	INTOFF;
10447109Sbostic 	if (chdir(dest) < 0) {
10547109Sbostic 		INTON;
10647109Sbostic 		return -1;
10747109Sbostic 	}
10847109Sbostic 	updatepwd(dest);
10947109Sbostic 	INTON;
11047109Sbostic 	if (print && iflag)
11147109Sbostic 		out1fmt("%s\n", stackblock());
11247109Sbostic 	return 0;
11347109Sbostic }
11447109Sbostic 
11547109Sbostic #else
11647109Sbostic 
11747109Sbostic 
11847109Sbostic 
11947109Sbostic STATIC int
docd(dest,print)12047109Sbostic docd(dest, print)
12147109Sbostic 	char *dest;
122*69272Schristos 	int print;
123*69272Schristos {
12447109Sbostic 	register char *p;
12547109Sbostic 	register char *q;
12647109Sbostic 	char *symlink;
12747109Sbostic 	char *component;
12847109Sbostic 	struct stat statb;
12947109Sbostic 	int first;
13047109Sbostic 	int i;
13147109Sbostic 
13247109Sbostic 	TRACE(("docd(\"%s\", %d) called\n", dest, print));
13347109Sbostic 
13447109Sbostic top:
13547109Sbostic 	cdcomppath = dest;
13647109Sbostic 	STARTSTACKSTR(p);
13747109Sbostic 	if (*dest == '/') {
13847109Sbostic 		STPUTC('/', p);
13947109Sbostic 		cdcomppath++;
14047109Sbostic 	}
14147109Sbostic 	first = 1;
14247109Sbostic 	while ((q = getcomponent()) != NULL) {
143*69272Schristos 		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
14447109Sbostic 			continue;
14547109Sbostic 		if (! first)
14647109Sbostic 			STPUTC('/', p);
14747109Sbostic 		first = 0;
14847109Sbostic 		component = q;
14947109Sbostic 		while (*q)
15047109Sbostic 			STPUTC(*q++, p);
15147109Sbostic 		if (equal(component, ".."))
15247109Sbostic 			continue;
15347109Sbostic 		STACKSTRNUL(p);
15447109Sbostic 		if (lstat(stackblock(), &statb) < 0)
15547109Sbostic 			error("lstat %s failed", stackblock());
156*69272Schristos 		if (!S_ISLNK(statb.st_mode))
15747109Sbostic 			continue;
15847109Sbostic 
15947109Sbostic 		/* Hit a symbolic link.  We have to start all over again. */
16047109Sbostic 		print = 1;
16147109Sbostic 		STPUTC('\0', p);
16247109Sbostic 		symlink = grabstackstr(p);
16347109Sbostic 		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
16447109Sbostic 		if (cdcomppath != NULL)
16547109Sbostic 			i += strlen(cdcomppath);
16647109Sbostic 		p = stalloc(i);
16747109Sbostic 		if (readlink(symlink, p, (int)statb.st_size) < 0) {
16847109Sbostic 			error("readlink %s failed", stackblock());
16947109Sbostic 		}
17047109Sbostic 		if (cdcomppath != NULL) {
17147109Sbostic 			p[(int)statb.st_size] = '/';
17247109Sbostic 			scopy(cdcomppath, p + (int)statb.st_size + 1);
17347109Sbostic 		} else {
17447109Sbostic 			p[(int)statb.st_size] = '\0';
17547109Sbostic 		}
17647109Sbostic 		if (p[0] != '/') {	/* relative path name */
17747109Sbostic 			char *r;
17847109Sbostic 			q = r = symlink;
17947109Sbostic 			while (*q) {
18047109Sbostic 				if (*q++ == '/')
18147109Sbostic 					r = q;
18247109Sbostic 			}
18347109Sbostic 			*r = '\0';
18447109Sbostic 			dest = stalloc(strlen(symlink) + strlen(p) + 1);
18547109Sbostic 			scopy(symlink, dest);
18647109Sbostic 			strcat(dest, p);
18747109Sbostic 		} else {
18847109Sbostic 			dest = p;
18947109Sbostic 		}
19047109Sbostic 		goto top;
19147109Sbostic 	}
19247109Sbostic 	STPUTC('\0', p);
19347109Sbostic 	p = grabstackstr(p);
19447109Sbostic 	INTOFF;
19547109Sbostic 	if (chdir(p) < 0) {
19647109Sbostic 		INTON;
19747109Sbostic 		return -1;
19847109Sbostic 	}
19947109Sbostic 	updatepwd(p);
20047109Sbostic 	INTON;
20147109Sbostic 	if (print && iflag)
20247109Sbostic 		out1fmt("%s\n", p);
20347109Sbostic 	return 0;
20447109Sbostic }
20547109Sbostic #endif /* SYMLINKS */
20647109Sbostic 
20747109Sbostic 
20847109Sbostic 
20947109Sbostic /*
21047109Sbostic  * Get the next component of the path name pointed to by cdcomppath.
21147109Sbostic  * This routine overwrites the string pointed to by cdcomppath.
21247109Sbostic  */
21347109Sbostic 
21447109Sbostic STATIC char *
getcomponent()21547109Sbostic getcomponent() {
21647109Sbostic 	register char *p;
21747109Sbostic 	char *start;
21847109Sbostic 
21947109Sbostic 	if ((p = cdcomppath) == NULL)
22047109Sbostic 		return NULL;
22147109Sbostic 	start = cdcomppath;
22247109Sbostic 	while (*p != '/' && *p != '\0')
22347109Sbostic 		p++;
22447109Sbostic 	if (*p == '\0') {
22547109Sbostic 		cdcomppath = NULL;
22647109Sbostic 	} else {
22747109Sbostic 		*p++ = '\0';
22847109Sbostic 		cdcomppath = p;
22947109Sbostic 	}
23047109Sbostic 	return start;
23147109Sbostic }
23247109Sbostic 
23347109Sbostic 
23447109Sbostic 
23547109Sbostic /*
23647109Sbostic  * Update curdir (the name of the current directory) in response to a
23747109Sbostic  * cd command.  We also call hashcd to let the routines in exec.c know
23847109Sbostic  * that the current directory has changed.
23947109Sbostic  */
24047109Sbostic 
24147109Sbostic void hashcd();
24247109Sbostic 
24347109Sbostic STATIC void
updatepwd(dir)24447109Sbostic updatepwd(dir)
24547109Sbostic 	char *dir;
24647109Sbostic 	{
24747109Sbostic 	char *new;
24847109Sbostic 	char *p;
24947109Sbostic 
25047109Sbostic 	hashcd();				/* update command hash table */
25147109Sbostic 	cdcomppath = stalloc(strlen(dir) + 1);
25247109Sbostic 	scopy(dir, cdcomppath);
25347109Sbostic 	STARTSTACKSTR(new);
25447109Sbostic 	if (*dir != '/') {
25547109Sbostic 		if (curdir == NULL)
25647109Sbostic 			return;
25747109Sbostic 		p = curdir;
25847109Sbostic 		while (*p)
25947109Sbostic 			STPUTC(*p++, new);
26047109Sbostic 		if (p[-1] == '/')
26147109Sbostic 			STUNPUTC(new);
26247109Sbostic 	}
26347109Sbostic 	while ((p = getcomponent()) != NULL) {
26447109Sbostic 		if (equal(p, "..")) {
26547109Sbostic 			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
26647109Sbostic 		} else if (*p != '\0' && ! equal(p, ".")) {
26747109Sbostic 			STPUTC('/', new);
26847109Sbostic 			while (*p)
26947109Sbostic 				STPUTC(*p++, new);
27047109Sbostic 		}
27147109Sbostic 	}
27247109Sbostic 	if (new == stackblock())
27347109Sbostic 		STPUTC('/', new);
27447109Sbostic 	STACKSTRNUL(new);
27555248Smarc 	INTOFF;
27655248Smarc 	if (prevdir)
27755248Smarc 		ckfree(prevdir);
27855248Smarc 	prevdir = curdir;
27947109Sbostic 	curdir = savestr(stackblock());
28055248Smarc 	INTON;
28147109Sbostic }
28247109Sbostic 
28347109Sbostic 
28447109Sbostic 
28547109Sbostic int
pwdcmd(argc,argv)286*69272Schristos pwdcmd(argc, argv)
287*69272Schristos 	int argc;
288*69272Schristos 	char **argv;
289*69272Schristos {
29047109Sbostic 	getpwd();
29147109Sbostic 	out1str(curdir);
29247109Sbostic 	out1c('\n');
29347109Sbostic 	return 0;
29447109Sbostic }
29547109Sbostic 
29647109Sbostic 
29747109Sbostic 
29847109Sbostic /*
29947109Sbostic  * Run /bin/pwd to find out what the current directory is.  We suppress
30047109Sbostic  * interrupts throughout most of this, but the user can still break out
30147109Sbostic  * of it by killing the pwd program.  If we already know the current
30247109Sbostic  * directory, this routine returns immediately.
30347109Sbostic  */
30447109Sbostic 
30547109Sbostic #define MAXPWD 256
30647109Sbostic 
30747109Sbostic STATIC void
getpwd()30847109Sbostic getpwd() {
30947109Sbostic 	char buf[MAXPWD];
31047109Sbostic 	char *p;
31147109Sbostic 	int i;
31247109Sbostic 	int status;
31347109Sbostic 	struct job *jp;
31447109Sbostic 	int pip[2];
31547109Sbostic 
31647109Sbostic 	if (curdir)
31747109Sbostic 		return;
31847109Sbostic 	INTOFF;
31947109Sbostic 	if (pipe(pip) < 0)
32047109Sbostic 		error("Pipe call failed");
32147109Sbostic 	jp = makejob((union node *)NULL, 1);
32247109Sbostic 	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
32347109Sbostic 		close(pip[0]);
32447109Sbostic 		if (pip[1] != 1) {
32547109Sbostic 			close(1);
32647109Sbostic 			copyfd(pip[1], 1);
32747109Sbostic 			close(pip[1]);
32847109Sbostic 		}
32947109Sbostic 		execl("/bin/pwd", "pwd", (char *)0);
33047109Sbostic 		error("Cannot exec /bin/pwd");
33147109Sbostic 	}
33247109Sbostic 	close(pip[1]);
33347109Sbostic 	pip[1] = -1;
33447109Sbostic 	p = buf;
33547109Sbostic 	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
336*69272Schristos 	     || (i == -1 && errno == EINTR)) {
33747109Sbostic 		if (i > 0)
33847109Sbostic 			p += i;
33947109Sbostic 	}
34047109Sbostic 	close(pip[0]);
34147109Sbostic 	pip[0] = -1;
34247109Sbostic 	status = waitforjob(jp);
34347109Sbostic 	if (status != 0)
34447109Sbostic 		error((char *)0);
34547109Sbostic 	if (i < 0 || p == buf || p[-1] != '\n')
34647109Sbostic 		error("pwd command failed");
34747109Sbostic 	p[-1] = '\0';
34847109Sbostic 	curdir = savestr(buf);
34947109Sbostic 	INTON;
35047109Sbostic }
351