cd.c revision 20425
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
353044Sdg *
3620425Ssteve *	$Id: cd.c,v 1.6 1996/09/01 10:19:47 peter Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
4020425Sstevestatic char const sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
411556Srgrimes#endif /* not lint */
421556Srgrimes
4317987Speter#include <sys/types.h>
4417987Speter#include <sys/stat.h>
4517987Speter#include <stdlib.h>
4620425Ssteve#include <string.h>
4717987Speter#include <unistd.h>
4817987Speter#include <errno.h>
4917987Speter
501556Srgrimes/*
511556Srgrimes * The cd and pwd commands.
521556Srgrimes */
531556Srgrimes
541556Srgrimes#include "shell.h"
551556Srgrimes#include "var.h"
561556Srgrimes#include "nodes.h"	/* for jobs.h */
571556Srgrimes#include "jobs.h"
581556Srgrimes#include "options.h"
591556Srgrimes#include "output.h"
601556Srgrimes#include "memalloc.h"
611556Srgrimes#include "error.h"
6220425Ssteve#include "exec.h"
6317987Speter#include "redir.h"
641556Srgrimes#include "mystring.h"
6517987Speter#include "show.h"
6620425Ssteve#include "cd.h"
671556Srgrimes
6817987SpeterSTATIC int docd __P((char *, int));
6917987SpeterSTATIC char *getcomponent __P((void));
7017987SpeterSTATIC void updatepwd __P((char *));
711556Srgrimes
7220425Sstevechar *curdir = NULL;		/* current working directory */
731556Srgrimeschar *prevdir;			/* previous working directory */
741556SrgrimesSTATIC char *cdcomppath;
751556Srgrimes
761556Srgrimesint
7717987Spetercdcmd(argc, argv)
7817987Speter	int argc;
7920425Ssteve	char **argv;
8017987Speter{
811556Srgrimes	char *dest;
821556Srgrimes	char *path;
831556Srgrimes	char *p;
841556Srgrimes	struct stat statb;
851556Srgrimes	int print = 0;
861556Srgrimes
871556Srgrimes	nextopt(nullstr);
881556Srgrimes	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
891556Srgrimes		error("HOME not set");
905234Sbde	if (*dest == '\0')
915234Sbde		dest = ".";
921556Srgrimes	if (dest[0] == '-' && dest[1] == '\0') {
931556Srgrimes		dest = prevdir ? prevdir : curdir;
9412273Speter		if (dest)
9512273Speter			print = 1;
9612273Speter		else
9712273Speter			dest = ".";
981556Srgrimes	}
991556Srgrimes	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
1001556Srgrimes		path = nullstr;
1011556Srgrimes	while ((p = padvance(&path, dest)) != NULL) {
10217987Speter		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1031556Srgrimes			if (!print) {
1041556Srgrimes				/*
1051556Srgrimes				 * XXX - rethink
1061556Srgrimes				 */
1071556Srgrimes				if (p[0] == '.' && p[1] == '/')
1081556Srgrimes					p += 2;
1091556Srgrimes				print = strcmp(p, dest);
1101556Srgrimes			}
1111556Srgrimes			if (docd(p, print) >= 0)
1121556Srgrimes				return 0;
1131556Srgrimes
1141556Srgrimes		}
1151556Srgrimes	}
1161556Srgrimes	error("can't cd to %s", dest);
11717987Speter	/*NOTREACHED*/
11817987Speter	return 0;
1191556Srgrimes}
1201556Srgrimes
1211556Srgrimes
1221556Srgrimes/*
1231556Srgrimes * Actually do the chdir.  If the name refers to symbolic links, we
1241556Srgrimes * compute the actual directory name before doing the cd.  In an
1251556Srgrimes * interactive shell, print the directory name if "print" is nonzero
1261556Srgrimes * or if the name refers to a symbolic link.  We also print the name
1271556Srgrimes * if "/u/logname" was expanded in it, since this is similar to a
1281556Srgrimes * symbolic link.  (The check for this breaks if the user gives the
1291556Srgrimes * cd command some additional, unused arguments.)
1301556Srgrimes */
1311556Srgrimes
1321556Srgrimes#if SYMLINKS == 0
1331556SrgrimesSTATIC int
1341556Srgrimesdocd(dest, print)
1351556Srgrimes	char *dest;
13617987Speter	int print;
13720425Ssteve{
13820425Ssteve
13920425Ssteve	TRACE(("docd(\"%s\", %d) called\n", dest, print));
1401556Srgrimes	INTOFF;
1411556Srgrimes	if (chdir(dest) < 0) {
1421556Srgrimes		INTON;
1431556Srgrimes		return -1;
1441556Srgrimes	}
1451556Srgrimes	updatepwd(dest);
1461556Srgrimes	INTON;
1471556Srgrimes	if (print && iflag)
1481556Srgrimes		out1fmt("%s\n", stackblock());
1491556Srgrimes	return 0;
1501556Srgrimes}
1511556Srgrimes
1521556Srgrimes#else
1531556Srgrimes
1541556Srgrimes
1551556Srgrimes
1561556SrgrimesSTATIC int
1571556Srgrimesdocd(dest, print)
1581556Srgrimes	char *dest;
15917987Speter	int print;
16017987Speter{
1611556Srgrimes	register char *p;
1621556Srgrimes	register char *q;
1631556Srgrimes	char *symlink;
1641556Srgrimes	char *component;
1651556Srgrimes	struct stat statb;
1661556Srgrimes	int first;
1671556Srgrimes	int i;
1681556Srgrimes
1691556Srgrimes	TRACE(("docd(\"%s\", %d) called\n", dest, print));
1701556Srgrimes
1711556Srgrimestop:
1721556Srgrimes	cdcomppath = dest;
1731556Srgrimes	STARTSTACKSTR(p);
1741556Srgrimes	if (*dest == '/') {
1751556Srgrimes		STPUTC('/', p);
1761556Srgrimes		cdcomppath++;
1771556Srgrimes	}
1781556Srgrimes	first = 1;
1791556Srgrimes	while ((q = getcomponent()) != NULL) {
18017987Speter		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
1811556Srgrimes			continue;
1821556Srgrimes		if (! first)
1831556Srgrimes			STPUTC('/', p);
1841556Srgrimes		first = 0;
1851556Srgrimes		component = q;
1861556Srgrimes		while (*q)
1871556Srgrimes			STPUTC(*q++, p);
1881556Srgrimes		if (equal(component, ".."))
1891556Srgrimes			continue;
1901556Srgrimes		STACKSTRNUL(p);
1911556Srgrimes		if (lstat(stackblock(), &statb) < 0)
1921556Srgrimes			error("lstat %s failed", stackblock());
19317987Speter		if (!S_ISLNK(statb.st_mode))
1941556Srgrimes			continue;
1951556Srgrimes
1961556Srgrimes		/* Hit a symbolic link.  We have to start all over again. */
1971556Srgrimes		print = 1;
1981556Srgrimes		STPUTC('\0', p);
1991556Srgrimes		symlink = grabstackstr(p);
2001556Srgrimes		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
2011556Srgrimes		if (cdcomppath != NULL)
2021556Srgrimes			i += strlen(cdcomppath);
2031556Srgrimes		p = stalloc(i);
2041556Srgrimes		if (readlink(symlink, p, (int)statb.st_size) < 0) {
2051556Srgrimes			error("readlink %s failed", stackblock());
2061556Srgrimes		}
2071556Srgrimes		if (cdcomppath != NULL) {
2081556Srgrimes			p[(int)statb.st_size] = '/';
2091556Srgrimes			scopy(cdcomppath, p + (int)statb.st_size + 1);
2101556Srgrimes		} else {
2111556Srgrimes			p[(int)statb.st_size] = '\0';
2121556Srgrimes		}
2131556Srgrimes		if (p[0] != '/') {	/* relative path name */
2141556Srgrimes			char *r;
2151556Srgrimes			q = r = symlink;
2161556Srgrimes			while (*q) {
2171556Srgrimes				if (*q++ == '/')
2181556Srgrimes					r = q;
2191556Srgrimes			}
2201556Srgrimes			*r = '\0';
2211556Srgrimes			dest = stalloc(strlen(symlink) + strlen(p) + 1);
2221556Srgrimes			scopy(symlink, dest);
2231556Srgrimes			strcat(dest, p);
2241556Srgrimes		} else {
2251556Srgrimes			dest = p;
2261556Srgrimes		}
2271556Srgrimes		goto top;
2281556Srgrimes	}
2291556Srgrimes	STPUTC('\0', p);
2301556Srgrimes	p = grabstackstr(p);
2311556Srgrimes	INTOFF;
2325234Sbde	if (chdir(*p ? p : ".") < 0) {
2331556Srgrimes		INTON;
2341556Srgrimes		return -1;
2351556Srgrimes	}
2361556Srgrimes	updatepwd(p);
2371556Srgrimes	INTON;
2381556Srgrimes	if (print && iflag)
2391556Srgrimes		out1fmt("%s\n", p);
2401556Srgrimes	return 0;
2411556Srgrimes}
2421556Srgrimes#endif /* SYMLINKS */
2431556Srgrimes
2441556Srgrimes
2451556Srgrimes
2461556Srgrimes/*
2471556Srgrimes * Get the next component of the path name pointed to by cdcomppath.
2481556Srgrimes * This routine overwrites the string pointed to by cdcomppath.
2491556Srgrimes */
2501556Srgrimes
2511556SrgrimesSTATIC char *
2521556Srgrimesgetcomponent() {
2531556Srgrimes	register char *p;
2541556Srgrimes	char *start;
2551556Srgrimes
2561556Srgrimes	if ((p = cdcomppath) == NULL)
2571556Srgrimes		return NULL;
2581556Srgrimes	start = cdcomppath;
2591556Srgrimes	while (*p != '/' && *p != '\0')
2601556Srgrimes		p++;
2611556Srgrimes	if (*p == '\0') {
2621556Srgrimes		cdcomppath = NULL;
2631556Srgrimes	} else {
2641556Srgrimes		*p++ = '\0';
2651556Srgrimes		cdcomppath = p;
2661556Srgrimes	}
2671556Srgrimes	return start;
2681556Srgrimes}
2691556Srgrimes
2701556Srgrimes
2711556Srgrimes
2721556Srgrimes/*
2731556Srgrimes * Update curdir (the name of the current directory) in response to a
2741556Srgrimes * cd command.  We also call hashcd to let the routines in exec.c know
2751556Srgrimes * that the current directory has changed.
2761556Srgrimes */
2771556Srgrimes
2781556Srgrimesvoid hashcd();
2791556Srgrimes
2801556SrgrimesSTATIC void
2811556Srgrimesupdatepwd(dir)
2821556Srgrimes	char *dir;
2831556Srgrimes	{
2841556Srgrimes	char *new;
2851556Srgrimes	char *p;
2861556Srgrimes
2871556Srgrimes	hashcd();				/* update command hash table */
2881556Srgrimes	cdcomppath = stalloc(strlen(dir) + 1);
2891556Srgrimes	scopy(dir, cdcomppath);
2901556Srgrimes	STARTSTACKSTR(new);
2911556Srgrimes	if (*dir != '/') {
2921556Srgrimes		if (curdir == NULL)
2931556Srgrimes			return;
2941556Srgrimes		p = curdir;
2951556Srgrimes		while (*p)
2961556Srgrimes			STPUTC(*p++, new);
2971556Srgrimes		if (p[-1] == '/')
2981556Srgrimes			STUNPUTC(new);
2991556Srgrimes	}
3001556Srgrimes	while ((p = getcomponent()) != NULL) {
3011556Srgrimes		if (equal(p, "..")) {
3021556Srgrimes			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
3031556Srgrimes		} else if (*p != '\0' && ! equal(p, ".")) {
3041556Srgrimes			STPUTC('/', new);
3051556Srgrimes			while (*p)
3061556Srgrimes				STPUTC(*p++, new);
3071556Srgrimes		}
3081556Srgrimes	}
3091556Srgrimes	if (new == stackblock())
3101556Srgrimes		STPUTC('/', new);
3111556Srgrimes	STACKSTRNUL(new);
3121556Srgrimes	INTOFF;
3131556Srgrimes	if (prevdir)
3141556Srgrimes		ckfree(prevdir);
3151556Srgrimes	prevdir = curdir;
3161556Srgrimes	curdir = savestr(stackblock());
3171556Srgrimes	INTON;
3181556Srgrimes}
3191556Srgrimes
3201556Srgrimes
3211556Srgrimes
3221556Srgrimesint
32317987Speterpwdcmd(argc, argv)
32417987Speter	int argc;
32520425Ssteve	char **argv;
32617987Speter{
3271556Srgrimes	getpwd();
3281556Srgrimes	out1str(curdir);
3291556Srgrimes	out1c('\n');
3301556Srgrimes	return 0;
3311556Srgrimes}
3321556Srgrimes
3331556Srgrimes
3341556Srgrimes
33520425Ssteve
33620425Ssteve#define MAXPWD 256
33720425Ssteve
3381556Srgrimes/*
3391556Srgrimes * Run /bin/pwd to find out what the current directory is.  We suppress
3401556Srgrimes * interrupts throughout most of this, but the user can still break out
3411556Srgrimes * of it by killing the pwd program.  If we already know the current
3421556Srgrimes * directory, this routine returns immediately.
3431556Srgrimes */
34420425Sstevevoid
34520425Sstevegetpwd()
34620425Ssteve{
3471556Srgrimes	char buf[MAXPWD];
3481556Srgrimes	char *p;
3491556Srgrimes	int i;
3501556Srgrimes	int status;
3511556Srgrimes	struct job *jp;
3521556Srgrimes	int pip[2];
3534192Sjkh	char *pwd_bin = "/bin/pwd";
3541556Srgrimes
3551556Srgrimes	if (curdir)
3561556Srgrimes		return;
3571556Srgrimes	INTOFF;
3581556Srgrimes	if (pipe(pip) < 0)
3591556Srgrimes		error("Pipe call failed");
3604192Sjkh	/* make a fall-back guess, otherwise we're simply screwed */
3614192Sjkh	if (access(pwd_bin, X_OK) == -1)
3624192Sjkh		pwd_bin = "/stand/pwd";
3631556Srgrimes	jp = makejob((union node *)NULL, 1);
3641556Srgrimes	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
3651556Srgrimes		close(pip[0]);
3661556Srgrimes		if (pip[1] != 1) {
3671556Srgrimes			close(1);
3681556Srgrimes			copyfd(pip[1], 1);
3691556Srgrimes			close(pip[1]);
3701556Srgrimes		}
37120425Ssteve		(void) execl(pwd_bin, "pwd", (char *)0);
3724192Sjkh		error("Cannot exec %s", pwd_bin);
3731556Srgrimes	}
37420425Ssteve	(void) close(pip[1]);
3751556Srgrimes	pip[1] = -1;
3761556Srgrimes	p = buf;
3771556Srgrimes	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
37817987Speter	     || (i == -1 && errno == EINTR)) {
3791556Srgrimes		if (i > 0)
3801556Srgrimes			p += i;
3811556Srgrimes	}
38220425Ssteve	(void) close(pip[0]);
3831556Srgrimes	pip[0] = -1;
3841556Srgrimes	status = waitforjob(jp);
3851556Srgrimes	if (status != 0)
3861556Srgrimes		error((char *)0);
3871556Srgrimes	if (i < 0 || p == buf || p[-1] != '\n')
3881556Srgrimes		error("pwd command failed");
3891556Srgrimes	p[-1] = '\0';
3901556Srgrimes	curdir = savestr(buf);
3911556Srgrimes	INTON;
3921556Srgrimes}
393