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