/*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Input: * line Line to fracture * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(const char *line) { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ char *args; /* Space used by the args */ char *buf, *p1; char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); size_t len; if (line == NULL) return; for (; *line == ' '; ++line) continue; if (!*line) return; buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); (void)snprintf(buf, len, "%s %s", argv0, line); free(p1); argv = brk_string(buf, &argc, TRUE, &args); if (argv == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); MainParseArgs(argc, argv); free(args); free(argv); }
/*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or MAKEFLAGS target * is encountered and by main() when reading the MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(char *line) /* Line to fracture */ { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ if (line == NULL) return; for (; *line == ' '; ++line); if (!*line) return; argv = brk_string(line, &argc); MainParseArgs(argc, argv); }
/*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Input: * line Line to fracture * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(const char *line) { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ char *args; /* Space used by the args */ char *buf, *p1; char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); size_t len; if (line == NULL) return; for (; *line == ' '; ++line) continue; if (!*line) return; #ifndef POSIX { /* * $MAKE may simply be naming the make(1) binary */ char *cp; if (!(cp = strrchr(line, '/'))) cp = line; if ((cp = strstr(cp, "make")) && strcmp(cp, "make") == 0) return; } #endif buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); (void)snprintf(buf, len, "%s %s", argv0, line); if (p1) free(p1); argv = brk_string(buf, &argc, TRUE, &args); if (argv == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); MainParseArgs(argc, argv); free(args); free(argv); }
/*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(const char *line) /* Line to fracture */ { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ char *args; /* Space used by the args */ char *buf; char *argv0; const char *s; size_t len; if (line == NULL) return; for (; *line == ' '; ++line) continue; if (!*line) return; /* POSIX rule: MAKEFLAGS can hold a set of option letters without * any blanks or dashes. */ for (s = line;; s++) { if (*s == '\0') { while (line != s) posixParseOptLetter(*line++); return; } if (strchr(OPTLETTERS, *s) == NULL) break; } argv0 = Var_Value(".MAKE"); len = strlen(line) + strlen(argv0) + 2; buf = emalloc(len); (void)snprintf(buf, len, "%s %s", argv0, line); argv = brk_string(buf, &argc, &args); free(buf); MainParseArgs(argc, argv); free(args); free(argv); }
static void run_command(const char *cmd, bool errCheck) { const char *p; char *shargv[4]; char **todo; shargv[0] = _PATH_BSHELL; shargv[1] = errCheck ? "-ec" : "-c"; shargv[2] = (char *)cmd; shargv[3] = NULL; todo = shargv; /* Search for meta characters in the command. If there are no meta * characters, there's no need to execute a shell to execute the * command. */ for (p = cmd; !meta[(unsigned char)*p]; p++) continue; if (*p == '\0') { char *bp; char **av; int argc; /* No meta-characters, so probably no need to exec a shell. * Break the command into words to form an argument vector * we can execute. */ av = brk_string(cmd, &argc, &bp); av = recheck_command_for_shell(av); if (av != NULL) todo = av; } execvp(todo[0], todo); if (errno == ENOENT) fprintf(stderr, "%s: not found\n", todo[0]); else perror(todo[0]); _exit(1); }
/** * Find a matching shell in 'shells' given its final component. * * 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. * * @result * A pointer to a Shell structure, or NULL if no shell with * the given name is found. */ Shell * Shell_Match(const char name[]) { Shell *shell; const char *shellDir = PATH_DEFSHELLDIR; shell = emalloc(sizeof(Shell)); if (strcmp(name, "csh") == 0) { /* * 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. */ shell->name = strdup(name); shell->path = str_concat(shellDir, '/', name); shell->hasEchoCtl = true; shell->echoOff = strdup("unset verbose"); shell->echoOn = strdup("set verbose"); shell->noPrint = strdup("unset verbose"); shell->hasErrCtl = false; shell->errCheck = strdup("echo \"%s\"\n"); shell->ignErr = strdup("csh -c \"%s || exit 0\""); shell->echo = strdup("v"); shell->exit = strdup("e"); shell->meta = strdup("#=|^(){};&<>*?[]:$`\\@\n"); brk_string(&shell->builtins, "alias cd eval exec exit read set ulimit unalias " "umask unset wait", true); shell->unsetenv = false; } else if (strcmp(name, "sh") == 0) { /* * SH description. Echo control is also possible and, under * sun UNIX anyway, one can even control error checking. */ shell->name = strdup(name); shell->path = str_concat(shellDir, '/', name); shell->hasEchoCtl = true; shell->echoOff = strdup("set -"); shell->echoOn = strdup("set -v"); shell->noPrint = strdup("set -"); #ifdef OLDBOURNESHELL shell->hasErrCtl = false; shell->errCheck = strdup("echo \"%s\"\n"); shell->ignErr = strdup("sh -c '%s || exit 0'\n"); #else shell->hasErrCtl = true; shell->errCheck = strdup("set -e"); shell->ignErr = strdup("set +e"); #endif shell->echo = strdup("v"); shell->exit = strdup("e"); shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n"); brk_string(&shell->builtins, "alias cd eval exec exit read set ulimit unalias " "umask unset wait", true); shell->unsetenv = false; } else if (strcmp(name, "ksh") == 0) { /* * KSH description. The Korn shell has a superset of * the Bourne shell's functionality. There are probably * builtins missing here. */ shell->name = strdup(name); shell->path = str_concat(shellDir, '/', name); shell->hasEchoCtl = true; shell->echoOff = strdup("set -"); shell->echoOn = strdup("set -v"); shell->noPrint = strdup("set -"); shell->hasErrCtl = true; shell->errCheck = strdup("set -e"); shell->ignErr = strdup("set +e"); shell->echo = strdup("v"); shell->exit = strdup("e"); shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n"); brk_string(&shell->builtins, "alias cd eval exec exit read set ulimit unalias " "umask unset wait", true); shell->unsetenv = true; } else { free(shell); shell = NULL; } return (shell); }
/** * Parse a shell specification line and return the new Shell structure. * In case of an error a message is printed and NULL is returned. * * 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. */ static Shell * ShellParseSpec(const char spec[], bool *fullSpec) { ArgArray aa; 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); Shell_Destroy(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); Shell_Destroy(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"); Shell_Destroy(sh); return (NULL); } if (sh->echoOn != NULL && sh->echoOff != NULL) sh->hasEchoCtl = true; } return (sh); }
/*- *----------------------------------------------------------------------- * CompatRunCommand -- * 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. * * Input: * cmdp Command to execute * gnp Node from which the command came * * Results: * 0 if the command succeeded, 1 if an error occurred. * * Side Effects: * The node's 'made' field may be set to ERROR. * *----------------------------------------------------------------------- */ int CompatRunCommand(void *cmdp, void *gnp) { char *cmdStart; /* Start of expanded command */ char *cp, *bp; Boolean silent, /* Don't print command */ doIt; /* Execute even if -n */ volatile Boolean errCheck; /* Check errors */ WAIT_T reason; /* Reason for child's death */ int status; /* Description of child's death */ pid_t cpid; /* Child actually found */ pid_t retstat; /* Result of wait */ LstNode cmdNode; /* Node where current command is located */ const char ** volatile av; /* Argument vector for thing to exec */ char ** volatile mav;/* Copy of the argument vector for freeing */ int argc; /* Number of arguments in av or 0 if not * dynamically allocated */ Boolean local; /* TRUE if command should be executed * locally */ Boolean useShell; /* TRUE if command should be executed * using a shell */ char * volatile cmd = (char *)cmdp; GNode *gn = (GNode *)gnp; silent = gn->type & OP_SILENT; errCheck = !(gn->type & OP_IGNORE); doIt = FALSE; cmdNode = Lst_Member(gn->commands, cmd); cmdStart = Var_Subst(NULL, cmd, gn, FALSE); /* * brk_string will return an argv with a NULL in av[0], thus causing * execvp to choke and die horribly. Besides, how can we execute a null * command? In any case, we warn the user that the command expanded to * nothing (is this the right thing to do?). */ if (*cmdStart == '\0') { free(cmdStart); Error("%s expands to empty string", cmd); return(0); } cmd = cmdStart; Lst_Replace(cmdNode, cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { (void)Lst_AtEnd(ENDNode->commands, cmdStart); return(0); } if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return(0); } while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { switch (*cmd) { case '@': silent = DEBUG(LOUD) ? FALSE : TRUE; break; case '-': errCheck = FALSE; break; case '+': doIt = TRUE; if (!meta[0]) /* we came here from jobs */ Compat_Init(); break; } cmd++; } while (isspace((unsigned char)*cmd)) cmd++; #if !defined(MAKE_NATIVE) /* * In a non-native build, the host environment might be weird enough * that it's necessary to go through a shell to get the correct * behaviour. Or perhaps the shell has been replaced with something * that does extra logging, and that should not be bypassed. */ useShell = TRUE; #else /* * Search for meta characters in the command. If there are no meta * characters, there's no need to execute a shell to execute the * command. */ for (cp = cmd; !meta[(unsigned char)*cp]; cp++) { continue; } useShell = (*cp != '\0'); #endif /* * Print the command before echoing if we're not supposed to be quiet for * this one. We also print the command if -n given. */ if (!silent || NoExecute(gn)) { printf("%s\n", cmd); fflush(stdout); } /* * If we're not supposed to execute any commands, this is as far as * we go... */ if (!doIt && NoExecute(gn)) { return (0); } if (DEBUG(JOB)) fprintf(debug_file, "Execute: '%s'\n", cmd); again: if (useShell) { /* * We need to pass the command off to the shell, typically * because the command contains a "meta" character. */ static const char *shargv[4]; shargv[0] = shellPath; /* * The following work for any of the builtin shell specs. */ if (DEBUG(SHELL)) shargv[1] = "-xc"; else shargv[1] = "-c"; shargv[2] = cmd; shargv[3] = NULL; av = shargv; argc = 0; bp = NULL; mav = NULL; } else { /* * No meta-characters, so no need to exec a shell. Break the command * into words to form an argument vector we can execute. */ mav = brk_string(cmd, &argc, TRUE, &bp); if (mav == NULL) { useShell = 1; goto again; } av = (const char **)mav; } local = TRUE; #ifdef USE_META if (useMeta) { meta_compat_start(); } #endif /* * Fork and execute the single command. If the fork fails, we abort. */ cpid = vFork(); if (cpid < 0) { Fatal("Could not fork"); } if (cpid == 0) { Check_Cwd(av); Var_ExportVars(); #ifdef USE_META if (useMeta) { meta_compat_child(); } #endif if (local) (void)execvp(av[0], (char *const *)UNCONST(av)); else (void)execv(av[0], (char *const *)UNCONST(av)); execError("exec", av[0]); _exit(1); } if (mav) free(mav); if (bp) free(bp); Lst_Replace(cmdNode, NULL); #ifdef USE_META if (useMeta) { meta_compat_parent(); } #endif /* * The child is off and running. Now all we can do is wait... */ while (1) { while ((retstat = wait(&reason)) != cpid) { if (retstat > 0) JobReapChild(retstat, reason, FALSE); /* not ours? */ if (retstat == -1 && errno != EINTR) { break; } } if (retstat > -1) { if (WIFSTOPPED(reason)) { status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { status = WEXITSTATUS(reason); /* exited */ #if defined(USE_META) && defined(USE_FILEMON_ONCE) if (useMeta) { meta_cmd_finish(NULL); } #endif if (status != 0) { if (DEBUG(ERROR)) { fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", gn->name); for (cp = cmd; *cp; ) { if (isspace((unsigned char)*cp)) { fprintf(debug_file, " "); while (isspace((unsigned char)*cp)) cp++; } else { fprintf(debug_file, "%c", *cp); cp++; } } fprintf(debug_file, "\n"); } printf("*** Error code %d", status); } } else { status = WTERMSIG(reason); /* signaled */ printf("*** Signal %d", status); } if (!WIFEXITED(reason) || (status != 0)) { if (errCheck) { #ifdef USE_META if (useMeta) { meta_job_error(NULL, gn, 0, status); } #endif gn->made = ERROR; if (keepgoing) { /* * Abort the current target, but let others * continue. */ printf(" (continuing)\n"); } } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ printf(" (ignored)\n"); status = 0; } } break; } else { Fatal("error in wait: %d: %s", retstat, strerror(errno)); /*NOTREACHED*/ } } free(cmdStart); return (status); }