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