Subversion Repositories HelenOS

Rev

Rev 2714 | Details | Compare with Previous | Last modification | View Log | RSS feed

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