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