cd.c revision 1.14 1 /* $NetBSD: cd.c,v 1.14 1995/11/19 23:27:37 christos 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.2 (Berkeley) 5/4/95";
42 #else
43 static char rcsid[] = "$NetBSD: cd.c,v 1.14 1995/11/19 23:27:37 christos Exp $";
44 #endif
45 #endif /* not lint */
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <errno.h>
52
53 /*
54 * The cd and pwd commands.
55 */
56
57 #include "shell.h"
58 #include "var.h"
59 #include "nodes.h" /* for jobs.h */
60 #include "jobs.h"
61 #include "options.h"
62 #include "output.h"
63 #include "memalloc.h"
64 #include "error.h"
65 #include "redir.h"
66 #include "mystring.h"
67 #include "show.h"
68 #include "cd.h"
69
70 STATIC int docd __P((char *, int));
71 STATIC char *getcomponent __P((void));
72 STATIC void updatepwd __P((char *));
73
74 char *curdir = NULL; /* current working directory */
75 char *prevdir; /* previous working directory */
76 STATIC char *cdcomppath;
77
78 int
79 cdcmd(argc, argv)
80 int argc;
81 char **argv;
82 {
83 char *dest;
84 char *path;
85 char *p;
86 struct stat statb;
87 char *padvance();
88 int print = 0;
89
90 nextopt(nullstr);
91 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
92 error("HOME not set");
93 if (dest[0] == '-' && dest[1] == '\0') {
94 dest = prevdir ? prevdir : curdir;
95 print = 1;
96 }
97 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
98 path = nullstr;
99 while ((p = padvance(&path, dest)) != NULL) {
100 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
101 if (!print) {
102 /*
103 * XXX - rethink
104 */
105 if (p[0] == '.' && p[1] == '/')
106 p += 2;
107 print = strcmp(p, dest);
108 }
109 if (docd(p, print) >= 0)
110 return 0;
111
112 }
113 }
114 error("can't cd to %s", dest);
115 /*NOTREACHED*/
116 return 0;
117 }
118
119
120 /*
121 * Actually do the chdir. If the name refers to symbolic links, we
122 * compute the actual directory name before doing the cd. In an
123 * interactive shell, print the directory name if "print" is nonzero
124 * or if the name refers to a symbolic link. We also print the name
125 * if "/u/logname" was expanded in it, since this is similar to a
126 * symbolic link. (The check for this breaks if the user gives the
127 * cd command some additional, unused arguments.)
128 */
129
130 #if SYMLINKS == 0
131 STATIC int
132 docd(dest, print)
133 char *dest;
134 {
135 INTOFF;
136 if (chdir(dest) < 0) {
137 INTON;
138 return -1;
139 }
140 updatepwd(dest);
141 INTON;
142 if (print && iflag)
143 out1fmt("%s\n", stackblock());
144 return 0;
145 }
146
147 #else
148
149
150
151 STATIC int
152 docd(dest, print)
153 char *dest;
154 int print;
155 {
156 register char *p;
157 register char *q;
158 char *symlink;
159 char *component;
160 struct stat statb;
161 int first;
162 int i;
163
164 TRACE(("docd(\"%s\", %d) called\n", dest, print));
165
166 top:
167 cdcomppath = dest;
168 STARTSTACKSTR(p);
169 if (*dest == '/') {
170 STPUTC('/', p);
171 cdcomppath++;
172 }
173 first = 1;
174 while ((q = getcomponent()) != NULL) {
175 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
176 continue;
177 if (! first)
178 STPUTC('/', p);
179 first = 0;
180 component = q;
181 while (*q)
182 STPUTC(*q++, p);
183 if (equal(component, ".."))
184 continue;
185 STACKSTRNUL(p);
186 if (lstat(stackblock(), &statb) < 0)
187 error("lstat %s failed", stackblock());
188 if (!S_ISLNK(statb.st_mode))
189 continue;
190
191 /* Hit a symbolic link. We have to start all over again. */
192 print = 1;
193 STPUTC('\0', p);
194 symlink = grabstackstr(p);
195 i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */
196 if (cdcomppath != NULL)
197 i += strlen(cdcomppath);
198 p = stalloc(i);
199 if (readlink(symlink, p, (int)statb.st_size) < 0) {
200 error("readlink %s failed", stackblock());
201 }
202 if (cdcomppath != NULL) {
203 p[(int)statb.st_size] = '/';
204 scopy(cdcomppath, p + (int)statb.st_size + 1);
205 } else {
206 p[(int)statb.st_size] = '\0';
207 }
208 if (p[0] != '/') { /* relative path name */
209 char *r;
210 q = r = symlink;
211 while (*q) {
212 if (*q++ == '/')
213 r = q;
214 }
215 *r = '\0';
216 dest = stalloc(strlen(symlink) + strlen(p) + 1);
217 scopy(symlink, dest);
218 strcat(dest, p);
219 } else {
220 dest = p;
221 }
222 goto top;
223 }
224 STPUTC('\0', p);
225 p = grabstackstr(p);
226 INTOFF;
227 if (chdir(p) < 0) {
228 INTON;
229 return -1;
230 }
231 updatepwd(p);
232 INTON;
233 if (print && iflag)
234 out1fmt("%s\n", p);
235 return 0;
236 }
237 #endif /* SYMLINKS */
238
239
240
241 /*
242 * Get the next component of the path name pointed to by cdcomppath.
243 * This routine overwrites the string pointed to by cdcomppath.
244 */
245
246 STATIC char *
247 getcomponent() {
248 register char *p;
249 char *start;
250
251 if ((p = cdcomppath) == NULL)
252 return NULL;
253 start = cdcomppath;
254 while (*p != '/' && *p != '\0')
255 p++;
256 if (*p == '\0') {
257 cdcomppath = NULL;
258 } else {
259 *p++ = '\0';
260 cdcomppath = p;
261 }
262 return start;
263 }
264
265
266
267 /*
268 * Update curdir (the name of the current directory) in response to a
269 * cd command. We also call hashcd to let the routines in exec.c know
270 * that the current directory has changed.
271 */
272
273 void hashcd();
274
275 STATIC void
276 updatepwd(dir)
277 char *dir;
278 {
279 char *new;
280 char *p;
281
282 hashcd(); /* update command hash table */
283 cdcomppath = stalloc(strlen(dir) + 1);
284 scopy(dir, cdcomppath);
285 STARTSTACKSTR(new);
286 if (*dir != '/') {
287 if (curdir == NULL)
288 return;
289 p = curdir;
290 while (*p)
291 STPUTC(*p++, new);
292 if (p[-1] == '/')
293 STUNPUTC(new);
294 }
295 while ((p = getcomponent()) != NULL) {
296 if (equal(p, "..")) {
297 while (new > stackblock() && (STUNPUTC(new), *new) != '/');
298 } else if (*p != '\0' && ! equal(p, ".")) {
299 STPUTC('/', new);
300 while (*p)
301 STPUTC(*p++, new);
302 }
303 }
304 if (new == stackblock())
305 STPUTC('/', new);
306 STACKSTRNUL(new);
307 INTOFF;
308 if (prevdir)
309 ckfree(prevdir);
310 prevdir = curdir;
311 curdir = savestr(stackblock());
312 INTON;
313 }
314
315
316
317 int
318 pwdcmd(argc, argv)
319 int argc;
320 char **argv;
321 {
322 getpwd();
323 out1str(curdir);
324 out1c('\n');
325 return 0;
326 }
327
328
329
330
331 #define MAXPWD 256
332
333 /*
334 * Find out what the current directory is. If we already know the current
335 * directory, this routine returns immediately.
336 */
337 void
338 getpwd()
339 {
340 char buf[MAXPWD];
341
342 if (curdir)
343 return;
344 /*
345 * Things are a bit complicated here; we could have just used
346 * getcwd, but traditionally getcwd is implemented using popen
347 * to /bin/pwd. This creates a problem for us, since we cannot
348 * keep track of the job if it is being ran behind our backs.
349 * So we re-implement getcwd(), and we suppress interrupts
350 * throughout the process. This is not completely safe, since
351 * the user can still break out of it by killing the pwd program.
352 * We still try to use getcwd for systems that we know have a
353 * c implementation of getcwd, that does not open a pipe to
354 * /bin/pwd.
355 */
356 #if defined(__NetBSD__) || defined(__svr4__)
357 if (getcwd(buf, sizeof(buf)) == NULL)
358 error("getcwd() failed");
359 #else
360 {
361 char *p;
362 int i;
363 int status;
364 struct job *jp;
365 int pip[2];
366
367 INTOFF;
368 if (pipe(pip) < 0)
369 error("Pipe call failed");
370 jp = makejob((union node *)NULL, 1);
371 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
372 (void) close(pip[0]);
373 if (pip[1] != 1) {
374 close(1);
375 copyfd(pip[1], 1);
376 close(pip[1]);
377 }
378 (void) execl("/bin/pwd", "pwd", (char *)0);
379 error("Cannot exec /bin/pwd");
380 }
381 (void) close(pip[1]);
382 pip[1] = -1;
383 p = buf;
384 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
385 || (i == -1 && errno == EINTR)) {
386 if (i > 0)
387 p += i;
388 }
389 (void) close(pip[0]);
390 pip[0] = -1;
391 status = waitforjob(jp);
392 if (status != 0)
393 error((char *)0);
394 if (i < 0 || p == buf || p[-1] != '\n')
395 error("pwd command failed");
396 p[-1] = '\0';
397 }
398 #endif
399 curdir = savestr(buf);
400 INTON;
401 }
402