[BACK]Return to exec.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / bin / sh

Annotation of src/bin/sh/exec.c, Revision 1.35

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

CVSweb <webmaster@jp.NetBSD.org>