Module Name: src
Committed By: rillig
Date: Sat Oct 17 20:37:38 UTC 2020
Modified Files:
src/usr.bin/make: parse.c
Log Message:
make(1): split Parse_File into smaller functions
To generate a diff of this commit:
cvs rdiff -u -r1.378 -r1.379 src/usr.bin/make/parse.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.bin/make/parse.c
diff -u src/usr.bin/make/parse.c:1.378 src/usr.bin/make/parse.c:1.379
--- src/usr.bin/make/parse.c:1.378 Sat Oct 17 20:32:20 2020
+++ src/usr.bin/make/parse.c Sat Oct 17 20:37:38 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.378 2020/10/17 20:32:20 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.379 2020/10/17 20:37:38 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -131,7 +131,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.378 2020/10/17 20:32:20 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.379 2020/10/17 20:37:38 rillig Exp $");
/* types and constants */
@@ -2883,6 +2883,155 @@ ParseLine_ShellCommand(const char *p)
}
}
+static Boolean
+ParseDirective(char *line)
+{
+ char *cp;
+
+ if (*line == '.') {
+ /*
+ * Lines that begin with the special character may be
+ * include or undef directives.
+ * On the other hand they can be suffix rules (.c.o: ...)
+ * or just dependencies for filenames that start '.'.
+ */
+ cp = line + 1;
+ pp_skip_whitespace(&cp);
+ if (IsInclude(cp, FALSE)) {
+ ParseDoInclude(cp);
+ return TRUE;
+ }
+ if (strncmp(cp, "undef", 5) == 0) {
+ const char *varname;
+ cp += 5;
+ pp_skip_whitespace(&cp);
+ varname = cp;
+ for (; !ch_isspace(*cp) && *cp != '\0'; cp++)
+ continue;
+ *cp = '\0';
+ Var_Delete(varname, VAR_GLOBAL);
+ /* TODO: undefine all variables, not only the first */
+ /* TODO: use Str_Words, like everywhere else */
+ return TRUE;
+ } else if (strncmp(cp, "export", 6) == 0) {
+ cp += 6;
+ pp_skip_whitespace(&cp);
+ Var_Export(cp, TRUE);
+ return TRUE;
+ } else if (strncmp(cp, "unexport", 8) == 0) {
+ Var_UnExport(cp);
+ return TRUE;
+ } else if (strncmp(cp, "info", 4) == 0 ||
+ strncmp(cp, "error", 5) == 0 ||
+ strncmp(cp, "warning", 7) == 0) {
+ if (ParseMessage(cp))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static Boolean
+ParseVarassign(const char *line)
+{
+ VarAssign var;
+ if (Parse_IsVar(line, &var)) {
+ FinishDependencyGroup();
+ Parse_DoVar(&var, VAR_GLOBAL);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* dependency -> target... op [source...]
+ * op -> ':' | '::' | '!' */
+static void
+ParseDependency(char *line, const char **out_shellcmd)
+{
+ VarEvalFlags eflags;
+ char *expanded_line;
+
+ /*
+ * For some reason - probably to make the parser impossible -
+ * a ';' can be used to separate commands from dependencies.
+ * Attempt to avoid ';' inside substitution patterns.
+ */
+ {
+ int level = 0;
+ char *cp;
+
+ for (cp = line; *cp != 0; cp++) {
+ if (*cp == '\\' && cp[1] != 0) {
+ cp++;
+ continue;
+ }
+ if (*cp == '$' &&
+ (cp[1] == '(' || cp[1] == '{')) {
+ level++;
+ continue;
+ }
+ if (level > 0) {
+ if (*cp == ')' || *cp == '}') {
+ level--;
+ continue;
+ }
+ } else if (*cp == ';') {
+ break;
+ }
+ }
+
+ if (*cp != 0) {
+ /* Terminate the dependency list at the ';' */
+ *cp++ = 0;
+ *out_shellcmd = cp;
+ } else
+ *out_shellcmd = NULL;
+ }
+
+ /*
+ * We now know it's a dependency line so it needs to have all
+ * variables expanded before being parsed.
+ *
+ * XXX: Ideally the dependency line would first be split into
+ * its left-hand side, dependency operator and right-hand side,
+ * and then each side would be expanded on its own. This would
+ * allow for the left-hand side to allow only defined variables
+ * and to allow variables on the right-hand side to be undefined
+ * as well.
+ *
+ * Parsing the line first would also prevent that targets
+ * generated from variable expressions are interpreted as the
+ * dependency operator, such as in "target${:U:} middle: source",
+ * in which the middle is interpreted as a source, not a target.
+ */
+
+ /* In lint mode, allow undefined variables to appear in
+ * dependency lines.
+ *
+ * Ideally, only the right-hand side would allow undefined
+ * variables since it is common to have no dependencies.
+ * Having undefined variables on the left-hand side is more
+ * unusual though. Since both sides are expanded in a single
+ * pass, there is not much choice what to do here.
+ *
+ * In normal mode, it does not matter whether undefined
+ * variables are allowed or not since as of 2020-09-14,
+ * Var_Parse does not print any parse errors in such a case.
+ * It simply returns the special empty string var_Error,
+ * which cannot be detected in the result of Var_Subst. */
+ eflags = DEBUG(LINT) ? VARE_WANTRES : VARE_UNDEFERR | VARE_WANTRES;
+ (void)Var_Subst(line, VAR_CMD, eflags, &expanded_line);
+ /* TODO: handle errors */
+
+ /* Need a fresh list for the target nodes */
+ if (targets != NULL)
+ Lst_Free(targets);
+ targets = Lst_Init();
+
+ ParseDoDependency(expanded_line);
+ free(expanded_line);
+}
+
/* Parse a top-level makefile into its component parts, incorporating them
* into the global dependency graph.
*
@@ -2893,7 +3042,6 @@ ParseLine_ShellCommand(const char *p)
void
Parse_File(const char *name, int fd)
{
- char *cp; /* pointer into the line */
char *line; /* the line we're working on */
struct loadedfile *lf;
@@ -2911,55 +3059,12 @@ Parse_File(const char *name, int fd)
do {
for (; (line = ParseReadLine()) != NULL; ) {
DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n", curFile->lineno, line);
- if (*line == '.') {
- /*
- * Lines that begin with the special character may be
- * include or undef directives.
- * On the other hand they can be suffix rules (.c.o: ...)
- * or just dependencies for filenames that start '.'.
- */
- cp = line + 1;
- pp_skip_whitespace(&cp);
- if (IsInclude(cp, FALSE)) {
- ParseDoInclude(cp);
- continue;
- }
- if (strncmp(cp, "undef", 5) == 0) {
- const char *varname;
- cp += 5;
- pp_skip_whitespace(&cp);
- varname = cp;
- for (; !ch_isspace(*cp) && *cp != '\0'; cp++)
- continue;
- *cp = '\0';
- Var_Delete(varname, VAR_GLOBAL);
- /* TODO: undefine all variables, not only the first */
- /* TODO: use Str_Words, like everywhere else */
- continue;
- } else if (strncmp(cp, "export", 6) == 0) {
- cp += 6;
- pp_skip_whitespace(&cp);
- Var_Export(cp, TRUE);
- continue;
- } else if (strncmp(cp, "unexport", 8) == 0) {
- Var_UnExport(cp);
- continue;
- } else if (strncmp(cp, "info", 4) == 0 ||
- strncmp(cp, "error", 5) == 0 ||
- strncmp(cp, "warning", 7) == 0) {
- if (ParseMessage(cp))
- continue;
- }
- }
+
+ if (ParseDirective(line))
+ continue;
if (*line == '\t') {
- /*
- * If a line starts with a tab, it can only hope to be
- * a creation command.
- */
- cp = line + 1;
- shellCommand:
- ParseLine_ShellCommand(cp);
+ ParseLine_ShellCommand(line + 1);
continue;
}
@@ -2982,100 +3087,20 @@ Parse_File(const char *name, int fd)
continue;
}
#endif
- {
- VarAssign var;
- if (Parse_IsVar(line, &var)) {
- FinishDependencyGroup();
- Parse_DoVar(&var, VAR_GLOBAL);
- continue;
- }
- }
+ if (ParseVarassign(line))
+ continue;
+#ifndef POSIX
+ if (ParseNoviceMistake())
+ continue;
+#endif
FinishDependencyGroup();
- /*
- * For some reason - probably to make the parser impossible -
- * a ';' can be used to separate commands from dependencies.
- * Attempt to avoid ';' inside substitution patterns.
- */
- {
- int level = 0;
-
- for (cp = line; *cp != 0; cp++) {
- if (*cp == '\\' && cp[1] != 0) {
- cp++;
- continue;
- }
- if (*cp == '$' &&
- (cp[1] == '(' || cp[1] == '{')) {
- level++;
- continue;
- }
- if (level > 0) {
- if (*cp == ')' || *cp == '}') {
- level--;
- continue;
- }
- } else if (*cp == ';') {
- break;
- }
- }
- }
- if (*cp != 0)
- /* Terminate the dependency list at the ';' */
- *cp++ = 0;
- else
- cp = NULL;
-
- /*
- * We now know it's a dependency line so it needs to have all
- * variables expanded before being parsed.
- *
- * XXX: Ideally the dependency line would first be split into
- * its left-hand side, dependency operator and right-hand side,
- * and then each side would be expanded on its own. This would
- * allow for the left-hand side to allow only defined variables
- * and to allow variables on the right-hand side to be undefined
- * as well.
- *
- * Parsing the line first would also prevent that targets
- * generated from variable expressions are interpreted as the
- * dependency operator, such as in "target${:U:} middle: source",
- * in which the middle is interpreted as a source, not a target.
- */
{
- /* In lint mode, allow undefined variables to appear in
- * dependency lines.
- *
- * Ideally, only the right-hand side would allow undefined
- * variables since it is common to have no dependencies.
- * Having undefined variables on the left-hand side is more
- * unusual though. Since both sides are expanded in a single
- * pass, there is not much choice what to do here.
- *
- * In normal mode, it does not matter whether undefined
- * variables are allowed or not since as of 2020-09-14,
- * Var_Parse does not print any parse errors in such a case.
- * It simply returns the special empty string var_Error,
- * which cannot be detected in the result of Var_Subst. */
- VarEvalFlags eflags = DEBUG(LINT)
- ? VARE_WANTRES
- : VARE_UNDEFERR|VARE_WANTRES;
- (void)Var_Subst(line, VAR_CMD, eflags, &line);
- /* TODO: handle errors */
- }
-
- /* Need a fresh list for the target nodes */
- if (targets != NULL)
- Lst_Free(targets);
- targets = Lst_Init();
-
- ParseDoDependency(line);
- free(line);
-
- /* If there were commands after a ';', add them now */
- if (cp != NULL) {
- goto shellCommand;
+ const char *shellcmd;
+ ParseDependency(line, &shellcmd);
+ if (shellcmd != NULL)
+ ParseLine_ShellCommand(shellcmd);
}
}
/*