Home | History | Annotate | Line # | Download | only in sh
cd.c revision 1.12
      1 /*	$NetBSD: cd.c,v 1.12 1995/03/21 09:08:48 cgd Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1991, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Kenneth Almquist.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "@(#)cd.c	8.1 (Berkeley) 5/31/93";
     42 #else
     43 static char rcsid[] = "$NetBSD: cd.c,v 1.12 1995/03/21 09:08:48 cgd Exp $";
     44 #endif
     45 #endif /* not lint */
     46 
     47 #include <sys/types.h>
     48 #include <sys/stat.h>
     49 #include <unistd.h>
     50 #include <errno.h>
     51 
     52 /*
     53  * The cd and pwd commands.
     54  */
     55 
     56 #include "shell.h"
     57 #include "var.h"
     58 #include "nodes.h"	/* for jobs.h */
     59 #include "jobs.h"
     60 #include "options.h"
     61 #include "output.h"
     62 #include "memalloc.h"
     63 #include "error.h"
     64 #include "mystring.h"
     65 #include "extern.h"
     66 
     67 
     68 #ifdef __STDC__
     69 STATIC int docd(char *, int);
     70 STATIC void updatepwd(char *);
     71 STATIC void getpwd(void);
     72 STATIC char *getcomponent(void);
     73 #else
     74 STATIC int docd();
     75 STATIC void updatepwd();
     76 STATIC void getpwd();
     77 STATIC char *getcomponent();
     78 #endif
     79 
     80 
     81 char *curdir;			/* current working directory */
     82 char *prevdir;			/* previous working directory */
     83 STATIC char *cdcomppath;
     84 
     85 int
     86 cdcmd(argc, argv)
     87 	int argc;
     88 	char **argv;
     89 {
     90 	char *dest;
     91 	char *path;
     92 	char *p;
     93 	struct stat statb;
     94 	char *padvance();
     95 	int print = 0;
     96 
     97 	nextopt(nullstr);
     98 	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
     99 		error("HOME not set");
    100 	if (dest[0] == '-' && dest[1] == '\0') {
    101 		dest = prevdir ? prevdir : curdir;
    102 		print = 1;
    103 	}
    104 	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
    105 		path = nullstr;
    106 	while ((p = padvance(&path, dest)) != NULL) {
    107 		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
    108 			if (!print) {
    109 				/*
    110 				 * XXX - rethink
    111 				 */
    112 				if (p[0] == '.' && p[1] == '/')
    113 					p += 2;
    114 				print = strcmp(p, dest);
    115 			}
    116 			if (docd(p, print) >= 0)
    117 				return 0;
    118 
    119 		}
    120 	}
    121 	error("can't cd to %s", dest);
    122 }
    123 
    124 
    125 /*
    126  * Actually do the chdir.  If the name refers to symbolic links, we
    127  * compute the actual directory name before doing the cd.  In an
    128  * interactive shell, print the directory name if "print" is nonzero
    129  * or if the name refers to a symbolic link.  We also print the name
    130  * if "/u/logname" was expanded in it, since this is similar to a
    131  * symbolic link.  (The check for this breaks if the user gives the
    132  * cd command some additional, unused arguments.)
    133  */
    134 
    135 #if SYMLINKS == 0
    136 STATIC int
    137 docd(dest, print)
    138 	char *dest;
    139 	{
    140 	INTOFF;
    141 	if (chdir(dest) < 0) {
    142 		INTON;
    143 		return -1;
    144 	}
    145 	updatepwd(dest);
    146 	INTON;
    147 	if (print && iflag)
    148 		out1fmt("%s\n", stackblock());
    149 	return 0;
    150 }
    151 
    152 #else
    153 
    154 
    155 
    156 STATIC int
    157 docd(dest, print)
    158 	char *dest;
    159 	int print;
    160 {
    161 	register char *p;
    162 	register char *q;
    163 	char *symlink;
    164 	char *component;
    165 	struct stat statb;
    166 	int first;
    167 	int i;
    168 
    169 	TRACE(("docd(\"%s\", %d) called\n", dest, print));
    170 
    171 top:
    172 	cdcomppath = dest;
    173 	STARTSTACKSTR(p);
    174 	if (*dest == '/') {
    175 		STPUTC('/', p);
    176 		cdcomppath++;
    177 	}
    178 	first = 1;
    179 	while ((q = getcomponent()) != NULL) {
    180 		if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
    181 			continue;
    182 		if (! first)
    183 			STPUTC('/', p);
    184 		first = 0;
    185 		component = q;
    186 		while (*q)
    187 			STPUTC(*q++, p);
    188 		if (equal(component, ".."))
    189 			continue;
    190 		STACKSTRNUL(p);
    191 		if (lstat(stackblock(), &statb) < 0)
    192 			error("lstat %s failed", stackblock());
    193 		if (!S_ISLNK(statb.st_mode))
    194 			continue;
    195 
    196 		/* Hit a symbolic link.  We have to start all over again. */
    197 		print = 1;
    198 		STPUTC('\0', p);
    199 		symlink = grabstackstr(p);
    200 		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
    201 		if (cdcomppath != NULL)
    202 			i += strlen(cdcomppath);
    203 		p = stalloc(i);
    204 		if (readlink(symlink, p, (int)statb.st_size) < 0) {
    205 			error("readlink %s failed", stackblock());
    206 		}
    207 		if (cdcomppath != NULL) {
    208 			p[(int)statb.st_size] = '/';
    209 			scopy(cdcomppath, p + (int)statb.st_size + 1);
    210 		} else {
    211 			p[(int)statb.st_size] = '\0';
    212 		}
    213 		if (p[0] != '/') {	/* relative path name */
    214 			char *r;
    215 			q = r = symlink;
    216 			while (*q) {
    217 				if (*q++ == '/')
    218 					r = q;
    219 			}
    220 			*r = '\0';
    221 			dest = stalloc(strlen(symlink) + strlen(p) + 1);
    222 			scopy(symlink, dest);
    223 			strcat(dest, p);
    224 		} else {
    225 			dest = p;
    226 		}
    227 		goto top;
    228 	}
    229 	STPUTC('\0', p);
    230 	p = grabstackstr(p);
    231 	INTOFF;
    232 	if (chdir(p) < 0) {
    233 		INTON;
    234 		return -1;
    235 	}
    236 	updatepwd(p);
    237 	INTON;
    238 	if (print && iflag)
    239 		out1fmt("%s\n", p);
    240 	return 0;
    241 }
    242 #endif /* SYMLINKS */
    243 
    244 
    245 
    246 /*
    247  * Get the next component of the path name pointed to by cdcomppath.
    248  * This routine overwrites the string pointed to by cdcomppath.
    249  */
    250 
    251 STATIC char *
    252 getcomponent() {
    253 	register char *p;
    254 	char *start;
    255 
    256 	if ((p = cdcomppath) == NULL)
    257 		return NULL;
    258 	start = cdcomppath;
    259 	while (*p != '/' && *p != '\0')
    260 		p++;
    261 	if (*p == '\0') {
    262 		cdcomppath = NULL;
    263 	} else {
    264 		*p++ = '\0';
    265 		cdcomppath = p;
    266 	}
    267 	return start;
    268 }
    269 
    270 
    271 
    272 /*
    273  * Update curdir (the name of the current directory) in response to a
    274  * cd command.  We also call hashcd to let the routines in exec.c know
    275  * that the current directory has changed.
    276  */
    277 
    278 void hashcd();
    279 
    280 STATIC void
    281 updatepwd(dir)
    282 	char *dir;
    283 	{
    284 	char *new;
    285 	char *p;
    286 
    287 	hashcd();				/* update command hash table */
    288 	cdcomppath = stalloc(strlen(dir) + 1);
    289 	scopy(dir, cdcomppath);
    290 	STARTSTACKSTR(new);
    291 	if (*dir != '/') {
    292 		if (curdir == NULL)
    293 			return;
    294 		p = curdir;
    295 		while (*p)
    296 			STPUTC(*p++, new);
    297 		if (p[-1] == '/')
    298 			STUNPUTC(new);
    299 	}
    300 	while ((p = getcomponent()) != NULL) {
    301 		if (equal(p, "..")) {
    302 			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
    303 		} else if (*p != '\0' && ! equal(p, ".")) {
    304 			STPUTC('/', new);
    305 			while (*p)
    306 				STPUTC(*p++, new);
    307 		}
    308 	}
    309 	if (new == stackblock())
    310 		STPUTC('/', new);
    311 	STACKSTRNUL(new);
    312 	INTOFF;
    313 	if (prevdir)
    314 		ckfree(prevdir);
    315 	prevdir = curdir;
    316 	curdir = savestr(stackblock());
    317 	INTON;
    318 }
    319 
    320 
    321 
    322 int
    323 pwdcmd(argc, argv)
    324 	int argc;
    325 	char **argv;
    326 {
    327 	getpwd();
    328 	out1str(curdir);
    329 	out1c('\n');
    330 	return 0;
    331 }
    332 
    333 
    334 
    335 /*
    336  * Run /bin/pwd to find out what the current directory is.  We suppress
    337  * interrupts throughout most of this, but the user can still break out
    338  * of it by killing the pwd program.  If we already know the current
    339  * directory, this routine returns immediately.
    340  */
    341 
    342 #define MAXPWD 256
    343 
    344 STATIC void
    345 getpwd() {
    346 	char buf[MAXPWD];
    347 	char *p;
    348 	int i;
    349 	int status;
    350 	struct job *jp;
    351 	int pip[2];
    352 
    353 	if (curdir)
    354 		return;
    355 	INTOFF;
    356 	if (pipe(pip) < 0)
    357 		error("Pipe call failed");
    358 	jp = makejob((union node *)NULL, 1);
    359 	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
    360 		close(pip[0]);
    361 		if (pip[1] != 1) {
    362 			close(1);
    363 			copyfd(pip[1], 1);
    364 			close(pip[1]);
    365 		}
    366 		execl("/bin/pwd", "pwd", (char *)0);
    367 		error("Cannot exec /bin/pwd");
    368 	}
    369 	close(pip[1]);
    370 	pip[1] = -1;
    371 	p = buf;
    372 	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
    373 	     || i == -1 && errno == EINTR) {
    374 		if (i > 0)
    375 			p += i;
    376 	}
    377 	close(pip[0]);
    378 	pip[0] = -1;
    379 	status = waitforjob(jp);
    380 	if (status != 0)
    381 		error((char *)0);
    382 	if (i < 0 || p == buf || p[-1] != '\n')
    383 		error("pwd command failed");
    384 	p[-1] = '\0';
    385 	curdir = savestr(buf);
    386 	INTON;
    387 }
    388