147109Sbostic /*-
260698Sbostic * Copyright (c) 1991, 1993
360698Sbostic * The Regents of the University of California. All rights reserved.
447109Sbostic *
547109Sbostic * This code is derived from software contributed to Berkeley by
647109Sbostic * Kenneth Almquist.
747109Sbostic *
847109Sbostic * %sccs.include.redist.c%
947109Sbostic */
1047109Sbostic
1147109Sbostic #ifndef lint
12*69272Schristos static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 05/04/95";
1347109Sbostic #endif /* not lint */
1447109Sbostic
15*69272Schristos #include <sys/types.h>
16*69272Schristos #include <sys/stat.h>
17*69272Schristos #include <stdlib.h>
18*69272Schristos #include <unistd.h>
19*69272Schristos #include <errno.h>
20*69272Schristos
2147109Sbostic /*
2247109Sbostic * The cd and pwd commands.
2347109Sbostic */
2447109Sbostic
2547109Sbostic #include "shell.h"
2647109Sbostic #include "var.h"
2747109Sbostic #include "nodes.h" /* for jobs.h */
2847109Sbostic #include "jobs.h"
2947109Sbostic #include "options.h"
3047109Sbostic #include "output.h"
3147109Sbostic #include "memalloc.h"
3247109Sbostic #include "error.h"
33*69272Schristos #include "redir.h"
3447109Sbostic #include "mystring.h"
35*69272Schristos #include "show.h"
3647109Sbostic
37*69272Schristos STATIC int docd __P((char *, int));
38*69272Schristos STATIC char *getcomponent __P((void));
39*69272Schristos STATIC void updatepwd __P((char *));
40*69272Schristos STATIC void getpwd __P((void));
4147109Sbostic
4247109Sbostic char *curdir; /* current working directory */
4355248Smarc char *prevdir; /* previous working directory */
4447109Sbostic STATIC char *cdcomppath;
4547109Sbostic
4647109Sbostic int
cdcmd(argc,argv)47*69272Schristos cdcmd(argc, argv)
48*69272Schristos int argc;
49*69272Schristos char **argv;
50*69272Schristos {
5147109Sbostic char *dest;
5247109Sbostic char *path;
5347109Sbostic char *p;
5447109Sbostic struct stat statb;
5547109Sbostic char *padvance();
5655248Smarc int print = 0;
5747109Sbostic
5847291Smarc nextopt(nullstr);
5947291Smarc if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
6047109Sbostic error("HOME not set");
6155248Smarc if (dest[0] == '-' && dest[1] == '\0') {
6255248Smarc dest = prevdir ? prevdir : curdir;
6355248Smarc print = 1;
6455248Smarc }
6547109Sbostic if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
6647109Sbostic path = nullstr;
6747109Sbostic while ((p = padvance(&path, dest)) != NULL) {
68*69272Schristos if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
6955248Smarc if (!print) {
7055248Smarc /*
7155248Smarc * XXX - rethink
7255248Smarc */
7355248Smarc if (p[0] == '.' && p[1] == '/')
7455248Smarc p += 2;
7555248Smarc print = strcmp(p, dest);
7655248Smarc }
7755248Smarc if (docd(p, print) >= 0)
7855248Smarc return 0;
7955248Smarc
8055248Smarc }
8147109Sbostic }
8247109Sbostic error("can't cd to %s", dest);
83*69272Schristos /*NOTREACHED*/
84*69272Schristos return 0;
8547109Sbostic }
8647109Sbostic
8747109Sbostic
8847109Sbostic /*
8947109Sbostic * Actually do the chdir. If the name refers to symbolic links, we
9047109Sbostic * compute the actual directory name before doing the cd. In an
9147109Sbostic * interactive shell, print the directory name if "print" is nonzero
9247109Sbostic * or if the name refers to a symbolic link. We also print the name
9347109Sbostic * if "/u/logname" was expanded in it, since this is similar to a
9447109Sbostic * symbolic link. (The check for this breaks if the user gives the
9547109Sbostic * cd command some additional, unused arguments.)
9647109Sbostic */
9747109Sbostic
9847109Sbostic #if SYMLINKS == 0
9947109Sbostic STATIC int
docd(dest,print)10047109Sbostic docd(dest, print)
10147109Sbostic char *dest;
10247109Sbostic {
10347109Sbostic INTOFF;
10447109Sbostic if (chdir(dest) < 0) {
10547109Sbostic INTON;
10647109Sbostic return -1;
10747109Sbostic }
10847109Sbostic updatepwd(dest);
10947109Sbostic INTON;
11047109Sbostic if (print && iflag)
11147109Sbostic out1fmt("%s\n", stackblock());
11247109Sbostic return 0;
11347109Sbostic }
11447109Sbostic
11547109Sbostic #else
11647109Sbostic
11747109Sbostic
11847109Sbostic
11947109Sbostic STATIC int
docd(dest,print)12047109Sbostic docd(dest, print)
12147109Sbostic char *dest;
122*69272Schristos int print;
123*69272Schristos {
12447109Sbostic register char *p;
12547109Sbostic register char *q;
12647109Sbostic char *symlink;
12747109Sbostic char *component;
12847109Sbostic struct stat statb;
12947109Sbostic int first;
13047109Sbostic int i;
13147109Sbostic
13247109Sbostic TRACE(("docd(\"%s\", %d) called\n", dest, print));
13347109Sbostic
13447109Sbostic top:
13547109Sbostic cdcomppath = dest;
13647109Sbostic STARTSTACKSTR(p);
13747109Sbostic if (*dest == '/') {
13847109Sbostic STPUTC('/', p);
13947109Sbostic cdcomppath++;
14047109Sbostic }
14147109Sbostic first = 1;
14247109Sbostic while ((q = getcomponent()) != NULL) {
143*69272Schristos if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
14447109Sbostic continue;
14547109Sbostic if (! first)
14647109Sbostic STPUTC('/', p);
14747109Sbostic first = 0;
14847109Sbostic component = q;
14947109Sbostic while (*q)
15047109Sbostic STPUTC(*q++, p);
15147109Sbostic if (equal(component, ".."))
15247109Sbostic continue;
15347109Sbostic STACKSTRNUL(p);
15447109Sbostic if (lstat(stackblock(), &statb) < 0)
15547109Sbostic error("lstat %s failed", stackblock());
156*69272Schristos if (!S_ISLNK(statb.st_mode))
15747109Sbostic continue;
15847109Sbostic
15947109Sbostic /* Hit a symbolic link. We have to start all over again. */
16047109Sbostic print = 1;
16147109Sbostic STPUTC('\0', p);
16247109Sbostic symlink = grabstackstr(p);
16347109Sbostic i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */
16447109Sbostic if (cdcomppath != NULL)
16547109Sbostic i += strlen(cdcomppath);
16647109Sbostic p = stalloc(i);
16747109Sbostic if (readlink(symlink, p, (int)statb.st_size) < 0) {
16847109Sbostic error("readlink %s failed", stackblock());
16947109Sbostic }
17047109Sbostic if (cdcomppath != NULL) {
17147109Sbostic p[(int)statb.st_size] = '/';
17247109Sbostic scopy(cdcomppath, p + (int)statb.st_size + 1);
17347109Sbostic } else {
17447109Sbostic p[(int)statb.st_size] = '\0';
17547109Sbostic }
17647109Sbostic if (p[0] != '/') { /* relative path name */
17747109Sbostic char *r;
17847109Sbostic q = r = symlink;
17947109Sbostic while (*q) {
18047109Sbostic if (*q++ == '/')
18147109Sbostic r = q;
18247109Sbostic }
18347109Sbostic *r = '\0';
18447109Sbostic dest = stalloc(strlen(symlink) + strlen(p) + 1);
18547109Sbostic scopy(symlink, dest);
18647109Sbostic strcat(dest, p);
18747109Sbostic } else {
18847109Sbostic dest = p;
18947109Sbostic }
19047109Sbostic goto top;
19147109Sbostic }
19247109Sbostic STPUTC('\0', p);
19347109Sbostic p = grabstackstr(p);
19447109Sbostic INTOFF;
19547109Sbostic if (chdir(p) < 0) {
19647109Sbostic INTON;
19747109Sbostic return -1;
19847109Sbostic }
19947109Sbostic updatepwd(p);
20047109Sbostic INTON;
20147109Sbostic if (print && iflag)
20247109Sbostic out1fmt("%s\n", p);
20347109Sbostic return 0;
20447109Sbostic }
20547109Sbostic #endif /* SYMLINKS */
20647109Sbostic
20747109Sbostic
20847109Sbostic
20947109Sbostic /*
21047109Sbostic * Get the next component of the path name pointed to by cdcomppath.
21147109Sbostic * This routine overwrites the string pointed to by cdcomppath.
21247109Sbostic */
21347109Sbostic
21447109Sbostic STATIC char *
getcomponent()21547109Sbostic getcomponent() {
21647109Sbostic register char *p;
21747109Sbostic char *start;
21847109Sbostic
21947109Sbostic if ((p = cdcomppath) == NULL)
22047109Sbostic return NULL;
22147109Sbostic start = cdcomppath;
22247109Sbostic while (*p != '/' && *p != '\0')
22347109Sbostic p++;
22447109Sbostic if (*p == '\0') {
22547109Sbostic cdcomppath = NULL;
22647109Sbostic } else {
22747109Sbostic *p++ = '\0';
22847109Sbostic cdcomppath = p;
22947109Sbostic }
23047109Sbostic return start;
23147109Sbostic }
23247109Sbostic
23347109Sbostic
23447109Sbostic
23547109Sbostic /*
23647109Sbostic * Update curdir (the name of the current directory) in response to a
23747109Sbostic * cd command. We also call hashcd to let the routines in exec.c know
23847109Sbostic * that the current directory has changed.
23947109Sbostic */
24047109Sbostic
24147109Sbostic void hashcd();
24247109Sbostic
24347109Sbostic STATIC void
updatepwd(dir)24447109Sbostic updatepwd(dir)
24547109Sbostic char *dir;
24647109Sbostic {
24747109Sbostic char *new;
24847109Sbostic char *p;
24947109Sbostic
25047109Sbostic hashcd(); /* update command hash table */
25147109Sbostic cdcomppath = stalloc(strlen(dir) + 1);
25247109Sbostic scopy(dir, cdcomppath);
25347109Sbostic STARTSTACKSTR(new);
25447109Sbostic if (*dir != '/') {
25547109Sbostic if (curdir == NULL)
25647109Sbostic return;
25747109Sbostic p = curdir;
25847109Sbostic while (*p)
25947109Sbostic STPUTC(*p++, new);
26047109Sbostic if (p[-1] == '/')
26147109Sbostic STUNPUTC(new);
26247109Sbostic }
26347109Sbostic while ((p = getcomponent()) != NULL) {
26447109Sbostic if (equal(p, "..")) {
26547109Sbostic while (new > stackblock() && (STUNPUTC(new), *new) != '/');
26647109Sbostic } else if (*p != '\0' && ! equal(p, ".")) {
26747109Sbostic STPUTC('/', new);
26847109Sbostic while (*p)
26947109Sbostic STPUTC(*p++, new);
27047109Sbostic }
27147109Sbostic }
27247109Sbostic if (new == stackblock())
27347109Sbostic STPUTC('/', new);
27447109Sbostic STACKSTRNUL(new);
27555248Smarc INTOFF;
27655248Smarc if (prevdir)
27755248Smarc ckfree(prevdir);
27855248Smarc prevdir = curdir;
27947109Sbostic curdir = savestr(stackblock());
28055248Smarc INTON;
28147109Sbostic }
28247109Sbostic
28347109Sbostic
28447109Sbostic
28547109Sbostic int
pwdcmd(argc,argv)286*69272Schristos pwdcmd(argc, argv)
287*69272Schristos int argc;
288*69272Schristos char **argv;
289*69272Schristos {
29047109Sbostic getpwd();
29147109Sbostic out1str(curdir);
29247109Sbostic out1c('\n');
29347109Sbostic return 0;
29447109Sbostic }
29547109Sbostic
29647109Sbostic
29747109Sbostic
29847109Sbostic /*
29947109Sbostic * Run /bin/pwd to find out what the current directory is. We suppress
30047109Sbostic * interrupts throughout most of this, but the user can still break out
30147109Sbostic * of it by killing the pwd program. If we already know the current
30247109Sbostic * directory, this routine returns immediately.
30347109Sbostic */
30447109Sbostic
30547109Sbostic #define MAXPWD 256
30647109Sbostic
30747109Sbostic STATIC void
getpwd()30847109Sbostic getpwd() {
30947109Sbostic char buf[MAXPWD];
31047109Sbostic char *p;
31147109Sbostic int i;
31247109Sbostic int status;
31347109Sbostic struct job *jp;
31447109Sbostic int pip[2];
31547109Sbostic
31647109Sbostic if (curdir)
31747109Sbostic return;
31847109Sbostic INTOFF;
31947109Sbostic if (pipe(pip) < 0)
32047109Sbostic error("Pipe call failed");
32147109Sbostic jp = makejob((union node *)NULL, 1);
32247109Sbostic if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
32347109Sbostic close(pip[0]);
32447109Sbostic if (pip[1] != 1) {
32547109Sbostic close(1);
32647109Sbostic copyfd(pip[1], 1);
32747109Sbostic close(pip[1]);
32847109Sbostic }
32947109Sbostic execl("/bin/pwd", "pwd", (char *)0);
33047109Sbostic error("Cannot exec /bin/pwd");
33147109Sbostic }
33247109Sbostic close(pip[1]);
33347109Sbostic pip[1] = -1;
33447109Sbostic p = buf;
33547109Sbostic while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
336*69272Schristos || (i == -1 && errno == EINTR)) {
33747109Sbostic if (i > 0)
33847109Sbostic p += i;
33947109Sbostic }
34047109Sbostic close(pip[0]);
34147109Sbostic pip[0] = -1;
34247109Sbostic status = waitforjob(jp);
34347109Sbostic if (status != 0)
34447109Sbostic error((char *)0);
34547109Sbostic if (i < 0 || p == buf || p[-1] != '\n')
34647109Sbostic error("pwd command failed");
34747109Sbostic p[-1] = '\0';
34847109Sbostic curdir = savestr(buf);
34947109Sbostic INTON;
35047109Sbostic }
351