cd.c revision 1.3 1 /*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * 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 5.2 (Berkeley) 3/13/91";
39 static char rcsid[] = "$Id: cd.c,v 1.3 1993/03/22 08:04:00 cgd 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 <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 STATIC char *cdcomppath;
75
76 #if UDIR
77 extern int didudir; /* set if /u/logname expanded */
78 #endif
79
80
81 int
82 cdcmd(argc, argv) char **argv; {
83 char *dest;
84 char *path;
85 char *p;
86 struct stat statb;
87 char *padvance();
88
89 nextopt(nullstr);
90 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
91 error("HOME not set");
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 && docd(p, strcmp(p, dest)) >= 0)
98 return 0;
99 }
100 error("can't cd to %s", dest);
101 }
102
103
104 /*
105 * Actually do the chdir. If the name refers to symbolic links, we
106 * compute the actual directory name before doing the cd. In an
107 * interactive shell, print the directory name if "print" is nonzero
108 * or if the name refers to a symbolic link. We also print the name
109 * if "/u/logname" was expanded in it, since this is similar to a
110 * symbolic link. (The check for this breaks if the user gives the
111 * cd command some additional, unused arguments.)
112 */
113
114 #if SYMLINKS == 0
115 STATIC int
116 docd(dest, print)
117 char *dest;
118 {
119 #if UDIR
120 if (didudir)
121 print = 1;
122 #endif
123 INTOFF;
124 if (chdir(dest) < 0) {
125 INTON;
126 return -1;
127 }
128 updatepwd(dest);
129 INTON;
130 #ifdef not
131 if (print && iflag)
132 out1fmt("%s\n", stackblock());
133 #endif
134 return 0;
135 }
136
137 #else
138
139
140
141 STATIC int
142 docd(dest, print)
143 char *dest;
144 {
145 register char *p;
146 register char *q;
147 char *symlink;
148 char *component;
149 struct stat statb;
150 int first;
151 int i;
152
153 TRACE(("docd(\"%s\", %d) called\n", dest, print));
154 #if UDIR
155 if (didudir)
156 print = 1;
157 #endif
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 #ifdef not
227 if (print && iflag)
228 out1fmt("%s\n", p);
229 #endif
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 if (curdir)
303 ckfree(curdir);
304 curdir = savestr(stackblock());
305 }
306
307
308
309 int
310 pwdcmd(argc, argv) char **argv; {
311 getpwd();
312 out1str(curdir);
313 out1c('\n');
314 return 0;
315 }
316
317
318
319 /*
320 * Run /bin/pwd to find out what the current directory is. We suppress
321 * interrupts throughout most of this, but the user can still break out
322 * of it by killing the pwd program. If we already know the current
323 * directory, this routine returns immediately.
324 */
325
326 #define MAXPWD 256
327
328 STATIC void
329 getpwd() {
330 char buf[MAXPWD];
331 char *p;
332 int i;
333 int status;
334 struct job *jp;
335 int pip[2];
336
337 if (curdir)
338 return;
339 INTOFF;
340 if (pipe(pip) < 0)
341 error("Pipe call failed");
342 jp = makejob((union node *)NULL, 1);
343 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
344 close(pip[0]);
345 if (pip[1] != 1) {
346 close(1);
347 copyfd(pip[1], 1);
348 close(pip[1]);
349 }
350 execl("/bin/pwd", "pwd", (char *)0);
351 /* error("Cannot exec /bin/pwd");*/
352 out2str("Cannot exec /bin/pwd\n"); /* 22 Aug 92*/
353 flushall();
354 _exit(1);
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