summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.bin/make/job.c594
1 files changed, 305 insertions, 289 deletions
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
index b49602c..f0de17e 100644
--- a/usr.bin/make/job.c
+++ b/usr.bin/make/job.c
@@ -277,45 +277,44 @@ TAILQ_HEAD(JobList, Job);
* strings is empty when hasErrCtl is FALSE, the command will be executed
* anyway as is and if it causes an error, so be it.
*/
-#define DEF_SHELL_STRUCT(TAG, CONST) \
-struct TAG { \
- /* \
- * the name of the shell. For Bourne and C shells, this is used \
- * only to find the shell description when used as the single \
- * source of a .SHELL target. For user-defined shells, this is \
- * the full path of the shell. \
- */ \
- CONST char *name; \
- \
- /* True if both echoOff and echoOn defined */ \
- Boolean hasEchoCtl; \
- \
- CONST char *echoOff; /* command to turn off echo */ \
- CONST char *echoOn; /* command to turn it back on */\
- \
- /* \
- * What the shell prints, and its length, when given the \
- * echo-off command. This line will not be printed when \
- * received from the shell. This is usually the command which \
- * was executed to turn off echoing \
- */ \
- CONST char *noPrint; \
- \
- /* set if can control error checking for individual commands */ \
- Boolean hasErrCtl; \
- \
- /* string to turn error checking on */ \
- CONST char *errCheck; \
- \
- /* string to turn off error checking */ \
- CONST char *ignErr; \
- \
- CONST char *echo; /* command line flag: echo commands */ \
- CONST char *exit; /* command line flag: exit on error */ \
-}
+struct Shell {
+ /*
+ * the name of the shell. For Bourne and C shells, this is used
+ * only to find the shell description when used as the single
+ * source of a .SHELL target. For user-defined shells, this is
+ * the full path of the shell.
+ */
+ char *name;
+ char *path;
+
+ /* True if both echoOff and echoOn defined */
+ Boolean hasEchoCtl;
+
+ char *echoOff; /* command to turn off echo */
+ char *echoOn; /* command to turn it back on */
+
+ /*
+ * What the shell prints, when given the echo-off command.
+ * This line will not be printed when received from the shell.
+ * This is usually the command which was executed to turn off echoing.
+ */
+ char *noPrint;
+
+ /* set if can control error checking for individual commands */
+ Boolean hasErrCtl;
-DEF_SHELL_STRUCT(Shell,);
-DEF_SHELL_STRUCT(CShell, const);
+ /* string to turn error checking on */
+ char *errCheck;
+
+ /* string to turn off error checking */
+ char *ignErr;
+
+ char *echo; /* command line flag: echo commands */
+ char *exit; /* command line flag: exit on error */
+
+ ArgArray builtins;
+ char *meta;
+};
/*
* error handling variables
@@ -354,43 +353,67 @@ static int numCommands;
#define JOB_STOPPED 3 /* The job is stopped */
/*
- * Descriptions for various shells.
+ * Descriptions for various shells. What the list of builtins should contain
+ * is debatable: either all builtins or only those which may specified on
+ * a single line without use of meta-characters. For correct makefiles that
+ * contain only correct command lines there is no difference. But if a command
+ * line, for example, is: 'if -foo bar' and there is an executable named 'if'
+ * in the path, the first possibility would execute that 'if' while in the
+ * second case the shell would give an error. Histerically only a small
+ * subset of the builtins and no reserved words where given in the list which
+ * corresponds roughly to the first variant. So go with this but add missing
+ * words.
*/
-static const struct CShell shells[] = {
+#define CSH_BUILTINS \
+ "alias cd eval exec exit read set ulimit unalias " \
+ "umask unset wait"
+
+#define SH_BUILTINS \
+ "alias cd eval exec exit read set ulimit unalias " \
+ "umask unset wait"
+
+#define CSH_META "#=|^(){};&<>*?[]:$`\\@\n"
+#define SH_META "#=|^(){};&<>*?[]:$`\\\n"
+
+static const char *const shells[][2] = {
/*
* CSH description. The csh can do echo control by playing
* with the setting of the 'echo' shell variable. Sadly,
* however, it is unable to do error control nicely.
*/
- {
- "csh",
- TRUE, "unset verbose", "set verbose", "unset verbose",
- FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"",
- "v", "e",
+ { "csh",
+ "name=csh path='" PATH_DEFSHELLDIR "/csh' "
+ "quiet='unset verbose' echo='set verbose' filter='unset verbose' "
+ "hasErrCtl=N check='echo \"%s\"\n' ignore='csh -c \"%s || exit 0\"' "
+ "echoFlag=v errFlag=e "
+ "meta='" CSH_META "' builtins='" CSH_BUILTINS "'"
},
/*
* SH description. Echo control is also possible and, under
* sun UNIX anyway, one can even control error checking.
*/
{
- "sh",
- TRUE, "set -", "set -v", "set -",
- TRUE, "set -e", "set +e",
-#ifdef OLDBOURNESHELL
- FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n",
-#endif
- "v", "e",
+ "sh",
+ "name=sh path='" PATH_DEFSHELLDIR "/sh' "
+ "quiet='set -' echo='set -v' filter='set -' "
+ "hasErrCtl=Y check='set -e' ignore='set +e' "
+ "echoFlag=v errFlag=e "
+ "meta='" SH_META "' builtins='" SH_BUILTINS "'"
},
/*
* KSH description. The Korn shell has a superset of
- * the Bourne shell's functionality.
+ * the Bourne shell's functionality. There are probably builtins
+ * missing here.
*/
{
- "ksh",
- TRUE, "set -", "set -v", "set -",
- TRUE, "set -e", "set +e",
- "v", "e",
+ "ksh",
+ "name=ksh path='" PATH_DEFSHELLDIR "/ksh' "
+ "quiet='set -' echo='set -v' filter='set -' "
+ "hasErrCtl=Y check='set -e' ignore='set +e' "
+ "echoFlag=v errFlag=e "
+ "meta='" SH_META "' builtins='" SH_BUILTINS "'"
},
+ { NULL, NULL }
};
/*
@@ -398,8 +421,6 @@ static const struct CShell shells[] = {
* It is set by the Job_ParseShell function.
*/
static struct Shell *commandShell = NULL;
-static char *shellPath = NULL; /* full pathname of executable image */
-static char *shellName = NULL; /* last component of shell */
/*
* The maximum number of jobs that may run. This is initialize from the
@@ -506,21 +527,7 @@ static void JobInterrupt(int, int);
static void JobRestartJobs(void);
static void ProcExec(const ProcStuff *) __dead2;
static int Compat_RunCommand(char *, struct GNode *);
-
-/*
- * The following array is used to make a fast determination of which
- * commands and characters are interpreted specially by the shell.
- * If a command is one of these or contains any of these characters,
- * it is executed by the shell, not directly by us.
- * XXX Both of these arrays should be configurable via .SHELL
- */
-static const char const* sh_builtin[] = {
- "alias", "cd", "eval", "exec",
- "exit", "read", "set", "ulimit",
- "unalias", "umask", "unset", "wait",
- ":", NULL
-};
-static const char *sh_meta = "#=|^(){};&<>*?[]:$`\\\n";
+static void JobShellDump(const struct Shell *) __unused;
static GNode *curTarg = NULL;
static GNode *ENDNode;
@@ -723,7 +730,7 @@ ProcExec(const ProcStuff *ps)
write(STDERR_FILENO, strerror(errno), strlen(strerror(errno)));
write(STDERR_FILENO, "\n", 1);
} else {
- execv(shellPath, ps->argv);
+ execv(commandShell->path, ps->argv);
write(STDERR_FILENO,
"Could not execute shell\n",
@@ -1661,7 +1668,7 @@ JobMakeArgv(Job *job, char **argv)
int argc;
static char args[10]; /* For merged arguments */
- argv[0] = shellName;
+ argv[0] = commandShell->name;
argc = 1;
if ((commandShell->exit && *commandShell->exit != '-') ||
@@ -2521,58 +2528,11 @@ Job_Make(GNode *gn)
JobStart(gn, 0, NULL);
}
-/**
- * JobCopyShell
- * Make a new copy of the shell structure including a copy of the strings
- * in it. This also defaults some fields in case they are NULL.
- *
- * Returns:
- * The function returns a pointer to the new shell structure.
- */
-static struct Shell *
-JobCopyShell(const struct Shell *osh)
+static int
+sort_builtins(const void *p1, const void *p2)
{
- struct Shell *nsh;
-
- nsh = emalloc(sizeof(*nsh));
- nsh->name = estrdup(osh->name);
-
- if (osh->echoOff != NULL)
- nsh->echoOff = estrdup(osh->echoOff);
- else
- nsh->echoOff = NULL;
- if (osh->echoOn != NULL)
- nsh->echoOn = estrdup(osh->echoOn);
- else
- nsh->echoOn = NULL;
- nsh->hasEchoCtl = osh->hasEchoCtl;
-
- if (osh->noPrint != NULL)
- nsh->noPrint = estrdup(osh->noPrint);
- else
- nsh->noPrint = NULL;
-
- nsh->hasErrCtl = osh->hasErrCtl;
- if (osh->errCheck == NULL)
- nsh->errCheck = estrdup("");
- else
- nsh->errCheck = estrdup(osh->errCheck);
- if (osh->ignErr == NULL)
- nsh->ignErr = estrdup("%s");
- else
- nsh->ignErr = estrdup(osh->ignErr);
-
- if (osh->echo == NULL)
- nsh->echo = estrdup("");
- else
- nsh->echo = estrdup(osh->echo);
-
- if (osh->exit == NULL)
- nsh->exit = estrdup("");
- else
- nsh->exit = estrdup(osh->exit);
-
- return (nsh);
+
+ return (strcmp(*(const char* const*)p1, *(const char* const*)p2));
}
/**
@@ -2585,6 +2545,7 @@ JobFreeShell(struct Shell *sh)
if (sh != NULL) {
free(sh->name);
+ free(sh->path);
free(sh->echoOff);
free(sh->echoOn);
free(sh->noPrint);
@@ -2592,29 +2553,40 @@ JobFreeShell(struct Shell *sh)
free(sh->ignErr);
free(sh->echo);
free(sh->exit);
+ ArgArray_Done(&sh->builtins);
+ free(sh->meta);
free(sh);
}
}
+/**
+ * Dump a shell specification to stderr.
+ */
static void
-Shell_Init(void)
+JobShellDump(const struct Shell *sh)
{
+ int i;
- if (commandShell == NULL)
- commandShell = JobMatchShell(shells[DEFSHELL].name);
+ fprintf(stderr, "Shell %p:\n", sh);
+ fprintf(stderr, " name='%s' path='%s'\n", sh->name, sh->path);
+ fprintf(stderr, " hasEchoCtl=%d echoOff='%s' echoOn='%s'\n",
+ sh->hasEchoCtl, sh->echoOff, sh->echoOn);
+ fprintf(stderr, " noPrint='%s'\n", sh->noPrint);
+ fprintf(stderr, " hasErrCtl=%d errCheck='%s' ignErr='%s'\n",
+ sh->hasErrCtl, sh->errCheck, sh->ignErr);
+ fprintf(stderr, " echo='%s' exit='%s'\n", sh->echo, sh->exit);
+ fprintf(stderr, " builtins=%d\n", sh->builtins.argc - 1);
+ for (i = 1; i < sh->builtins.argc; i++)
+ fprintf(stderr, " '%s'", sh->builtins.argv[i]);
+ fprintf(stderr, "\n meta='%s'\n", sh->meta);
+}
- if (shellPath == NULL) {
- /*
- * The user didn't specify a shell to use, so we are using the
- * default one... Both the absolute path and the last component
- * must be set. The last component is taken from the 'name'
- * field of the default shell description pointed-to by
- * commandShell. All default shells are located in
- * PATH_DEFSHELLDIR.
- */
- shellName = commandShell->name;
- shellPath = str_concat(PATH_DEFSHELLDIR, shellName,
- STR_ADDSLASH);
+static void
+Shell_Init(void)
+{
+
+ if (commandShell == NULL) {
+ commandShell = JobMatchShell(shells[DEFSHELL][0]);
}
}
@@ -2815,58 +2787,147 @@ Job_Empty(void)
}
}
+static struct Shell *
+JobParseShellSpec(const char *spec, Boolean *fullSpec)
+{
+ ArgArray aa;
+ struct Shell *sh;
+ char *eq;
+ char *keyw;
+ int arg;
+
+ *fullSpec = FALSE;
+
+ sh = emalloc(sizeof(*sh));
+ memset(sh, 0, sizeof(*sh));
+ ArgArray_Init(&sh->builtins);
+
+ /*
+ * Parse the specification by keyword but skip the first word
+ */
+ brk_string(&aa, spec, TRUE);
+
+ for (arg = 1; arg < aa.argc; arg++) {
+ /*
+ * Split keyword and value
+ */
+ keyw = aa.argv[arg];
+ if ((eq = strchr(keyw, '=')) == NULL) {
+ Parse_Error(PARSE_FATAL, "missing '=' in shell "
+ "specification keyword '%s'", keyw);
+ ArgArray_Done(&aa);
+ JobFreeShell(sh);
+ return (NULL);
+ }
+ *eq++ = '\0';
+
+ if (strcmp(keyw, "path") == 0) {
+ free(sh->path);
+ sh->path = estrdup(eq);
+ } else if (strcmp(keyw, "name") == 0) {
+ free(sh->name);
+ sh->name = estrdup(eq);
+ } else if (strcmp(keyw, "quiet") == 0) {
+ free(sh->echoOff);
+ sh->echoOff = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "echo") == 0) {
+ free(sh->echoOn);
+ sh->echoOn = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "filter") == 0) {
+ free(sh->noPrint);
+ sh->noPrint = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "echoFlag") == 0) {
+ free(sh->echo);
+ sh->echo = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "errFlag") == 0) {
+ free(sh->exit);
+ sh->exit = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "hasErrCtl") == 0) {
+ sh->hasErrCtl = (*eq == 'Y' || *eq == 'y' ||
+ *eq == 'T' || *eq == 't');
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "check") == 0) {
+ free(sh->errCheck);
+ sh->errCheck = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "ignore") == 0) {
+ free(sh->ignErr);
+ sh->ignErr = estrdup(eq);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "builtins") == 0) {
+ ArgArray_Done(&sh->builtins);
+ brk_string(&sh->builtins, eq, TRUE);
+ qsort(sh->builtins.argv + 1, sh->builtins.argc - 1,
+ sizeof(char *), sort_builtins);
+ *fullSpec = TRUE;
+ } else if (strcmp(keyw, "meta") == 0) {
+ free(sh->meta);
+ sh->meta = estrdup(eq);
+ *fullSpec = TRUE;
+ } else {
+ Parse_Error(PARSE_FATAL, "unknown keyword in shell "
+ "specification '%s'", keyw);
+ ArgArray_Done(&aa);
+ JobFreeShell(sh);
+ return (NULL);
+ }
+ }
+ ArgArray_Done(&aa);
+
+ /*
+ * Some checks (could be more)
+ */
+ if (*fullSpec) {
+ if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) {
+ Parse_Error(PARSE_FATAL, "Shell must have either both "
+ "echoOff and echoOn or none of them");
+ JobFreeShell(sh);
+ return (NULL);
+ }
+
+ if (sh->echoOn != NULL && sh->echoOff != NULL)
+ sh->hasEchoCtl = TRUE;
+ }
+
+ return (sh);
+}
+
/**
- * JobMatchShell
- * Find a matching shell in 'shells' given its final component.
+ * Find a matching shell in 'shells' given its final component.
*
* Results:
- * A pointer to a freshly allocated Shell structure with a copy
- * of the static structure or NULL if no shell with the given name
+ * A pointer to a freshly allocated Shell structure with the contents
+ * from static description or NULL if no shell with the given name
* is found.
*/
static struct Shell *
JobMatchShell(const char *name)
{
- const struct CShell *sh; /* Pointer into shells table */
- struct Shell *nsh;
-
- for (sh = shells; sh < shells + sizeof(shells)/sizeof(shells[0]); sh++)
- if (strcmp(sh->name, name) == 0)
- break;
+ u_int sh;
+ Boolean fullSpec;
- if (sh == shells + sizeof(shells)/sizeof(shells[0]))
- return (NULL);
+ for (sh = 0; shells[sh][0] != NULL; sh++)
+ if (strcmp(shells[sh][0], name) == 0)
+ return (JobParseShellSpec(shells[sh][1], &fullSpec));
- /* make a copy */
- nsh = emalloc(sizeof(*nsh));
-
- nsh->name = estrdup(sh->name);
- nsh->echoOff = estrdup(sh->echoOff);
- nsh->echoOn = estrdup(sh->echoOn);
- nsh->hasEchoCtl = sh->hasEchoCtl;
- nsh->noPrint = estrdup(sh->noPrint);
- nsh->hasErrCtl = sh->hasErrCtl;
- nsh->errCheck = estrdup(sh->errCheck);
- nsh->ignErr = estrdup(sh->ignErr);
- nsh->echo = estrdup(sh->echo);
- nsh->exit = estrdup(sh->exit);
-
- return (nsh);
+ return (NULL);
}
/**
* Job_ParseShell
- * Parse a shell specification and set up commandShell, shellPath
- * and shellName appropriately.
+ * Parse a shell specification and set up commandShell appropriately.
*
* Results:
* TRUE if the specification was correct. FALSE otherwise.
*
* Side Effects:
* commandShell points to a Shell structure (either predefined or
- * created from the shell spec), shellPath is the full path of the
- * shell described by commandShell, while shellName is just the
- * final component of shellPath.
+ * created from the shell spec).
*
* Notes:
* A shell specification consists of a .SHELL target, with dependency
@@ -2893,108 +2954,54 @@ JobMatchShell(const char *name)
* is TRUE or template of command to execute a
* command so as to ignore any errors it returns if
* hasErrCtl is FALSE.
+ * builtins A space separated list of builtins. If one
+ * of these builtins is detected when make wants
+ * to execute a command line, the command line is
+ * handed to the shell. Otherwise make may try to
+ * execute the command directly. If this list is empty
+ * it is assumed, that the command must always be
+ * handed over to the shell.
+ * meta The shell meta characters. If this is not specified
+ * or empty, commands are alway passed to the shell.
+ * Otherwise they are not passed when they contain
+ * neither a meta character nor a builtin command.
*/
Boolean
Job_ParseShell(const char line[])
{
- ArgArray aa;
- char **argv;
- int argc;
- char *path;
- char *eq;
- Boolean fullSpec = FALSE;
- struct Shell newShell;
+ Boolean fullSpec;
struct Shell *sh;
+ struct Shell *match;
- memset(&newShell, 0, sizeof(newShell));
- path = NULL;
-
- /*
- * Parse the specification by keyword but skip the first word
- */
- brk_string(&aa, line, TRUE);
-
- for (argc = aa.argc - 1, argv = aa.argv + 1; argc != 0;
- argc--, argv++) {
- /*
- * Split keyword and value
- */
- if ((eq = strchr(*argv, '=')) == NULL) {
- Parse_Error(PARSE_FATAL, "missing '=' in shell "
- "specification keyword '%s'", *argv);
- ArgArray_Done(&aa);
- return (FALSE);
- }
- *eq++ = '\0';
-
- if (strcmp(*argv, "path") == 0) {
- path = eq;
- } else if (strcmp(*argv, "name") == 0) {
- newShell.name = eq;
- } else if (strcmp(*argv, "quiet") == 0) {
- newShell.echoOff = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "echo") == 0) {
- newShell.echoOn = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "filter") == 0) {
- newShell.noPrint = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "echoFlag") == 0) {
- newShell.echo = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "errFlag") == 0) {
- newShell.exit = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "hasErrCtl") == 0) {
- newShell.hasErrCtl = (*eq == 'Y' || *eq == 'y' ||
- *eq == 'T' || *eq == 't');
- fullSpec = TRUE;
- } else if (strcmp(*argv, "check") == 0) {
- newShell.errCheck = eq;
- fullSpec = TRUE;
- } else if (strcmp(*argv, "ignore") == 0) {
- newShell.ignErr = eq;
- fullSpec = TRUE;
- } else {
- Parse_Error(PARSE_FATAL, "unknown keyword in shell "
- "specification '%s'", *argv);
- ArgArray_Done(&aa);
- return (FALSE);
- }
- }
-
- /*
- * Some checks (could be more)
- */
- if (fullSpec) {
- if ((newShell.echoOn != NULL) ^ (newShell.echoOff != NULL))
- Parse_Error(PARSE_FATAL, "Shell must have either both "
- "echoOff and echoOn or none of them");
-
- if (newShell.echoOn != NULL && newShell.echoOff)
- newShell.hasEchoCtl = TRUE;
- }
+ /* parse the specification */
+ if ((sh = JobParseShellSpec(line, &fullSpec)) == NULL)
+ return (FALSE);
- if (path == NULL) {
+ if (sh->path == NULL) {
/*
* If no path was given, the user wants one of the pre-defined
* shells, yes? So we find the one s/he wants with the help of
- * JobMatchShell and set things up the right way. shellPath
- * will be set up by Job_Init.
+ * JobMatchShell and set things up the right way.
*/
- if (newShell.name == NULL) {
+ if (sh->name == NULL) {
Parse_Error(PARSE_FATAL,
"Neither path nor name specified");
- ArgArray_Done(&aa);
+ JobFreeShell(sh);
return (FALSE);
}
- if ((sh = JobMatchShell(newShell.name)) == NULL) {
+ if (fullSpec) {
+ Parse_Error(PARSE_FATAL, "No path specified");
+ JobFreeShell(sh);
+ return (FALSE);
+ }
+ if ((match = JobMatchShell(sh->name)) == NULL) {
Parse_Error(PARSE_FATAL, "%s: no matching shell",
- newShell.name);
- ArgArray_Done(&aa);
+ sh->name);
+ JobFreeShell(sh);
return (FALSE);
}
+ JobFreeShell(sh);
+ sh = match;
} else {
/*
@@ -3004,38 +3011,34 @@ Job_ParseShell(const char line[])
* word and copy it to a new location. In either case, we need
* to record the path the user gave for the shell.
*/
- path = estrdup(path);
- if (newShell.name == NULL) {
+ if (sh->name == NULL) {
/* get the base name as the name */
- if ((newShell.name = strrchr(path, '/')) == NULL) {
- newShell.name = path;
+ if ((sh->name = strrchr(sh->path, '/')) == NULL) {
+ sh->name = estrdup(sh->path);
} else {
- newShell.name += 1;
+ sh->name = estrdup(sh->name + 1);
}
}
if (!fullSpec) {
- if ((sh = JobMatchShell(newShell.name)) == NULL) {
+ if ((match = JobMatchShell(sh->name)) == NULL) {
Parse_Error(PARSE_FATAL,
- "%s: no matching shell", newShell.name);
- free(path);
- ArgArray_Done(&aa);
+ "%s: no matching shell", sh->name);
+ JobFreeShell(sh);
return (FALSE);
}
- } else {
- sh = JobCopyShell(&newShell);
+ free(match->path);
+ match->path = sh->path;
+ sh->path = NULL;
+ JobFreeShell(sh);
+ sh = match;
}
- free(shellPath);
- shellPath = path;
}
/* set the new shell */
JobFreeShell(commandShell);
commandShell = sh;
- shellName = commandShell->name;
-
- ArgArray_Done(&aa);
return (TRUE);
}
@@ -3227,7 +3230,7 @@ Cmd_Exec(const char *cmd, const char **error)
*error = NULL;
buf = Buf_Init(0);
- if (shellPath == NULL)
+ if (commandShell == NULL)
Shell_Init();
/*
* Open a pipe for fetching its output
@@ -3250,7 +3253,7 @@ Cmd_Exec(const char *cmd, const char **error)
/* Set up arguments for shell */
ps.argv = emalloc(4 * sizeof(char *));
- ps.argv[0] = strdup(shellName);
+ ps.argv[0] = strdup(commandShell->name);
ps.argv[1] = strdup("-c");
ps.argv[2] = strdup(cmd);
ps.argv[3] = NULL;
@@ -3392,9 +3395,14 @@ CompatInterrupt(int signo)
static char **
shellneed(ArgArray *aa, char *cmd)
{
- const char **p;
+ char **p;
+ int ret;
+
+ if (commandShell->meta == NULL || commandShell->builtins.argc <= 1)
+ /* use shell */
+ return (NULL);
- if (strpbrk(cmd, sh_meta) != NULL)
+ if (strpbrk(cmd, commandShell->meta) != NULL)
return (NULL);
/*
@@ -3402,29 +3410,37 @@ shellneed(ArgArray *aa, char *cmd)
* vector we can execute.
*/
brk_string(aa, cmd, TRUE);
- for (p = sh_builtin; *p != 0; p++) {
- if (strcmp(aa->argv[1], *p) == 0) {
+ for (p = commandShell->builtins.argv + 1; *p != 0; p++) {
+ if ((ret = strcmp(aa->argv[1], *p)) == 0) {
+ /* found - use shell */
ArgArray_Done(aa);
return (NULL);
}
+ if (ret < 0) {
+ /* not found */
+ break;
+ }
}
return (aa->argv + 1);
}
-/*-
- *-----------------------------------------------------------------------
- * Compat_RunCommand --
- * Execute the next command for a target. If the command returns an
- * error, the node's made field is set to ERROR and creation stops.
- * The node from which the command came is also given.
+/**
+ * Execute the next command for a target. If the command returns an
+ * error, the node's made field is set to ERROR and creation stops.
+ * The node from which the command came is also given. This is used
+ * to execute the commands in compat mode and when executing commands
+ * with the '+' flag in non-compat mode. In these modes each command
+ * line should be executed by its own shell. We do some optimisation here:
+ * if the shell description defines both a string of meta characters and
+ * a list of builtins and the command line neither contains a meta character
+ * nor starts with one of the builtins then we execute the command directly
+ * without invoking a shell.
*
* Results:
* 0 if the command succeeded, 1 if an error occurred.
*
* Side Effects:
* The node's 'made' field may be set to ERROR.
- *
- *-----------------------------------------------------------------------
*/
static int
Compat_RunCommand(char *cmd, GNode *gn)
@@ -3523,7 +3539,7 @@ Compat_RunCommand(char *cmd, GNode *gn)
* well as -c if it is supposed to exit when it hits an error.
*/
ps.argv = emalloc(4 * sizeof(char *));
- ps.argv[0] = strdup(shellPath);
+ ps.argv[0] = strdup(commandShell->path);
ps.argv[1] = strdup(errCheck ? "-ec" : "-c");
ps.argv[2] = strdup(cmd);
ps.argv[3] = NULL;
OpenPOWER on IntegriCloud