~ubuntu-branches/ubuntu/hardy/klibc/hardy-updates

« back to all changes in this revision

Viewing changes to ash/cd.c

  • Committer: Bazaar Package Importer
  • Author(s): Jeff Bailey
  • Date: 2006-01-04 20:24:52 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060104202452-ec4v3n829rymukuv
Tags: 1.1.15-0ubuntu1
* New upstream version.

* Patch to fix compilation on parisc64 kernels.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
#ifndef __KLIBC__
36
 
#include <sys/cdefs.h>
37
 
#endif
38
 
#ifndef __RCSID
39
 
#define __RCSID(arg)
40
 
#endif
41
 
#ifndef lint
42
 
#if 0
43
 
static char sccsid[] = "@(#)cd.c        8.2 (Berkeley) 5/4/95";
44
 
#else
45
 
__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $");
46
 
#endif
47
 
#endif /* not lint */
48
 
 
49
 
#include <sys/types.h>
50
 
#include <sys/stat.h>
51
 
#include <stdlib.h>
52
 
#include <string.h>
53
 
#include <unistd.h>
54
 
#include <errno.h>
55
 
 
56
 
/*
57
 
 * The cd and pwd commands.
58
 
 */
59
 
 
60
 
#include "shell.h"
61
 
#include "var.h"
62
 
#include "nodes.h"      /* for jobs.h */
63
 
#include "jobs.h"
64
 
#include "options.h"
65
 
#include "output.h"
66
 
#include "memalloc.h"
67
 
#include "error.h"
68
 
#include "exec.h"
69
 
#include "redir.h"
70
 
#include "mystring.h"
71
 
#include "show.h"
72
 
#include "cd.h"
73
 
 
74
 
STATIC int docd(char *, int);
75
 
STATIC char *getcomponent(void);
76
 
STATIC void updatepwd(char *);
77
 
STATIC void find_curdir(int noerror);
78
 
 
79
 
char *curdir = NULL;            /* current working directory */
80
 
char *prevdir;                  /* previous working directory */
81
 
STATIC char *cdcomppath;
82
 
 
83
 
int
84
 
cdcmd(int argc, char **argv)
85
 
{
86
 
        const char *dest;
87
 
        const char *path;
88
 
        char *p, *d;
89
 
        struct stat statb;
90
 
        int print = cdprint;    /* set -cdprint to enable */
91
 
 
92
 
        (void)argc; (void)argv;
93
 
 
94
 
        nextopt(nullstr);
95
 
 
96
 
        /*
97
 
         * Try (quite hard) to have 'curdir' defined, nothing has set
98
 
         * it on entry to the shell, but we want 'cd fred; cd -' to work.
99
 
         */
100
 
        getpwd(1);
101
 
        dest = *argptr;
102
 
        if (dest == NULL) {
103
 
                dest = bltinlookup("HOME", 1);
104
 
                if (dest == NULL)
105
 
                        error("HOME not set");
106
 
        } else {
107
 
                if (argptr[1]) {
108
 
                        /* Do 'ksh' style substitution */
109
 
                        if (!curdir)
110
 
                                error("PWD not set");
111
 
                        p = strstr(curdir, dest);
112
 
                        if (!p)
113
 
                                error("bad substitution");
114
 
                        d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1);
115
 
                        memcpy(d, curdir, p - curdir);
116
 
                        strcpy(d + (p - curdir), argptr[1]);
117
 
                        strcat(d, p + strlen(dest));
118
 
                        dest = d;
119
 
                        print = 1;
120
 
                }
121
 
        }
122
 
 
123
 
        if (dest[0] == '-' && dest[1] == '\0') {
124
 
                dest = prevdir ? prevdir : curdir;
125
 
                print = 1;
126
 
        }
127
 
        if (*dest == '\0')
128
 
                dest = ".";
129
 
        if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
130
 
                path = nullstr;
131
 
        while ((p = padvance(&path, dest)) != NULL) {
132
 
                if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
133
 
                        if (!print) {
134
 
                                /*
135
 
                                 * XXX - rethink
136
 
                                 */
137
 
                                if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
138
 
                                        p += 2;
139
 
                                print = strcmp(p, dest);
140
 
                        }
141
 
                        if (docd(p, print) >= 0)
142
 
                                return 0;
143
 
 
144
 
                }
145
 
        }
146
 
        error("can't cd to %s", dest);
147
 
        /* NOTREACHED */
148
 
}
149
 
 
150
 
 
151
 
/*
152
 
 * Actually do the chdir.  In an interactive shell, print the
153
 
 * directory name if "print" is nonzero.
154
 
 */
155
 
 
156
 
STATIC int
157
 
docd(char *dest, int print)
158
 
{
159
 
        char *p;
160
 
        char *q;
161
 
        char *component;
162
 
        struct stat statb;
163
 
        int first;
164
 
        int badstat;
165
 
 
166
 
        TRACE(("docd(\"%s\", %d) called\n", dest, print));
167
 
 
168
 
        /*
169
 
         *  Check each component of the path. If we find a symlink or
170
 
         *  something we can't stat, clear curdir to force a getcwd()
171
 
         *  next time we get the value of the current directory.
172
 
         */
173
 
        badstat = 0;
174
 
        cdcomppath = stalloc(strlen(dest) + 1);
175
 
        scopy(dest, cdcomppath);
176
 
        STARTSTACKSTR(p);
177
 
        if (*dest == '/') {
178
 
                STPUTC('/', p);
179
 
                cdcomppath++;
180
 
        }
181
 
        first = 1;
182
 
        while ((q = getcomponent()) != NULL) {
183
 
                if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
184
 
                        continue;
185
 
                if (! first)
186
 
                        STPUTC('/', p);
187
 
                first = 0;
188
 
                component = q;
189
 
                while (*q)
190
 
                        STPUTC(*q++, p);
191
 
                if (equal(component, ".."))
192
 
                        continue;
193
 
                STACKSTRNUL(p);
194
 
                if ((lstat(stackblock(), &statb) < 0)
195
 
                    || (S_ISLNK(statb.st_mode)))  {
196
 
                        /* print = 1; */
197
 
                        badstat = 1;
198
 
                        break;
199
 
                }
200
 
        }
201
 
 
202
 
        INTOFF;
203
 
        if (chdir(dest) < 0) {
204
 
                INTON;
205
 
                return -1;
206
 
        }
207
 
        updatepwd(badstat ? NULL : dest);
208
 
        INTON;
209
 
        if (print && iflag && curdir)
210
 
                out1fmt("%s\n", curdir);
211
 
        return 0;
212
 
}
213
 
 
214
 
 
215
 
/*
216
 
 * Get the next component of the path name pointed to by cdcomppath.
217
 
 * This routine overwrites the string pointed to by cdcomppath.
218
 
 */
219
 
 
220
 
STATIC char *
221
 
getcomponent()
222
 
{
223
 
        char *p;
224
 
        char *start;
225
 
 
226
 
        if ((p = cdcomppath) == NULL)
227
 
                return NULL;
228
 
        start = cdcomppath;
229
 
        while (*p != '/' && *p != '\0')
230
 
                p++;
231
 
        if (*p == '\0') {
232
 
                cdcomppath = NULL;
233
 
        } else {
234
 
                *p++ = '\0';
235
 
                cdcomppath = p;
236
 
        }
237
 
        return start;
238
 
}
239
 
 
240
 
 
241
 
 
242
 
/*
243
 
 * Update curdir (the name of the current directory) in response to a
244
 
 * cd command.  We also call hashcd to let the routines in exec.c know
245
 
 * that the current directory has changed.
246
 
 */
247
 
 
248
 
STATIC void
249
 
updatepwd(char *dir)
250
 
{
251
 
        char *new;
252
 
        char *p;
253
 
 
254
 
        hashcd();                               /* update command hash table */
255
 
 
256
 
        /*
257
 
         * If our argument is NULL, we don't know the current directory
258
 
         * any more because we traversed a symbolic link or something
259
 
         * we couldn't stat().
260
 
         */
261
 
        if (dir == NULL || curdir == NULL)  {
262
 
                if (prevdir)
263
 
                        ckfree(prevdir);
264
 
                INTOFF;
265
 
                prevdir = curdir;
266
 
                curdir = NULL;
267
 
                getpwd(1);
268
 
                INTON;
269
 
                if (curdir)
270
 
                        setvar("PWD", curdir, VEXPORT);
271
 
                else
272
 
                        unsetvar("PWD", 0);
273
 
                return;
274
 
        }
275
 
        cdcomppath = stalloc(strlen(dir) + 1);
276
 
        scopy(dir, cdcomppath);
277
 
        STARTSTACKSTR(new);
278
 
        if (*dir != '/') {
279
 
                p = curdir;
280
 
                while (*p)
281
 
                        STPUTC(*p++, new);
282
 
                if (p[-1] == '/')
283
 
                        STUNPUTC(new);
284
 
        }
285
 
        while ((p = getcomponent()) != NULL) {
286
 
                if (equal(p, "..")) {
287
 
                        while (new > stackblock() && (STUNPUTC(new), *new) != '/');
288
 
                } else if (*p != '\0' && ! equal(p, ".")) {
289
 
                        STPUTC('/', new);
290
 
                        while (*p)
291
 
                                STPUTC(*p++, new);
292
 
                }
293
 
        }
294
 
        if (new == stackblock())
295
 
                STPUTC('/', new);
296
 
        STACKSTRNUL(new);
297
 
        INTOFF;
298
 
        if (prevdir)
299
 
                ckfree(prevdir);
300
 
        prevdir = curdir;
301
 
        curdir = savestr(stackblock());
302
 
        setvar("PWD", curdir, VEXPORT);
303
 
        INTON;
304
 
}
305
 
 
306
 
/*
307
 
 * Posix says the default should be 'pwd -L' (as below), however
308
 
 * the 'cd' command (above) does something much nearer to the
309
 
 * posix 'cd -P' (not the posix default of 'cd -L').
310
 
 * If 'cd' is changed to support -P/L then the default here
311
 
 * needs to be revisited if the historic behaviour is to be kept.
312
 
 */
313
 
 
314
 
int
315
 
pwdcmd(int argc, char **argv)
316
 
{
317
 
        int i;
318
 
        char opt = 'L';
319
 
 
320
 
        (void)argc; (void)argv;
321
 
 
322
 
        while ((i = nextopt("LP")) != '\0')
323
 
                opt = i;
324
 
        if (*argptr)
325
 
                error("unexpected argument");
326
 
 
327
 
        if (opt == 'L')
328
 
                getpwd(0);
329
 
        else
330
 
                find_curdir(0);
331
 
 
332
 
        setvar("PWD", curdir, VEXPORT);
333
 
        out1str(curdir);
334
 
        out1c('\n');
335
 
        return 0;
336
 
}
337
 
 
338
 
 
339
 
 
340
 
 
341
 
#define MAXPWD PATH_MAX
342
 
 
343
 
/*
344
 
 * Find out what the current directory is. If we already know the current
345
 
 * directory, this routine returns immediately.
346
 
 */
347
 
void
348
 
getpwd(int noerror)
349
 
{
350
 
        char *pwd;
351
 
        struct stat stdot, stpwd;
352
 
        static int first = 1;
353
 
 
354
 
        if (curdir)
355
 
                return;
356
 
 
357
 
        if (first) {
358
 
                first = 0;
359
 
                pwd = getenv("PWD");
360
 
                if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
361
 
                    stat(pwd, &stpwd) != -1 &&
362
 
                    stdot.st_dev == stpwd.st_dev &&
363
 
                    stdot.st_ino == stpwd.st_ino) {
364
 
                        curdir = savestr(pwd);
365
 
                        return;
366
 
                }
367
 
        }
368
 
 
369
 
        find_curdir(noerror);
370
 
 
371
 
        return;
372
 
}
373
 
 
374
 
STATIC void
375
 
find_curdir(int noerror)
376
 
{
377
 
        int i;
378
 
        char *pwd;
379
 
 
380
 
        /*
381
 
         * Things are a bit complicated here; we could have just used
382
 
         * getcwd, but traditionally getcwd is implemented using popen
383
 
         * to /bin/pwd. This creates a problem for us, since we cannot
384
 
         * keep track of the job if it is being ran behind our backs.
385
 
         * So we re-implement getcwd(), and we suppress interrupts
386
 
         * throughout the process. This is not completely safe, since
387
 
         * the user can still break out of it by killing the pwd program.
388
 
         * We still try to use getcwd for systems that we know have a
389
 
         * c implementation of getcwd, that does not open a pipe to
390
 
         * /bin/pwd.
391
 
         */
392
 
#if defined(__NetBSD__) || defined(__SVR4) || defined(__linux__)
393
 
 
394
 
        for (i = MAXPWD;; i *= 2) {
395
 
                pwd = stalloc(i);
396
 
                if (getcwd(pwd, i) != NULL) {
397
 
                        curdir = savestr(pwd);
398
 
                        return;
399
 
                }
400
 
                stunalloc(pwd);
401
 
                if (errno == ERANGE)
402
 
                        continue;
403
 
                if (!noerror)
404
 
                        error("getcwd() failed: %s", strerror(errno));
405
 
                return;
406
 
        }
407
 
#else
408
 
        {
409
 
                char *p;
410
 
                int status;
411
 
                struct job *jp;
412
 
                int pip[2];
413
 
 
414
 
                pwd = stalloc(MAXPWD);
415
 
                INTOFF;
416
 
                if (pipe(pip) < 0)
417
 
                        error("Pipe call failed");
418
 
                jp = makejob((union node *)NULL, 1);
419
 
                if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
420
 
                        (void) close(pip[0]);
421
 
                        if (pip[1] != 1) {
422
 
                                close(1);
423
 
                                copyfd(pip[1], 1);
424
 
                                close(pip[1]);
425
 
                        }
426
 
                        (void) execl("/bin/pwd", "pwd", (char *)0);
427
 
                        error("Cannot exec /bin/pwd");
428
 
                }
429
 
                (void) close(pip[1]);
430
 
                pip[1] = -1;
431
 
                p = pwd;
432
 
                while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0
433
 
                     || (i == -1 && errno == EINTR)) {
434
 
                        if (i > 0)
435
 
                                p += i;
436
 
                }
437
 
                (void) close(pip[0]);
438
 
                pip[0] = -1;
439
 
                status = waitforjob(jp);
440
 
                if (status != 0)
441
 
                        error((char *)0);
442
 
                if (i < 0 || p == pwd || p[-1] != '\n') {
443
 
                        if (noerror) {
444
 
                                INTON;
445
 
                                return;
446
 
                        }
447
 
                        error("pwd command failed");
448
 
                }
449
 
                p[-1] = '\0';
450
 
                INTON;
451
 
                curdir = savestr(pwd);
452
 
                return;
453
 
        }
454
 
#endif
455
 
}