From c848bc18e8d3de0c74834f0980f57b141aa9b989 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Sat, 6 Mar 2010 16:57:53 +0000 Subject: sh: Improve the command builtin: * avoid unnecessary fork * allow executing builtins via command * executing a special builtin via command removes its special properties Obtained from: NetBSD (parts) --- bin/sh/eval.c | 95 +++++++++++++++++++++++++++++++++++++++-------------------- bin/sh/exec.c | 24 ++++++++++----- bin/sh/exec.h | 4 +++ bin/sh/sh.1 | 10 ++++--- 4 files changed, 90 insertions(+), 43 deletions(-) (limited to 'bin') diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 30e05b8b6435..c0a7601a6165 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -597,6 +597,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) char *lastarg; int realstatus; int do_clearcmdentry; + char *path = pathval(); /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); @@ -682,7 +683,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) cmdentry.special = 1; } else { static const char PATH[] = "PATH="; - char *path = pathval(); + int cmd_flags = 0, bltinonly = 0; /* * Modify the command lookup path, if a PATH= assignment @@ -713,24 +714,68 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) do_clearcmdentry = 1; } - find_command(argv[0], &cmdentry, 0, path); - /* implement the bltin builtin here */ - if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { - for (;;) { - argv++; - if (--argc == 0) - break; - if ((cmdentry.u.index = find_builtin(*argv, - &cmdentry.special)) < 0) { + for (;;) { + if (bltinonly) { + cmdentry.u.index = find_builtin(*argv, &cmdentry.special); + if (cmdentry.u.index < 0) { cmdentry.u.index = BLTINCMD; argv--; argc++; break; } - if (cmdentry.u.index != BLTINCMD) + } else + find_command(argv[0], &cmdentry, cmd_flags, path); + /* implement the bltin and command builtins here */ + if (cmdentry.cmdtype != CMDBUILTIN) + break; + if (cmdentry.u.index == BLTINCMD) { + if (argc == 1) break; - } + argv++; + argc--; + bltinonly = 1; + } else if (cmdentry.u.index == COMMANDCMD) { + if (argc == 1) + break; + if (!strcmp(argv[1], "-p")) { + if (argc == 2) + break; + if (argv[2][0] == '-') { + if (strcmp(argv[2], "--")) + break; + if (argc == 3) + break; + argv += 3; + argc -= 3; + } else { + argv += 2; + argc -= 2; + } + path = _PATH_STDPATH; + clearcmdentry(0); + do_clearcmdentry = 1; + } else if (!strcmp(argv[1], "--")) { + if (argc == 2) + break; + argv += 2; + argc -= 2; + } else if (argv[1][0] == '-') + break; + else { + argv++; + argc--; + } + cmd_flags |= DO_NOFUNC; + bltinonly = 0; + } else + break; } + /* + * Special builtins lose their special properties when + * called via 'command'. + */ + if (cmd_flags & DO_NOFUNC) + cmdentry.special = 0; } /* Fork off a child process if necessary. */ @@ -741,9 +786,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) && (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.index == CDCMD || cmdentry.u.index == DOTCMD - || cmdentry.u.index == EVALCMD)) - || (cmdentry.cmdtype == CMDBUILTIN && - cmdentry.u.index == COMMANDCMD)) { + || cmdentry.u.index == EVALCMD))) { jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { @@ -889,7 +932,7 @@ cmddone: for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); - shellexec(argv, envp, pathval(), cmdentry.u.index); + shellexec(argv, envp, path, cmdentry.u.index); /*NOTREACHED*/ } goto out; @@ -996,15 +1039,11 @@ int commandcmd(int argc, char **argv) { static char stdpath[] = _PATH_STDPATH; - struct jmploc loc, *old; - struct strlist *sp; char *path; int ch; int cmd = -1; - for (sp = cmdenviron; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - path = pathval(); + path = bltinlookup("PATH", 1); optind = optreset = 1; opterr = 0; @@ -1032,22 +1071,14 @@ commandcmd(int argc, char **argv) error("wrong number of arguments"); return typecmd_impl(2, argv - 1, cmd, path); } - if (argc != 0) { - old = handler; - handler = &loc; - if (setjmp(handler->loc) == 0) - shellexec(argv, environment(), path, 0); - handler = old; - if (exception == EXEXEC) - exit(exerrno); - exraise(exception); - } + if (argc != 0) + error("commandcmd() called while it should not be"); /* * Do nothing successfully if no command was specified; * ksh also does this. */ - exit(0); + return 0; } diff --git a/bin/sh/exec.c b/bin/sh/exec.c index a28ab0364a50..7c15f7960574 100644 --- a/bin/sh/exec.c +++ b/bin/sh/exec.c @@ -248,7 +248,7 @@ hashcmd(int argc __unused, char **argv __unused) && (cmdp->cmdtype == CMDNORMAL || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) delete_cmd_entry(); - find_command(name, &entry, 1, pathval()); + find_command(name, &entry, DO_ERR, pathval()); if (verbose) { if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ cmdp = cmdlookup(name, 0); @@ -310,10 +310,10 @@ printentry(struct tblentry *cmdp, int verbose) */ void -find_command(const char *name, struct cmdentry *entry, int printerr, +find_command(const char *name, struct cmdentry *entry, int act, const char *path) { - struct tblentry *cmdp; + struct tblentry *cmdp, loc_cmd; int idx; int prev; char *fullname; @@ -330,13 +330,19 @@ find_command(const char *name, struct cmdentry *entry, int printerr, } /* If name is in the table, and not invalidated by cd, we're done */ - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) - goto success; + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { + if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC) + cmdp = NULL; + else + goto success; + } /* If %builtin not in path, check for builtin next */ if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) { INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDBUILTIN; cmdp->param.index = i; cmdp->special = spec; @@ -365,6 +371,8 @@ loop: goto loop; INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDBUILTIN; cmdp->param.index = i; cmdp->special = spec; @@ -414,6 +422,8 @@ loop: TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDNORMAL; cmdp->param.index = idx; INTON; @@ -421,9 +431,9 @@ loop: } /* We failed. If there was an entry for this command, delete it */ - if (cmdp) + if (cmdp && cmdp->cmdtype != CMDFUNCTION) delete_cmd_entry(); - if (printerr) { + if (act & DO_ERR) { if (e == ENOENT || e == ENOTDIR) outfmt(out2, "%s: not found\n", name); else diff --git a/bin/sh/exec.h b/bin/sh/exec.h index 32bf13168342..45330a16ad6c 100644 --- a/bin/sh/exec.h +++ b/bin/sh/exec.h @@ -57,6 +57,10 @@ struct cmdentry { }; +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_NOFUNC 0x02 /* don't return shell functions, for command */ + extern const char *pathopt; /* set by padvance */ extern int exerrno; /* last exec error */ diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index 8ec119545cae..4df5212e1900 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -32,7 +32,7 @@ .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" $FreeBSD$ .\" -.Dd December 31, 2009 +.Dd March 6, 2010 .Dt SH 1 .Os .Sh NAME @@ -1571,10 +1571,12 @@ built-in command. .It Ic command Oo Fl p Oc Op Ar utility Op Ar argument ... .It Ic command Oo Fl v | V Oc Op Ar utility The first form of invocation executes the specified +.Ar utility , +ignoring shell functions in the search. +If .Ar utility -as a simple command (see the -.Sx Simple Commands -section). +is a special builtin, +it is executed as if it were a regular builtin. .Pp If the .Fl p -- cgit v1.2.3