VirtualBox

source: kBuild/trunk/src/kash/cd.c@ 2299

Last change on this file since 2299 was 2290, checked in by bird, 16 years ago

kash: malloc/free/friends gets a psh.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 10.7 KB
Line 
1/* $NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#if 0
36#ifndef lint
37static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95";
38#else
39__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $");
40#endif /* not lint */
41#endif
42
43#include <sys/types.h>
44#include <stdlib.h>
45#include <string.h>
46#include <errno.h>
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 "exec.h"
61#include "redir.h"
62#include "mystring.h"
63#include "show.h"
64#include "cd.h"
65#include "shinstance.h"
66
67STATIC int docd(shinstance *psh, char *, int);
68STATIC char *getcomponent(shinstance *psh);
69STATIC void updatepwd(shinstance *psh, char *);
70STATIC void find_curdir(shinstance *psh, int noerror);
71
72/*char *curdir = NULL;*/ /* current working directory */
73/*char *prevdir;*/ /* previous working directory */
74/*STATIC char *cdcomppath;*/
75
76int
77cdcmd(shinstance *psh, int argc, char **argv)
78{
79 const char *dest;
80 const char *path;
81 char *p, *d;
82 struct stat statb;
83 int print = cdprint(psh); /* set -cdprint to enable */
84
85 nextopt(psh, nullstr);
86
87 /*
88 * Try (quite hard) to have 'curdir' defined, nothing has set
89 * it on entry to the shell, but we want 'cd fred; cd -' to work.
90 */
91 getpwd(psh, 1);
92 dest = *psh->argptr;
93 if (dest == NULL) {
94 dest = bltinlookup(psh, "HOME", 1);
95 if (dest == NULL)
96 error(psh, "HOME not set");
97 } else {
98 if (psh->argptr[1]) {
99 /* Do 'ksh' style substitution */
100 if (!psh->curdir)
101 error(psh, "PWD not set");
102 p = strstr(psh->curdir, dest);
103 if (!p)
104 error(psh, "bad substitution");
105 d = stalloc(psh, strlen(psh->curdir) + strlen(psh->argptr[1]) + 1);
106 memcpy(d, psh->curdir, p - psh->curdir);
107 strcpy(d + (p - psh->curdir), psh->argptr[1]);
108 strcat(d, p + strlen(dest));
109 dest = d;
110 print = 1;
111 }
112 }
113
114 if (dest[0] == '-' && dest[1] == '\0') {
115 dest = psh->prevdir ? psh->prevdir : psh->curdir;
116 print = 1;
117 }
118 if (*dest == '\0')
119 dest = ".";
120 if (IS_ROOT(dest) || (path = bltinlookup(psh, "CDPATH", 1)) == NULL)
121 path = nullstr;
122 while ((p = padvance(psh, &path, dest)) != NULL) {
123 if (shfile_stat(&psh->fdtab, p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
124 if (!print) {
125 /*
126 * XXX - rethink
127 */
128 if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
129 p += 2;
130 print = strcmp(p, dest);
131 }
132 if (docd(psh, p, print) >= 0)
133 return 0;
134
135 }
136 }
137 error(psh, "can't cd to %s", dest);
138 /* NOTREACHED */
139 return 1;
140}
141
142
143/*
144 * Actually do the chdir. In an interactive shell, print the
145 * directory name if "print" is nonzero.
146 */
147
148STATIC int
149docd(shinstance *psh, char *dest, int print)
150{
151 char *p;
152 char *q;
153 char *component;
154 struct stat statb;
155 int first;
156 int badstat;
157
158 TRACE((psh, "docd(\"%s\", %d) called\n", dest, print));
159
160 /*
161 * Check each component of the path. If we find a symlink or
162 * something we can't stat, clear curdir to force a getcwd()
163 * next time we get the value of the current directory.
164 */
165 badstat = 0;
166 psh->cdcomppath = stalloc(psh, strlen(dest) + 1);
167 scopy(dest, psh->cdcomppath);
168 STARTSTACKSTR(psh, p);
169 if (IS_ROOT(dest)) {
170 STPUTC(psh, '/', p);
171 psh->cdcomppath++;
172 }
173 first = 1;
174 while ((q = getcomponent(psh)) != NULL) {
175 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
176 continue;
177 if (! first)
178 STPUTC(psh, '/', p);
179 first = 0;
180 component = q;
181 while (*q)
182 STPUTC(psh, *q++, p);
183 if (equal(component, ".."))
184 continue;
185 STACKSTRNUL(psh, p);
186 if ((shfile_lstat(&psh->fdtab, stackblock(psh), &statb) < 0)
187 || (S_ISLNK(statb.st_mode))) {
188 /* print = 1; */
189 badstat = 1;
190 break;
191 }
192 }
193
194 INTOFF;
195 if (shfile_chdir(&psh->fdtab, dest) < 0) {
196 INTON;
197 return -1;
198 }
199 updatepwd(psh, badstat ? NULL : dest);
200 INTON;
201 if (print && iflag(psh) && psh->curdir)
202 out1fmt(psh, "%s\n", psh->curdir);
203 return 0;
204}
205
206
207/*
208 * Get the next component of the path name pointed to by psh->cdcomppath.
209 * This routine overwrites the string pointed to by psh->cdcomppath.
210 */
211
212STATIC char *
213getcomponent(shinstance *psh)
214{
215 char *p;
216 char *start;
217
218 if ((p = psh->cdcomppath) == NULL)
219 return NULL;
220 start = psh->cdcomppath;
221 while (*p != '/' && *p != '\0')
222 p++;
223 if (*p == '\0') {
224 psh->cdcomppath = NULL;
225 } else {
226 *p++ = '\0';
227 psh->cdcomppath = p;
228 }
229 return start;
230}
231
232
233
234/*
235 * Update curdir (the name of the current directory) in response to a
236 * cd command. We also call hashcd to let the routines in exec.c know
237 * that the current directory has changed.
238 */
239
240STATIC void
241updatepwd(shinstance *psh, char *dir)
242{
243 char *new;
244 char *p;
245
246 hashcd(psh); /* update command hash table */
247
248 /*
249 * If our argument is NULL, we don't know the current directory
250 * any more because we traversed a symbolic link or something
251 * we couldn't stat().
252 */
253 if (dir == NULL || psh->curdir == NULL) {
254 if (psh->prevdir)
255 ckfree(psh, psh->prevdir);
256 INTOFF;
257 psh->prevdir = psh->curdir;
258 psh->curdir = NULL;
259 getpwd(psh, 1);
260 INTON;
261 if (psh->curdir)
262 setvar(psh, "PWD", psh->curdir, VEXPORT);
263 else
264 unsetvar(psh, "PWD", 0);
265 return;
266 }
267 psh->cdcomppath = stalloc(psh, strlen(dir) + 1);
268 scopy(dir, psh->cdcomppath);
269 STARTSTACKSTR(psh, new);
270 if (!IS_ROOT(dir)) {
271 p = psh->curdir;
272 while (*p)
273 STPUTC(psh, *p++, new);
274 if (p[-1] == '/')
275 STUNPUTC(psh, new);
276 }
277 while ((p = getcomponent(psh)) != NULL) {
278 if (equal(p, "..")) {
279 while (new > stackblock(psh) && (STUNPUTC(psh, new), *new) != '/');
280 } else if (*p != '\0' && ! equal(p, ".")) {
281 STPUTC(psh, '/', new);
282 while (*p)
283 STPUTC(psh, *p++, new);
284 }
285 }
286 if (new == stackblock(psh))
287 STPUTC(psh, '/', new);
288 STACKSTRNUL(psh, new);
289 INTOFF;
290 if (psh->prevdir)
291 ckfree(psh, psh->prevdir);
292 psh->prevdir = psh->curdir;
293 psh->curdir = savestr(psh, stackblock(psh));
294 setvar(psh, "PWD", psh->curdir, VEXPORT);
295 INTON;
296}
297
298/*
299 * Posix says the default should be 'pwd -L' (as below), however
300 * the 'cd' command (above) does something much nearer to the
301 * posix 'cd -P' (not the posix default of 'cd -L').
302 * If 'cd' is changed to support -P/L then the default here
303 * needs to be revisited if the historic behaviour is to be kept.
304 */
305
306int
307pwdcmd(shinstance *psh, int argc, char **argv)
308{
309 int i;
310 char opt = 'L';
311
312 while ((i = nextopt(psh, "LP")) != '\0')
313 opt = i;
314 if (*psh->argptr)
315 error(psh, "unexpected argument");
316
317 if (opt == 'L')
318 getpwd(psh, 0);
319 else
320 find_curdir(psh, 0);
321
322 setvar(psh, "PWD", psh->curdir, VEXPORT);
323 out1str(psh, psh->curdir);
324 out1c(psh, '\n');
325 return 0;
326}
327
328
329
330
331#define MAXPWD 256
332
333/*
334 * Find out what the current directory is. If we already know the current
335 * directory, this routine returns immediately.
336 */
337const char *
338getpwd(shinstance *psh, int noerror)
339{
340 char *pwd;
341 struct stat stdot, stpwd;
342 /*static int first = 1;*/
343
344 if (psh->curdir)
345 return psh->curdir;
346
347 if (psh->getpwd_first) {
348 psh->getpwd_first = 0;
349 pwd = sh_getenv(psh, "PWD");
350 if (pwd && IS_ROOT(pwd) && shfile_stat(&psh->fdtab, ".", &stdot) != -1 &&
351 shfile_stat(&psh->fdtab, pwd, &stpwd) != -1 &&
352 stdot.st_dev == stpwd.st_dev &&
353 stdot.st_ino == stpwd.st_ino) {
354 psh->curdir = savestr(psh, pwd);
355 return psh->curdir;
356 }
357 }
358
359 find_curdir(psh, noerror);
360
361 return psh->curdir;
362}
363
364STATIC void
365find_curdir(shinstance *psh, int noerror)
366{
367 int i;
368 char *pwd;
369
370 /*
371 * Things are a bit complicated here; we could have just used
372 * getcwd, but traditionally getcwd is implemented using popen
373 * to /bin/pwd. This creates a problem for us, since we cannot
374 * keep track of the job if it is being ran behind our backs.
375 * So we re-implement getcwd(), and we suppress interrupts
376 * throughout the process. This is not completely safe, since
377 * the user can still break out of it by killing the pwd program.
378 * We still try to use getcwd for systems that we know have a
379 * c implementation of getcwd, that does not open a pipe to
380 * /bin/pwd.
381 */
382#if 1 //defined(__NetBSD__) || defined(__SVR4) || defined(__INNOTEK_LIBC__)
383
384 for (i = MAXPWD;; i *= 2) {
385 pwd = stalloc(psh, i);
386 if (shfile_getcwd(&psh->fdtab, pwd, i) != NULL) {
387 psh->curdir = savestr(psh, pwd);
388 return;
389 }
390 stunalloc(psh, pwd);
391 if (errno == ERANGE)
392 continue;
393 if (!noerror)
394 error(psh, "getcwd() failed: %s", strerror(errno));
395 return;
396 }
397#else
398 {
399 char *p;
400 int status;
401 struct job *jp;
402 int pip[2];
403
404 pwd = stalloc(psh, MAXPWD);
405 INTOFF;
406 if (pipe(pip) < 0)
407 error(psh, "Pipe call failed");
408 jp = makejob((union node *)NULL, 1);
409 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
410 (void) close(pip[0]);
411 if (pip[1] != 1) {
412 close(1);
413 copyfd(pip[1], 1);
414 close(pip[1]);
415 }
416 (void) execl("/bin/pwd", "pwd", (char *)0);
417 error(psh, "Cannot exec /bin/pwd");
418 }
419 (void) close(pip[1]);
420 pip[1] = -1;
421 p = pwd;
422 while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0
423 || (i == -1 && errno == EINTR)) {
424 if (i > 0)
425 p += i;
426 }
427 (void) close(pip[0]);
428 pip[0] = -1;
429 status = waitforjob(jp);
430 if (status != 0)
431 error(psh, (char *)0);
432 if (i < 0 || p == pwd || p[-1] != '\n') {
433 if (noerror) {
434 INTON;
435 return;
436 }
437 error(psh, "pwd command failed");
438 }
439 p[-1] = '\0';
440 INTON;
441 psh->curdir = savestr(pwd);
442 return;
443 }
444#endif
445}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette