Пример #1
0
/*-
 * 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);
}
Пример #2
0
/*-
 * 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);
}
Пример #3
0
/*-
 * 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);
}
Пример #4
0
/*-
 * 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);
}
Пример #5
0
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);
}
Пример #6
0
/**
 * 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);
}
Пример #7
0
/**
 * 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);
}
Пример #8
0
/*-
 *-----------------------------------------------------------------------
 * 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);
}