[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.46.6.1

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

CVSweb <webmaster@jp.NetBSD.org>