source: trunk/minix/commands/ash/cd.c@ 21

Last change on this file since 21 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 8.3 KB
Line 
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
38static char sccsid[] = "@(#)cd.c 5.2 (Berkeley) 3/13/91";
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__
60STATIC int docd(char *, int, int);
61STATIC void updatepwd(char *);
62STATIC void getpwd(void);
63STATIC char *getcomponent(void);
64#else
65STATIC int docd();
66STATIC void updatepwd();
67STATIC void getpwd();
68STATIC char *getcomponent();
69#endif
70
71
72char *curdir; /* current working directory */
73STATIC char *cdcomppath;
74
75#if UDIR || TILDE
76extern int didudir; /* set if /u/logname or ~logname expanded */
77#endif
78
79
80int
81cdcmd(argc, argv) char **argv; {
82 char *dest;
83 char *path;
84 char *p;
85 struct stat statb;
86 char *padvance();
87 int tohome= 0;
88
89 nextopt(nullstr);
90 if ((dest = *argptr) == NULL) {
91 if ((dest = bltinlookup("HOME", 1)) == NULL)
92 error("HOME not set");
93 tohome = 1;
94 }
95 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
96 path = nullstr;
97 while ((p = padvance(&path, dest)) != NULL) {
98 if (stat(p, &statb) >= 0
99 && (statb.st_mode & S_IFMT) == S_IFDIR
100 && docd(p, strcmp(p, dest), tohome) >= 0)
101 return 0;
102 }
103 error("can't cd to %s", dest);
104}
105
106
107/*
108 * Actually do the chdir. If the name refers to symbolic links, we
109 * compute the actual directory name before doing the cd. In an
110 * interactive shell, print the directory name if "print" is nonzero
111 * or if the name refers to a symbolic link. We also print the name
112 * if "/u/logname" was expanded in it, since this is similar to a
113 * symbolic link. (The check for this breaks if the user gives the
114 * cd command some additional, unused arguments.)
115 */
116
117#if SYMLINKS == 0
118STATIC int
119docd(dest, print, tohome)
120 char *dest;
121 {
122#if UDIR || TILDE
123 if (didudir)
124 print = 1;
125#endif
126 INTOFF;
127 if (chdir(dest) < 0) {
128 INTON;
129 return -1;
130 }
131 updatepwd(dest);
132 INTON;
133 if (print && iflag)
134 out1fmt("%s\n", stackblock());
135 return 0;
136}
137
138#else
139
140
141
142STATIC int
143docd(dest, print, tohome)
144 char *dest;
145 {
146 register char *p;
147 register char *q;
148 char *symlink;
149 char *component;
150 struct stat statb;
151 int first;
152 int i;
153
154 TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, tohome));
155#if UDIR || TILDE
156 if (didudir)
157 print = 1;
158#endif
159
160top:
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 /* The empty string is not a legal argument to chdir on a POSIX 1003.1
222 * system. */
223 if (p[0] != '\0' && chdir(p) < 0) {
224 INTON;
225 return -1;
226 }
227 updatepwd(p);
228 INTON;
229 if (print && !tohome && iflag)
230 out1fmt("%s\n", p);
231 return 0;
232}
233#endif /* SYMLINKS */
234
235
236
237/*
238 * Get the next component of the path name pointed to by cdcomppath.
239 * This routine overwrites the string pointed to by cdcomppath.
240 */
241
242STATIC char *
243getcomponent() {
244 register char *p;
245 char *start;
246
247 if ((p = cdcomppath) == NULL)
248 return NULL;
249 start = cdcomppath;
250 while (*p != '/' && *p != '\0')
251 p++;
252 if (*p == '\0') {
253 cdcomppath = NULL;
254 } else {
255 *p++ = '\0';
256 cdcomppath = p;
257 }
258 return start;
259}
260
261
262
263/*
264 * Update curdir (the name of the current directory) in response to a
265 * cd command. We also call hashcd to let the routines in exec.c know
266 * that the current directory has changed.
267 */
268
269void hashcd();
270
271STATIC void
272updatepwd(dir)
273 char *dir;
274 {
275 char *new;
276 char *p;
277
278 hashcd(); /* update command hash table */
279 cdcomppath = stalloc(strlen(dir) + 1);
280 scopy(dir, cdcomppath);
281 STARTSTACKSTR(new);
282 if (*dir != '/') {
283 if (curdir == NULL)
284 return;
285 p = curdir;
286 while (*p)
287 STPUTC(*p++, new);
288 if (p[-1] == '/')
289 STUNPUTC(new);
290 }
291 while ((p = getcomponent()) != NULL) {
292 if (equal(p, "..")) {
293 while (new > stackblock() && (STUNPUTC(new), *new) != '/');
294 } else if (*p != '\0' && ! equal(p, ".")) {
295 STPUTC('/', new);
296 while (*p)
297 STPUTC(*p++, new);
298 }
299 }
300 if (new == stackblock())
301 STPUTC('/', new);
302 STACKSTRNUL(new);
303 if (curdir)
304 ckfree(curdir);
305 curdir = savestr(stackblock());
306}
307
308
309
310int
311pwdcmd(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
329STATIC void
330getpwd() {
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}
Note: See TracBrowser for help on using the repository browser.