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