Subversion Repositories HelenOS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2714 cejka 1
/*  $NetBSD: cd.c,v 1.27 1999/07/09 03:05:49 christos 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. All advertising materials mentioning features or use of this software
19
 *    must display the following acknowledgement:
20
 *  This product includes software developed by the University of
21
 *  California, Berkeley and its contributors.
22
 * 4. Neither the name of the University nor the names of its contributors
23
 *    may be used to endorse or promote products derived from this software
24
 *    without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
 * SUCH DAMAGE.
37
 */
38
 
39
#include <sys/cdefs.h>
40
#ifndef lint
41
#if 0
42
static char sccsid[] = "@(#)cd.c    8.2 (Berkeley) 5/4/95";
43
#else
44
__RCSID("$NetBSD: cd.c,v 1.27 1999/07/09 03:05:49 christos Exp $");
45
#endif
46
#endif /* not lint */
47
 
48
#include <sys/types.h>
49
#include <sys/stat.h>
50
#include <stdlib.h>
51
#include <string.h>
52
#include <unistd.h>
53
#include <errno.h>
54
 
55
/*
56
 * The cd and pwd commands.
57
 */
58
 
59
#include "shell.h"
60
#include "var.h"
61
#include "nodes.h"  /* for jobs.h */
62
#include "jobs.h"
63
#include "options.h"
64
#include "output.h"
65
#include "memalloc.h"
66
#include "error.h"
67
#include "exec.h"
68
#include "redir.h"
69
#include "mystring.h"
70
#include "show.h"
71
#include "cd.h"
72
 
73
STATIC int docd (char *, int);
74
STATIC char *getcomponent (void);
75
STATIC void updatepwd (char *);
76
 
77
char *curdir = NULL;        /* current working directory */
78
char *prevdir;          /* previous working directory */
79
STATIC char *cdcomppath;
80
 
81
int
82
cdcmd(argc, argv)
83
    int argc;
84
    char **argv;
85
{
86
    const char *dest;
87
    const char *path;
88
    char *p;
89
    struct stat statb;
90
    int print = 0;
91
 
92
    nextopt(nullstr);
93
    if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
94
        error("HOME not set");
95
    if (*dest == '\0')
96
            dest = ".";
97
    if (dest[0] == '-' && dest[1] == '\0') {
98
        dest = prevdir ? prevdir : curdir;
99
        print = 1;
100
        if (dest)
101
                print = 1;
102
        else
103
                dest = ".";
104
    }
105
    if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
106
        path = nullstr;
107
    while ((p = padvance(&path, dest)) != NULL) {
108
        if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
109
            if (!print) {
110
                /*
111
                 * XXX - rethink
112
                 */
113
                if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
114
                    p += 2;
115
                print = strcmp(p, dest);
116
            }
117
            if (docd(p, print) >= 0)
118
                return 0;
119
 
120
        }
121
    }
122
    error("can't cd to %s", dest);
123
    /* NOTREACHED */
124
}
125
 
126
 
127
/*
128
 * Actually do the chdir.  In an interactive shell, print the
129
 * directory name if "print" is nonzero.
130
 */
131
 
132
STATIC int
133
docd(dest, print)
134
    char *dest;
135
    int print;
136
{
137
    char *p;
138
    char *q;
139
    char *component;
140
    struct stat statb;
141
    int first;
142
    int badstat;
143
 
144
    TRACE(("docd(\"%s\", %d) called\n", dest, print));
145
 
146
    /*
147
     *  Check each component of the path. If we find a symlink or
148
     *  something we can't stat, clear curdir to force a getcwd()
149
     *  next time we get the value of the current directory.
150
     */
151
    badstat = 0;
152
    cdcomppath = stalloc(strlen(dest) + 1);
153
    scopy(dest, cdcomppath);
154
    STARTSTACKSTR(p);
155
    if (*dest == '/') {
156
        STPUTC('/', p);
157
        cdcomppath++;
158
    }
159
    first = 1;
160
    while ((q = getcomponent()) != NULL) {
161
        if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
162
            continue;
163
        if (! first)
164
            STPUTC('/', p);
165
        first = 0;
166
        component = q;
167
        while (*q)
168
            STPUTC(*q++, p);
169
        if (equal(component, ".."))
170
            continue;
171
        STACKSTRNUL(p);
172
        if ((lstat(stackblock(), &statb) < 0)
173
            || (S_ISLNK(statb.st_mode)))  {
174
            /* print = 1; */
175
            badstat = 1;
176
            break;
177
        }
178
    }
179
 
180
    INTOFF;
181
    if (chdir(dest) < 0) {
182
        INTON;
183
        return -1;
184
    }
185
    updatepwd(badstat ? NULL : dest);
186
    INTON;
187
    if (print && iflag && curdir)
188
        out1fmt("%s\n", curdir);
189
    return 0;
190
}
191
 
192
 
193
/*
194
 * Get the next component of the path name pointed to by cdcomppath.
195
 * This routine overwrites the string pointed to by cdcomppath.
196
 */
197
 
198
STATIC char *
199
getcomponent() {
200
    char *p;
201
    char *start;
202
 
203
    if ((p = cdcomppath) == NULL)
204
        return NULL;
205
    start = cdcomppath;
206
    while (*p != '/' && *p != '\0')
207
        p++;
208
    if (*p == '\0') {
209
        cdcomppath = NULL;
210
    } else {
211
        *p++ = '\0';
212
        cdcomppath = p;
213
    }
214
    return start;
215
}
216
 
217
 
218
 
219
/*
220
 * Update curdir (the name of the current directory) in response to a
221
 * cd command.  We also call hashcd to let the routines in exec.c know
222
 * that the current directory has changed.
223
 */
224
 
225
STATIC void
226
updatepwd(dir)
227
    char *dir;
228
    {
229
    char *new;
230
    char *p;
231
 
232
    hashcd();               /* update command hash table */
233
 
234
    /*
235
     * If our argument is NULL, we don't know the current directory
236
     * any more because we traversed a symbolic link or something
237
     * we couldn't stat().
238
     */
239
    if (dir == NULL || curdir == NULL)  {
240
        if (prevdir)
241
            ckfree(prevdir);
242
        INTOFF;
243
        prevdir = curdir;
244
        curdir = NULL;
245
        getpwd();
246
        setvar("PWD", curdir, VEXPORT|VTEXTFIXED);
247
        setvar("OLDPWD", prevdir, VEXPORT|VTEXTFIXED);
248
        INTON;
249
        return;
250
    }
251
    cdcomppath = stalloc(strlen(dir) + 1);
252
    scopy(dir, cdcomppath);
253
    STARTSTACKSTR(new);
254
    if (*dir != '/') {
255
        p = curdir;
256
        while (*p)
257
            STPUTC(*p++, new);
258
        if (p[-1] == '/')
259
            STUNPUTC(new);
260
    }
261
    while ((p = getcomponent()) != NULL) {
262
        if (equal(p, "..")) {
263
            while (new > stackblock() && (STUNPUTC(new), *new) != '/');
264
        } else if (*p != '\0' && ! equal(p, ".")) {
265
            STPUTC('/', new);
266
            while (*p)
267
                STPUTC(*p++, new);
268
        }
269
    }
270
    if (new == stackblock())
271
        STPUTC('/', new);
272
    STACKSTRNUL(new);
273
    INTOFF;
274
    if (prevdir)
275
        ckfree(prevdir);
276
    prevdir = curdir;
277
    curdir = savestr(stackblock());
278
    setvar("PWD", curdir, VEXPORT|VTEXTFIXED);
279
    setvar("OLDPWD", prevdir, VEXPORT|VTEXTFIXED);
280
    INTON;
281
}
282
 
283
 
284
 
285
int
286
pwdcmd(argc, argv)
287
    int argc;
288
    char **argv;
289
{
290
    getpwd();
291
    out1str(curdir);
292
    out1c('\n');
293
    return 0;
294
}
295
 
296
 
297
 
298
 
299
#define MAXPWD 256
300
 
301
/*
302
 * Find out what the current directory is. If we already know the current
303
 * directory, this routine returns immediately.
304
 */
305
void
306
getpwd()
307
{
308
    char buf[MAXPWD];
309
 
310
    if (curdir)
311
        return;
312
    /*
313
     * Things are a bit complicated here; we could have just used
314
     * getcwd, but traditionally getcwd is implemented using popen
315
     * to /bin/pwd. This creates a problem for us, since we cannot
316
     * keep track of the job if it is being ran behind our backs.
317
     * So we re-implement getcwd(), and we suppress interrupts
318
     * throughout the process. This is not completely safe, since
319
     * the user can still break out of it by killing the pwd program.
320
     * We still try to use getcwd for systems that we know have a
321
     * c implementation of getcwd, that does not open a pipe to
322
     * /bin/pwd.
323
     */
324
#if defined(__NetBSD__) || defined(__SVR4) || defined(__GLIBC__)
325
 
326
    if (getcwd(buf, sizeof(buf)) == NULL) {
327
        char *pwd = getenv("PWD");
328
        struct stat stdot, stpwd;
329
 
330
        if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
331
            stat(pwd, &stpwd) != -1 &&
332
            stdot.st_dev == stpwd.st_dev &&
333
            stdot.st_ino == stpwd.st_ino) {
334
            curdir = savestr(pwd);
335
            return;
336
        }
337
        error("getcwd() failed: %s", strerror(errno));
338
    }
339
    curdir = savestr(buf);
340
#else
341
    {
342
        char *p;
343
        int i;
344
        int status;
345
        struct job *jp;
346
        int pip[2];
347
 
348
        INTOFF;
349
        if (pipe(pip) < 0)
350
            error("Pipe call failed");
351
        jp = makejob((union node *)NULL, 1);
352
        if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
353
            (void) close(pip[0]);
354
            if (pip[1] != 1) {
355
                close(1);
356
                copyfd(pip[1], 1);
357
                close(pip[1]);
358
            }
359
            (void) execl("/bin/pwd", "pwd", (char *)0);
360
            error("Cannot exec /bin/pwd");
361
        }
362
        (void) 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
        (void) 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
    }
379
    curdir = savestr(buf);
380
    INTON;
381
#endif
382
}