$ ); echo $? echoed 0,all other shells echo 2.
[unix-history] / usr / src / bin / sh / cd.c
CommitLineData
d003fee2 1/*-
d1b73048
KB
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
d003fee2
KB
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
13b3e634 12static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) %G%";
d003fee2
KB
13#endif /* not lint */
14
13b3e634
CZ
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <errno.h>
20
d003fee2
KB
21/*
22 * The cd and pwd commands.
23 */
24
25#include "shell.h"
26#include "var.h"
27#include "nodes.h" /* for jobs.h */
28#include "jobs.h"
29#include "options.h"
30#include "output.h"
31#include "memalloc.h"
32#include "error.h"
13b3e634 33#include "redir.h"
d003fee2 34#include "mystring.h"
13b3e634 35#include "show.h"
d003fee2 36
13b3e634
CZ
37STATIC int docd __P((char *, int));
38STATIC char *getcomponent __P((void));
39STATIC void updatepwd __P((char *));
40STATIC void getpwd __P((void));
d003fee2
KB
41
42char *curdir; /* current working directory */
af4f2a31 43char *prevdir; /* previous working directory */
d003fee2
KB
44STATIC char *cdcomppath;
45
d003fee2 46int
13b3e634
CZ
47cdcmd(argc, argv)
48 int argc;
49 char **argv;
50{
d003fee2
KB
51 char *dest;
52 char *path;
53 char *p;
54 struct stat statb;
55 char *padvance();
af4f2a31 56 int print = 0;
d003fee2 57
ddba57cd
MT
58 nextopt(nullstr);
59 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
d003fee2 60 error("HOME not set");
af4f2a31
MT
61 if (dest[0] == '-' && dest[1] == '\0') {
62 dest = prevdir ? prevdir : curdir;
63 print = 1;
64 }
d003fee2
KB
65 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
66 path = nullstr;
67 while ((p = padvance(&path, dest)) != NULL) {
13b3e634 68 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
af4f2a31
MT
69 if (!print) {
70 /*
71 * XXX - rethink
72 */
73 if (p[0] == '.' && p[1] == '/')
74 p += 2;
75 print = strcmp(p, dest);
76 }
77 if (docd(p, print) >= 0)
78 return 0;
79
80 }
d003fee2
KB
81 }
82 error("can't cd to %s", dest);
13b3e634
CZ
83 /*NOTREACHED*/
84 return 0;
d003fee2
KB
85}
86
87
88/*
89 * Actually do the chdir. If the name refers to symbolic links, we
90 * compute the actual directory name before doing the cd. In an
91 * interactive shell, print the directory name if "print" is nonzero
92 * or if the name refers to a symbolic link. We also print the name
93 * if "/u/logname" was expanded in it, since this is similar to a
94 * symbolic link. (The check for this breaks if the user gives the
95 * cd command some additional, unused arguments.)
96 */
97
98#if SYMLINKS == 0
99STATIC int
100docd(dest, print)
101 char *dest;
102 {
d003fee2
KB
103 INTOFF;
104 if (chdir(dest) < 0) {
105 INTON;
106 return -1;
107 }
108 updatepwd(dest);
109 INTON;
110 if (print && iflag)
111 out1fmt("%s\n", stackblock());
112 return 0;
113}
114
115#else
116
117
118
119STATIC int
120docd(dest, print)
121 char *dest;
13b3e634
CZ
122 int print;
123{
d003fee2
KB
124 register char *p;
125 register char *q;
126 char *symlink;
127 char *component;
128 struct stat statb;
129 int first;
130 int i;
131
132 TRACE(("docd(\"%s\", %d) called\n", dest, print));
d003fee2
KB
133
134top:
135 cdcomppath = dest;
136 STARTSTACKSTR(p);
137 if (*dest == '/') {
138 STPUTC('/', p);
139 cdcomppath++;
140 }
141 first = 1;
142 while ((q = getcomponent()) != NULL) {
13b3e634 143 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
d003fee2
KB
144 continue;
145 if (! first)
146 STPUTC('/', p);
147 first = 0;
148 component = q;
149 while (*q)
150 STPUTC(*q++, p);
151 if (equal(component, ".."))
152 continue;
153 STACKSTRNUL(p);
154 if (lstat(stackblock(), &statb) < 0)
155 error("lstat %s failed", stackblock());
13b3e634 156 if (!S_ISLNK(statb.st_mode))
d003fee2
KB
157 continue;
158
159 /* Hit a symbolic link. We have to start all over again. */
160 print = 1;
161 STPUTC('\0', p);
162 symlink = grabstackstr(p);
163 i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */
164 if (cdcomppath != NULL)
165 i += strlen(cdcomppath);
166 p = stalloc(i);
167 if (readlink(symlink, p, (int)statb.st_size) < 0) {
168 error("readlink %s failed", stackblock());
169 }
170 if (cdcomppath != NULL) {
171 p[(int)statb.st_size] = '/';
172 scopy(cdcomppath, p + (int)statb.st_size + 1);
173 } else {
174 p[(int)statb.st_size] = '\0';
175 }
176 if (p[0] != '/') { /* relative path name */
177 char *r;
178 q = r = symlink;
179 while (*q) {
180 if (*q++ == '/')
181 r = q;
182 }
183 *r = '\0';
184 dest = stalloc(strlen(symlink) + strlen(p) + 1);
185 scopy(symlink, dest);
186 strcat(dest, p);
187 } else {
188 dest = p;
189 }
190 goto top;
191 }
192 STPUTC('\0', p);
193 p = grabstackstr(p);
194 INTOFF;
195 if (chdir(p) < 0) {
196 INTON;
197 return -1;
198 }
199 updatepwd(p);
200 INTON;
201 if (print && iflag)
202 out1fmt("%s\n", p);
203 return 0;
204}
205#endif /* SYMLINKS */
206
207
208
209/*
210 * Get the next component of the path name pointed to by cdcomppath.
211 * This routine overwrites the string pointed to by cdcomppath.
212 */
213
214STATIC char *
215getcomponent() {
216 register char *p;
217 char *start;
218
219 if ((p = cdcomppath) == NULL)
220 return NULL;
221 start = cdcomppath;
222 while (*p != '/' && *p != '\0')
223 p++;
224 if (*p == '\0') {
225 cdcomppath = NULL;
226 } else {
227 *p++ = '\0';
228 cdcomppath = p;
229 }
230 return start;
231}
232
233
234
235/*
236 * Update curdir (the name of the current directory) in response to a
237 * cd command. We also call hashcd to let the routines in exec.c know
238 * that the current directory has changed.
239 */
240
241void hashcd();
242
243STATIC void
244updatepwd(dir)
245 char *dir;
246 {
247 char *new;
248 char *p;
249
250 hashcd(); /* update command hash table */
251 cdcomppath = stalloc(strlen(dir) + 1);
252 scopy(dir, cdcomppath);
253 STARTSTACKSTR(new);
254 if (*dir != '/') {
255 if (curdir == NULL)
256 return;
257 p = curdir;
258 while (*p)
259 STPUTC(*p++, new);
260 if (p[-1] == '/')
261 STUNPUTC(new);
262 }
263 while ((p = getcomponent()) != NULL) {
264 if (equal(p, "..")) {
265 while (new > stackblock() && (STUNPUTC(new), *new) != '/');
266 } else if (*p != '\0' && ! equal(p, ".")) {
267 STPUTC('/', new);
268 while (*p)
269 STPUTC(*p++, new);
270 }
271 }
272 if (new == stackblock())
273 STPUTC('/', new);
274 STACKSTRNUL(new);
af4f2a31
MT
275 INTOFF;
276 if (prevdir)
277 ckfree(prevdir);
278 prevdir = curdir;
d003fee2 279 curdir = savestr(stackblock());
af4f2a31 280 INTON;
d003fee2
KB
281}
282
283
284
285int
13b3e634
CZ
286pwdcmd(argc, argv)
287 int argc;
288 char **argv;
289{
d003fee2
KB
290 getpwd();
291 out1str(curdir);
292 out1c('\n');
293 return 0;
294}
295
296
297
298/*
299 * Run /bin/pwd to find out what the current directory is. We suppress
300 * interrupts throughout most of this, but the user can still break out
301 * of it by killing the pwd program. If we already know the current
302 * directory, this routine returns immediately.
303 */
304
305#define MAXPWD 256
306
307STATIC void
308getpwd() {
309 char buf[MAXPWD];
310 char *p;
311 int i;
312 int status;
313 struct job *jp;
314 int pip[2];
315
316 if (curdir)
317 return;
318 INTOFF;
319 if (pipe(pip) < 0)
320 error("Pipe call failed");
321 jp = makejob((union node *)NULL, 1);
322 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
323 close(pip[0]);
324 if (pip[1] != 1) {
325 close(1);
326 copyfd(pip[1], 1);
327 close(pip[1]);
328 }
329 execl("/bin/pwd", "pwd", (char *)0);
330 error("Cannot exec /bin/pwd");
331 }
332 close(pip[1]);
333 pip[1] = -1;
334 p = buf;
335 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
13b3e634 336 || (i == -1 && errno == EINTR)) {
d003fee2
KB
337 if (i > 0)
338 p += i;
339 }
340 close(pip[0]);
341 pip[0] = -1;
342 status = waitforjob(jp);
343 if (status != 0)
344 error((char *)0);
345 if (i < 0 || p == buf || p[-1] != '\n')
346 error("pwd command failed");
347 p[-1] = '\0';
348 curdir = savestr(buf);
349 INTON;
350}