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