cd.c revision 25222
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 *
3625222Ssteve *	$Id: cd.c,v 1.14 1997/02/22 13:58:22 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/*
12320774Ssteve * Actually do the chdir.  In an interactive shell, print the
12420774Ssteve * directory name if "print" is nonzero.
1251556Srgrimes */
1261556SrgrimesSTATIC int
1271556Srgrimesdocd(dest, print)
1281556Srgrimes	char *dest;
12917987Speter	int print;
13020425Ssteve{
13120425Ssteve
13220425Ssteve	TRACE(("docd(\"%s\", %d) called\n", dest, print));
1331556Srgrimes	INTOFF;
13421301Ssteve	updatepwd(dest);
13521301Ssteve	if (chdir(stackblock()) < 0) {
1361556Srgrimes		INTON;
1371556Srgrimes		return -1;
1381556Srgrimes	}
13921301Ssteve	hashcd();				/* update command hash table */
14021301Ssteve	if (prevdir)
14121301Ssteve		ckfree(prevdir);
14221301Ssteve	prevdir = curdir;
14321301Ssteve	curdir = savestr(stackblock());
1441556Srgrimes	INTON;
1451556Srgrimes	if (print && iflag)
1461556Srgrimes		out1fmt("%s\n", stackblock());
1471556Srgrimes	return 0;
1481556Srgrimes}
1491556Srgrimes
1501556Srgrimes
1511556Srgrimes/*
1521556Srgrimes * Get the next component of the path name pointed to by cdcomppath.
1531556Srgrimes * This routine overwrites the string pointed to by cdcomppath.
1541556Srgrimes */
1551556SrgrimesSTATIC char *
15620774Sstevegetcomponent()
15720774Ssteve{
15825222Ssteve	char *p;
1591556Srgrimes	char *start;
1601556Srgrimes
1611556Srgrimes	if ((p = cdcomppath) == NULL)
1621556Srgrimes		return NULL;
1631556Srgrimes	start = cdcomppath;
1641556Srgrimes	while (*p != '/' && *p != '\0')
1651556Srgrimes		p++;
1661556Srgrimes	if (*p == '\0') {
1671556Srgrimes		cdcomppath = NULL;
1681556Srgrimes	} else {
1691556Srgrimes		*p++ = '\0';
1701556Srgrimes		cdcomppath = p;
1711556Srgrimes	}
1721556Srgrimes	return start;
1731556Srgrimes}
1741556Srgrimes
1751556Srgrimes
1761556Srgrimes/*
17721301Ssteve * Determine the new working directory, but don't actually enforce
17821301Ssteve * any changes.
1791556Srgrimes */
1801556SrgrimesSTATIC void
1811556Srgrimesupdatepwd(dir)
1821556Srgrimes	char *dir;
18320774Ssteve{
1841556Srgrimes	char *new;
1851556Srgrimes	char *p;
1861556Srgrimes
1871556Srgrimes	cdcomppath = stalloc(strlen(dir) + 1);
1881556Srgrimes	scopy(dir, cdcomppath);
1891556Srgrimes	STARTSTACKSTR(new);
1901556Srgrimes	if (*dir != '/') {
1911556Srgrimes		if (curdir == NULL)
1921556Srgrimes			return;
1931556Srgrimes		p = curdir;
1941556Srgrimes		while (*p)
1951556Srgrimes			STPUTC(*p++, new);
1961556Srgrimes		if (p[-1] == '/')
1971556Srgrimes			STUNPUTC(new);
1981556Srgrimes	}
1991556Srgrimes	while ((p = getcomponent()) != NULL) {
2001556Srgrimes		if (equal(p, "..")) {
2011556Srgrimes			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
2021556Srgrimes		} else if (*p != '\0' && ! equal(p, ".")) {
2031556Srgrimes			STPUTC('/', new);
2041556Srgrimes			while (*p)
2051556Srgrimes				STPUTC(*p++, new);
2061556Srgrimes		}
2071556Srgrimes	}
2081556Srgrimes	if (new == stackblock())
2091556Srgrimes		STPUTC('/', new);
2101556Srgrimes	STACKSTRNUL(new);
2111556Srgrimes}
2121556Srgrimes
2131556Srgrimes
2141556Srgrimesint
21517987Speterpwdcmd(argc, argv)
21617987Speter	int argc;
21720425Ssteve	char **argv;
21817987Speter{
21921301Ssteve	if (!getpwd())
22020774Ssteve		error("getcwd() failed: %s", strerror(errno));
2211556Srgrimes	out1str(curdir);
2221556Srgrimes	out1c('\n');
2231556Srgrimes	return 0;
2241556Srgrimes}
2251556Srgrimes
2261556Srgrimes
2271556Srgrimes/*
22820774Ssteve * Find out what the current directory is. If we already know the current
2291556Srgrimes * directory, this routine returns immediately.
2301556Srgrimes */
23120774Sstevechar *
23220425Sstevegetpwd()
23320425Ssteve{
2341556Srgrimes	if (curdir)
23520774Ssteve		return (curdir);
23620887Ssteve	return ((curdir = getcwd(NULL, 0)));
2371556Srgrimes}
238