var: Remove unused VNOSET
[dash.git] / src / exec.c
blob6fe0fed0ffefa5b7d211121b0071722fdd9a941c
1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1997-2005
5 * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
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.
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #ifdef HAVE_PATHS_H
42 #include <paths.h>
43 #endif
46 * When commands are first encountered, they are entered in a hash table.
47 * This ensures that a full path search will not have to be done for them
48 * on each invocation.
50 * We should investigate converting to a linear search, even though that
51 * would make the command name "hash" a misnomer.
54 #include "shell.h"
55 #include "main.h"
56 #include "nodes.h"
57 #include "parser.h"
58 #include "redir.h"
59 #include "eval.h"
60 #include "exec.h"
61 #include "builtins.h"
62 #include "var.h"
63 #include "options.h"
64 #include "output.h"
65 #include "syntax.h"
66 #include "memalloc.h"
67 #include "error.h"
68 #include "init.h"
69 #include "mystring.h"
70 #include "show.h"
71 #include "jobs.h"
72 #include "alias.h"
73 #include "system.h"
76 #define CMDTABLESIZE 31 /* should be prime */
77 #define ARB 1 /* actual size determined at run time */
81 struct tblentry {
82 struct tblentry *next; /* next entry in hash chain */
83 union param param; /* definition of builtin function */
84 short cmdtype; /* index identifying command */
85 char rehash; /* if set, cd done since entry created */
86 char cmdname[ARB]; /* name of command */
90 STATIC struct tblentry *cmdtable[CMDTABLESIZE];
91 STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
94 STATIC void tryexec(char *, char **, char **);
95 STATIC void printentry(struct tblentry *);
96 STATIC void clearcmdentry(void);
97 STATIC struct tblentry *cmdlookup(const char *, int);
98 STATIC void delete_cmd_entry(void);
99 STATIC void addcmdentry(char *, struct cmdentry *);
100 STATIC int describe_command(struct output *, char *, const char *, int);
104 * Exec a program. Never returns. If you change this routine, you may
105 * have to change the find_command routine as well.
108 void
109 shellexec(char **argv, const char *path, int idx)
111 char *cmdname;
112 int e;
113 char **envp;
114 int exerrno;
116 envp = environment();
117 if (strchr(argv[0], '/') != NULL) {
118 tryexec(argv[0], argv, envp);
119 e = errno;
120 } else {
121 e = ENOENT;
122 while (padvance(&path, argv[0]) >= 0) {
123 cmdname = stackblock();
124 if (--idx < 0 && pathopt == NULL) {
125 tryexec(cmdname, argv, envp);
126 if (errno != ENOENT && errno != ENOTDIR)
127 e = errno;
132 /* Map to POSIX errors */
133 switch (e) {
134 default:
135 exerrno = 126;
136 break;
137 case ELOOP:
138 case ENAMETOOLONG:
139 case ENOENT:
140 case ENOTDIR:
141 exerrno = 127;
142 break;
144 exitstatus = exerrno;
145 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
146 argv[0], e, suppressint ));
147 exerror(EXEND, "%s: %s", argv[0], errmsg(e, E_EXEC));
148 /* NOTREACHED */
152 STATIC void
153 tryexec(char *cmd, char **argv, char **envp)
155 char *const path_bshell = _PATH_BSHELL;
157 repeat:
158 #ifdef SYSV
159 do {
160 execve(cmd, argv, envp);
161 } while (errno == EINTR);
162 #else
163 execve(cmd, argv, envp);
164 #endif
165 if (cmd != path_bshell && errno == ENOEXEC) {
166 *argv-- = cmd;
167 *argv = cmd = path_bshell;
168 goto repeat;
172 static const char *legal_pathopt(const char *opt, const char *term, int magic)
174 switch (magic) {
175 case 0:
176 opt = NULL;
177 break;
179 case 1:
180 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
181 break;
183 default:
184 opt += strcspn(opt, term);
185 break;
188 if (opt && *opt == '%')
189 opt++;
191 return opt;
195 * Do a path search. The variable path (passed by reference) should be
196 * set to the start of the path before the first call; padvance will update
197 * this value as it proceeds. Successive calls to padvance will return
198 * the possible path expansions in sequence. If an option (indicated by
199 * a percent sign) appears in the path entry then the global variable
200 * pathopt will be set to point to it; otherwise pathopt will be set to
201 * NULL.
203 * If magic is 0 then pathopt recognition will be disabled. If magic is
204 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
205 * pathopt.
208 const char *pathopt;
210 int padvance_magic(const char **path, const char *name, int magic)
212 const char *term = "%:";
213 const char *lpathopt;
214 const char *p;
215 char *q;
216 const char *start;
217 size_t qlen;
218 size_t len;
220 if (*path == NULL)
221 return -1;
223 lpathopt = NULL;
224 start = *path;
226 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
227 lpathopt = start + 1;
228 start = p;
229 term = ":";
232 len = strcspn(start, term);
233 p = start + len;
235 if (*p == '%') {
236 size_t extra = strchrnul(p, ':') - p;
238 if (legal_pathopt(p + 1, term, magic))
239 lpathopt = p + 1;
240 else
241 len += extra;
243 p += extra;
246 pathopt = lpathopt;
247 *path = *p == ':' ? p + 1 : NULL;
249 /* "2" is for '/' and '\0' */
250 qlen = len + strlen(name) + 2;
251 q = growstackto(qlen);
253 if (likely(len)) {
254 q = mempcpy(q, start, len);
255 *q++ = '/';
257 strcpy(q, name);
259 return qlen;
264 /*** Command hashing code ***/
268 hashcmd(int argc, char **argv)
270 struct tblentry **pp;
271 struct tblentry *cmdp;
272 int c;
273 struct cmdentry entry;
274 char *name;
275 bool clear;
277 clear = false;
278 while ((c = nextopt("r")) != '\0')
279 clear = true;
280 if(clear) {
281 clearcmdentry();
282 return 0;
285 if (*argptr == NULL) {
286 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
287 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
288 if (cmdp->cmdtype == CMDNORMAL)
289 printentry(cmdp);
292 return 0;
294 c = 0;
295 while ((name = *argptr) != NULL) {
296 if ((cmdp = cmdlookup(name, 0)) &&
297 (cmdp->cmdtype == CMDNORMAL ||
298 (cmdp->cmdtype == CMDBUILTIN &&
299 !(cmdp->param.cmd->flags & BUILTIN_REGULAR) &&
300 builtinloc > 0)))
301 delete_cmd_entry();
302 find_command(name, &entry, DO_ERR, pathval());
303 if (entry.cmdtype == CMDUNKNOWN)
304 c = 1;
305 argptr++;
307 return c;
311 STATIC void
312 printentry(struct tblentry *cmdp)
314 int idx;
315 const char *path;
316 char *name;
318 idx = cmdp->param.index;
319 path = pathval();
320 do {
321 padvance(&path, cmdp->cmdname);
322 } while (--idx >= 0);
323 name = stackblock();
324 out1str(name);
325 out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
328 static int test_exec(const char *fullname, struct stat64 *statb)
330 if (!S_ISREG(statb->st_mode))
331 return 0;
333 if ((statb->st_mode & 0111) != 0111 &&
334 #ifdef HAVE_FACCESSAT
335 !test_file_access(fullname, X_OK)
336 #else
337 !test_access(statb, X_OK)
338 #endif
340 return 0;
342 return 1;
346 * Resolve a command name. If you change this routine, you may have to
347 * change the shellexec routine as well.
350 void
351 find_command(char *name, struct cmdentry *entry, int act, const char *path)
353 struct tblentry *cmdp;
354 int idx;
355 int prev;
356 char *fullname;
357 struct stat64 statb;
358 int e;
359 int updatetbl;
360 struct builtincmd *bcmd;
361 int len;
363 /* If name contains a slash, don't use PATH or hash table */
364 if (strchr(name, '/') != NULL) {
365 entry->u.index = -1;
366 if (act & DO_ABS) {
367 while (stat64(name, &statb) < 0) {
368 #ifdef SYSV
369 if (errno == EINTR)
370 continue;
371 #endif
372 absfail:
373 entry->cmdtype = CMDUNKNOWN;
374 return;
376 if (!test_exec(name, &statb))
377 goto absfail;
379 entry->cmdtype = CMDNORMAL;
380 return;
383 updatetbl = (path == pathval());
384 if (!updatetbl)
385 act |= DO_ALTPATH;
387 /* If name is in the table, check answer will be ok */
388 if ((cmdp = cmdlookup(name, 0)) != NULL) {
389 int bit;
391 switch (cmdp->cmdtype) {
392 default:
393 #if DEBUG
394 abort();
395 #endif
396 case CMDNORMAL:
397 bit = DO_ALTPATH | DO_REGBLTIN;
398 break;
399 case CMDFUNCTION:
400 bit = DO_NOFUNC;
401 break;
402 case CMDBUILTIN:
403 bit = cmdp->param.cmd->flags & BUILTIN_REGULAR ?
404 0 : DO_REGBLTIN;
405 break;
407 if (act & bit) {
408 if (act & bit & DO_REGBLTIN)
409 goto fail;
411 updatetbl = 0;
412 cmdp = NULL;
413 } else if (cmdp->rehash == 0)
414 /* if not invalidated by cd, we're done */
415 goto success;
418 /* If %builtin not in path, check for builtin next */
419 bcmd = find_builtin(name);
420 if (bcmd && ((bcmd->flags & BUILTIN_REGULAR) | (act & DO_ALTPATH) |
421 (builtinloc <= 0)))
422 goto builtin_success;
424 if (act & DO_REGBLTIN)
425 goto fail;
427 /* We have to search path. */
428 prev = -1; /* where to start */
429 if (cmdp && cmdp->rehash) { /* doing a rehash */
430 if (cmdp->cmdtype == CMDBUILTIN)
431 prev = builtinloc;
432 else
433 prev = cmdp->param.index;
436 e = ENOENT;
437 idx = -1;
438 loop:
439 while ((len = padvance(&path, name)) >= 0) {
440 const char *lpathopt = pathopt;
442 fullname = stackblock();
443 idx++;
444 if (lpathopt) {
445 if (*lpathopt == 'b') {
446 if (bcmd)
447 goto builtin_success;
448 continue;
449 } else if (!(act & DO_NOFUNC)) {
450 /* handled below */
451 } else {
452 /* ignore unimplemented options */
453 continue;
456 /* if rehash, don't redo absolute path names */
457 if (fullname[0] == '/' && idx <= prev) {
458 if (idx < prev)
459 continue;
460 TRACE(("searchexec \"%s\": no change\n", name));
461 goto success;
463 while (stat64(fullname, &statb) < 0) {
464 #ifdef SYSV
465 if (errno == EINTR)
466 continue;
467 #endif
468 if (errno != ENOENT && errno != ENOTDIR)
469 e = errno;
470 goto loop;
472 if (lpathopt) { /* this is a %func directory */
473 stalloc(len);
474 readcmdfile(fullname);
475 if ((cmdp = cmdlookup(name, 0)) == NULL ||
476 cmdp->cmdtype != CMDFUNCTION)
477 sh_error("%s not defined in %s", name,
478 fullname);
479 stunalloc(fullname);
480 goto success;
482 e = EACCES; /* if we fail, this will be the error */
483 if (!test_exec(fullname, &statb))
484 continue;
485 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
486 if (!updatetbl) {
487 entry->cmdtype = CMDNORMAL;
488 entry->u.index = idx;
489 return;
491 INTOFF;
492 cmdp = cmdlookup(name, 1);
493 cmdp->cmdtype = CMDNORMAL;
494 cmdp->param.index = idx;
495 INTON;
496 goto success;
499 /* We failed. If there was an entry for this command, delete it */
500 if (cmdp && updatetbl)
501 delete_cmd_entry();
502 if (act & DO_ERR)
503 sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
504 fail:
505 entry->cmdtype = CMDUNKNOWN;
506 return;
508 builtin_success:
509 if (!updatetbl) {
510 entry->cmdtype = CMDBUILTIN;
511 entry->u.cmd = bcmd;
512 return;
514 INTOFF;
515 cmdp = cmdlookup(name, 1);
516 cmdp->cmdtype = CMDBUILTIN;
517 cmdp->param.cmd = bcmd;
518 INTON;
519 success:
520 cmdp->rehash = 0;
521 entry->cmdtype = cmdp->cmdtype;
522 entry->u = cmdp->param;
528 * Search the table of builtin commands.
531 struct builtincmd *
532 find_builtin(const char *name)
534 struct builtincmd *bp;
536 bp = bsearch(
537 &name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
538 pstrcmp
540 return bp;
546 * Called when a cd is done. Marks all commands so the next time they
547 * are executed they will be rehashed.
550 void
551 hashcd(void)
553 struct tblentry **pp;
554 struct tblentry *cmdp;
556 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
557 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
558 if (cmdp->cmdtype == CMDNORMAL || (
559 cmdp->cmdtype == CMDBUILTIN &&
560 !(cmdp->param.cmd->flags & BUILTIN_REGULAR) &&
561 builtinloc > 0
563 cmdp->rehash = 1;
571 * Fix command hash table when PATH changed.
572 * Called before PATH is changed. The argument is the new value of PATH;
573 * pathval() still returns the old value at this point.
574 * Called with interrupts off.
577 void
578 changepath(const char *newval)
580 const char *new;
581 int idx;
582 int bltin;
584 new = newval;
585 idx = 0;
586 bltin = -1;
587 for (;;) {
588 if (*new == '%' && prefix(new + 1, "builtin")) {
589 bltin = idx;
590 break;
592 new = strchr(new, ':');
593 if (!new)
594 break;
595 idx++;
596 new++;
598 builtinloc = bltin;
599 clearcmdentry();
604 * Clear out command entries. The argument specifies the first entry in
605 * PATH which has changed.
608 STATIC void
609 clearcmdentry(void)
611 struct tblentry **tblp;
612 struct tblentry **pp;
613 struct tblentry *cmdp;
615 INTOFF;
616 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
617 pp = tblp;
618 while ((cmdp = *pp) != NULL) {
619 if (cmdp->cmdtype == CMDNORMAL ||
620 (cmdp->cmdtype == CMDBUILTIN &&
621 !(cmdp->param.cmd->flags & BUILTIN_REGULAR) &&
622 builtinloc > 0)) {
623 *pp = cmdp->next;
624 ckfree(cmdp);
625 } else {
626 pp = &cmdp->next;
630 INTON;
636 * Locate a command in the command hash table. If "add" is nonzero,
637 * add the command to the table if it is not already present. The
638 * variable "lastcmdentry" is set to point to the address of the link
639 * pointing to the entry, so that delete_cmd_entry can delete the
640 * entry.
642 * Interrupts must be off if called with add != 0.
645 struct tblentry **lastcmdentry;
648 STATIC struct tblentry *
649 cmdlookup(const char *name, int add)
651 unsigned int hashval;
652 const char *p;
653 struct tblentry *cmdp;
654 struct tblentry **pp;
656 p = name;
657 hashval = (unsigned char)*p << 4;
658 while (*p)
659 hashval += (unsigned char)*p++;
660 hashval &= 0x7FFF;
661 pp = &cmdtable[hashval % CMDTABLESIZE];
662 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
663 if (equal(cmdp->cmdname, name))
664 break;
665 pp = &cmdp->next;
667 if (add && cmdp == NULL) {
668 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
669 + strlen(name) + 1);
670 cmdp->next = NULL;
671 cmdp->cmdtype = CMDUNKNOWN;
672 strcpy(cmdp->cmdname, name);
674 lastcmdentry = pp;
675 return cmdp;
679 * Delete the command entry returned on the last lookup.
682 STATIC void
683 delete_cmd_entry(void)
685 struct tblentry *cmdp;
687 INTOFF;
688 cmdp = *lastcmdentry;
689 *lastcmdentry = cmdp->next;
690 if (cmdp->cmdtype == CMDFUNCTION)
691 freefunc(cmdp->param.func);
692 ckfree(cmdp);
693 INTON;
698 #ifdef notdef
699 void
700 getcmdentry(char *name, struct cmdentry *entry)
702 struct tblentry *cmdp = cmdlookup(name, 0);
704 if (cmdp) {
705 entry->u = cmdp->param;
706 entry->cmdtype = cmdp->cmdtype;
707 } else {
708 entry->cmdtype = CMDUNKNOWN;
709 entry->u.index = 0;
712 #endif
716 * Add a new command entry, replacing any existing command entry for
717 * the same name - except special builtins.
720 STATIC void
721 addcmdentry(char *name, struct cmdentry *entry)
723 struct tblentry *cmdp;
725 cmdp = cmdlookup(name, 1);
726 if (cmdp->cmdtype == CMDFUNCTION) {
727 freefunc(cmdp->param.func);
729 cmdp->cmdtype = entry->cmdtype;
730 cmdp->param = entry->u;
731 cmdp->rehash = 0;
736 * Define a shell function.
739 void
740 defun(union node *func)
742 struct cmdentry entry;
744 INTOFF;
745 entry.cmdtype = CMDFUNCTION;
746 entry.u.func = copyfunc(func);
747 addcmdentry(func->ndefun.text, &entry);
748 INTON;
753 * Delete a function if it exists.
756 void
757 unsetfunc(const char *name)
759 struct tblentry *cmdp;
761 if ((cmdp = cmdlookup(name, 0)) != NULL &&
762 cmdp->cmdtype == CMDFUNCTION)
763 delete_cmd_entry();
767 * Locate and print what a word is...
771 typecmd(int argc, char **argv)
773 int err = 0;
775 nextopt(nullstr);
776 while (*argptr) {
777 err |= describe_command(out1, *argptr++, NULL, 1);
779 return err;
782 static int describe_command(struct output *out, char *command,
783 const char *path, int verbose)
785 struct cmdentry entry;
786 struct tblentry *cmdp;
787 const struct alias *ap;
789 if (verbose) {
790 outstr(command, out);
793 /* First look at the keywords */
794 if (findkwd(command)) {
795 outstr(verbose ? " is a shell keyword" : command, out);
796 goto out;
799 /* Then look at the aliases */
800 if ((ap = lookupalias(command, 0)) != NULL) {
801 if (verbose) {
802 outfmt(out, " is an alias for %s", ap->val);
803 } else {
804 outstr("alias ", out);
805 printalias(ap);
806 return 0;
808 goto out;
811 /* Then if the standard search path is used, check if it is
812 * a tracked alias.
814 if (path == NULL) {
815 path = pathval();
816 cmdp = cmdlookup(command, 0);
817 } else {
818 cmdp = NULL;
821 if (cmdp != NULL) {
822 entry.cmdtype = cmdp->cmdtype;
823 entry.u = cmdp->param;
824 } else {
825 /* Finally use brute force */
826 find_command(command, &entry, DO_ABS, path);
829 switch (entry.cmdtype) {
830 case CMDNORMAL: {
831 int j = entry.u.index;
832 char *p;
833 if (j == -1) {
834 p = command;
835 } else {
836 do {
837 padvance(&path, command);
838 } while (--j >= 0);
839 p = stackblock();
841 if (verbose) {
842 outfmt(
843 out, " is%s %s",
844 cmdp ? " a tracked alias for" : nullstr, p
846 } else {
847 outstr(p, out);
849 break;
852 case CMDFUNCTION:
853 if (verbose) {
854 outstr(" is a shell function", out);
855 } else {
856 outstr(command, out);
858 break;
860 case CMDBUILTIN:
861 if (verbose) {
862 outfmt(
863 out, " is a %sshell builtin",
864 entry.u.cmd->flags & BUILTIN_SPECIAL ?
865 "special " : nullstr
867 } else {
868 outstr(command, out);
870 break;
872 default:
873 if (verbose) {
874 outstr(": not found\n", out);
876 return 127;
879 out:
880 outc('\n', out);
881 return 0;
884 int commandcmd(int argc, char **argv)
886 char *cmd;
887 int c;
888 enum {
889 VERIFY_BRIEF = 1,
890 VERIFY_VERBOSE = 2,
891 } verify = 0;
892 const char *path = NULL;
894 while ((c = nextopt("pvV")) != '\0')
895 if (c == 'V')
896 verify |= VERIFY_VERBOSE;
897 else if (c == 'v')
898 verify |= VERIFY_BRIEF;
899 #ifdef DEBUG
900 else if (c != 'p')
901 abort();
902 #endif
903 else
904 path = defpath;
906 cmd = *argptr;
907 if (verify && cmd)
908 return describe_command(out1, cmd, path, verify - VERIFY_BRIEF);
910 return 0;