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