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

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

CVSweb <webmaster@jp.NetBSD.org>