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

« back to all changes in this revision

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