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