cd.c revision 5234
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 *
365234Sbde *	$Id: cd.c,v 1.3 1994/11/06 01:29:26 jkh Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
401556Srgrimesstatic char sccsid[] = "@(#)cd.c	8.1 (Berkeley) 5/31/93";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes/*
441556Srgrimes * The cd and pwd commands.
451556Srgrimes */
461556Srgrimes
471556Srgrimes#include "shell.h"
481556Srgrimes#include "var.h"
491556Srgrimes#include "nodes.h"	/* for jobs.h */
501556Srgrimes#include "jobs.h"
511556Srgrimes#include "options.h"
521556Srgrimes#include "output.h"
531556Srgrimes#include "memalloc.h"
541556Srgrimes#include "error.h"
551556Srgrimes#include "mystring.h"
561556Srgrimes#include <sys/types.h>
571556Srgrimes#include <sys/stat.h>
584192Sjkh#include <sys/unistd.h>
591556Srgrimes#include <errno.h>
601556Srgrimes
611556Srgrimes
621556Srgrimes#ifdef __STDC__
631556SrgrimesSTATIC int docd(char *, int);
641556SrgrimesSTATIC void updatepwd(char *);
651556SrgrimesSTATIC void getpwd(void);
661556SrgrimesSTATIC char *getcomponent(void);
671556Srgrimes#else
681556SrgrimesSTATIC int docd();
691556SrgrimesSTATIC void updatepwd();
701556SrgrimesSTATIC void getpwd();
711556SrgrimesSTATIC char *getcomponent();
721556Srgrimes#endif
731556Srgrimes
741556Srgrimes
751556Srgrimeschar *curdir;			/* current working directory */
761556Srgrimeschar *prevdir;			/* previous working directory */
771556SrgrimesSTATIC char *cdcomppath;
781556Srgrimes
791556Srgrimesint
801556Srgrimescdcmd(argc, argv)  char **argv; {
811556Srgrimes	char *dest;
821556Srgrimes	char *path;
831556Srgrimes	char *p;
841556Srgrimes	struct stat statb;
851556Srgrimes	char *padvance();
861556Srgrimes	int print = 0;
871556Srgrimes
881556Srgrimes	nextopt(nullstr);
891556Srgrimes	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
901556Srgrimes		error("HOME not set");
915234Sbde	if (*dest == '\0')
925234Sbde		dest = ".";
931556Srgrimes	if (dest[0] == '-' && dest[1] == '\0') {
941556Srgrimes		dest = prevdir ? prevdir : curdir;
951556Srgrimes		print = 1;
961556Srgrimes	}
971556Srgrimes	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
981556Srgrimes		path = nullstr;
991556Srgrimes	while ((p = padvance(&path, dest)) != NULL) {
1001556Srgrimes		if (stat(p, &statb) >= 0
1011556Srgrimes		 && (statb.st_mode & S_IFMT) == S_IFDIR) {
1021556Srgrimes			if (!print) {
1031556Srgrimes				/*
1041556Srgrimes				 * XXX - rethink
1051556Srgrimes				 */
1061556Srgrimes				if (p[0] == '.' && p[1] == '/')
1071556Srgrimes					p += 2;
1081556Srgrimes				print = strcmp(p, dest);
1091556Srgrimes			}
1101556Srgrimes			if (docd(p, print) >= 0)
1111556Srgrimes				return 0;
1121556Srgrimes
1131556Srgrimes		}
1141556Srgrimes	}
1151556Srgrimes	error("can't cd to %s", dest);
1161556Srgrimes}
1171556Srgrimes
1181556Srgrimes
1191556Srgrimes/*
1201556Srgrimes * Actually do the chdir.  If the name refers to symbolic links, we
1211556Srgrimes * compute the actual directory name before doing the cd.  In an
1221556Srgrimes * interactive shell, print the directory name if "print" is nonzero
1231556Srgrimes * or if the name refers to a symbolic link.  We also print the name
1241556Srgrimes * if "/u/logname" was expanded in it, since this is similar to a
1251556Srgrimes * symbolic link.  (The check for this breaks if the user gives the
1261556Srgrimes * cd command some additional, unused arguments.)
1271556Srgrimes */
1281556Srgrimes
1291556Srgrimes#if SYMLINKS == 0
1301556SrgrimesSTATIC int
1311556Srgrimesdocd(dest, print)
1321556Srgrimes	char *dest;
1331556Srgrimes	{
1341556Srgrimes	INTOFF;
1351556Srgrimes	if (chdir(dest) < 0) {
1361556Srgrimes		INTON;
1371556Srgrimes		return -1;
1381556Srgrimes	}
1391556Srgrimes	updatepwd(dest);
1401556Srgrimes	INTON;
1411556Srgrimes	if (print && iflag)
1421556Srgrimes		out1fmt("%s\n", stackblock());
1431556Srgrimes	return 0;
1441556Srgrimes}
1451556Srgrimes
1461556Srgrimes#else
1471556Srgrimes
1481556Srgrimes
1491556Srgrimes
1501556SrgrimesSTATIC int
1511556Srgrimesdocd(dest, print)
1521556Srgrimes	char *dest;
1531556Srgrimes	{
1541556Srgrimes	register char *p;
1551556Srgrimes	register char *q;
1561556Srgrimes	char *symlink;
1571556Srgrimes	char *component;
1581556Srgrimes	struct stat statb;
1591556Srgrimes	int first;
1601556Srgrimes	int i;
1611556Srgrimes
1621556Srgrimes	TRACE(("docd(\"%s\", %d) called\n", dest, print));
1631556Srgrimes
1641556Srgrimestop:
1651556Srgrimes	cdcomppath = dest;
1661556Srgrimes	STARTSTACKSTR(p);
1671556Srgrimes	if (*dest == '/') {
1681556Srgrimes		STPUTC('/', p);
1691556Srgrimes		cdcomppath++;
1701556Srgrimes	}
1711556Srgrimes	first = 1;
1721556Srgrimes	while ((q = getcomponent()) != NULL) {
1731556Srgrimes		if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
1741556Srgrimes			continue;
1751556Srgrimes		if (! first)
1761556Srgrimes			STPUTC('/', p);
1771556Srgrimes		first = 0;
1781556Srgrimes		component = q;
1791556Srgrimes		while (*q)
1801556Srgrimes			STPUTC(*q++, p);
1811556Srgrimes		if (equal(component, ".."))
1821556Srgrimes			continue;
1831556Srgrimes		STACKSTRNUL(p);
1841556Srgrimes		if (lstat(stackblock(), &statb) < 0)
1851556Srgrimes			error("lstat %s failed", stackblock());
1861556Srgrimes		if ((statb.st_mode & S_IFMT) != S_IFLNK)
1871556Srgrimes			continue;
1881556Srgrimes
1891556Srgrimes		/* Hit a symbolic link.  We have to start all over again. */
1901556Srgrimes		print = 1;
1911556Srgrimes		STPUTC('\0', p);
1921556Srgrimes		symlink = grabstackstr(p);
1931556Srgrimes		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
1941556Srgrimes		if (cdcomppath != NULL)
1951556Srgrimes			i += strlen(cdcomppath);
1961556Srgrimes		p = stalloc(i);
1971556Srgrimes		if (readlink(symlink, p, (int)statb.st_size) < 0) {
1981556Srgrimes			error("readlink %s failed", stackblock());
1991556Srgrimes		}
2001556Srgrimes		if (cdcomppath != NULL) {
2011556Srgrimes			p[(int)statb.st_size] = '/';
2021556Srgrimes			scopy(cdcomppath, p + (int)statb.st_size + 1);
2031556Srgrimes		} else {
2041556Srgrimes			p[(int)statb.st_size] = '\0';
2051556Srgrimes		}
2061556Srgrimes		if (p[0] != '/') {	/* relative path name */
2071556Srgrimes			char *r;
2081556Srgrimes			q = r = symlink;
2091556Srgrimes			while (*q) {
2101556Srgrimes				if (*q++ == '/')
2111556Srgrimes					r = q;
2121556Srgrimes			}
2131556Srgrimes			*r = '\0';
2141556Srgrimes			dest = stalloc(strlen(symlink) + strlen(p) + 1);
2151556Srgrimes			scopy(symlink, dest);
2161556Srgrimes			strcat(dest, p);
2171556Srgrimes		} else {
2181556Srgrimes			dest = p;
2191556Srgrimes		}
2201556Srgrimes		goto top;
2211556Srgrimes	}
2221556Srgrimes	STPUTC('\0', p);
2231556Srgrimes	p = grabstackstr(p);
2241556Srgrimes	INTOFF;
2255234Sbde	if (chdir(*p ? p : ".") < 0) {
2261556Srgrimes		INTON;
2271556Srgrimes		return -1;
2281556Srgrimes	}
2291556Srgrimes	updatepwd(p);
2301556Srgrimes	INTON;
2311556Srgrimes	if (print && iflag)
2321556Srgrimes		out1fmt("%s\n", p);
2331556Srgrimes	return 0;
2341556Srgrimes}
2351556Srgrimes#endif /* SYMLINKS */
2361556Srgrimes
2371556Srgrimes
2381556Srgrimes
2391556Srgrimes/*
2401556Srgrimes * Get the next component of the path name pointed to by cdcomppath.
2411556Srgrimes * This routine overwrites the string pointed to by cdcomppath.
2421556Srgrimes */
2431556Srgrimes
2441556SrgrimesSTATIC char *
2451556Srgrimesgetcomponent() {
2461556Srgrimes	register char *p;
2471556Srgrimes	char *start;
2481556Srgrimes
2491556Srgrimes	if ((p = cdcomppath) == NULL)
2501556Srgrimes		return NULL;
2511556Srgrimes	start = cdcomppath;
2521556Srgrimes	while (*p != '/' && *p != '\0')
2531556Srgrimes		p++;
2541556Srgrimes	if (*p == '\0') {
2551556Srgrimes		cdcomppath = NULL;
2561556Srgrimes	} else {
2571556Srgrimes		*p++ = '\0';
2581556Srgrimes		cdcomppath = p;
2591556Srgrimes	}
2601556Srgrimes	return start;
2611556Srgrimes}
2621556Srgrimes
2631556Srgrimes
2641556Srgrimes
2651556Srgrimes/*
2661556Srgrimes * Update curdir (the name of the current directory) in response to a
2671556Srgrimes * cd command.  We also call hashcd to let the routines in exec.c know
2681556Srgrimes * that the current directory has changed.
2691556Srgrimes */
2701556Srgrimes
2711556Srgrimesvoid hashcd();
2721556Srgrimes
2731556SrgrimesSTATIC void
2741556Srgrimesupdatepwd(dir)
2751556Srgrimes	char *dir;
2761556Srgrimes	{
2771556Srgrimes	char *new;
2781556Srgrimes	char *p;
2791556Srgrimes
2801556Srgrimes	hashcd();				/* update command hash table */
2811556Srgrimes	cdcomppath = stalloc(strlen(dir) + 1);
2821556Srgrimes	scopy(dir, cdcomppath);
2831556Srgrimes	STARTSTACKSTR(new);
2841556Srgrimes	if (*dir != '/') {
2851556Srgrimes		if (curdir == NULL)
2861556Srgrimes			return;
2871556Srgrimes		p = curdir;
2881556Srgrimes		while (*p)
2891556Srgrimes			STPUTC(*p++, new);
2901556Srgrimes		if (p[-1] == '/')
2911556Srgrimes			STUNPUTC(new);
2921556Srgrimes	}
2931556Srgrimes	while ((p = getcomponent()) != NULL) {
2941556Srgrimes		if (equal(p, "..")) {
2951556Srgrimes			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
2961556Srgrimes		} else if (*p != '\0' && ! equal(p, ".")) {
2971556Srgrimes			STPUTC('/', new);
2981556Srgrimes			while (*p)
2991556Srgrimes				STPUTC(*p++, new);
3001556Srgrimes		}
3011556Srgrimes	}
3021556Srgrimes	if (new == stackblock())
3031556Srgrimes		STPUTC('/', new);
3041556Srgrimes	STACKSTRNUL(new);
3051556Srgrimes	INTOFF;
3061556Srgrimes	if (prevdir)
3071556Srgrimes		ckfree(prevdir);
3081556Srgrimes	prevdir = curdir;
3091556Srgrimes	curdir = savestr(stackblock());
3101556Srgrimes	INTON;
3111556Srgrimes}
3121556Srgrimes
3131556Srgrimes
3141556Srgrimes
3151556Srgrimesint
3161556Srgrimespwdcmd(argc, argv)  char **argv; {
3171556Srgrimes	getpwd();
3181556Srgrimes	out1str(curdir);
3191556Srgrimes	out1c('\n');
3201556Srgrimes	return 0;
3211556Srgrimes}
3221556Srgrimes
3231556Srgrimes
3241556Srgrimes
3251556Srgrimes/*
3261556Srgrimes * Run /bin/pwd to find out what the current directory is.  We suppress
3271556Srgrimes * interrupts throughout most of this, but the user can still break out
3281556Srgrimes * of it by killing the pwd program.  If we already know the current
3291556Srgrimes * directory, this routine returns immediately.
3301556Srgrimes */
3311556Srgrimes
3321556Srgrimes#define MAXPWD 256
3331556Srgrimes
3341556SrgrimesSTATIC void
3351556Srgrimesgetpwd() {
3361556Srgrimes	char buf[MAXPWD];
3371556Srgrimes	char *p;
3381556Srgrimes	int i;
3391556Srgrimes	int status;
3401556Srgrimes	struct job *jp;
3411556Srgrimes	int pip[2];
3424192Sjkh	char *pwd_bin = "/bin/pwd";
3431556Srgrimes
3441556Srgrimes	if (curdir)
3451556Srgrimes		return;
3461556Srgrimes	INTOFF;
3471556Srgrimes	if (pipe(pip) < 0)
3481556Srgrimes		error("Pipe call failed");
3494192Sjkh	/* make a fall-back guess, otherwise we're simply screwed */
3504192Sjkh	if (access(pwd_bin, X_OK) == -1)
3514192Sjkh		pwd_bin = "/stand/pwd";
3521556Srgrimes	jp = makejob((union node *)NULL, 1);
3531556Srgrimes	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
3541556Srgrimes		close(pip[0]);
3551556Srgrimes		if (pip[1] != 1) {
3561556Srgrimes			close(1);
3571556Srgrimes			copyfd(pip[1], 1);
3581556Srgrimes			close(pip[1]);
3591556Srgrimes		}
3604192Sjkh		execl(pwd_bin, "pwd", (char *)0);
3614192Sjkh		error("Cannot exec %s", pwd_bin);
3621556Srgrimes	}
3631556Srgrimes	close(pip[1]);
3641556Srgrimes	pip[1] = -1;
3651556Srgrimes	p = buf;
3661556Srgrimes	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
3671556Srgrimes	     || i == -1 && errno == EINTR) {
3681556Srgrimes		if (i > 0)
3691556Srgrimes			p += i;
3701556Srgrimes	}
3711556Srgrimes	close(pip[0]);
3721556Srgrimes	pip[0] = -1;
3731556Srgrimes	status = waitforjob(jp);
3741556Srgrimes	if (status != 0)
3751556Srgrimes		error((char *)0);
3761556Srgrimes	if (i < 0 || p == buf || p[-1] != '\n')
3771556Srgrimes		error("pwd command failed");
3781556Srgrimes	p[-1] = '\0';
3791556Srgrimes	curdir = savestr(buf);
3801556Srgrimes	INTON;
3811556Srgrimes}
382