Deleted Added
full compact
43c43
< __FBSDID("$FreeBSD: head/usr.bin/make/job.c 146571 2005-05-24 13:19:40Z harti $");
---
> __FBSDID("$FreeBSD: head/usr.bin/make/job.c 146572 2005-05-24 15:30:03Z harti $");
79,82d78
< * Job_ParseShell Given the line following a .SHELL target, parse the
< * line as a shell specification. Returns FALSE if the
< * spec was incorrect.
< *
140a137
> #include "shell.h"
271,324d267
< * Shell Specifications:
< *
< * Some special stuff goes on if a shell doesn't have error control. In such
< * a case, errCheck becomes a printf template for echoing the command,
< * should echoing be on and ignErr becomes another printf template for
< * executing the command while ignoring the return status. If either of these
< * strings is empty when hasErrCtl is FALSE, the command will be executed
< * anyway as is and if it causes an error, so be it.
< */
< struct Shell {
< TAILQ_ENTRY(Shell) link; /* link all shell descriptions */
<
< /*
< * 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;
<
< /* 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;
<
< Boolean unsetenv; /* unsetenv("ENV") before exec */
< };
< TAILQ_HEAD(Shells, Shell);
<
< /*
361,430d303
< * 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.
< */
< #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_init[] = {
< /*
< * 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.
< */
< "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.
< */
< "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. There are probably builtins
< * missing here.
< */
< "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 "' unsetenv=T",
<
< NULL
< };
<
< /*
< * This is the shell to which we pass all commands in the Makefile.
< * It is set by the Job_ParseShell function.
< */
< static struct Shell *commandShell = NULL;
<
< /*
< * This is the list of all known shells.
< */
< static struct Shells shells = TAILQ_HEAD_INITIALIZER(shells);
<
< /*
530d402
< static struct Shell *JobMatchShell(const char *);
535d406
< static void JobShellDump(const struct Shell *) __unused;
2534,2540d2404
< static int
< sort_builtins(const void *p1, const void *p2)
< {
<
< return (strcmp(*(const char* const*)p1, *(const char* const*)p2));
< }
<
2542,2727d2405
< * JobFreeShell
< * Free a shell structure and all associated strings.
< */
< static void
< JobFreeShell(struct Shell *sh)
< {
<
< if (sh != NULL) {
< free(sh->name);
< free(sh->path);
< free(sh->echoOff);
< free(sh->echoOn);
< free(sh->noPrint);
< free(sh->errCheck);
< 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
< JobShellDump(const struct Shell *sh)
< {
< int i;
<
< 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);
< fprintf(stderr, " unsetenv=%d\n", sh->unsetenv);
< }
<
< /**
< * Parse a shell specification line and return the new Shell structure.
< * In case of an error a message is printed and NULL is returned.
< */
< 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 if (strcmp(keyw, "unsetenv") == 0) {
< sh->unsetenv = (*eq == 'Y' || *eq == 'y' ||
< *eq == 'T' || *eq == 't');
< *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);
< }
<
< /**
< * Parse the builtin shell specifications and put them into the shell
< * list. Then select the default shell to be the current shell. This
< * is called from main() before any parsing (including MAKEFLAGS and
< * command line) is done.
< */
< void
< Shell_Init(void)
< {
< int i;
< struct Shell *sh;
< Boolean fullSpec;
<
< for (i = 0; shells_init[i] != NULL; i++) {
< sh = JobParseShellSpec(shells_init[i], &fullSpec);
< TAILQ_INSERT_TAIL(&shells, sh, link);
< if (strcmp(sh->name, DEFSHELLNAME) == 0)
< commandShell = sh;
< }
< }
<
< /**
2923,3071d2600
< * Find a matching shell in 'shells' given its final component.
< *
< * Results:
< * 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)
< {
< struct Shell *sh;
<
< TAILQ_FOREACH(sh, &shells, link)
< if (strcmp(sh->name, name) == 0)
< return (sh);
<
< return (NULL);
< }
<
< /**
< * Job_ParseShell
< * 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).
< *
< * Notes:
< * A shell specification consists of a .SHELL target, with dependency
< * operator, followed by a series of blank-separated words. Double
< * quotes can be used to use blanks in words. A backslash escapes
< * anything (most notably a double-quote and a space) and
< * provides the functionality it does in C. Each word consists of
< * keyword and value separated by an equal sign. There should be no
< * unnecessary spaces in the word. The keywords are as follows:
< * name Name of shell.
< * path Location of shell. Overrides "name" if given
< * quiet Command to turn off echoing.
< * echo Command to turn echoing on
< * filter Result of turning off echoing that shouldn't be
< * printed.
< * echoFlag Flag to turn echoing on at the start
< * errFlag Flag to turn error checking on at the start
< * hasErrCtl True if shell has error checking control
< * check Command to turn on error checking if hasErrCtl
< * is TRUE or template of command to echo a command
< * for which error checking is off if hasErrCtl is
< * FALSE.
< * ignore Command to turn off error checking if hasErrCtl
< * 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[])
< {
< Boolean fullSpec;
< struct Shell *sh;
< struct Shell *match;
<
< /* parse the specification */
< if ((sh = JobParseShellSpec(line, &fullSpec)) == NULL)
< return (FALSE);
<
< 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.
< */
< if (sh->name == NULL) {
< Parse_Error(PARSE_FATAL,
< "Neither path nor name specified");
< JobFreeShell(sh);
< return (FALSE);
< }
< 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",
< sh->name);
< JobFreeShell(sh);
< return (FALSE);
< }
< JobFreeShell(sh);
< commandShell = match;
<
< return (TRUE);
< }
<
< /*
< * The user provided a path. If s/he gave nothing else
< * (fullSpec is FALSE), try and find a matching shell in the
< * ones we know of. Else we just take the specification at its
< * word and copy it to a new location. In either case, we need
< * to record the path the user gave for the shell.
< */
< if (sh->name == NULL) {
< /* get the base name as the name */
< if ((sh->name = strrchr(sh->path, '/')) == NULL) {
< sh->name = estrdup(sh->path);
< } else {
< sh->name = estrdup(sh->name + 1);
< }
< }
<
< if (!fullSpec) {
< if ((match = JobMatchShell(sh->name)) == NULL) {
< Parse_Error(PARSE_FATAL,
< "%s: no matching shell", sh->name);
< JobFreeShell(sh);
< return (FALSE);
< }
<
< /* set the patch on the matching shell */
< free(match->path);
< match->path = sh->path;
< sh->path = NULL;
<
< JobFreeShell(sh);
< commandShell = match;
< return (TRUE);
< }
<
< TAILQ_INSERT_HEAD(&shells, sh, link);
<
< /* set the new shell */
< commandShell = sh;
< return (TRUE);
< }
<
< /**