Subversion Repositories HelenOS

Rev

Rev 2714 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2714 Rev 2782
1
/*  $NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $   */
1
/*  $NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $   */
2
 
2
 
3
/*-
3
/*-
4
 * Copyright (c) 1991, 1993
4
 * Copyright (c) 1991, 1993
5
 *  The Regents of the University of California.  All rights reserved.
5
 *  The Regents of the University of California.  All rights reserved.
6
 *
6
 *
7
 * This code is derived from software contributed to Berkeley by
7
 * This code is derived from software contributed to Berkeley by
8
 * Kenneth Almquist.
8
 * Kenneth Almquist.
9
 *
9
 *
10
 * Redistribution and use in source and binary forms, with or without
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
11
 * modification, are permitted provided that the following conditions
12
 * are met:
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
17
 *    documentation and/or other materials provided with the distribution.
18
 * 3. All advertising materials mentioning features or use of this software
18
 * 3. All advertising materials mentioning features or use of this software
19
 *    must display the following acknowledgement:
19
 *    must display the following acknowledgement:
20
 *  This product includes software developed by the University of
20
 *  This product includes software developed by the University of
21
 *  California, Berkeley and its contributors.
21
 *  California, Berkeley and its contributors.
22
 * 4. Neither the name of the University nor the names of 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
23
 *    may be used to endorse or promote products derived from this software
24
 *    without specific prior written permission.
24
 *    without specific prior written permission.
25
 *
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
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
27
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
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
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
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
35
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
 * SUCH DAMAGE.
36
 * SUCH DAMAGE.
37
 */
37
 */
38
 
38
 
39
#include <sys/cdefs.h>
39
#include <sys/cdefs.h>
40
#ifndef lint
40
#ifndef lint
41
#if 0
41
#if 0
42
static char sccsid[] = "@(#)exec.c  8.4 (Berkeley) 6/8/95";
42
static char sccsid[] = "@(#)exec.c  8.4 (Berkeley) 6/8/95";
43
#else
43
#else
44
__RCSID("$NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $");
44
__RCSID("$NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $");
45
#endif
45
#endif
46
#endif /* not lint */
46
#endif /* not lint */
47
 
47
 
48
#include <sys/types.h>
48
#include <sys/types.h>
49
#include <sys/stat.h>
49
#include <sys/stat.h>
50
#include <unistd.h>
50
#include <unistd.h>
51
#include <fcntl.h>
51
#include <fcntl.h>
52
#include <errno.h>
52
#include <errno.h>
53
#include <stdlib.h>
53
#include <stdlib.h>
54
#include <sysexits.h>
54
#include <sysexits.h>
55
 
55
 
56
/*
56
/*
57
 * When commands are first encountered, they are entered in a hash table.
57
 * When commands are first encountered, they are entered in a hash table.
58
 * This ensures that a full path search will not have to be done for them
58
 * This ensures that a full path search will not have to be done for them
59
 * on each invocation.
59
 * on each invocation.
60
 *
60
 *
61
 * We should investigate converting to a linear search, even though that
61
 * We should investigate converting to a linear search, even though that
62
 * would make the command name "hash" a misnomer.
62
 * would make the command name "hash" a misnomer.
63
 */
63
 */
64
 
64
 
65
#include "shell.h"
65
#include "shell.h"
66
#include "main.h"
66
#include "main.h"
67
#include "nodes.h"
67
#include "nodes.h"
68
#include "parser.h"
68
#include "parser.h"
69
#include "redir.h"
69
#include "redir.h"
70
#include "eval.h"
70
#include "eval.h"
71
#include "exec.h"
71
#include "exec.h"
72
#include "builtins.h"
72
#include "builtins.h"
73
#include "var.h"
73
#include "var.h"
74
#include "options.h"
74
#include "options.h"
75
#include "input.h"
75
#include "input.h"
76
#include "output.h"
76
#include "output.h"
77
#include "syntax.h"
77
#include "syntax.h"
78
#include "memalloc.h"
78
#include "memalloc.h"
79
#include "error.h"
79
#include "error.h"
80
#include "init.h"
80
#include "init.h"
81
#include "mystring.h"
81
#include "mystring.h"
82
#include "show.h"
82
#include "show.h"
83
#include "jobs.h"
83
#include "jobs.h"
84
#include "alias.h"
84
#include "alias.h"
85
 
85
 
86
 
86
 
87
#define CMDTABLESIZE 31     /* should be prime */
87
#define CMDTABLESIZE 31     /* should be prime */
88
#define ARB 1           /* actual size determined at run time */
88
#define ARB 1           /* actual size determined at run time */
89
 
89
 
90
 
90
 
91
 
91
 
92
struct tblentry {
92
struct tblentry {
93
    struct tblentry *next;  /* next entry in hash chain */
93
    struct tblentry *next;  /* next entry in hash chain */
94
    union param param;  /* definition of builtin function */
94
    union param param;  /* definition of builtin function */
95
    short cmdtype;      /* index identifying command */
95
    short cmdtype;      /* index identifying command */
96
    char rehash;        /* if set, cd done since entry created */
96
    char rehash;        /* if set, cd done since entry created */
97
    char cmdname[ARB];  /* name of command */
97
    char cmdname[ARB];  /* name of command */
98
};
98
};
99
 
99
 
100
 
100
 
101
STATIC struct tblentry *cmdtable[CMDTABLESIZE];
101
STATIC struct tblentry *cmdtable[CMDTABLESIZE];
102
STATIC int builtinloc = -1;     /* index in path of %builtin, or -1 */
102
STATIC int builtinloc = -1;     /* index in path of %builtin, or -1 */
103
int exerrno = 0;            /* Last exec error */
103
int exerrno = 0;            /* Last exec error */
104
 
104
 
105
 
105
 
106
STATIC void tryexec (char *, char **, char **);
106
STATIC void tryexec (char *, char **, char **);
107
STATIC void execinterp (char **, char **);
107
STATIC void execinterp (char **, char **);
108
STATIC void printentry (struct tblentry *, int);
108
STATIC void printentry (struct tblentry *, int);
109
STATIC void clearcmdentry (int);
109
STATIC void clearcmdentry (int);
110
STATIC struct tblentry *cmdlookup (char *, int);
110
STATIC struct tblentry *cmdlookup (char *, int);
111
STATIC void delete_cmd_entry (void);
111
STATIC void delete_cmd_entry (void);
112
STATIC int describe_command (char *, int);
112
STATIC int describe_command (char *, int);
113
STATIC int path_change (const char *, int *);
113
STATIC int path_change (const char *, int *);
114
STATIC int is_regular_builtin (const char *);
114
STATIC int is_regular_builtin (const char *);
115
 
115
 
116
 
116
 
117
 
117
 
118
/*
118
/*
119
 * Exec a program.  Never returns.  If you change this routine, you may
119
 * Exec a program.  Never returns.  If you change this routine, you may
120
 * have to change the find_command routine as well.
120
 * have to change the find_command routine as well.
121
 */
121
 */
122
 
122
 
123
void
123
void
124
shellexec(argv, envp, path, idx)
124
shellexec(argv, envp, path, idx)
125
    char **argv, **envp;
125
    char **argv, **envp;
126
    const char *path;
126
    const char *path;
127
    int idx;
127
    int idx;
128
{
128
{
129
    char *cmdname;
129
    char *cmdname;
130
    int e;
130
    int e;
131
 
131
 
132
    if (fd2 >= 0 && fd2 != 2) {
132
    if (fd2 >= 0 && fd2 != 2) {
133
        close(fd2);
133
        close(fd2);
134
    }
134
    }
135
 
135
 
136
    if (strchr(argv[0], '/') != NULL) {
136
    if (strchr(argv[0], '/') != NULL) {
137
        tryexec(argv[0], argv, envp);
137
        tryexec(argv[0], argv, envp);
138
        e = errno;
138
        e = errno;
139
    } else {
139
    } else {
140
        e = ENOENT;
140
        e = ENOENT;
141
        while ((cmdname = padvance(&path, argv[0])) != NULL) {
141
        while ((cmdname = padvance(&path, argv[0])) != NULL) {
142
            if (--idx < 0 && pathopt == NULL) {
142
            if (--idx < 0 && pathopt == NULL) {
143
                tryexec(cmdname, argv, envp);
143
                tryexec(cmdname, argv, envp);
144
                if (errno != ENOENT && errno != ENOTDIR)
144
                if (errno != ENOENT && errno != ENOTDIR)
145
                    e = errno;
145
                    e = errno;
146
            }
146
            }
147
            stunalloc(cmdname);
147
            stunalloc(cmdname);
148
        }
148
        }
149
    }
149
    }
150
 
150
 
151
    /* Map to POSIX errors */
151
    /* Map to POSIX errors */
152
    switch (e) {
152
    switch (e) {
153
    case EACCES:
153
    case EACCES:
154
        exerrno = 126;
154
        exerrno = 126;
155
        break;
155
        break;
156
    case ENOENT:
156
    case ENOENT:
157
        exerrno = 127;
157
        exerrno = 127;
158
        break;
158
        break;
159
    default:
159
    default:
160
        exerrno = 2;
160
        exerrno = 2;
161
        break;
161
        break;
162
    }
162
    }
163
    exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
163
    exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
164
    /* NOTREACHED */
164
    /* NOTREACHED */
165
}
165
}
166
 
166
 
167
 
167
 
168
STATIC void
168
STATIC void
169
tryexec(cmd, argv, envp)
169
tryexec(cmd, argv, envp)
170
    char *cmd;
170
    char *cmd;
171
    char **argv;
171
    char **argv;
172
    char **envp;
172
    char **envp;
173
    {
173
    {
174
    int e;
174
    int e;
175
#if !defined(BSD) && !defined(linux)
175
#if !defined(BSD) && !defined(linux)
176
    char *p;
176
    char *p;
177
#endif
177
#endif
178
 
178
 
179
#ifdef SYSV
179
#ifdef SYSV
180
    do {
180
    do {
181
        execve(cmd, argv, envp);
181
        execve(cmd, argv, envp);
182
    } while (errno == EINTR);
182
    } while (errno == EINTR);
183
#else
183
#else
184
    execve(cmd, argv, envp);
184
    execve(cmd, argv, envp);
185
#endif
185
#endif
186
    e = errno;
186
    e = errno;
187
    if (e == ENOEXEC) {
187
    if (e == ENOEXEC) {
188
        initshellproc();
188
        initshellproc();
189
        setinputfile(cmd, 0);
189
        setinputfile(cmd, 0);
190
        commandname = arg0 = savestr(argv[0]);
190
        commandname = arg0 = savestr(argv[0]);
191
#if !defined(BSD) && !defined(linux)
191
#if !defined(BSD) && !defined(linux)
192
        pgetc(); pungetc();     /* fill up input buffer */
192
        pgetc(); pungetc();     /* fill up input buffer */
193
        p = parsenextc;
193
        p = parsenextc;
194
        if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
194
        if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
195
            argv[0] = cmd;
195
            argv[0] = cmd;
196
            execinterp(argv, envp);
196
            execinterp(argv, envp);
197
        }
197
        }
198
#endif
198
#endif
199
        setparam(argv + 1);
199
        setparam(argv + 1);
200
        exraise(EXSHELLPROC);
200
        exraise(EXSHELLPROC);
201
    }
201
    }
202
    errno = e;
202
    errno = e;
203
}
203
}
204
 
204
 
205
 
205
 
206
#if !defined(BSD) && !defined(linux)
206
#if !defined(BSD) && !defined(linux)
207
/*
207
/*
208
 * Execute an interpreter introduced by "#!", for systems where this
208
 * Execute an interpreter introduced by "#!", for systems where this
209
 * feature has not been built into the kernel.  If the interpreter is
209
 * feature has not been built into the kernel.  If the interpreter is
210
 * the shell, return (effectively ignoring the "#!").  If the execution
210
 * the shell, return (effectively ignoring the "#!").  If the execution
211
 * of the interpreter fails, exit.
211
 * of the interpreter fails, exit.
212
 *
212
 *
213
 * This code peeks inside the input buffer in order to avoid actually
213
 * This code peeks inside the input buffer in order to avoid actually
214
 * reading any input.  It would benefit from a rewrite.
214
 * reading any input.  It would benefit from a rewrite.
215
 */
215
 */
216
 
216
 
217
#define NEWARGS 5
217
#define NEWARGS 5
218
 
218
 
219
STATIC void
219
STATIC void
220
execinterp(argv, envp)
220
execinterp(argv, envp)
221
    char **argv, **envp;
221
    char **argv, **envp;
222
    {
222
    {
223
    int n;
223
    int n;
224
    char *inp;
224
    char *inp;
225
    char *outp;
225
    char *outp;
226
    char c;
226
    char c;
227
    char *p;
227
    char *p;
228
    char **ap;
228
    char **ap;
229
    char *newargs[NEWARGS];
229
    char *newargs[NEWARGS];
230
    int i;
230
    int i;
231
    char **ap2;
231
    char **ap2;
232
    char **new;
232
    char **new;
233
 
233
 
234
    n = parsenleft - 2;
234
    n = parsenleft - 2;
235
    inp = parsenextc + 2;
235
    inp = parsenextc + 2;
236
    ap = newargs;
236
    ap = newargs;
237
    for (;;) {
237
    for (;;) {
238
        while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
238
        while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
239
            inp++;
239
            inp++;
240
        if (n < 0)
240
        if (n < 0)
241
            goto bad;
241
            goto bad;
242
        if ((c = *inp++) == '\n')
242
        if ((c = *inp++) == '\n')
243
            break;
243
            break;
244
        if (ap == &newargs[NEWARGS])
244
        if (ap == &newargs[NEWARGS])
245
bad:          error("Bad #! line");
245
bad:          error("Bad #! line");
246
        STARTSTACKSTR(outp);
246
        STARTSTACKSTR(outp);
247
        do {
247
        do {
248
            STPUTC(c, outp);
248
            STPUTC(c, outp);
249
        } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
249
        } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
250
        STPUTC('\0', outp);
250
        STPUTC('\0', outp);
251
        n++, inp--;
251
        n++, inp--;
252
        *ap++ = grabstackstr(outp);
252
        *ap++ = grabstackstr(outp);
253
    }
253
    }
254
    if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
254
    if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
255
        p = newargs[0];
255
        p = newargs[0];
256
        for (;;) {
256
        for (;;) {
257
            if (equal(p, "sh") || equal(p, "ash")) {
257
            if (equal(p, "sh") || equal(p, "ash")) {
258
                return;
258
                return;
259
            }
259
            }
260
            while (*p != '/') {
260
            while (*p != '/') {
261
                if (*p == '\0')
261
                if (*p == '\0')
262
                    goto break2;
262
                    goto break2;
263
                p++;
263
                p++;
264
            }
264
            }
265
            p++;
265
            p++;
266
        }
266
        }
267
break2:;
267
break2:;
268
    }
268
    }
269
    i = (char *)ap - (char *)newargs;       /* size in bytes */
269
    i = (char *)ap - (char *)newargs;       /* size in bytes */
270
    if (i == 0)
270
    if (i == 0)
271
        error("Bad #! line");
271
        error("Bad #! line");
272
    for (ap2 = argv ; *ap2++ != NULL ; );
272
    for (ap2 = argv ; *ap2++ != NULL ; );
273
    new = ckmalloc(i + ((char *)ap2 - (char *)argv));
273
    new = ckmalloc(i + ((char *)ap2 - (char *)argv));
274
    ap = newargs, ap2 = new;
274
    ap = newargs, ap2 = new;
275
    while ((i -= sizeof (char **)) >= 0)
275
    while ((i -= sizeof (char **)) >= 0)
276
        *ap2++ = *ap++;
276
        *ap2++ = *ap++;
277
    ap = argv;
277
    ap = argv;
278
    while (*ap2++ = *ap++);
278
    while (*ap2++ = *ap++);
279
    shellexec(new, envp, pathval(), 0);
279
    shellexec(new, envp, pathval(), 0);
280
    /* NOTREACHED */
280
    /* NOTREACHED */
281
}
281
}
282
#endif
282
#endif
283
 
283
 
284
 
284
 
285
 
285
 
286
/*
286
/*
287
 * Do a path search.  The variable path (passed by reference) should be
287
 * Do a path search.  The variable path (passed by reference) should be
288
 * set to the start of the path before the first call; padvance will update
288
 * set to the start of the path before the first call; padvance will update
289
 * this value as it proceeds.  Successive calls to padvance will return
289
 * this value as it proceeds.  Successive calls to padvance will return
290
 * the possible path expansions in sequence.  If an option (indicated by
290
 * the possible path expansions in sequence.  If an option (indicated by
291
 * a percent sign) appears in the path entry then the global variable
291
 * a percent sign) appears in the path entry then the global variable
292
 * pathopt will be set to point to it; otherwise pathopt will be set to
292
 * pathopt will be set to point to it; otherwise pathopt will be set to
293
 * NULL.
293
 * NULL.
294
 */
294
 */
295
 
295
 
296
const char *pathopt;
296
const char *pathopt;
297
 
297
 
298
char *
298
char *
299
padvance(path, name)
299
padvance(path, name)
300
    const char **path;
300
    const char **path;
301
    const char *name;
301
    const char *name;
302
    {
302
    {
303
    const char *p;
303
    const char *p;
304
    char *q;
304
    char *q;
305
    const char *start;
305
    const char *start;
306
    int len;
306
    int len;
307
 
307
 
308
    if (*path == NULL)
308
    if (*path == NULL)
309
        return NULL;
309
        return NULL;
310
    start = *path;
310
    start = *path;
311
    for (p = start ; *p && *p != ':' && *p != '%' ; p++);
311
    for (p = start ; *p && *p != ':' && *p != '%' ; p++);
312
    len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
312
    len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
313
    while (stackblocksize() < len)
313
    while (stackblocksize() < len)
314
        growstackblock();
314
        growstackblock();
315
    q = stackblock();
315
    q = stackblock();
316
    if (p != start) {
316
    if (p != start) {
317
        memcpy(q, start, p - start);
317
        memcpy(q, start, p - start);
318
        q += p - start;
318
        q += p - start;
319
        *q++ = '/';
319
        *q++ = '/';
320
    }
320
    }
321
    strcpy(q, name);
321
    strcpy(q, name);
322
    pathopt = NULL;
322
    pathopt = NULL;
323
    if (*p == '%') {
323
    if (*p == '%') {
324
        pathopt = ++p;
324
        pathopt = ++p;
325
        while (*p && *p != ':')  p++;
325
        while (*p && *p != ':')  p++;
326
    }
326
    }
327
    if (*p == ':')
327
    if (*p == ':')
328
        *path = p + 1;
328
        *path = p + 1;
329
    else
329
    else
330
        *path = NULL;
330
        *path = NULL;
331
    return stalloc(len);
331
    return stalloc(len);
332
}
332
}
333
 
333
 
334
 
334
 
335
 
335
 
336
/*** Command hashing code ***/
336
/*** Command hashing code ***/
337
 
337
 
338
 
338
 
339
int
339
int
340
hashcmd(argc, argv)
340
hashcmd(argc, argv)
341
    int argc;
341
    int argc;
342
    char **argv;
342
    char **argv;
343
{
343
{
344
    struct tblentry **pp;
344
    struct tblentry **pp;
345
    struct tblentry *cmdp;
345
    struct tblentry *cmdp;
346
    int c;
346
    int c;
347
    int verbose;
347
    int verbose;
348
    struct cmdentry entry;
348
    struct cmdentry entry;
349
    char *name;
349
    char *name;
350
 
350
 
351
    verbose = 0;
351
    verbose = 0;
352
    while ((c = nextopt("rv")) != '\0') {
352
    while ((c = nextopt("rv")) != '\0') {
353
        if (c == 'r') {
353
        if (c == 'r') {
354
            clearcmdentry(0);
354
            clearcmdentry(0);
355
        } else if (c == 'v') {
355
        } else if (c == 'v') {
356
            verbose++;
356
            verbose++;
357
        }
357
        }
358
    }
358
    }
359
    if (*argptr == NULL) {
359
    if (*argptr == NULL) {
360
        for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
360
        for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
361
            for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
361
            for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
362
                if (cmdp->cmdtype != CMDBUILTIN) {
362
                if (cmdp->cmdtype != CMDBUILTIN) {
363
                    printentry(cmdp, verbose);
363
                    printentry(cmdp, verbose);
364
                }
364
                }
365
            }
365
            }
366
        }
366
        }
367
        return 0;
367
        return 0;
368
    }
368
    }
369
    c = 0;
369
    c = 0;
370
    while ((name = *argptr) != NULL) {
370
    while ((name = *argptr) != NULL) {
371
        if ((cmdp = cmdlookup(name, 0)) != NULL
371
        if ((cmdp = cmdlookup(name, 0)) != NULL
372
         && (cmdp->cmdtype == CMDNORMAL
372
         && (cmdp->cmdtype == CMDNORMAL
373
             || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
373
             || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
374
            delete_cmd_entry();
374
            delete_cmd_entry();
375
        find_command(name, &entry, DO_ERR, pathval());
375
        find_command(name, &entry, DO_ERR, pathval());
376
        if (entry.cmdtype == CMDUNKNOWN) c = 1;
376
        if (entry.cmdtype == CMDUNKNOWN) c = 1;
377
        else if (verbose) {
377
        else if (verbose) {
378
            cmdp = cmdlookup(name, 0);
378
            cmdp = cmdlookup(name, 0);
379
            if (cmdp) printentry(cmdp, verbose);
379
            if (cmdp) printentry(cmdp, verbose);
380
            flushall();
380
            flushall();
381
        }
381
        }
382
        argptr++;
382
        argptr++;
383
    }
383
    }
384
    return c;
384
    return c;
385
}
385
}
386
 
386
 
387
 
387
 
388
STATIC void
388
STATIC void
389
printentry(cmdp, verbose)
389
printentry(cmdp, verbose)
390
    struct tblentry *cmdp;
390
    struct tblentry *cmdp;
391
    int verbose;
391
    int verbose;
392
    {
392
    {
393
    int idx;
393
    int idx;
394
    const char *path;
394
    const char *path;
395
    char *name;
395
    char *name;
396
 
396
 
397
    if (cmdp->cmdtype == CMDNORMAL) {
397
    if (cmdp->cmdtype == CMDNORMAL) {
398
        idx = cmdp->param.index;
398
        idx = cmdp->param.index;
399
        path = pathval();
399
        path = pathval();
400
        do {
400
        do {
401
            name = padvance(&path, cmdp->cmdname);
401
            name = padvance(&path, cmdp->cmdname);
402
            stunalloc(name);
402
            stunalloc(name);
403
        } while (--idx >= 0);
403
        } while (--idx >= 0);
404
        out1str(name);
404
        out1str(name);
405
    } else if (cmdp->cmdtype == CMDBUILTIN) {
405
    } else if (cmdp->cmdtype == CMDBUILTIN) {
406
        out1fmt("builtin %s", cmdp->cmdname);
406
        out1fmt("builtin %s", cmdp->cmdname);
407
    } else if (cmdp->cmdtype == CMDFUNCTION) {
407
    } else if (cmdp->cmdtype == CMDFUNCTION) {
408
        out1fmt("function %s", cmdp->cmdname);
408
        out1fmt("function %s", cmdp->cmdname);
409
        if (verbose) {
409
        if (verbose) {
410
            INTOFF;
410
            INTOFF;
411
            name = commandtext(cmdp->param.func);
411
            name = commandtext(cmdp->param.func);
412
            out1c(' ');
412
            out1c(' ');
413
            out1str(name);
413
            out1str(name);
414
            ckfree(name);
414
            ckfree(name);
415
            INTON;
415
            INTON;
416
        }
416
        }
417
#ifdef DEBUG
417
#ifdef DEBUG
418
    } else {
418
    } else {
419
        error("internal error: cmdtype %d", cmdp->cmdtype);
419
        error("internal error: cmdtype %d", cmdp->cmdtype);
420
#endif
420
#endif
421
    }
421
    }
422
    if (cmdp->rehash)
422
    if (cmdp->rehash)
423
        out1c('*');
423
        out1c('*');
424
    out1c('\n');
424
    out1c('\n');
425
}
425
}
426
 
426
 
427
 
427
 
428
 
428
 
429
/*
429
/*
430
 * Resolve a command name.  If you change this routine, you may have to
430
 * Resolve a command name.  If you change this routine, you may have to
431
 * change the shellexec routine as well.
431
 * change the shellexec routine as well.
432
 */
432
 */
433
 
433
 
434
void
434
void
435
find_command(name, entry, act, path)
435
find_command(name, entry, act, path)
436
    char *name;
436
    char *name;
437
    struct cmdentry *entry;
437
    struct cmdentry *entry;
438
    int act;
438
    int act;
439
    const char *path;
439
    const char *path;
440
{
440
{
441
    struct tblentry *cmdp;
441
    struct tblentry *cmdp;
442
    int idx;
442
    int idx;
443
    int prev;
443
    int prev;
444
    char *fullname;
444
    char *fullname;
445
    struct stat statb;
445
    struct stat statb;
446
    int e;
446
    int e;
447
    int i;
447
    int i;
448
    int bltin;
448
    int bltin;
449
    int firstchange;
449
    int firstchange;
450
    int updatetbl;
450
    int updatetbl;
451
    int regular;
451
    int regular;
452
 
452
 
453
    /* If name contains a slash, don't use the hash table */
453
    /* If name contains a slash, don't use the hash table */
454
    if (strchr(name, '/') != NULL) {
454
    if (strchr(name, '/') != NULL) {
455
        if (act & DO_ABS) {
455
        if (act & DO_ABS) {
456
            while (stat(name, &statb) < 0) {
456
            while (stat(name, &statb) < 0) {
457
    #ifdef SYSV
457
    #ifdef SYSV
458
                if (errno == EINTR)
458
                if (errno == EINTR)
459
                    continue;
459
                    continue;
460
    #endif
460
    #endif
461
                if (errno != ENOENT && errno != ENOTDIR)
461
                if (errno != ENOENT && errno != ENOTDIR)
462
                    e = errno;
462
                    e = errno;
463
                entry->cmdtype = CMDUNKNOWN;
463
                entry->cmdtype = CMDUNKNOWN;
464
                entry->u.index = -1;
464
                entry->u.index = -1;
465
                return;
465
                return;
466
            }
466
            }
467
            entry->cmdtype = CMDNORMAL;
467
            entry->cmdtype = CMDNORMAL;
468
            entry->u.index = -1;
468
            entry->u.index = -1;
469
            return;
469
            return;
470
        }
470
        }
471
        entry->cmdtype = CMDNORMAL;
471
        entry->cmdtype = CMDNORMAL;
472
        entry->u.index = 0;
472
        entry->u.index = 0;
473
        return;
473
        return;
474
    }
474
    }
475
 
475
 
476
    updatetbl = 1;
476
    updatetbl = 1;
477
    if (act & DO_BRUTE) {
477
    if (act & DO_BRUTE) {
478
        firstchange = path_change(path, &bltin);
478
        firstchange = path_change(path, &bltin);
479
    } else {
479
    } else {
480
        bltin = builtinloc;
480
        bltin = builtinloc;
481
        firstchange = 9999;
481
        firstchange = 9999;
482
    }
482
    }
483
 
483
 
484
    /* If name is in the table, and not invalidated by cd, we're done */
484
    /* If name is in the table, and not invalidated by cd, we're done */
485
    if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
485
    if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
486
        if (cmdp->cmdtype == CMDFUNCTION) {
486
        if (cmdp->cmdtype == CMDFUNCTION) {
487
            if (act & DO_NOFUN) {
487
            if (act & DO_NOFUN) {
488
                updatetbl = 0;
488
                updatetbl = 0;
489
            } else {
489
            } else {
490
                goto success;
490
                goto success;
491
            }
491
            }
492
        } else if (act & DO_BRUTE) {
492
        } else if (act & DO_BRUTE) {
493
            if ((cmdp->cmdtype == CMDNORMAL &&
493
            if ((cmdp->cmdtype == CMDNORMAL &&
494
                 cmdp->param.index >= firstchange) ||
494
                 cmdp->param.index >= firstchange) ||
495
                (cmdp->cmdtype == CMDBUILTIN &&
495
                (cmdp->cmdtype == CMDBUILTIN &&
496
                 ((builtinloc < 0 && bltin >= 0) ?
496
                 ((builtinloc < 0 && bltin >= 0) ?
497
                  bltin : builtinloc) >= firstchange)) {
497
                  bltin : builtinloc) >= firstchange)) {
498
                /* need to recompute the entry */
498
                /* need to recompute the entry */
499
            } else {
499
            } else {
500
                goto success;
500
                goto success;
501
            }
501
            }
502
        } else {
502
        } else {
503
            goto success;
503
            goto success;
504
        }
504
        }
505
    }
505
    }
506
 
506
 
507
    if ((regular = is_regular_builtin(name))) {
507
    if ((regular = is_regular_builtin(name))) {
508
        if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
508
        if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
509
                goto success;
509
                goto success;
510
        }
510
        }
511
    } else if (act & DO_BRUTE) {
511
    } else if (act & DO_BRUTE) {
512
        if (firstchange == 0) {
512
        if (firstchange == 0) {
513
            updatetbl = 0;
513
            updatetbl = 0;
514
        }
514
        }
515
    }
515
    }
516
 
516
 
517
    /* If %builtin not in path, check for builtin next */
517
    /* If %builtin not in path, check for builtin next */
518
    if ((bltin < 0 || regular) && (i = find_builtin(name)) >= 0) {
518
    if ((bltin < 0 || regular) && (i = find_builtin(name)) >= 0) {
519
        if (!updatetbl) {
519
        if (!updatetbl) {
520
            entry->cmdtype = CMDBUILTIN;
520
            entry->cmdtype = CMDBUILTIN;
521
            entry->u.index = i;
521
            entry->u.index = i;
522
            return;
522
            return;
523
        }
523
        }
524
        INTOFF;
524
        INTOFF;
525
        cmdp = cmdlookup(name, 1);
525
        cmdp = cmdlookup(name, 1);
526
        cmdp->cmdtype = CMDBUILTIN;
526
        cmdp->cmdtype = CMDBUILTIN;
527
        cmdp->param.index = i;
527
        cmdp->param.index = i;
528
        INTON;
528
        INTON;
529
        goto success;
529
        goto success;
530
    }
530
    }
531
 
531
 
532
    /* We have to search path. */
532
    /* We have to search path. */
533
    prev = -1;      /* where to start */
533
    prev = -1;      /* where to start */
534
    if (cmdp && cmdp->rehash) { /* doing a rehash */
534
    if (cmdp && cmdp->rehash) { /* doing a rehash */
535
        if (cmdp->cmdtype == CMDBUILTIN)
535
        if (cmdp->cmdtype == CMDBUILTIN)
536
            prev = builtinloc;
536
            prev = builtinloc;
537
        else
537
        else
538
            prev = cmdp->param.index;
538
            prev = cmdp->param.index;
539
    }
539
    }
540
 
540
 
541
    e = ENOENT;
541
    e = ENOENT;
542
    idx = -1;
542
    idx = -1;
543
loop:
543
loop:
544
    while ((fullname = padvance(&path, name)) != NULL) {
544
    while ((fullname = padvance(&path, name)) != NULL) {
545
        stunalloc(fullname);
545
        stunalloc(fullname);
546
        idx++;
546
        idx++;
547
        if (idx >= firstchange) {
547
        if (idx >= firstchange) {
548
            updatetbl = 0;
548
            updatetbl = 0;
549
        }
549
        }
550
        if (pathopt) {
550
        if (pathopt) {
551
            if (prefix("builtin", pathopt)) {
551
            if (prefix("builtin", pathopt)) {
552
                if ((i = find_builtin(name)) >= 0) {
552
                if ((i = find_builtin(name)) >= 0) {
553
                    if (!updatetbl) {
553
                    if (!updatetbl) {
554
                        entry->cmdtype = CMDBUILTIN;
554
                        entry->cmdtype = CMDBUILTIN;
555
                        entry->u.index = i;
555
                        entry->u.index = i;
556
                        return;
556
                        return;
557
                    }
557
                    }
558
                    INTOFF;
558
                    INTOFF;
559
                    cmdp = cmdlookup(name, 1);
559
                    cmdp = cmdlookup(name, 1);
560
                    cmdp->cmdtype = CMDBUILTIN;
560
                    cmdp->cmdtype = CMDBUILTIN;
561
                    cmdp->param.index = i;
561
                    cmdp->param.index = i;
562
                    INTON;
562
                    INTON;
563
                    goto success;
563
                    goto success;
564
                } else {
564
                } else {
565
                    continue;
565
                    continue;
566
                }
566
                }
567
            } else if (!(act & DO_NOFUN) &&
567
            } else if (!(act & DO_NOFUN) &&
568
                   prefix("func", pathopt)) {
568
                   prefix("func", pathopt)) {
569
                /* handled below */
569
                /* handled below */
570
            } else {
570
            } else {
571
                continue;   /* ignore unimplemented options */
571
                continue;   /* ignore unimplemented options */
572
            }
572
            }
573
        }
573
        }
574
        /* if rehash, don't redo absolute path names */
574
        /* if rehash, don't redo absolute path names */
575
        if (fullname[0] == '/' && idx <= prev &&
575
        if (fullname[0] == '/' && idx <= prev &&
576
            idx < firstchange) {
576
            idx < firstchange) {
577
            if (idx < prev)
577
            if (idx < prev)
578
                continue;
578
                continue;
579
            TRACE(("searchexec \"%s\": no change\n", name));
579
            TRACE(("searchexec \"%s\": no change\n", name));
580
            goto success;
580
            goto success;
581
        }
581
        }
582
        while (stat(fullname, &statb) < 0) {
582
        while (stat(fullname, &statb) < 0) {
583
#ifdef SYSV
583
#ifdef SYSV
584
            if (errno == EINTR)
584
            if (errno == EINTR)
585
                continue;
585
                continue;
586
#endif
586
#endif
587
            if (errno != ENOENT && errno != ENOTDIR)
587
            if (errno != ENOENT && errno != ENOTDIR)
588
                e = errno;
588
                e = errno;
589
            goto loop;
589
            goto loop;
590
        }
590
        }
591
        e = EACCES; /* if we fail, this will be the error */
591
        e = EACCES; /* if we fail, this will be the error */
592
        if (!S_ISREG(statb.st_mode))
592
        if (!S_ISREG(statb.st_mode))
593
            continue;
593
            continue;
594
        if (pathopt) {      /* this is a %func directory */
594
        if (pathopt) {      /* this is a %func directory */
595
            stalloc(strlen(fullname) + 1);
595
            stalloc(strlen(fullname) + 1);
596
            readcmdfile(fullname);
596
            readcmdfile(fullname);
597
            if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
597
            if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
598
                error("%s not defined in %s", name, fullname);
598
                error("%s not defined in %s", name, fullname);
599
            stunalloc(fullname);
599
            stunalloc(fullname);
600
            goto success;
600
            goto success;
601
        }
601
        }
602
#ifdef notdef
602
#ifdef notdef
603
        if (statb.st_uid == geteuid()) {
603
        if (statb.st_uid == geteuid()) {
604
            if ((statb.st_mode & 0100) == 0)
604
            if ((statb.st_mode & 0100) == 0)
605
                goto loop;
605
                goto loop;
606
        } else if (statb.st_gid == getegid()) {
606
        } else if (statb.st_gid == getegid()) {
607
            if ((statb.st_mode & 010) == 0)
607
            if ((statb.st_mode & 010) == 0)
608
                goto loop;
608
                goto loop;
609
        } else {
609
        } else {
610
            if ((statb.st_mode & 01) == 0)
610
            if ((statb.st_mode & 01) == 0)
611
                goto loop;
611
                goto loop;
612
        }
612
        }
613
#endif
613
#endif
614
        TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
614
        TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
615
        /* If we aren't called with DO_BRUTE and cmdp is set, it must
615
        /* If we aren't called with DO_BRUTE and cmdp is set, it must
616
           be a function and we're being called with DO_NOFUN */
616
           be a function and we're being called with DO_NOFUN */
617
        if (!updatetbl) {
617
        if (!updatetbl) {
618
            entry->cmdtype = CMDNORMAL;
618
            entry->cmdtype = CMDNORMAL;
619
            entry->u.index = idx;
619
            entry->u.index = idx;
620
            return;
620
            return;
621
        }
621
        }
622
        INTOFF;
622
        INTOFF;
623
        cmdp = cmdlookup(name, 1);
623
        cmdp = cmdlookup(name, 1);
624
        cmdp->cmdtype = CMDNORMAL;
624
        cmdp->cmdtype = CMDNORMAL;
625
        cmdp->param.index = idx;
625
        cmdp->param.index = idx;
626
        INTON;
626
        INTON;
627
        goto success;
627
        goto success;
628
    }
628
    }
629
 
629
 
630
    /* We failed.  If there was an entry for this command, delete it */
630
    /* We failed.  If there was an entry for this command, delete it */
631
    if (cmdp && updatetbl)
631
    if (cmdp && updatetbl)
632
        delete_cmd_entry();
632
        delete_cmd_entry();
633
    if (act & DO_ERR)
633
    if (act & DO_ERR)
634
        outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
634
        outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
635
    entry->cmdtype = CMDUNKNOWN;
635
    entry->cmdtype = CMDUNKNOWN;
636
    return;
636
    return;
637
 
637
 
638
success:
638
success:
639
    cmdp->rehash = 0;
639
    cmdp->rehash = 0;
640
    entry->cmdtype = cmdp->cmdtype;
640
    entry->cmdtype = cmdp->cmdtype;
641
    entry->u = cmdp->param;
641
    entry->u = cmdp->param;
642
}
642
}
643
 
643
 
644
 
644
 
645
 
645
 
646
/*
646
/*
647
 * Search the table of builtin commands.
647
 * Search the table of builtin commands.
648
 */
648
 */
649
 
649
 
650
int
650
int
651
find_builtin(name)
651
find_builtin(name)
652
    char *name;
652
    char *name;
653
{
653
{
654
    const struct builtincmd *bp;
654
    const struct builtincmd *bp;
655
 
655
 
656
    for (bp = builtincmd ; bp->name ; bp++) {
656
    for (bp = builtincmd ; bp->name ; bp++) {
657
        if (*bp->name == *name && equal(bp->name, name))
657
        if (*bp->name == *name && equal(bp->name, name))
658
            return bp->code;
658
            return bp->code;
659
    }
659
    }
660
    return -1;
660
    return -1;
661
}
661
}
662
 
662
 
663
 
663
 
664
 
664
 
665
/*
665
/*
666
 * Called when a cd is done.  Marks all commands so the next time they
666
 * Called when a cd is done.  Marks all commands so the next time they
667
 * are executed they will be rehashed.
667
 * are executed they will be rehashed.
668
 */
668
 */
669
 
669
 
670
void
670
void
671
hashcd() {
671
hashcd() {
672
    struct tblentry **pp;
672
    struct tblentry **pp;
673
    struct tblentry *cmdp;
673
    struct tblentry *cmdp;
674
 
674
 
675
    for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
675
    for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
676
        for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
676
        for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
677
            if (cmdp->cmdtype == CMDNORMAL
677
            if (cmdp->cmdtype == CMDNORMAL
678
             || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
678
             || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
679
                cmdp->rehash = 1;
679
                cmdp->rehash = 1;
680
        }
680
        }
681
    }
681
    }
682
}
682
}
683
 
683
 
684
 
684
 
685
 
685
 
686
/*
686
/*
687
 * Called before PATH is changed.  The argument is the new value of PATH;
687
 * Called before PATH is changed.  The argument is the new value of PATH;
688
 * pathval() still returns the old value at this point.  Called with
688
 * pathval() still returns the old value at this point.  Called with
689
 * interrupts off.
689
 * interrupts off.
690
 */
690
 */
691
 
691
 
692
void
692
void
693
changepath(newval)
693
changepath(newval)
694
    const char *newval;
694
    const char *newval;
695
{
695
{
696
    int firstchange;
696
    int firstchange;
697
    int bltin;
697
    int bltin;
698
 
698
 
699
    firstchange = path_change(newval, &bltin);
699
    firstchange = path_change(newval, &bltin);
700
    if (builtinloc < 0 && bltin >= 0)
700
    if (builtinloc < 0 && bltin >= 0)
701
        builtinloc = bltin;     /* zap builtins */
701
        builtinloc = bltin;     /* zap builtins */
702
    clearcmdentry(firstchange);
702
    clearcmdentry(firstchange);
703
    builtinloc = bltin;
703
    builtinloc = bltin;
704
}
704
}
705
 
705
 
706
 
706
 
707
/*
707
/*
708
 * Clear out command entries.  The argument specifies the first entry in
708
 * Clear out command entries.  The argument specifies the first entry in
709
 * PATH which has changed.
709
 * PATH which has changed.
710
 */
710
 */
711
 
711
 
712
STATIC void
712
STATIC void
713
clearcmdentry(firstchange)
713
clearcmdentry(firstchange)
714
    int firstchange;
714
    int firstchange;
715
{
715
{
716
    struct tblentry **tblp;
716
    struct tblentry **tblp;
717
    struct tblentry **pp;
717
    struct tblentry **pp;
718
    struct tblentry *cmdp;
718
    struct tblentry *cmdp;
719
 
719
 
720
    INTOFF;
720
    INTOFF;
721
    for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
721
    for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
722
        pp = tblp;
722
        pp = tblp;
723
        while ((cmdp = *pp) != NULL) {
723
        while ((cmdp = *pp) != NULL) {
724
            if ((cmdp->cmdtype == CMDNORMAL &&
724
            if ((cmdp->cmdtype == CMDNORMAL &&
725
                 cmdp->param.index >= firstchange)
725
                 cmdp->param.index >= firstchange)
726
             || (cmdp->cmdtype == CMDBUILTIN &&
726
             || (cmdp->cmdtype == CMDBUILTIN &&
727
                 builtinloc >= firstchange)) {
727
                 builtinloc >= firstchange)) {
728
                *pp = cmdp->next;
728
                *pp = cmdp->next;
729
                ckfree(cmdp);
729
                ckfree(cmdp);
730
            } else {
730
            } else {
731
                pp = &cmdp->next;
731
                pp = &cmdp->next;
732
            }
732
            }
733
        }
733
        }
734
    }
734
    }
735
    INTON;
735
    INTON;
736
}
736
}
737
 
737
 
738
 
738
 
739
/*
739
/*
740
 * Delete all functions.
740
 * Delete all functions.
741
 */
741
 */
742
 
742
 
743
#ifdef mkinit
743
#ifdef mkinit
744
MKINIT void deletefuncs (void);
744
MKINIT void deletefuncs (void);
745
 
745
 
746
SHELLPROC {
746
SHELLPROC {
747
    deletefuncs();
747
    deletefuncs();
748
}
748
}
749
#endif
749
#endif
750
 
750
 
751
void
751
void
752
deletefuncs() {
752
deletefuncs() {
753
    struct tblentry **tblp;
753
    struct tblentry **tblp;
754
    struct tblentry **pp;
754
    struct tblentry **pp;
755
    struct tblentry *cmdp;
755
    struct tblentry *cmdp;
756
 
756
 
757
    INTOFF;
757
    INTOFF;
758
    for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
758
    for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
759
        pp = tblp;
759
        pp = tblp;
760
        while ((cmdp = *pp) != NULL) {
760
        while ((cmdp = *pp) != NULL) {
761
            if (cmdp->cmdtype == CMDFUNCTION) {
761
            if (cmdp->cmdtype == CMDFUNCTION) {
762
                *pp = cmdp->next;
762
                *pp = cmdp->next;
763
                freefunc(cmdp->param.func);
763
                freefunc(cmdp->param.func);
764
                ckfree(cmdp);
764
                ckfree(cmdp);
765
            } else {
765
            } else {
766
                pp = &cmdp->next;
766
                pp = &cmdp->next;
767
            }
767
            }
768
        }
768
        }
769
    }
769
    }
770
    INTON;
770
    INTON;
771
}
771
}
772
 
772
 
773
 
773
 
774
 
774
 
775
/*
775
/*
776
 * Locate a command in the command hash table.  If "add" is nonzero,
776
 * Locate a command in the command hash table.  If "add" is nonzero,
777
 * add the command to the table if it is not already present.  The
777
 * add the command to the table if it is not already present.  The
778
 * variable "lastcmdentry" is set to point to the address of the link
778
 * variable "lastcmdentry" is set to point to the address of the link
779
 * pointing to the entry, so that delete_cmd_entry can delete the
779
 * pointing to the entry, so that delete_cmd_entry can delete the
780
 * entry.
780
 * entry.
781
 */
781
 */
782
 
782
 
783
struct tblentry **lastcmdentry;
783
struct tblentry **lastcmdentry;
784
 
784
 
785
 
785
 
786
STATIC struct tblentry *
786
STATIC struct tblentry *
787
cmdlookup(name, add)
787
cmdlookup(name, add)
788
    char *name;
788
    char *name;
789
    int add;
789
    int add;
790
{
790
{
791
    int hashval;
791
    int hashval;
792
    char *p;
792
    char *p;
793
    struct tblentry *cmdp;
793
    struct tblentry *cmdp;
794
    struct tblentry **pp;
794
    struct tblentry **pp;
795
 
795
 
796
    p = name;
796
    p = name;
797
    hashval = *p << 4;
797
    hashval = *p << 4;
798
    while (*p)
798
    while (*p)
799
        hashval += *p++;
799
        hashval += *p++;
800
    hashval &= 0x7FFF;
800
    hashval &= 0x7FFF;
801
    pp = &cmdtable[hashval % CMDTABLESIZE];
801
    pp = &cmdtable[hashval % CMDTABLESIZE];
802
    for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
802
    for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
803
        if (equal(cmdp->cmdname, name))
803
        if (equal(cmdp->cmdname, name))
804
            break;
804
            break;
805
        pp = &cmdp->next;
805
        pp = &cmdp->next;
806
    }
806
    }
807
    if (add && cmdp == NULL) {
807
    if (add && cmdp == NULL) {
808
        INTOFF;
808
        INTOFF;
809
        cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
809
        cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
810
                    + strlen(name) + 1);
810
                    + strlen(name) + 1);
811
        cmdp->next = NULL;
811
        cmdp->next = NULL;
812
        cmdp->cmdtype = CMDUNKNOWN;
812
        cmdp->cmdtype = CMDUNKNOWN;
813
        cmdp->rehash = 0;
813
        cmdp->rehash = 0;
814
        strcpy(cmdp->cmdname, name);
814
        strcpy(cmdp->cmdname, name);
815
        INTON;
815
        INTON;
816
    }
816
    }
817
    lastcmdentry = pp;
817
    lastcmdentry = pp;
818
    return cmdp;
818
    return cmdp;
819
}
819
}
820
 
820
 
821
/*
821
/*
822
 * Delete the command entry returned on the last lookup.
822
 * Delete the command entry returned on the last lookup.
823
 */
823
 */
824
 
824
 
825
STATIC void
825
STATIC void
826
delete_cmd_entry() {
826
delete_cmd_entry() {
827
    struct tblentry *cmdp;
827
    struct tblentry *cmdp;
828
 
828
 
829
    INTOFF;
829
    INTOFF;
830
    cmdp = *lastcmdentry;
830
    cmdp = *lastcmdentry;
831
    *lastcmdentry = cmdp->next;
831
    *lastcmdentry = cmdp->next;
832
    ckfree(cmdp);
832
    ckfree(cmdp);
833
    INTON;
833
    INTON;
834
}
834
}
835
 
835
 
836
 
836
 
837
 
837
 
838
#ifdef notdef
838
#ifdef notdef
839
void
839
void
840
getcmdentry(name, entry)
840
getcmdentry(name, entry)
841
    char *name;
841
    char *name;
842
    struct cmdentry *entry;
842
    struct cmdentry *entry;
843
    {
843
    {
844
    struct tblentry *cmdp = cmdlookup(name, 0);
844
    struct tblentry *cmdp = cmdlookup(name, 0);
845
 
845
 
846
    if (cmdp) {
846
    if (cmdp) {
847
        entry->u = cmdp->param;
847
        entry->u = cmdp->param;
848
        entry->cmdtype = cmdp->cmdtype;
848
        entry->cmdtype = cmdp->cmdtype;
849
    } else {
849
    } else {
850
        entry->cmdtype = CMDUNKNOWN;
850
        entry->cmdtype = CMDUNKNOWN;
851
        entry->u.index = 0;
851
        entry->u.index = 0;
852
    }
852
    }
853
}
853
}
854
#endif
854
#endif
855
 
855
 
856
 
856
 
857
/*
857
/*
858
 * Add a new command entry, replacing any existing command entry for
858
 * Add a new command entry, replacing any existing command entry for
859
 * the same name.
859
 * the same name.
860
 */
860
 */
861
 
861
 
862
void
862
void
863
addcmdentry(name, entry)
863
addcmdentry(name, entry)
864
    char *name;
864
    char *name;
865
    struct cmdentry *entry;
865
    struct cmdentry *entry;
866
    {
866
    {
867
    struct tblentry *cmdp;
867
    struct tblentry *cmdp;
868
 
868
 
869
    INTOFF;
869
    INTOFF;
870
    cmdp = cmdlookup(name, 1);
870
    cmdp = cmdlookup(name, 1);
871
    if (cmdp->cmdtype == CMDFUNCTION) {
871
    if (cmdp->cmdtype == CMDFUNCTION) {
872
        freefunc(cmdp->param.func);
872
        freefunc(cmdp->param.func);
873
    }
873
    }
874
    cmdp->cmdtype = entry->cmdtype;
874
    cmdp->cmdtype = entry->cmdtype;
875
    cmdp->param = entry->u;
875
    cmdp->param = entry->u;
876
    INTON;
876
    INTON;
877
}
877
}
878
 
878
 
879
 
879
 
880
/*
880
/*
881
 * Define a shell function.
881
 * Define a shell function.
882
 */
882
 */
883
 
883
 
884
void
884
void
885
defun(name, func)
885
defun(name, func)
886
    char *name;
886
    char *name;
887
    union node *func;
887
    union node *func;
888
    {
888
    {
889
    struct cmdentry entry;
889
    struct cmdentry entry;
890
 
890
 
891
    entry.cmdtype = CMDFUNCTION;
891
    entry.cmdtype = CMDFUNCTION;
892
    entry.u.func = copyfunc(func);
892
    entry.u.func = copyfunc(func);
893
    addcmdentry(name, &entry);
893
    addcmdentry(name, &entry);
894
}
894
}
895
 
895
 
896
 
896
 
897
/*
897
/*
898
 * Delete a function if it exists.
898
 * Delete a function if it exists.
899
 */
899
 */
900
 
900
 
901
int
901
int
902
unsetfunc(name)
902
unsetfunc(name)
903
    char *name;
903
    char *name;
904
    {
904
    {
905
    struct tblentry *cmdp;
905
    struct tblentry *cmdp;
906
 
906
 
907
    if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
907
    if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
908
        freefunc(cmdp->param.func);
908
        freefunc(cmdp->param.func);
909
        delete_cmd_entry();
909
        delete_cmd_entry();
910
        return (0);
910
        return (0);
911
    }
911
    }
912
    return (1);
912
    return (1);
913
}
913
}
914
 
914
 
915
/*
915
/*
916
 * Locate and print what a word is...
916
 * Locate and print what a word is...
917
 */
917
 */
918
 
918
 
919
int
919
int
920
typecmd(argc, argv)
920
typecmd(argc, argv)
921
    int argc;
921
    int argc;
922
    char **argv;
922
    char **argv;
923
{
923
{
924
    struct cmdentry entry;
924
    struct cmdentry entry;
925
    struct tblentry *cmdp;
925
    struct tblentry *cmdp;
926
    char * const *pp;
926
    char * const *pp;
927
    struct alias *ap;
927
    struct alias *ap;
928
    int i;
928
    int i;
929
    int err = 0;
929
    int err = 0;
930
    extern char *const parsekwd[];
930
    extern char *const parsekwd[];
931
 
931
 
932
    for (i = 1; i < argc; i++) {
932
    for (i = 1; i < argc; i++) {
933
        out1str(argv[i]);
933
        out1str(argv[i]);
934
        /* First look at the keywords */
934
        /* First look at the keywords */
935
        for (pp = parsekwd; *pp; pp++)
935
        for (pp = parsekwd; *pp; pp++)
936
            if (**pp == *argv[i] && equal(*pp, argv[i]))
936
            if (**pp == *argv[i] && equal(*pp, argv[i]))
937
                break;
937
                break;
938
 
938
 
939
        if (*pp) {
939
        if (*pp) {
940
            out1str(" is a shell keyword\n");
940
            out1str(" is a shell keyword\n");
941
            continue;
941
            continue;
942
        }
942
        }
943
 
943
 
944
        /* Then look at the aliases */
944
        /* Then look at the aliases */
945
        if ((ap = lookupalias(argv[i], 1)) != NULL) {
945
        if ((ap = lookupalias(argv[i], 1)) != NULL) {
946
            out1fmt(" is an alias for %s\n", ap->val);
946
            out1fmt(" is an alias for %s\n", ap->val);
947
            continue;
947
            continue;
948
        }
948
        }
949
 
949
 
950
        /* Then check if it is a tracked alias */
950
        /* Then check if it is a tracked alias */
951
        if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
951
        if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
952
            entry.cmdtype = cmdp->cmdtype;
952
            entry.cmdtype = cmdp->cmdtype;
953
            entry.u = cmdp->param;
953
            entry.u = cmdp->param;
954
        }
954
        }
955
        else {
955
        else {
956
            /* Finally use brute force */
956
            /* Finally use brute force */
957
            find_command(argv[i], &entry, DO_ABS, pathval());
957
            find_command(argv[i], &entry, DO_ABS, pathval());
958
        }
958
        }
959
 
959
 
960
        switch (entry.cmdtype) {
960
        switch (entry.cmdtype) {
961
        case CMDNORMAL: {
961
        case CMDNORMAL: {
962
            if (strchr(argv[i], '/') == NULL) {
962
            if (strchr(argv[i], '/') == NULL) {
963
                const char *path = pathval();
963
                const char *path = pathval();
964
                char *name;
964
                char *name;
965
                int j = entry.u.index;
965
                int j = entry.u.index;
966
                do {
966
                do {
967
                    name = padvance(&path, argv[i]);
967
                    name = padvance(&path, argv[i]);
968
                    stunalloc(name);
968
                    stunalloc(name);
969
                } while (--j >= 0);
969
                } while (--j >= 0);
970
                out1fmt(" is%s %s\n",
970
                out1fmt(" is%s %s\n",
971
                    cmdp ? " a tracked alias for" : "", name);
971
                    cmdp ? " a tracked alias for" : "", name);
972
            } else {
972
            } else {
973
                if (access(argv[i], X_OK) == 0)
973
                if (access(argv[i], X_OK) == 0)
974
                    out1fmt(" is %s\n", argv[i]);
974
                    out1fmt(" is %s\n", argv[i]);
975
                else
975
                else
976
                    out1fmt(": %s\n", strerror(errno));
976
                    out1fmt(": %s\n", strerror(errno));
977
            }
977
            }
978
            break;
978
            break;
979
        }
979
        }
980
        case CMDFUNCTION:
980
        case CMDFUNCTION:
981
            out1str(" is a shell function\n");
981
            out1str(" is a shell function\n");
982
            break;
982
            break;
983
 
983
 
984
        case CMDBUILTIN:
984
        case CMDBUILTIN:
985
            out1str(" is a shell builtin\n");
985
            out1str(" is a shell builtin\n");
986
            break;
986
            break;
987
 
987
 
988
        default:
988
        default:
989
            out1str(": not found\n");
989
            out1str(": not found\n");
990
            err |= 127;
990
            err |= 127;
991
            break;
991
            break;
992
        }
992
        }
993
    }
993
    }
994
    return err;
994
    return err;
995
}
995
}
996
 
996
 
997
STATIC int
997
STATIC int
998
describe_command(command, verbose)
998
describe_command(command, verbose)
999
    char *command;
999
    char *command;
1000
    int verbose;
1000
    int verbose;
1001
{
1001
{
1002
    struct cmdentry entry;
1002
    struct cmdentry entry;
1003
    struct tblentry *cmdp;
1003
    struct tblentry *cmdp;
1004
    char **pp;
1004
    char **pp;
1005
    struct alias *ap;
1005
    struct alias *ap;
1006
    extern char *const parsekwd[];
1006
    extern char *const parsekwd[];
1007
 
1007
 
1008
    for (pp = (char **)parsekwd; *pp; pp++)
1008
    for (pp = (char **)parsekwd; *pp; pp++)
1009
        if (**pp == *command && equal(*pp, command))
1009
        if (**pp == *command && equal(*pp, command))
1010
            break;
1010
            break;
1011
 
1011
 
1012
    if (*pp) {
1012
    if (*pp) {
1013
        if (verbose) {
1013
        if (verbose) {
1014
            out1fmt("%s is a reserved word\n", command);
1014
            out1fmt("%s is a reserved word\n", command);
1015
        } else {
1015
        } else {
1016
            out1fmt("%s\n", command);
1016
            out1fmt("%s\n", command);
1017
        }
1017
        }
1018
        return 0;
1018
        return 0;
1019
    }
1019
    }
1020
 
1020
 
1021
    /* Then look at the aliases */
1021
    /* Then look at the aliases */
1022
    if ((ap = lookupalias(command, 1)) != NULL) {
1022
    if ((ap = lookupalias(command, 1)) != NULL) {
1023
        if (verbose) {
1023
        if (verbose) {
1024
            out1fmt("%s is aliased to `%s'\n", command, ap->val);
1024
            out1fmt("%s is aliased to `%s'\n", command, ap->val);
1025
        } else {
1025
        } else {
1026
            out1fmt("alias %s='%s'\n", command, ap->val);
1026
            out1fmt("alias %s='%s'\n", command, ap->val);
1027
        }
1027
        }
1028
        return 0;
1028
        return 0;
1029
    }
1029
    }
1030
 
1030
 
1031
    /* Then check if it is a tracked alias */
1031
    /* Then check if it is a tracked alias */
1032
    if ((cmdp = cmdlookup(command, 0)) != NULL) {
1032
    if ((cmdp = cmdlookup(command, 0)) != NULL) {
1033
        entry.cmdtype = cmdp->cmdtype;
1033
        entry.cmdtype = cmdp->cmdtype;
1034
        entry.u = cmdp->param;
1034
        entry.u = cmdp->param;
1035
    }
1035
    }
1036
    else {
1036
    else {
1037
        /* Finally use brute force */
1037
        /* Finally use brute force */
1038
        find_command(command, &entry, DO_ABS, pathval());
1038
        find_command(command, &entry, DO_ABS, pathval());
1039
    }
1039
    }
1040
 
1040
 
1041
    switch (entry.cmdtype) {
1041
    switch (entry.cmdtype) {
1042
    case CMDNORMAL: {
1042
    case CMDNORMAL: {
1043
        int j = entry.u.index;
1043
        int j = entry.u.index;
1044
        const char *path = pathval();
1044
        const char *path = pathval();
1045
        char *name;
1045
        char *name;
1046
        if (j == -1)
1046
        if (j == -1)
1047
            name = command;
1047
            name = command;
1048
        else {
1048
        else {
1049
            do {
1049
            do {
1050
                name = padvance(&path, command);
1050
                name = padvance(&path, command);
1051
                stunalloc(name);
1051
                stunalloc(name);
1052
            } while (--j >= 0);
1052
            } while (--j >= 0);
1053
        }
1053
        }
1054
        if (verbose) {
1054
        if (verbose) {
1055
            out1fmt("%s is %s\n", command, name);
1055
            out1fmt("%s is %s\n", command, name);
1056
        } else {
1056
        } else {
1057
            out1fmt("%s\n", name);
1057
            out1fmt("%s\n", name);
1058
        }
1058
        }
1059
        break;
1059
        break;
1060
    }
1060
    }
1061
    case CMDFUNCTION:
1061
    case CMDFUNCTION:
1062
        if (verbose) {
1062
        if (verbose) {
1063
            out1fmt("%s is a function\n", command);
1063
            out1fmt("%s is a function\n", command);
1064
        } else {
1064
        } else {
1065
            out1fmt("%s\n", command);
1065
            out1fmt("%s\n", command);
1066
        }
1066
        }
1067
        break;
1067
        break;
1068
    case CMDBUILTIN:
1068
    case CMDBUILTIN:
1069
        if (verbose) {
1069
        if (verbose) {
1070
            if (is_special_builtin(command)) {
1070
            if (is_special_builtin(command)) {
1071
                out1fmt("%s is a special built-in utility\n", command);
1071
                out1fmt("%s is a special built-in utility\n", command);
1072
            } else {
1072
            } else {
1073
                out1fmt("%s is a built-in utility\n", command);
1073
                out1fmt("%s is a built-in utility\n", command);
1074
            }
1074
            }
1075
        } else {
1075
        } else {
1076
            out1fmt("%s\n", command);
1076
            out1fmt("%s\n", command);
1077
        }
1077
        }
1078
        break;
1078
        break;
1079
    default:
1079
    default:
1080
        outfmt(out2, "%s not found\n", command);
1080
        outfmt(out2, "%s not found\n", command);
1081
        return 127;
1081
        return 127;
1082
    }
1082
    }
1083
 
1083
 
1084
    return 0;
1084
    return 0;
1085
}
1085
}
1086
 
1086
 
1087
int
1087
int
1088
commandcmd(argc, argv)
1088
commandcmd(argc, argv)
1089
    int argc;
1089
    int argc;
1090
    char **argv;
1090
    char **argv;
1091
{
1091
{
1092
    int c;
1092
    int c;
1093
    int default_path = 0;
1093
    int default_path = 0;
1094
    int verify_only = 0;
1094
    int verify_only = 0;
1095
    int verbose_verify_only = 0;
1095
    int verbose_verify_only = 0;
1096
 
1096
 
1097
    while ((c = nextopt("pvV")) != '\0')
1097
    while ((c = nextopt("pvV")) != '\0')
1098
        switch (c) {
1098
        switch (c) {
1099
        case 'p':
1099
        case 'p':
1100
            default_path = 1;
1100
            default_path = 1;
1101
            break;
1101
            break;
1102
        case 'v':
1102
        case 'v':
1103
            verify_only = 1;
1103
            verify_only = 1;
1104
            break;
1104
            break;
1105
        case 'V':
1105
        case 'V':
1106
            verbose_verify_only = 1;
1106
            verbose_verify_only = 1;
1107
            break;
1107
            break;
1108
        default:
1108
        default:
1109
            outfmt(out2,
1109
            outfmt(out2,
1110
"command: nextopt returned character code 0%o\n", c);
1110
"command: nextopt returned character code 0%o\n", c);
1111
            return EX_SOFTWARE;
1111
            return EX_SOFTWARE;
1112
        }
1112
        }
1113
 
1113
 
1114
    if (default_path + verify_only + verbose_verify_only > 1 ||
1114
    if (default_path + verify_only + verbose_verify_only > 1 ||
1115
        !*argptr) {
1115
        !*argptr) {
1116
            outfmt(out2,
1116
            outfmt(out2,
1117
"command [-p] command [arg ...]\n");
1117
"command [-p] command [arg ...]\n");
1118
            outfmt(out2,
1118
            outfmt(out2,
1119
"command {-v|-V} command\n");
1119
"command {-v|-V} command\n");
1120
            return EX_USAGE;
1120
            return EX_USAGE;
1121
    }
1121
    }
1122
 
1122
 
1123
    if (verify_only || verbose_verify_only) {
1123
    if (verify_only || verbose_verify_only) {
1124
        return describe_command(*argptr, verbose_verify_only);
1124
        return describe_command(*argptr, verbose_verify_only);
1125
    }
1125
    }
1126
 
1126
 
1127
    return 0;
1127
    return 0;
1128
}
1128
}
1129
 
1129
 
1130
STATIC int
1130
STATIC int
1131
path_change(newval, bltin)
1131
path_change(newval, bltin)
1132
    const char *newval;
1132
    const char *newval;
1133
    int *bltin;
1133
    int *bltin;
1134
{
1134
{
1135
    const char *old, *new;
1135
    const char *old, *new;
1136
    int idx;
1136
    int idx;
1137
    int firstchange;
1137
    int firstchange;
1138
 
1138
 
1139
    old = pathval();
1139
    old = pathval();
1140
    new = newval;
1140
    new = newval;
1141
    firstchange = 9999; /* assume no change */
1141
    firstchange = 9999; /* assume no change */
1142
    idx = 0;
1142
    idx = 0;
1143
    *bltin = -1;
1143
    *bltin = -1;
1144
    for (;;) {
1144
    for (;;) {
1145
        if (*old != *new) {
1145
        if (*old != *new) {
1146
            firstchange = idx;
1146
            firstchange = idx;
1147
            if ((*old == '\0' && *new == ':')
1147
            if ((*old == '\0' && *new == ':')
1148
             || (*old == ':' && *new == '\0'))
1148
             || (*old == ':' && *new == '\0'))
1149
                firstchange++;
1149
                firstchange++;
1150
            old = new;  /* ignore subsequent differences */
1150
            old = new;  /* ignore subsequent differences */
1151
        }
1151
        }
1152
        if (*new == '\0')
1152
        if (*new == '\0')
1153
            break;
1153
            break;
1154
        if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
1154
        if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
1155
            *bltin = idx;
1155
            *bltin = idx;
1156
        if (*new == ':') {
1156
        if (*new == ':') {
1157
            idx++;
1157
            idx++;
1158
        }
1158
        }
1159
        new++, old++;
1159
        new++, old++;
1160
    }
1160
    }
1161
    if (builtinloc >= 0 && *bltin < 0)
1161
    if (builtinloc >= 0 && *bltin < 0)
1162
        firstchange = 0;
1162
        firstchange = 0;
1163
    return firstchange;
1163
    return firstchange;
1164
}
1164
}
1165
 
1165
 
1166
STATIC int
1166
STATIC int
1167
is_regular_builtin(name)
1167
is_regular_builtin(name)
1168
        const char *name;
1168
        const char *name;
1169
{
1169
{
1170
        static const char *regular_builtins[] = {
1170
        static const char *regular_builtins[] = {
1171
            "alias", "bg", "cd", "command", "false", "fc", "fg",
1171
            "alias", "bg", "cd", "command", "false", "fc", "fg",
1172
            "getopts", "jobs", "kill", "newgrp", "read", "true",
1172
            "getopts", "jobs", "kill", "newgrp", "read", "true",
1173
            "umask", "unalias", "wait", (char *)NULL
1173
            "umask", "unalias", "wait", (char *)NULL
1174
        };
1174
        };
1175
        int i;
1175
        int i;
1176
 
1176
 
1177
        if (!name) return 0;
1177
        if (!name) return 0;
1178
        for (i = 0; regular_builtins[i]; i++)
1178
        for (i = 0; regular_builtins[i]; i++)
1179
                if (equal(name, regular_builtins[i])) return 1;
1179
                if (equal(name, regular_builtins[i])) return 1;
1180
        return 0;
1180
        return 0;
1181
}
1181
}
1182
 
1182