cd.c revision 12273
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 *
3612273Speter *	$Id: cd.c,v 1.4 1994/12/26 13:02:05 bde 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;
9512273Speter		if (dest)
9612273Speter			print = 1;
9712273Speter		else
9812273Speter			dest = ".";
991556Srgrimes	}
1001556Srgrimes	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
1011556Srgrimes		path = nullstr;
1021556Srgrimes	while ((p = padvance(&path, dest)) != NULL) {
1031556Srgrimes		if (stat(p, &statb) >= 0
1041556Srgrimes		 && (statb.st_mode & S_IFMT) == S_IFDIR) {
1051556Srgrimes			if (!print) {
1061556Srgrimes				/*
1071556Srgrimes				 * XXX - rethink
1081556Srgrimes				 */
1091556Srgrimes				if (p[0] == '.' && p[1] == '/')
1101556Srgrimes					p += 2;
1111556Srgrimes				print = strcmp(p, dest);
1121556Srgrimes			}
1131556Srgrimes			if (docd(p, print) >= 0)
1141556Srgrimes				return 0;
1151556Srgrimes
1161556Srgrimes		}
1171556Srgrimes	}
1181556Srgrimes	error("can't cd to %s", dest);
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;
1361556Srgrimes	{
1371556Srgrimes	INTOFF;
1381556Srgrimes	if (chdir(dest) < 0) {
1391556Srgrimes		INTON;
1401556Srgrimes		return -1;
1411556Srgrimes	}
1421556Srgrimes	updatepwd(dest);
1431556Srgrimes	INTON;
1441556Srgrimes	if (print && iflag)
1451556Srgrimes		out1fmt("%s\n", stackblock());
1461556Srgrimes	return 0;
1471556Srgrimes}
1481556Srgrimes
1491556Srgrimes#else
1501556Srgrimes
1511556Srgrimes
1521556Srgrimes
1531556SrgrimesSTATIC int
1541556Srgrimesdocd(dest, print)
1551556Srgrimes	char *dest;
1561556Srgrimes	{
1571556Srgrimes	register char *p;
1581556Srgrimes	register char *q;
1591556Srgrimes	char *symlink;
1601556Srgrimes	char *component;
1611556Srgrimes	struct stat statb;
1621556Srgrimes	int first;
1631556Srgrimes	int i;
1641556Srgrimes
1651556Srgrimes	TRACE(("docd(\"%s\", %d) called\n", dest, print));
1661556Srgrimes
1671556Srgrimestop:
1681556Srgrimes	cdcomppath = dest;
1691556Srgrimes	STARTSTACKSTR(p);
1701556Srgrimes	if (*dest == '/') {
1711556Srgrimes		STPUTC('/', p);
1721556Srgrimes		cdcomppath++;
1731556Srgrimes	}
1741556Srgrimes	first = 1;
1751556Srgrimes	while ((q = getcomponent()) != NULL) {
1761556Srgrimes		if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
1771556Srgrimes			continue;
1781556Srgrimes		if (! first)
1791556Srgrimes			STPUTC('/', p);
1801556Srgrimes		first = 0;
1811556Srgrimes		component = q;
1821556Srgrimes		while (*q)
1831556Srgrimes			STPUTC(*q++, p);
1841556Srgrimes		if (equal(component, ".."))
1851556Srgrimes			continue;
1861556Srgrimes		STACKSTRNUL(p);
1871556Srgrimes		if (lstat(stackblock(), &statb) < 0)
1881556Srgrimes			error("lstat %s failed", stackblock());
1891556Srgrimes		if ((statb.st_mode & S_IFMT) != S_IFLNK)
1901556Srgrimes			continue;
1911556Srgrimes
1921556Srgrimes		/* Hit a symbolic link.  We have to start all over again. */
1931556Srgrimes		print = 1;
1941556Srgrimes		STPUTC('\0', p);
1951556Srgrimes		symlink = grabstackstr(p);
1961556Srgrimes		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
1971556Srgrimes		if (cdcomppath != NULL)
1981556Srgrimes			i += strlen(cdcomppath);
1991556Srgrimes		p = stalloc(i);
2001556Srgrimes		if (readlink(symlink, p, (int)statb.st_size) < 0) {
2011556Srgrimes			error("readlink %s failed", stackblock());
2021556Srgrimes		}
2031556Srgrimes		if (cdcomppath != NULL) {
2041556Srgrimes			p[(int)statb.st_size] = '/';
2051556Srgrimes			scopy(cdcomppath, p + (int)statb.st_size + 1);
2061556Srgrimes		} else {
2071556Srgrimes			p[(int)statb.st_size] = '\0';
2081556Srgrimes		}
2091556Srgrimes		if (p[0] != '/') {	/* relative path name */
2101556Srgrimes			char *r;
2111556Srgrimes			q = r = symlink;
2121556Srgrimes			while (*q) {
2131556Srgrimes				if (*q++ == '/')
2141556Srgrimes					r = q;
2151556Srgrimes			}
2161556Srgrimes			*r = '\0';
2171556Srgrimes			dest = stalloc(strlen(symlink) + strlen(p) + 1);
2181556Srgrimes			scopy(symlink, dest);
2191556Srgrimes			strcat(dest, p);
2201556Srgrimes		} else {
2211556Srgrimes			dest = p;
2221556Srgrimes		}
2231556Srgrimes		goto top;
2241556Srgrimes	}
2251556Srgrimes	STPUTC('\0', p);
2261556Srgrimes	p = grabstackstr(p);
2271556Srgrimes	INTOFF;
2285234Sbde	if (chdir(*p ? p : ".") < 0) {
2291556Srgrimes		INTON;
2301556Srgrimes		return -1;
2311556Srgrimes	}
2321556Srgrimes	updatepwd(p);
2331556Srgrimes	INTON;
2341556Srgrimes	if (print && iflag)
2351556Srgrimes		out1fmt("%s\n", p);
2361556Srgrimes	return 0;
2371556Srgrimes}
2381556Srgrimes#endif /* SYMLINKS */
2391556Srgrimes
2401556Srgrimes
2411556Srgrimes
2421556Srgrimes/*
2431556Srgrimes * Get the next component of the path name pointed to by cdcomppath.
2441556Srgrimes * This routine overwrites the string pointed to by cdcomppath.
2451556Srgrimes */
2461556Srgrimes
2471556SrgrimesSTATIC char *
2481556Srgrimesgetcomponent() {
2491556Srgrimes	register char *p;
2501556Srgrimes	char *start;
2511556Srgrimes
2521556Srgrimes	if ((p = cdcomppath) == NULL)
2531556Srgrimes		return NULL;
2541556Srgrimes	start = cdcomppath;
2551556Srgrimes	while (*p != '/' && *p != '\0')
2561556Srgrimes		p++;
2571556Srgrimes	if (*p == '\0') {
2581556Srgrimes		cdcomppath = NULL;
2591556Srgrimes	} else {
2601556Srgrimes		*p++ = '\0';
2611556Srgrimes		cdcomppath = p;
2621556Srgrimes	}
2631556Srgrimes	return start;
2641556Srgrimes}
2651556Srgrimes
2661556Srgrimes
2671556Srgrimes
2681556Srgrimes/*
2691556Srgrimes * Update curdir (the name of the current directory) in response to a
2701556Srgrimes * cd command.  We also call hashcd to let the routines in exec.c know
2711556Srgrimes * that the current directory has changed.
2721556Srgrimes */
2731556Srgrimes
2741556Srgrimesvoid hashcd();
2751556Srgrimes
2761556SrgrimesSTATIC void
2771556Srgrimesupdatepwd(dir)
2781556Srgrimes	char *dir;
2791556Srgrimes	{
2801556Srgrimes	char *new;
2811556Srgrimes	char *p;
2821556Srgrimes
2831556Srgrimes	hashcd();				/* update command hash table */
2841556Srgrimes	cdcomppath = stalloc(strlen(dir) + 1);
2851556Srgrimes	scopy(dir, cdcomppath);
2861556Srgrimes	STARTSTACKSTR(new);
2871556Srgrimes	if (*dir != '/') {
2881556Srgrimes		if (curdir == NULL)
2891556Srgrimes			return;
2901556Srgrimes		p = curdir;
2911556Srgrimes		while (*p)
2921556Srgrimes			STPUTC(*p++, new);
2931556Srgrimes		if (p[-1] == '/')
2941556Srgrimes			STUNPUTC(new);
2951556Srgrimes	}
2961556Srgrimes	while ((p = getcomponent()) != NULL) {
2971556Srgrimes		if (equal(p, "..")) {
2981556Srgrimes			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
2991556Srgrimes		} else if (*p != '\0' && ! equal(p, ".")) {
3001556Srgrimes			STPUTC('/', new);
3011556Srgrimes			while (*p)
3021556Srgrimes				STPUTC(*p++, new);
3031556Srgrimes		}
3041556Srgrimes	}
3051556Srgrimes	if (new == stackblock())
3061556Srgrimes		STPUTC('/', new);
3071556Srgrimes	STACKSTRNUL(new);
3081556Srgrimes	INTOFF;
3091556Srgrimes	if (prevdir)
3101556Srgrimes		ckfree(prevdir);
3111556Srgrimes	prevdir = curdir;
3121556Srgrimes	curdir = savestr(stackblock());
3131556Srgrimes	INTON;
3141556Srgrimes}
3151556Srgrimes
3161556Srgrimes
3171556Srgrimes
3181556Srgrimesint
3191556Srgrimespwdcmd(argc, argv)  char **argv; {
3201556Srgrimes	getpwd();
3211556Srgrimes	out1str(curdir);
3221556Srgrimes	out1c('\n');
3231556Srgrimes	return 0;
3241556Srgrimes}
3251556Srgrimes
3261556Srgrimes
3271556Srgrimes
3281556Srgrimes/*
3291556Srgrimes * Run /bin/pwd to find out what the current directory is.  We suppress
3301556Srgrimes * interrupts throughout most of this, but the user can still break out
3311556Srgrimes * of it by killing the pwd program.  If we already know the current
3321556Srgrimes * directory, this routine returns immediately.
3331556Srgrimes */
3341556Srgrimes
3351556Srgrimes#define MAXPWD 256
3361556Srgrimes
3371556SrgrimesSTATIC void
3381556Srgrimesgetpwd() {
3391556Srgrimes	char buf[MAXPWD];
3401556Srgrimes	char *p;
3411556Srgrimes	int i;
3421556Srgrimes	int status;
3431556Srgrimes	struct job *jp;
3441556Srgrimes	int pip[2];
3454192Sjkh	char *pwd_bin = "/bin/pwd";
3461556Srgrimes
3471556Srgrimes	if (curdir)
3481556Srgrimes		return;
3491556Srgrimes	INTOFF;
3501556Srgrimes	if (pipe(pip) < 0)
3511556Srgrimes		error("Pipe call failed");
3524192Sjkh	/* make a fall-back guess, otherwise we're simply screwed */
3534192Sjkh	if (access(pwd_bin, X_OK) == -1)
3544192Sjkh		pwd_bin = "/stand/pwd";
3551556Srgrimes	jp = makejob((union node *)NULL, 1);
3561556Srgrimes	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
3571556Srgrimes		close(pip[0]);
3581556Srgrimes		if (pip[1] != 1) {
3591556Srgrimes			close(1);
3601556Srgrimes			copyfd(pip[1], 1);
3611556Srgrimes			close(pip[1]);
3621556Srgrimes		}
3634192Sjkh		execl(pwd_bin, "pwd", (char *)0);
3644192Sjkh		error("Cannot exec %s", pwd_bin);
3651556Srgrimes	}
3661556Srgrimes	close(pip[1]);
3671556Srgrimes	pip[1] = -1;
3681556Srgrimes	p = buf;
3691556Srgrimes	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
3701556Srgrimes	     || i == -1 && errno == EINTR) {
3711556Srgrimes		if (i > 0)
3721556Srgrimes			p += i;
3731556Srgrimes	}
3741556Srgrimes	close(pip[0]);
3751556Srgrimes	pip[0] = -1;
3761556Srgrimes	status = waitforjob(jp);
3771556Srgrimes	if (status != 0)
3781556Srgrimes		error((char *)0);
3791556Srgrimes	if (i < 0 || p == buf || p[-1] != '\n')
3801556Srgrimes		error("pwd command failed");
3811556Srgrimes	p[-1] = '\0';
3821556Srgrimes	curdir = savestr(buf);
3831556Srgrimes	INTON;
3841556Srgrimes}
385