aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2021-06-25 21:31:14 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2021-06-25 21:31:14 +0000
commitb0c40a00a67f611868fc0f10bde6b28eb75931be (patch)
tree52a1546ba15bad38b8f9613fd19d4215195bb0cf
parentcab31e0e216c7defefd4aba14693ba2252ea7308 (diff)
parentee914ef902ae018bd4f67192832120f9bf05651f (diff)
downloadsrc-b0c40a00a67f611868fc0f10bde6b28eb75931be.tar.gz
src-b0c40a00a67f611868fc0f10bde6b28eb75931be.zip
Merge commit 'ee914ef902ae018bd4f67192832120f9bf05651f' into new_merge
-rw-r--r--contrib/bmake/ChangeLog113
-rw-r--r--contrib/bmake/FILES13
-rw-r--r--contrib/bmake/VERSION2
-rw-r--r--contrib/bmake/arch.c104
-rw-r--r--contrib/bmake/buf.h4
-rw-r--r--contrib/bmake/compat.c42
-rw-r--r--contrib/bmake/cond.c279
-rw-r--r--contrib/bmake/dir.c62
-rw-r--r--contrib/bmake/dir.h6
-rwxr-xr-xcontrib/bmake/enum.h62
-rw-r--r--contrib/bmake/for.c46
-rw-r--r--contrib/bmake/hash.c68
-rw-r--r--contrib/bmake/hash.h14
-rwxr-xr-xcontrib/bmake/import.sh17
-rw-r--r--contrib/bmake/job.c423
-rw-r--r--contrib/bmake/job.h20
-rw-r--r--contrib/bmake/lst.c10
-rw-r--r--contrib/bmake/lst.h12
-rw-r--r--contrib/bmake/main.c250
-rw-r--r--contrib/bmake/make.c91
-rw-r--r--contrib/bmake/make.h129
-rw-r--r--contrib/bmake/meta.c194
-rw-r--r--contrib/bmake/meta.h8
-rw-r--r--contrib/bmake/metachar.h4
-rw-r--r--contrib/bmake/mk/ChangeLog35
-rw-r--r--contrib/bmake/mk/dirdeps.mk76
-rw-r--r--contrib/bmake/mk/dpadd.mk7
-rwxr-xr-xcontrib/bmake/mk/install-mk4
-rw-r--r--contrib/bmake/mk/meta.autodep.mk6
-rwxr-xr-xcontrib/bmake/mk/meta2deps.py54
-rw-r--r--contrib/bmake/mk/rst2htm.mk6
-rw-r--r--contrib/bmake/nonints.h153
-rw-r--r--contrib/bmake/parse.c512
-rw-r--r--contrib/bmake/str.c101
-rw-r--r--contrib/bmake/str.h366
-rw-r--r--contrib/bmake/suff.c71
-rw-r--r--contrib/bmake/targ.c14
-rw-r--r--contrib/bmake/unit-tests/Makefile28
-rw-r--r--contrib/bmake/unit-tests/archive.mk6
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-jobs.exp2
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-lint.exp2
-rw-r--r--contrib/bmake/unit-tests/cmd-errors.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk10
-rw-r--r--contrib/bmake/unit-tests/cond-func-make-main.mk6
-rw-r--r--contrib/bmake/unit-tests/cond-late.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-short.mk14
-rw-r--r--contrib/bmake/unit-tests/cond-token-string.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-token-var.mk23
-rw-r--r--contrib/bmake/unit-tests/cond1.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/counter-append.mk4
-rw-r--r--contrib/bmake/unit-tests/counter.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.mk4
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.exp10
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.exp3
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.mk18
-rw-r--r--contrib/bmake/unit-tests/deptgt.exp8
-rw-r--r--contrib/bmake/unit-tests/deptgt.mk4
-rw-r--r--contrib/bmake/unit-tests/directive-export-impl.exp88
-rw-r--r--contrib/bmake/unit-tests/directive-export-impl.mk23
-rw-r--r--contrib/bmake/unit-tests/directive-export.mk15
-rw-r--r--contrib/bmake/unit-tests/directive-for-errors.exp2
-rw-r--r--contrib/bmake/unit-tests/directive-for-errors.mk6
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.exp32
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.mk21
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for.exp2
-rw-r--r--contrib/bmake/unit-tests/directive-undef.exp3
-rw-r--r--contrib/bmake/unit-tests/directive-undef.mk21
-rw-r--r--contrib/bmake/unit-tests/directive-unexport-env.exp20
-rw-r--r--contrib/bmake/unit-tests/directive.exp8
-rw-r--r--contrib/bmake/unit-tests/include-main.exp2
-rw-r--r--contrib/bmake/unit-tests/job-output-null.exp4
-rw-r--r--contrib/bmake/unit-tests/job-output-null.mk32
-rw-r--r--contrib/bmake/unit-tests/jobs-empty-commands-error.exp5
-rw-r--r--contrib/bmake/unit-tests/jobs-empty-commands-error.mk19
-rw-r--r--contrib/bmake/unit-tests/moderrs.exp97
-rw-r--r--contrib/bmake/unit-tests/moderrs.mk25
-rw-r--r--contrib/bmake/unit-tests/modts.exp4
-rw-r--r--contrib/bmake/unit-tests/modword.exp24
-rw-r--r--contrib/bmake/unit-tests/modword.mk3
-rw-r--r--contrib/bmake/unit-tests/opt-chdir.mk6
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.exp48
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.mk36
-rw-r--r--contrib/bmake/unit-tests/opt-debug-lint.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-debug-lint.mk20
-rw-r--r--contrib/bmake/unit-tests/opt-debug.exp6
-rw-r--r--contrib/bmake/unit-tests/opt-file.mk6
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-no-action.mk4
-rw-r--r--contrib/bmake/unit-tests/recursive.mk7
-rw-r--r--contrib/bmake/unit-tests/sh-jobs.mk8
-rw-r--r--contrib/bmake/unit-tests/shell-csh.mk4
-rw-r--r--contrib/bmake/unit-tests/suff-incomplete.exp10
-rw-r--r--contrib/bmake/unit-tests/suff-main-several.exp24
-rw-r--r--contrib/bmake/unit-tests/suff-rebuild.exp14
-rw-r--r--contrib/bmake/unit-tests/var-class-cmdline.exp3
-rw-r--r--contrib/bmake/unit-tests/var-class-cmdline.mk80
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.exp29
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.mk163
-rw-r--r--contrib/bmake/unit-tests/var-op-append.exp12
-rw-r--r--contrib/bmake/unit-tests/var-op-append.mk4
-rw-r--r--contrib/bmake/unit-tests/var-op-assign.mk4
-rw-r--r--contrib/bmake/unit-tests/var-op-sunsh.mk12
-rw-r--r--contrib/bmake/unit-tests/varcmd.mk14
-rw-r--r--contrib/bmake/unit-tests/vardebug.exp119
-rw-r--r--contrib/bmake/unit-tests/varmisc.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.exp22
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.mk39
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.exp34
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.mk6
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.exp12
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.mk26
-rw-r--r--contrib/bmake/unit-tests/varmod-hash.exp6
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.exp22
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.mk64
-rw-r--r--contrib/bmake/unit-tests/varmod-indirect.exp84
-rw-r--r--contrib/bmake/unit-tests/varmod-indirect.mk100
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.exp11
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.mk127
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.exp19
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.mk156
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.exp74
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.mk8
-rw-r--r--contrib/bmake/unit-tests/varmod-order.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-range.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-remember.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-remember.mk29
-rw-r--r--contrib/bmake/unit-tests/varmod-shell.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.exp25
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.mk54
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-sun-shell.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-sun-shell.mk21
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.exp147
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.mk57
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.exp12
-rw-r--r--contrib/bmake/unit-tests/varmod-unique.mk12
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-shell.exp32
-rw-r--r--contrib/bmake/unit-tests/varname-empty.exp60
-rwxr-xr-xcontrib/bmake/unit-tests/varname-empty.mk4
-rw-r--r--contrib/bmake/unit-tests/varname.exp33
-rw-r--r--contrib/bmake/unit-tests/varparse-dynamic.mk6
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.exp4
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.mk4
-rw-r--r--contrib/bmake/var.c3057
144 files changed, 5491 insertions, 3540 deletions
diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog
index 5cf6f9d8fe57..35235e1f8205 100644
--- a/contrib/bmake/ChangeLog
+++ b/contrib/bmake/ChangeLog
@@ -1,3 +1,116 @@
+2021-06-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210621
+ Merge with NetBSD make, pick up
+ o var.c: only report error for unmatched regex subexpression
+ when linting (-dL) since we cannot tell when an unmatched
+ subexpression is an expected result.
+ o move unmatched regex subexpression tests to
+ varmod-subst-regex.mk and enable strict (lint) mode
+
+2021-06-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210616
+ Merge with NetBSD make, pick up
+ o more unit tests
+ o cond.c: rename If_Eval to EvalBare
+ improve function names for parsing conditions
+ o job.c: fix error handling of targets that cannot be made
+ o var.c: uncompress code in ApplyModifier_Unique
+
+2021-05-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210518
+ Merge with NetBSD make, pick up
+ o fix unit-tests/opt-chdir to cope with /nonexistent existing.
+ o job.c: Print -de error information when running multiple jobs
+
+2021-04-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210420
+ Merge with NetBSD make, pick up
+ o use C99 bool type
+ o convert VarEvalFlags back into an enum
+ o cond.c: do not complain when skipping the condition 'no >= 10'
+ o hash.c: avoid allocating memory for simple variable names
+ o job.c: use distinct wording for writing to the shell commands file
+ remove type name for the abort status in job handling
+ rename PrintOutput to PrintFilteredOutput to avoid confusion
+ o main.c: avoid double slash in name of temporary directory
+ o var.c: use straight quotes for error 'Bad conditional expression'
+ reduce memory allocations in the modifiers ':D' and ':U'
+ rename members of ModifyWord_LoopArgs
+ clean up pattern flags for the modifiers ':S' and ':C'
+ reduce memory allocation and strlen calls in modifier ':from=to'
+ in the ':Q' modifier, only allocate memory if necessary
+ improve performance for LazyBuf
+ remove redundant parameter from ParseVarnameLong
+ migrate ParseModifierPart to use Substring
+ avoid unnecessary calls to strlen when evaluating modifiers
+ migrate ModifyWord functions to use Substring
+ migrate handling of the modifier ':S,from,to,' to Substring
+ reduce debug logging and memory allocation for ${:U...}
+ reduce verbosity of the -dv debug logging for standard cases
+ clean up debug logging for ':M' and ':N'
+ disallow '$' in the variable name of the modifier ':@'
+ simplify access to the name of an expression during evaluation
+
+2021-03-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210330
+ Merge with NetBSD make, pick up
+ o replace enum bit-field with struct bit-field for VarEvalFlags
+ o rename VARE_NONE to VARE_PARSE_ONLY
+ o var.c: rename ApplyModifiersState to ModChain
+ fix double varname expansion in the variable modifier '::='
+ change debug log for variable evaluation flags to lowercase
+
+2021-03-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210314
+ Merge with NetBSD make, pick up
+ o var.c: avoid evaluating many modifiers in parse only mode
+ in strict mode (-dL) many variable references are parsed twice,
+ the first time just to report parse errors early, so we want to
+ avoid side effects and wasted effort to the extent possible.
+
+2021-02-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210226
+ Merge with NetBSD make, pick up
+ o remove freestanding freeIt variables
+ link via FStr
+ o var.c: restructure code in ParseVarname to target human readers
+ improve error message for;
+ bad modifier in variable expression
+ unclosed modifier
+ unknown modifier
+ remove redundant parameter of ApplySingleModifier
+ explain non-obvious code around indirect variable modifiers
+ quote ':S' in error message about missing delimiter
+ extract ParseModifier_Match into separate function
+ add context information to error message about ':range' modifier
+ add quotes around variable name in an error message
+ reorder code in ModifyWords
+ use more common parameter order for VarSelectWords
+ make ModifyWord_Subst a little easier to understand
+ do not expand variable name from the command line twice
+ extract ExistsInCmdline from Var_SetWithFlags
+ save a hash map lookup when defining a cmdline variable
+ clean up VarAdd, Var_Delete, Var_ReexportVars
+ use bit-shift expressions for VarFlags constants
+ rename constants for VarFlags
+ rename ExprDefined constants for debug logging
+ rename ExprStatus to ExprDefined
+ split parameters for evaluating variable expressions
+ reduce redundant code around ModifyWords
+ print error about failed shell command before overwriting variable
+ clean up ValidShortVarname, ParseVarnameShort
+ rename VarExprStatus to ExprStatus
+ add functions for assigning the value of an expression
+ rename ApplyModifiersState_Define to Expr_Define
+ condense the code for parsing :S and :C modifiers
+
2021-02-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210206
diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES
index 5e7b8301da87..dc0f6f33c763 100644
--- a/contrib/bmake/FILES
+++ b/contrib/bmake/FILES
@@ -63,6 +63,7 @@ realpath.c
setenv.c
sigcompat.c
str.c
+str.h
stresep.c
strlcpy.c
suff.c
@@ -399,6 +400,10 @@ unit-tests/job-flags.exp
unit-tests/job-flags.mk
unit-tests/job-output-long-lines.exp
unit-tests/job-output-long-lines.mk
+unit-tests/job-output-null.exp
+unit-tests/job-output-null.mk
+unit-tests/jobs-empty-commands-error.exp
+unit-tests/jobs-empty-commands-error.mk
unit-tests/jobs-empty-commands.exp
unit-tests/jobs-empty-commands.mk
unit-tests/jobs-error-indirect.exp
@@ -439,6 +444,8 @@ unit-tests/opt-debug-curdir.exp
unit-tests/opt-debug-curdir.mk
unit-tests/opt-debug-dir.exp
unit-tests/opt-debug-dir.mk
+unit-tests/opt-debug-errors-jobs.exp
+unit-tests/opt-debug-errors-jobs.mk
unit-tests/opt-debug-errors.exp
unit-tests/opt-debug-errors.mk
unit-tests/opt-debug-file.exp
@@ -627,6 +634,8 @@ unit-tests/var-class-local.exp
unit-tests/var-class-local.mk
unit-tests/var-class.exp
unit-tests/var-class.mk
+unit-tests/var-eval-short.exp
+unit-tests/var-eval-short.mk
unit-tests/var-op-append.exp
unit-tests/var-op-append.mk
unit-tests/var-op-assign.exp
@@ -675,6 +684,8 @@ unit-tests/varmod-l-name-to-value.exp
unit-tests/varmod-l-name-to-value.mk
unit-tests/varmod-localtime.exp
unit-tests/varmod-localtime.mk
+unit-tests/varmod-loop-varname.exp
+unit-tests/varmod-loop-varname.mk
unit-tests/varmod-loop.exp
unit-tests/varmod-loop.mk
unit-tests/varmod-match-escape.exp
@@ -709,6 +720,8 @@ unit-tests/varmod-subst-regex.exp
unit-tests/varmod-subst-regex.mk
unit-tests/varmod-subst.exp
unit-tests/varmod-subst.mk
+unit-tests/varmod-sun-shell.exp
+unit-tests/varmod-sun-shell.mk
unit-tests/varmod-sysv.exp
unit-tests/varmod-sysv.mk
unit-tests/varmod-tail.exp
diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION
index 0af794962680..7c28f11013b7 100644
--- a/contrib/bmake/VERSION
+++ b/contrib/bmake/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20210206
+_MAKE_VERSION=20210621
diff --git a/contrib/bmake/arch.c b/contrib/bmake/arch.c
index e5c0a5e4ac0f..6d9dd60dfbe9 100644
--- a/contrib/bmake/arch.c
+++ b/contrib/bmake/arch.c
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -147,7 +147,7 @@ struct ar_hdr {
#include "dir.h"
/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@@ -216,6 +216,19 @@ ArchFree(void *ap)
}
#endif
+/* Return "archive(member)". */
+static char *
+FullName(const char *archive, const char *member)
+{
+ size_t len1 = strlen(archive);
+ size_t len3 = strlen(member);
+ char *result = bmake_malloc(len1 + 1 + len3 + 1 + 1);
+ memcpy(result, archive, len1);
+ memcpy(result + len1, "(", 1);
+ memcpy(result + len1 + 1, member, len3);
+ memcpy(result + len1 + 1 + len3, ")", 1 + 1);
+ return result;
+}
/*
* Parse an archive specification such as "archive.a(member1 member2.${EXT})",
@@ -228,10 +241,10 @@ ArchFree(void *ap)
* scope The scope in which to expand variables.
*
* Output:
- * return TRUE if it was a valid specification.
+ * return True if it was a valid specification.
* *pp Points to the first non-space after the archive spec.
*/
-Boolean
+bool
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
{
char *cp; /* Pointer into line */
@@ -239,12 +252,12 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
MFStr libName; /* Library-part of specification */
char *memName; /* Member-part of specification */
char saveChar; /* Ending delimiter of member-name */
- Boolean expandLibName; /* Whether the parsed libName contains
+ bool expandLibName; /* Whether the parsed libName contains
* variable expressions that need to be
* expanded */
libName = MFStr_InitRefer(*pp);
- expandLibName = FALSE;
+ expandLibName = false;
for (cp = libName.str; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
@@ -252,18 +265,18 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* XXX: This code can probably be shortened. */
const char *nested_p = cp;
FStr result;
- Boolean isError;
+ bool isError;
/* XXX: is expanded twice: once here and once below */
(void)Var_Parse(&nested_p, scope,
- VARE_WANTRES | VARE_UNDEFERR, &result);
+ VARE_UNDEFERR, &result);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
if (isError)
- return FALSE;
+ return false;
- expandLibName = TRUE;
+ expandLibName = true;
cp += nested_p - cp;
} else
cp++;
@@ -272,8 +285,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
*cp++ = '\0';
if (expandLibName) {
char *expanded;
- (void)Var_Subst(libName.str, scope,
- VARE_WANTRES | VARE_UNDEFERR, &expanded);
+ (void)Var_Subst(libName.str, scope, VARE_UNDEFERR, &expanded);
/* TODO: handle errors */
libName = MFStr_InitOwn(expanded);
}
@@ -285,7 +297,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* place and skip to the end of it (either white-space or
* a close paren).
*/
- Boolean doSubst = FALSE;
+ bool doSubst = false;
pp_skip_whitespace(&cp);
@@ -295,20 +307,19 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* Expand nested variable expressions. */
/* XXX: This code can probably be shortened. */
FStr result;
- Boolean isError;
+ bool isError;
const char *nested_p = cp;
(void)Var_Parse(&nested_p, scope,
- VARE_WANTRES | VARE_UNDEFERR,
- &result);
+ VARE_UNDEFERR, &result);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
if (isError)
- return FALSE;
+ return false;
- doSubst = TRUE;
+ doSubst = true;
cp += nested_p - cp;
} else {
cp++;
@@ -325,7 +336,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Parse_Error(PARSE_FATAL,
"No closing parenthesis "
"in archive specification");
- return FALSE;
+ return false;
}
/*
@@ -355,16 +366,15 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
char *p;
char *unexpandedMemName = memName;
- (void)Var_Subst(memName, scope,
- VARE_WANTRES | VARE_UNDEFERR,
- &memName);
+ (void)Var_Subst(memName, scope, VARE_UNDEFERR,
+ &memName);
/* TODO: handle errors */
/*
* Now form an archive spec and recurse to deal with
* nested variables and multi-word variable values.
*/
- fullName = str_concat4(libName.str, "(", memName, ")");
+ fullName = FullName(libName.str, memName);
p = fullName;
if (strchr(memName, '$') != NULL &&
@@ -383,7 +393,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* Error in nested call. */
free(fullName);
/* XXX: does unexpandedMemName leak? */
- return FALSE;
+ return false;
}
free(fullName);
/* XXX: does unexpandedMemName leak? */
@@ -394,8 +404,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
while (!Lst_IsEmpty(&members)) {
char *member = Lst_Dequeue(&members);
- char *fullname = str_concat4(libName.str, "(",
- member, ")");
+ char *fullname = FullName(libName.str, member);
free(member);
gn = Targ_GetNode(fullname);
@@ -407,8 +416,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Lst_Done(&members);
} else {
- char *fullname = str_concat4(libName.str, "(", memName,
- ")");
+ char *fullname = FullName(libName.str, memName);
gn = Targ_GetNode(fullname);
free(fullname);
@@ -434,7 +442,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* We promised that pp would be set up at the next non-space. */
pp_skip_whitespace(&cp);
*pp = cp;
- return TRUE;
+ return true;
}
/*
@@ -444,7 +452,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* Input:
* archive Path to the archive
* member Name of member; only its basename is used.
- * addToCache TRUE if archive should be cached if not already so.
+ * addToCache True if archive should be cached if not already so.
*
* Results:
* The ar_hdr for the member, or NULL.
@@ -452,7 +460,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* See ArchFindMember for an almost identical copy of this code.
*/
static struct ar_hdr *
-ArchStatMember(const char *archive, const char *member, Boolean addToCache)
+ArchStatMember(const char *archive, const char *member, bool addToCache)
{
#define AR_MAX_NAME_LEN (sizeof arh.ar_name - 1)
FILE *arch;
@@ -713,7 +721,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
#endif
-static Boolean
+static bool
ArchiveMember_HasName(const struct ar_hdr *hdr,
const char *name, size_t namelen)
{
@@ -721,22 +729,22 @@ ArchiveMember_HasName(const struct ar_hdr *hdr,
const char *ar_name = hdr->AR_NAME;
if (strncmp(ar_name, name, namelen) != 0)
- return FALSE;
+ return false;
if (namelen >= ar_name_len)
return namelen == ar_name_len;
/* hdr->AR_NAME is space-padded to the right. */
if (ar_name[namelen] == ' ')
- return TRUE;
+ return true;
/* In archives created by GNU binutils 2.27, the member names end with
* a slash. */
if (ar_name[namelen] == '/' &&
(namelen == ar_name_len || ar_name[namelen + 1] == ' '))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
/*
@@ -951,7 +959,7 @@ Arch_UpdateMTime(GNode *gn)
{
struct ar_hdr *arh;
- arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
+ arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), true);
if (arh != NULL)
gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10);
else
@@ -1058,26 +1066,26 @@ Arch_FindLib(GNode *gn, SearchPath *path)
* since this is used by 'ar' rules that affect the data contents of the
* archive, not by ranlib rules, which affect the TOC.
*/
-Boolean
+bool
Arch_LibOODate(GNode *gn)
{
- Boolean oodate;
+ bool oodate;
if (gn->type & OP_PHONY) {
- oodate = TRUE;
+ oodate = true;
} else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) {
- oodate = FALSE;
+ oodate = false;
} else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
(gn->mtime > now) ||
(gn->youngestChild != NULL &&
gn->mtime < gn->youngestChild->mtime)) {
- oodate = TRUE;
+ oodate = true;
} else {
#ifdef RANLIBMAG
struct ar_hdr *arh; /* Header for __.SYMDEF */
int modTimeTOC; /* The table-of-contents' mod time */
- arh = ArchStatMember(gn->path, RANLIBMAG, FALSE);
+ arh = ArchStatMember(gn->path, RANLIBMAG, false);
if (arh != NULL) {
modTimeTOC = (int)strtol(arh->ar_date, NULL, 10);
@@ -1094,10 +1102,10 @@ Arch_LibOODate(GNode *gn)
*/
if (DEBUG(ARCH) || DEBUG(MAKE))
debug_printf("no toc...");
- oodate = TRUE;
+ oodate = true;
}
#else
- oodate = FALSE;
+ oodate = false;
#endif
}
return oodate;
@@ -1119,7 +1127,7 @@ Arch_End(void)
#endif
}
-Boolean
+bool
Arch_IsLib(GNode *gn)
{
static const char armag[] = "!<arch>\n";
@@ -1127,11 +1135,11 @@ Arch_IsLib(GNode *gn)
int fd;
if ((fd = open(gn->path, O_RDONLY)) == -1)
- return FALSE;
+ return false;
if (read(fd, buf, sizeof buf) != sizeof buf) {
(void)close(fd);
- return FALSE;
+ return false;
}
(void)close(fd);
diff --git a/contrib/bmake/buf.h b/contrib/bmake/buf.h
index 594e9651dbfb..938820e4745f 100644
--- a/contrib/bmake/buf.h
+++ b/contrib/bmake/buf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.h,v 1.42 2021/01/30 21:25:10 rillig Exp $ */
+/* $NetBSD: buf.h,v 1.43 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -101,7 +101,7 @@ Buf_AddByte(Buffer *buf, char byte)
end[1] = '\0';
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
Buf_EndsWith(const Buffer *buf, char ch)
{
return buf->len > 0 && buf->data[buf->len - 1] == ch;
diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c
index 59190d8c4354..f8c47397f3df 100644
--- a/contrib/bmake/compat.c
+++ b/contrib/bmake/compat.c
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -99,7 +99,7 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
@@ -164,7 +164,7 @@ CompatInterrupt(int signo)
}
static void
-DebugFailedTarget(const char *cmd, GNode *gn)
+DebugFailedTarget(const char *cmd, const GNode *gn)
{
const char *p = cmd;
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
@@ -184,7 +184,7 @@ DebugFailedTarget(const char *cmd, GNode *gn)
debug_printf("\n");
}
-static Boolean
+static bool
UseShell(const char *cmd MAKE_ATTR_UNUSED)
{
#if !defined(MAKE_NATIVE)
@@ -194,7 +194,7 @@ UseShell(const char *cmd MAKE_ATTR_UNUSED)
* behaviour. Or perhaps the shell has been replaced with something
* that does extra logging, and that should not be bypassed.
*/
- return TRUE;
+ return true;
#else
/*
* Search for meta characters in the command. If there are no meta
@@ -227,22 +227,22 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
{
char *cmdStart; /* Start of expanded command */
char *bp;
- Boolean silent; /* Don't print command */
- Boolean doIt; /* Execute even if -n */
- volatile Boolean errCheck; /* Check errors */
+ bool silent; /* Don't print command */
+ bool doIt; /* Execute even if -n */
+ volatile bool errCheck; /* Check errors */
WAIT_T reason; /* Reason for child's death */
WAIT_T status; /* Description of child's death */
pid_t cpid; /* Child actually found */
pid_t retstat; /* Result of wait */
const char **volatile av; /* Argument vector for thing to exec */
char **volatile mav; /* Copy of the argument vector for freeing */
- Boolean useShell; /* TRUE if command should be executed
+ bool useShell; /* True if command should be executed
* using a shell */
const char *volatile cmd = cmdp;
silent = (gn->type & OP_SILENT) != 0;
errCheck = !(gn->type & OP_IGNORE);
- doIt = FALSE;
+ doIt = false;
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
/* TODO: handle errors */
@@ -281,9 +281,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
if (*cmd == '@')
silent = !DEBUG(LOUD);
else if (*cmd == '-')
- errCheck = FALSE;
+ errCheck = false;
else if (*cmd == '+') {
- doIt = TRUE;
+ doIt = true;
if (shellName == NULL) /* we came here from jobs */
Shell_Init();
} else
@@ -343,7 +343,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
* command into words to form an argument vector we can
* execute.
*/
- Words words = Str_Words(cmd, FALSE);
+ Words words = Str_Words(cmd, false);
mav = words.words;
bp = words.freeIt;
av = (void *)mav;
@@ -392,7 +392,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
*/
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
- JobReapChild(retstat, reason, FALSE); /* not ours? */
+ JobReapChild(retstat, reason, false); /* not ours? */
if (retstat == -1 && errno != EINTR) {
break;
}
@@ -425,7 +425,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
if (errCheck) {
#ifdef USE_META
if (useMeta) {
- meta_job_error(NULL, gn, FALSE, status);
+ meta_job_error(NULL, gn, false, status);
}
#endif
gn->made = ERROR;
@@ -483,7 +483,7 @@ MakeNodes(GNodeList *gnodes, GNode *pgn)
}
}
-static Boolean
+static bool
MakeUnmade(GNode *gn, GNode *pgn)
{
@@ -493,7 +493,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* First mark ourselves to be made, then apply whatever transformations
* the suffix module thinks are necessary. Once that's done, we can
* descend and make all our children. If any of them has an error
- * but the -k flag was given, our 'make' field will be set to FALSE
+ * but the -k flag was given, our 'make' field will be set to false
* again. This is our signal to not attempt to do anything but abort
* our parent as well.
*/
@@ -508,7 +508,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
if (!(gn->flags & REMAKE)) {
gn->made = ABORTED;
pgn->flags &= ~(unsigned)REMAKE;
- return FALSE;
+ return false;
}
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
@@ -524,7 +524,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
if (!GNode_IsOODate(gn)) {
gn->made = UPTODATE;
DEBUG0(MAKE, "up-to-date.\n");
- return FALSE;
+ return false;
}
/*
@@ -539,7 +539,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* We need to be re-made.
* Ensure that $? (.OODATE) and $> (.ALLSRC) are both set.
*/
- Make_DoAllVar(gn);
+ GNode_SetLocalVars(gn);
/*
* Alter our type to tell if errors should be ignored or things
@@ -596,7 +596,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
PrintOnError(gn, "\nStop.");
exit(1);
}
- return TRUE;
+ return true;
}
static void
diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c
index 8f36fda22f12..a8d88d1d6816 100644
--- a/contrib/bmake/cond.c
+++ b/contrib/bmake/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -95,7 +95,7 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $");
/*
* The parsing of conditional expressions is based on this grammar:
@@ -148,11 +148,11 @@ typedef struct CondParser {
* expression has length > 0. The other '.if' variants delegate
* to evalBare instead.
*/
- Boolean plain;
+ bool plain;
/* The function to apply on unquoted bare words. */
- Boolean (*evalBare)(size_t, const char *);
- Boolean negateEvalBare;
+ bool (*evalBare)(size_t, const char *);
+ bool negateEvalBare;
const char *p; /* The remaining condition to parse */
Token curr; /* Single push-back token used in parsing */
@@ -163,10 +163,10 @@ typedef struct CondParser {
* specific one, therefore it makes sense to suppress the standard
* "Malformed conditional" message.
*/
- Boolean printedError;
+ bool printedError;
} CondParser;
-static CondResult CondParser_Or(CondParser *par, Boolean);
+static CondResult CondParser_Or(CondParser *par, bool);
static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */
@@ -178,21 +178,21 @@ static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
* In strict mode, the lhs must be a variable expression or a string literal
* in quotes. In non-strict mode it may also be an unquoted string literal.
*
- * TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
- * FALSE when CondEvalExpression is called from ApplyModifier_IfElse
+ * True when CondEvalExpression is called from Cond_EvalLine (.if etc).
+ * False when CondEvalExpression is called from ApplyModifier_IfElse
* since lhs is already expanded, and at that point we cannot tell if
* it was a variable reference or not.
*/
-static Boolean lhsStrict;
+static bool lhsStrict;
-static Boolean
+static bool
is_token(const char *str, const char *tok, size_t len)
{
return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
}
static Token
-ToToken(Boolean cond)
+ToToken(bool cond)
{
return cond ? TOK_TRUE : TOK_FALSE;
}
@@ -228,7 +228,7 @@ CondParser_SkipWhitespace(CondParser *par)
* Return the length of the argument, or 0 on error.
*/
static size_t
-ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
+ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
char **out_arg)
{
const char *p = *pp;
@@ -264,11 +264,11 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
* so we don't need to do it. Nor do we return an
* error, though perhaps we should.
*/
- VarEvalFlags eflags = doEval
- ? VARE_WANTRES | VARE_UNDEFERR
- : VARE_NONE;
+ VarEvalMode emode = doEval
+ ? VARE_UNDEFERR
+ : VARE_PARSE_ONLY;
FStr nestedVal;
- (void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal);
+ (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
/* TODO: handle errors */
Buf_AddStr(&argBuf, nestedVal.str);
FStr_Done(&nestedVal);
@@ -290,7 +290,7 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
if (func != NULL && *p++ != ')') {
Parse_Error(PARSE_FATAL,
"Missing closing parenthesis for %s()", func);
- par->printedError = TRUE;
+ par->printedError = true;
return 0;
}
@@ -300,34 +300,34 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
/* Test whether the given variable is defined. */
/*ARGSUSED*/
-static Boolean
+static bool
FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
FStr value = Var_Value(SCOPE_CMDLINE, arg);
- Boolean result = value.str != NULL;
+ bool result = value.str != NULL;
FStr_Done(&value);
return result;
}
/* See if the given target is being made. */
/*ARGSUSED*/
-static Boolean
+static bool
FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
StringListNode *ln;
for (ln = opts.create.first; ln != NULL; ln = ln->next)
if (Str_Match(ln->datum, arg))
- return TRUE;
- return FALSE;
+ return true;
+ return false;
}
/* See if the given file exists. */
/*ARGSUSED*/
-static Boolean
+static bool
FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- Boolean result;
+ bool result;
char *path;
path = Dir_FindFile(arg, &dirSearchPath);
@@ -340,7 +340,7 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
/* See if the given node exists and is an actual target. */
/*ARGSUSED*/
-static Boolean
+static bool
FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
GNode *gn = Targ_FindNode(arg);
@@ -352,7 +352,7 @@ FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
* associated with it.
*/
/*ARGSUSED*/
-static Boolean
+static bool
FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
GNode *gn = Targ_FindNode(arg);
@@ -365,10 +365,10 @@ FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
* then we try a floating point conversion instead.
*
* Results:
- * Returns TRUE if the conversion succeeded.
+ * Returns true if the conversion succeeded.
* Sets 'out_value' to the converted number.
*/
-static Boolean
+static bool
TryParseNumber(const char *str, double *out_value)
{
char *end;
@@ -378,29 +378,30 @@ TryParseNumber(const char *str, double *out_value)
errno = 0;
if (str[0] == '\0') { /* XXX: why is an empty string a number? */
*out_value = 0.0;
- return TRUE;
+ return true;
}
ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
if (*end == '\0' && errno != ERANGE) {
*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
- return TRUE;
+ return true;
}
if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
- return FALSE; /* skip the expensive strtod call */
+ return false; /* skip the expensive strtod call */
dbl_val = strtod(str, &end);
if (*end != '\0')
- return FALSE;
+ return false;
*out_value = dbl_val;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
is_separator(char ch)
{
- return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
+ return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
+ ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
}
/*
@@ -409,24 +410,24 @@ is_separator(char ch)
*
* Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
*/
-static Boolean
+static bool
CondParser_StringExpr(CondParser *par, const char *start,
- Boolean const doEval, Boolean const quoted,
+ bool const doEval, bool const quoted,
Buffer *buf, FStr *const inout_str)
{
- VarEvalFlags eflags;
+ VarEvalMode emode;
const char *nested_p;
- Boolean atStart;
+ bool atStart;
VarParseResult parseResult;
/* if we are in quotes, an undefined variable is ok */
- eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR
+ emode = doEval && !quoted ? VARE_UNDEFERR
: doEval ? VARE_WANTRES
- : VARE_NONE;
+ : VARE_PARSE_ONLY;
nested_p = par->p;
atStart = nested_p == start;
- parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, eflags, inout_str);
+ parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
/* TODO: handle errors */
if (inout_str->str == var_Error) {
if (parseResult == VPR_ERR) {
@@ -436,11 +437,11 @@ CondParser_StringExpr(CondParser *par, const char *start,
*
* See cond-token-plain.mk $$$$$$$$.
*/
- par->printedError = TRUE;
+ par->printedError = true;
}
/*
* XXX: Can there be any situation in which a returned
- * var_Error requires freeIt?
+ * var_Error needs to be freed?
*/
FStr_Done(inout_str);
/*
@@ -448,7 +449,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
* what getting var_Error back with !doEval means.
*/
*inout_str = FStr_InitRefer(NULL);
- return FALSE;
+ return false;
}
par->p = nested_p;
@@ -458,30 +459,30 @@ CondParser_StringExpr(CondParser *par, const char *start,
* comparison operator or is the end of the expression, we are done.
*/
if (atStart && is_separator(par->p[0]))
- return FALSE;
+ return false;
Buf_AddStr(buf, inout_str->str);
FStr_Done(inout_str);
*inout_str = FStr_InitRefer(NULL); /* not finished yet */
- return TRUE;
+ return true;
}
/*
- * Parse a string from a variable reference or an optionally quoted
- * string. This is called for the lhs and rhs of string comparisons.
+ * Parse a string from a variable expression or an optionally quoted
+ * string. This is called for the left-hand and right-hand sides of
+ * comparisons.
*
* Results:
* Returns the string, absent any quotes, or NULL on error.
- * Sets out_quoted if the string was quoted.
- * Sets out_freeIt.
+ * Sets out_quoted if the leaf was a quoted string literal.
*/
static void
-CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
- FStr *out_str, Boolean *out_quoted)
+CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
+ FStr *out_str, bool *out_quoted)
{
Buffer buf;
FStr str;
- Boolean quoted;
+ bool quoted;
const char *start;
Buf_Init(&buf);
@@ -541,14 +542,14 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
got_str:
str = FStr_InitOwn(buf.data);
cleanup:
- Buf_DoneData(&buf);
+ Buf_DoneData(&buf); /* XXX: memory leak on failure? */
*out_str = str;
}
-static Boolean
-If_Eval(const CondParser *par, const char *arg, size_t arglen)
+static bool
+EvalBare(const CondParser *par, const char *arg, size_t arglen)
{
- Boolean res = par->evalBare(arglen, arg);
+ bool res = par->evalBare(arglen, arg);
return par->negateEvalBare ? !res : res;
}
@@ -556,8 +557,8 @@ If_Eval(const CondParser *par, const char *arg, size_t arglen)
* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
* ".if 0".
*/
-static Boolean
-EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
+static bool
+EvalNotEmpty(CondParser *par, const char *value, bool quoted)
{
double num;
@@ -576,11 +577,11 @@ EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
if (par->plain)
return value[0] != '\0';
- return If_Eval(par, value, strlen(value));
+ return EvalBare(par, value, strlen(value));
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
-static Boolean
+static bool
EvalCompareNum(double lhs, ComparisonOp op, double rhs)
{
DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
@@ -608,7 +609,7 @@ EvalCompareStr(CondParser *par, const char *lhs,
if (op != EQ && op != NE) {
Parse_Error(PARSE_FATAL,
"String comparison operator must be either == or !=");
- par->printedError = TRUE;
+ par->printedError = true;
return TOK_ERROR;
}
@@ -619,8 +620,8 @@ EvalCompareStr(CondParser *par, const char *lhs,
/* Evaluate a comparison, such as "${VAR} == 12345". */
static Token
-EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
- ComparisonOp op, const char *rhs, Boolean rhsQuoted)
+EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
+ ComparisonOp op, const char *rhs, bool rhsQuoted)
{
double left, right;
@@ -631,7 +632,7 @@ EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
return EvalCompareStr(par, lhs, op, rhs);
}
-static Boolean
+static bool
CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
{
const char *p = par->p;
@@ -655,14 +656,14 @@ CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
*out_op = NE;
goto length_2;
}
- return FALSE;
+ return false;
length_2:
par->p = p + 2;
- return TRUE;
+ return true;
length_1:
par->p = p + 1;
- return TRUE;
+ return true;
}
/*
@@ -674,18 +675,18 @@ length_1:
* ${VAR:U0} < 12345
*/
static Token
-CondParser_Comparison(CondParser *par, Boolean doEval)
+CondParser_Comparison(CondParser *par, bool doEval)
{
Token t = TOK_ERROR;
FStr lhs, rhs;
ComparisonOp op;
- Boolean lhsQuoted, rhsQuoted;
+ bool lhsQuoted, rhsQuoted;
/*
* Parse the variable spec and skip over it, saving its
* value in lhs.
*/
- CondParser_String(par, doEval, lhsStrict, &lhs, &lhsQuoted);
+ CondParser_Leaf(par, doEval, lhsStrict, &lhs, &lhsQuoted);
if (lhs.str == NULL)
goto done_lhs;
@@ -702,11 +703,11 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
if (par->p[0] == '\0') {
Parse_Error(PARSE_FATAL,
"Missing right-hand-side of operator '%s'", opname[op]);
- par->printedError = TRUE;
+ par->printedError = true;
goto done_lhs;
}
- CondParser_String(par, doEval, FALSE, &rhs, &rhsQuoted);
+ CondParser_Leaf(par, doEval, false, &rhs, &rhsQuoted);
if (rhs.str == NULL)
goto done_rhs;
@@ -731,7 +732,7 @@ done_lhs:
/*ARGSUSED*/
static size_t
ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
- Boolean doEval, const char *func MAKE_ATTR_UNUSED,
+ bool doEval, const char *func MAKE_ATTR_UNUSED,
char **out_arg)
{
FStr val;
@@ -741,8 +742,8 @@ ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
*out_arg = NULL;
(*pp)--; /* Make (*pp)[1] point to the '('. */
- (void)Var_Parse(pp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
- &val);
+ (void)Var_Parse(pp, SCOPE_CMDLINE,
+ doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
/* TODO: handle errors */
/* If successful, *pp points beyond the closing ')' now. */
@@ -767,22 +768,23 @@ ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
}
/*ARGSUSED*/
-static Boolean
+static bool
FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
{
/* Magic values ahead, see ParseEmptyArg. */
return arglen == 1;
}
-static Boolean
-CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
+/* Parse a function call expression, such as 'defined(${file})'. */
+static bool
+CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
{
static const struct fn_def {
const char *fn_name;
size_t fn_name_len;
- size_t (*fn_parse)(CondParser *, const char **, Boolean,
+ size_t (*fn_parse)(CondParser *, const char **, bool,
const char *, char **);
- Boolean (*fn_eval)(size_t, const char *);
+ bool (*fn_eval)(size_t, const char *);
} fns[] = {
{ "defined", 7, ParseFuncArg, FuncDefined },
{ "make", 4, ParseFuncArg, FuncMake },
@@ -810,25 +812,25 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
if (arglen == 0 || arglen == (size_t)-1) {
par->p = cp;
*out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
- return TRUE;
+ return true;
}
/* Evaluate the argument using the required function. */
*out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
free(arg);
par->p = cp;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
- * Parse a function call, a number, a variable expression or a string
- * literal.
+ * Parse a comparison such as '${VAR} == "value"', or a simple leaf without
+ * operator, which is a number, a variable expression or a string literal.
*/
static Token
-CondParser_LeafToken(CondParser *par, Boolean doEval)
+CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
{
Token t;
char *arg = NULL;
@@ -836,9 +838,6 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
const char *cp;
const char *cp1;
- if (CondParser_Func(par, doEval, &t))
- return t;
-
/* Push anything numeric through the compare expression */
cp = par->p;
if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
@@ -852,10 +851,14 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
+ /*
+ * XXX: Is it possible to have a variable expression evaluated twice
+ * at this point?
+ */
arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
cp1 = cp;
cpp_skip_whitespace(&cp1);
- if (*cp1 == '=' || *cp1 == '!')
+ if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
return CondParser_Comparison(par, doEval);
par->p = cp;
@@ -865,14 +868,14 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion.
*/
- t = ToToken(!doEval || If_Eval(par, arg, arglen));
+ t = ToToken(!doEval || EvalBare(par, arg, arglen));
free(arg);
return t;
}
/* Return the next token or comparison result from the parser. */
static Token
-CondParser_Token(CondParser *par, Boolean doEval)
+CondParser_Token(CondParser *par, bool doEval)
{
Token t;
@@ -900,7 +903,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
par->p++;
else if (opts.strict) {
Parse_Error(PARSE_FATAL, "Unknown operator '|'");
- par->printedError = TRUE;
+ par->printedError = true;
return TOK_ERROR;
}
return TOK_OR;
@@ -911,7 +914,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
par->p++;
else if (opts.strict) {
Parse_Error(PARSE_FATAL, "Unknown operator '&'");
- par->printedError = TRUE;
+ par->printedError = true;
return TOK_ERROR;
}
return TOK_AND;
@@ -931,7 +934,9 @@ CondParser_Token(CondParser *par, Boolean doEval)
return CondParser_Comparison(par, doEval);
default:
- return CondParser_LeafToken(par, doEval);
+ if (CondParser_FuncCall(par, doEval, &t))
+ return t;
+ return CondParser_ComparisonOrLeaf(par, doEval);
}
}
@@ -942,7 +947,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
* Term -> Leaf
*/
static CondResult
-CondParser_Term(CondParser *par, Boolean doEval)
+CondParser_Term(CondParser *par, bool doEval)
{
CondResult res;
Token t;
@@ -979,7 +984,7 @@ CondParser_Term(CondParser *par, Boolean doEval)
* And -> Term
*/
static CondResult
-CondParser_And(CondParser *par, Boolean doEval)
+CondParser_And(CondParser *par, bool doEval)
{
CondResult res;
Token op;
@@ -992,7 +997,7 @@ CondParser_And(CondParser *par, Boolean doEval)
if (op == TOK_AND) {
if (res == CR_TRUE)
return CondParser_And(par, doEval);
- if (CondParser_And(par, FALSE) == CR_ERROR)
+ if (CondParser_And(par, false) == CR_ERROR)
return CR_ERROR;
return res;
}
@@ -1006,7 +1011,7 @@ CondParser_And(CondParser *par, Boolean doEval)
* Or -> And
*/
static CondResult
-CondParser_Or(CondParser *par, Boolean doEval)
+CondParser_Or(CondParser *par, bool doEval)
{
CondResult res;
Token op;
@@ -1019,7 +1024,7 @@ CondParser_Or(CondParser *par, Boolean doEval)
if (op == TOK_OR) {
if (res == CR_FALSE)
return CondParser_Or(par, doEval);
- if (CondParser_Or(par, FALSE) == CR_ERROR)
+ if (CondParser_Or(par, false) == CR_ERROR)
return CR_ERROR;
return res;
}
@@ -1029,17 +1034,17 @@ CondParser_Or(CondParser *par, Boolean doEval)
}
static CondEvalResult
-CondParser_Eval(CondParser *par, Boolean *out_value)
+CondParser_Eval(CondParser *par, bool *out_value)
{
CondResult res;
DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
- res = CondParser_Or(par, TRUE);
+ res = CondParser_Or(par, true);
if (res == CR_ERROR)
return COND_INVALID;
- if (CondParser_Token(par, FALSE) != TOK_EOF)
+ if (CondParser_Token(par, false) != TOK_EOF)
return COND_INVALID;
*out_value = res == CR_TRUE;
@@ -1058,9 +1063,9 @@ CondParser_Eval(CondParser *par, Boolean *out_value)
* (*value) is set to the boolean value of the condition
*/
static CondEvalResult
-CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
- Boolean (*evalBare)(size_t, const char *), Boolean negate,
- Boolean eprint, Boolean strictLHS)
+CondEvalExpression(const char *cond, bool *out_value, bool plain,
+ bool (*evalBare)(size_t, const char *), bool negate,
+ bool eprint, bool strictLHS)
{
CondParser par;
CondEvalResult rval;
@@ -1074,7 +1079,7 @@ CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
par.negateEvalBare = negate;
par.p = cond;
par.curr = TOK_NONE;
- par.printedError = FALSE;
+ par.printedError = false;
rval = CondParser_Eval(&par, out_value);
@@ -1089,33 +1094,33 @@ CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
* ${"${VAR}" == value:?yes:no}.
*/
CondEvalResult
-Cond_EvalCondition(const char *cond, Boolean *out_value)
+Cond_EvalCondition(const char *cond, bool *out_value)
{
- return CondEvalExpression(cond, out_value, TRUE,
- FuncDefined, FALSE, FALSE, FALSE);
+ return CondEvalExpression(cond, out_value, true,
+ FuncDefined, false, false, false);
}
-static Boolean
+static bool
IsEndif(const char *p)
{
return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
}
-static Boolean
-DetermineKindOfConditional(const char **pp, Boolean *out_plain,
- Boolean (**out_evalBare)(size_t, const char *),
- Boolean *out_negate)
+static bool
+DetermineKindOfConditional(const char **pp, bool *out_plain,
+ bool (**out_evalBare)(size_t, const char *),
+ bool *out_negate)
{
const char *p = *pp;
p += 2;
- *out_plain = FALSE;
+ *out_plain = false;
*out_evalBare = FuncDefined;
- *out_negate = FALSE;
+ *out_negate = false;
if (*p == 'n') {
p++;
- *out_negate = TRUE;
+ *out_negate = true;
}
if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
p += 3;
@@ -1123,7 +1128,7 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
p += 4;
*out_evalBare = FuncMake;
} else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
- *out_plain = TRUE;
+ *out_plain = true;
} else {
/*
* TODO: Add error message about unknown directive,
@@ -1132,11 +1137,11 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
*
* Example: .elifx 123
*/
- return FALSE;
+ return false;
}
*pp = p;
- return TRUE;
+ return true;
}
/*
@@ -1161,9 +1166,9 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
*
* Results:
* COND_PARSE to continue parsing the lines that follow the
- * conditional (when <cond> evaluates to TRUE)
+ * conditional (when <cond> evaluates to true)
* COND_SKIP to skip the lines after the conditional
- * (when <cond> evaluates to FALSE, or when a previous
+ * (when <cond> evaluates to false, or when a previous
* branch has already been taken)
* COND_INVALID if the conditional was not valid, either because of
* a syntax error or because some variable was undefined
@@ -1174,17 +1179,17 @@ Cond_EvalLine(const char *line)
{
typedef enum IfState {
- /* None of the previous <cond> evaluated to TRUE. */
+ /* None of the previous <cond> evaluated to true. */
IFS_INITIAL = 0,
- /* The previous <cond> evaluated to TRUE.
+ /* The previous <cond> evaluated to true.
* The lines following this condition are interpreted. */
IFS_ACTIVE = 1 << 0,
/* The previous directive was an '.else'. */
IFS_SEEN_ELSE = 1 << 1,
- /* One of the previous <cond> evaluated to TRUE. */
+ /* One of the previous <cond> evaluated to true. */
IFS_WAS_ACTIVE = 1 << 2
} IfState;
@@ -1192,11 +1197,11 @@ Cond_EvalLine(const char *line)
static enum IfState *cond_states = NULL;
static unsigned int cond_states_cap = 128;
- Boolean plain;
- Boolean (*evalBare)(size_t, const char *);
- Boolean negate;
- Boolean isElif;
- Boolean value;
+ bool plain;
+ bool (*evalBare)(size_t, const char *);
+ bool negate;
+ bool isElif;
+ bool value;
IfState state;
const char *p = line;
@@ -1265,9 +1270,9 @@ Cond_EvalLine(const char *line)
return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
}
/* Assume for now it is an elif */
- isElif = TRUE;
+ isElif = true;
} else
- isElif = FALSE;
+ isElif = false;
if (p[0] != 'i' || p[1] != 'f') {
/*
@@ -1323,7 +1328,7 @@ Cond_EvalLine(const char *line)
/* And evaluate the conditional expression */
if (CondEvalExpression(p, &value, plain, evalBare, negate,
- TRUE, TRUE) == COND_INVALID) {
+ true, true) == COND_INVALID) {
/* Syntax error in conditional, error message already output. */
/* Skip everything to matching .endif */
/* XXX: An extra '.else' is not detected in this case. */
diff --git a/contrib/bmake/dir.c b/contrib/bmake/dir.c
index 026983e3ec10..627e654387f8 100644
--- a/contrib/bmake/dir.c
+++ b/contrib/bmake/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -86,7 +86,7 @@
* Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
*
* Dir_HasWildcards
- * Returns TRUE if the name given it needs to
+ * Returns true if the name given it needs to
* be wildcard-expanded.
*
* SearchPath_Expand
@@ -138,7 +138,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -217,7 +217,7 @@ struct CachedDir {
* and "./." are different.
*
* Not sure what happens when .CURDIR is assigned a new value; see
- * Parse_DoVar.
+ * Parse_Var.
*/
char *name;
@@ -547,14 +547,14 @@ void
Dir_SetPATH(void)
{
CachedDirListNode *ln;
- Boolean seenDotLast = FALSE; /* true if we should search '.' last */
+ bool seenDotLast = false; /* true if we should search '.' last */
Global_Delete(".PATH");
if ((ln = dirSearchPath.dirs.first) != NULL) {
CachedDir *dir = ln->datum;
if (dir == dotLast) {
- seenDotLast = TRUE;
+ seenDotLast = true;
Global_Append(".PATH", dotLast->name);
}
}
@@ -591,34 +591,34 @@ Dir_SetPATH(void)
* that make(1) should be expanding patterns, because then you have to set a
* mechanism for escaping the expansion!
*
- * Return TRUE if the word should be expanded, FALSE otherwise.
+ * Return true if the word should be expanded, false otherwise.
*/
-Boolean
+bool
Dir_HasWildcards(const char *name)
{
const char *p;
- Boolean wild = FALSE;
+ bool wild = false;
int braces = 0, brackets = 0;
for (p = name; *p != '\0'; p++) {
switch (*p) {
case '{':
braces++;
- wild = TRUE;
+ wild = true;
break;
case '}':
braces--;
break;
case '[':
brackets++;
- wild = TRUE;
+ wild = true;
break;
case ']':
brackets--;
break;
case '?':
case '*':
- wild = TRUE;
+ wild = true;
break;
default:
break;
@@ -647,7 +647,7 @@ static void
DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
{
const char *dirName = dir->name;
- Boolean isDot = dirName[0] == '.' && dirName[1] == '\0';
+ bool isDot = dirName[0] == '.' && dirName[1] == '\0';
HashIter hi;
/*
@@ -725,7 +725,7 @@ separator_comma(const char *p)
return p;
}
-static Boolean
+static bool
contains_wildcard(const char *p)
{
for (; *p != '\0'; p++) {
@@ -734,10 +734,10 @@ contains_wildcard(const char *p)
case '?':
case '{':
case '[':
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
static char *
@@ -1064,19 +1064,19 @@ DirFindDot(const char *name, const char *base)
return NULL;
}
-static Boolean
-FindFileRelative(SearchPath *path, Boolean seenDotLast,
+static bool
+FindFileRelative(SearchPath *path, bool seenDotLast,
const char *name, char **out_file)
{
SearchPathNode *ln;
- Boolean checkedDot = FALSE;
+ bool checkedDot = false;
char *file;
DEBUG0(DIR, " Trying subdirectories...\n");
if (!seenDotLast) {
if (dot != NULL) {
- checkedDot = TRUE;
+ checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
@@ -1092,7 +1092,7 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
if (dir == dot) {
if (checkedDot)
continue;
- checkedDot = TRUE;
+ checkedDot = true;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
goto found;
@@ -1100,7 +1100,7 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
- checkedDot = TRUE;
+ checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
@@ -1119,15 +1119,15 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
goto found;
}
- return FALSE;
+ return false;
found:
*out_file = file;
- return TRUE;
+ return true;
}
-static Boolean
-FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
+static bool
+FindFileAbsolute(SearchPath *path, bool const seenDotLast,
const char *const name, const char *const base,
char **out_file)
{
@@ -1162,7 +1162,7 @@ FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
((file = DirLookupAbs(cur, name, base)) != NULL))
goto found;
- return FALSE;
+ return false;
found:
if (file[0] == '\0') {
@@ -1170,7 +1170,7 @@ found:
file = NULL;
}
*out_file = file;
- return TRUE;
+ return true;
}
/*
@@ -1194,7 +1194,7 @@ char *
Dir_FindFile(const char *name, SearchPath *path)
{
char *file; /* the current filename to check */
- Boolean seenDotLast = FALSE; /* true if we should search dot last */
+ bool seenDotLast = false; /* true if we should search dot last */
struct cached_stat cst; /* Buffer for stat, if necessary */
const char *trailing_dot = ".";
const char *base = str_basename(name);
@@ -1210,7 +1210,7 @@ Dir_FindFile(const char *name, SearchPath *path)
if (path->dirs.first != NULL) {
CachedDir *dir = path->dirs.first->datum;
if (dir == dotLast) {
- seenDotLast = TRUE;
+ seenDotLast = true;
DEBUG0(DIR, "[dot last]...");
}
}
@@ -1471,7 +1471,7 @@ ResolveFullName(GNode *gn)
* The found file is stored in gn->path, unless the node already had a path.
*/
void
-Dir_UpdateMTime(GNode *gn, Boolean recheck)
+Dir_UpdateMTime(GNode *gn, bool recheck)
{
char *fullName;
struct cached_stat cst;
diff --git a/contrib/bmake/dir.h b/contrib/bmake/dir.h
index f4bf32ab5b83..d96393c62ebb 100644
--- a/contrib/bmake/dir.h
+++ b/contrib/bmake/dir.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.h,v 1.43 2021/02/05 05:48:19 rillig Exp $ */
+/* $NetBSD: dir.h,v 1.44 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -82,11 +82,11 @@ void Dir_InitCur(const char *);
void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
-Boolean Dir_HasWildcards(const char *);
+bool Dir_HasWildcards(const char *);
void SearchPath_Expand(SearchPath *, const char *, StringList *);
char *Dir_FindFile(const char *, SearchPath *);
char *Dir_FindHereOrAbove(const char *, const char *);
-void Dir_UpdateMTime(GNode *, Boolean);
+void Dir_UpdateMTime(GNode *, bool);
CachedDir *SearchPath_Add(SearchPath *, const char *);
char *SearchPath_ToFlags(SearchPath *, const char *);
void SearchPath_Clear(SearchPath *);
diff --git a/contrib/bmake/enum.h b/contrib/bmake/enum.h
index b5f4630414ef..e10fcae045f6 100755
--- a/contrib/bmake/enum.h
+++ b/contrib/bmake/enum.h
@@ -1,4 +1,4 @@
-/* $NetBSD: enum.h,v 1.18 2021/02/02 21:26:51 rillig Exp $ */
+/* $NetBSD: enum.h,v 1.19 2021/03/15 16:00:05 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@@ -114,19 +114,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
/*
* Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 2 flags.
- */
-#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_1(v1), \
- ENUM__SPEC_1(v2)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_1(v1), \
- ENUM__JOIN_STR_1(v2)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 3 flags.
*/
#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
@@ -140,19 +127,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
/*
* Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 4 flags.
- */
-#define ENUM_FLAGS_RTTI_4(typnam, v1, v2, v3, v4) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_2(v1, v2), \
- ENUM__SPEC_2(v3, v4)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_2(v1, v2), \
- ENUM__JOIN_STR_2(v3, v4)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 6 flags.
*/
#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
@@ -166,19 +140,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
/*
* Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 8 flags.
- */
-#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_4(v1, v2, v3, v4), \
- ENUM__SPEC_4(v5, v6, v7, v8)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_4(v1, v2, v3, v4), \
- ENUM__JOIN_STR_4(v5, v6, v7, v8)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 9 flags.
*/
#define ENUM_FLAGS_RTTI_9(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9) \
@@ -215,25 +176,4 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
ENUM__JOIN_STR_2(v29, v30), \
ENUM__JOIN_STR_1(v31)))
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 32 flags.
- */
-#define ENUM_FLAGS_RTTI_32(typnam, \
- v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16, \
- v17, v18, v19, v20, v21, v22, v23, v24, \
- v25, v26, v27, v28, v29, v30, v31, v32) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16), \
- ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \
- v25, v26, v27, v28, v29, v30, v31, v32)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16), \
- ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \
- v25, v26, v27, v28, v29, v30, v31, v32)))
-
#endif
diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c
index 5705d9c5de0d..615efb7634c9 100644
--- a/contrib/bmake/for.c
+++ b/contrib/bmake/for.c
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $ */
+/* $NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $");
+MAKE_RCSID("$NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $");
/* One of the variables to the left of the "in" in a .for loop. */
@@ -75,7 +75,7 @@ typedef struct ForLoop {
/* Is any of the names 1 character long? If so, when the variable values
* are substituted, the parser must handle $V expressions as well, not
* only ${V} and $(V). */
- Boolean short_var;
+ bool short_var;
unsigned int sub_next; /* Where to continue iterating */
} ForLoop;
@@ -94,7 +94,7 @@ ForLoop_New(void)
f->items.words = NULL;
f->items.freeIt = NULL;
Buf_Init(&f->curBody);
- f->short_var = FALSE;
+ f->short_var = false;
f->sub_next = 0;
return f;
@@ -125,7 +125,7 @@ ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
var->nameLen = len;
}
-static Boolean
+static bool
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
@@ -136,7 +136,7 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
- return FALSE;
+ return false;
}
/*
@@ -151,7 +151,7 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
break;
}
if (len == 1)
- f->short_var = TRUE;
+ f->short_var = true;
ForLoop_AddVar(f, p, len);
p += len;
@@ -159,14 +159,14 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
- return FALSE;
+ return false;
}
*pp = p;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
ForLoop_ParseItems(ForLoop *f, const char *p)
{
char *items;
@@ -175,10 +175,10 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
Parse_Error(PARSE_FATAL, "Error in .for loop items");
- return FALSE;
+ return false;
}
- f->items = Str_Words(items, FALSE);
+ f->items = Str_Words(items, false);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
@@ -189,19 +189,19 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
(unsigned)f->items.len, (unsigned)f->vars.len);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
-static Boolean
+static bool
IsFor(const char *p)
{
return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
}
-static Boolean
+static bool
IsEndfor(const char *p)
{
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
@@ -257,9 +257,9 @@ For_Eval(const char *line)
/*
* Add another line to the .for loop that is being built up.
- * Returns FALSE when the matching .endfor is reached.
+ * Returns false when the matching .endfor is reached.
*/
-Boolean
+bool
For_Accum(const char *line)
{
const char *p = line;
@@ -271,7 +271,7 @@ For_Accum(const char *line)
if (IsEndfor(p)) {
DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0)
- return FALSE;
+ return false;
} else if (IsFor(p)) {
forLevel++;
DEBUG1(FOR, "For: new loop %d\n", forLevel);
@@ -280,7 +280,7 @@ For_Accum(const char *line)
Buf_AddStr(&accumFor->body, line);
Buf_AddByte(&accumFor->body, '\n');
- return TRUE;
+ return true;
}
@@ -319,16 +319,16 @@ for_var_len(const char *var)
* The .for loop substitutes the items as ${:U<value>...}, which means
* that characters that break this syntax must be backslash-escaped.
*/
-static Boolean
+static bool
NeedsEscapes(const char *value, char endc)
{
const char *p;
for (p = value; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
diff --git a/contrib/bmake/hash.c b/contrib/bmake/hash.c
index 3afc4ac7ec4e..8b503ac31fb5 100644
--- a/contrib/bmake/hash.c
+++ b/contrib/bmake/hash.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -74,7 +74,7 @@
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $");
+MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@@ -84,7 +84,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $");
/* This hash function matches Gosling's Emacs and java.lang.String. */
static unsigned int
-hash(const char *key, size_t *out_keylen)
+Hash_String(const char *key, size_t *out_keylen)
{
unsigned int h;
const char *p;
@@ -98,10 +98,17 @@ hash(const char *key, size_t *out_keylen)
return h;
}
+/* This hash function matches Gosling's Emacs and java.lang.String. */
unsigned int
-Hash_Hash(const char *key)
+Hash_Substring(Substring key)
{
- return hash(key, NULL);
+ unsigned int h;
+ const char *p;
+
+ h = 0;
+ for (p = key.start; p != key.end; p++)
+ h = 31 * h + (unsigned char)*p;
+ return h;
}
static HashEntry *
@@ -126,6 +133,41 @@ HashTable_Find(HashTable *t, unsigned int h, const char *key)
return e;
}
+static bool
+HashEntry_KeyEquals(const HashEntry *he, Substring key)
+{
+ const char *heKey, *p;
+
+ heKey = he->key;
+ for (p = key.start; p != key.end; p++, heKey++)
+ if (*p != *heKey || *heKey == '\0')
+ return false;
+ return *heKey == '\0';
+}
+
+static HashEntry *
+HashTable_FindEntryBySubstring(HashTable *t, Substring key, unsigned int h)
+{
+ HashEntry *e;
+ unsigned int chainlen = 0;
+
+#ifdef DEBUG_HASH_LOOKUP
+ DEBUG4(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h,
+ (int)Substring_Length(key), key.start);
+#endif
+
+ for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ chainlen++;
+ if (e->key_hash == h && HashEntry_KeyEquals(e, key))
+ break;
+ }
+
+ if (chainlen > t->maxchain)
+ t->maxchain = chainlen;
+
+ return e;
+}
+
/* Set up the hash table. */
void
HashTable_Init(HashTable *t)
@@ -171,7 +213,7 @@ HashTable_Done(HashTable *t)
HashEntry *
HashTable_FindEntry(HashTable *t, const char *key)
{
- unsigned int h = hash(key, NULL);
+ unsigned int h = Hash_String(key, NULL);
return HashTable_Find(t, h, key);
}
@@ -188,9 +230,9 @@ HashTable_FindValue(HashTable *t, const char *key)
* or return NULL.
*/
void *
-HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
+HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
{
- HashEntry *he = HashTable_Find(t, h, key);
+ HashEntry *he = HashTable_FindEntryBySubstring(t, key, h);
return he != NULL ? he->value : NULL;
}
@@ -227,7 +269,7 @@ HashTable_Enlarge(HashTable *t)
t->bucketsMask = newMask;
t->buckets = newBuckets;
DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
- __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
+ __func__, (void *)t, t->bucketsSize, t->numEntries, t->maxchain);
t->maxchain = 0;
}
@@ -236,15 +278,15 @@ HashTable_Enlarge(HashTable *t)
* Return in out_isNew whether a new entry has been created.
*/
HashEntry *
-HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
+HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
{
size_t keylen;
- unsigned int h = hash(key, &keylen);
+ unsigned int h = Hash_String(key, &keylen);
HashEntry *he = HashTable_Find(t, h, key);
if (he != NULL) {
if (out_isNew != NULL)
- *out_isNew = FALSE;
+ *out_isNew = false;
return he;
}
@@ -261,7 +303,7 @@ HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
t->numEntries++;
if (out_isNew != NULL)
- *out_isNew = TRUE;
+ *out_isNew = true;
return he;
}
diff --git a/contrib/bmake/hash.h b/contrib/bmake/hash.h
index b101137aa0ce..8e7a567b6dba 100644
--- a/contrib/bmake/hash.h
+++ b/contrib/bmake/hash.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.h,v 1.38 2020/12/15 01:23:55 rillig Exp $ */
+/* $NetBSD: hash.h,v 1.40 2021/04/11 12:46:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -124,9 +124,9 @@ void HashTable_Init(HashTable *);
void HashTable_Done(HashTable *);
HashEntry *HashTable_FindEntry(HashTable *, const char *);
void *HashTable_FindValue(HashTable *, const char *);
-unsigned int Hash_Hash(const char *);
-void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
-HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
+unsigned int Hash_Substring(Substring);
+void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int);
+HashEntry *HashTable_CreateEntry(HashTable *, const char *, bool *);
HashEntry *HashTable_Set(HashTable *, const char *, void *);
void HashTable_DeleteEntry(HashTable *, HashEntry *);
void HashTable_DebugStats(HashTable *, const char *);
@@ -146,16 +146,16 @@ HashSet_Done(HashSet *set)
HashTable_Done(&set->tbl);
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
HashSet_Add(HashSet *set, const char *key)
{
- Boolean isNew;
+ bool isNew;
(void)HashTable_CreateEntry(&set->tbl, key, &isNew);
return isNew;
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
HashSet_Contains(HashSet *set, const char *key)
{
return HashTable_FindEntry(&set->tbl, key) != NULL;
diff --git a/contrib/bmake/import.sh b/contrib/bmake/import.sh
index d4554a078951..b80e120daab1 100755
--- a/contrib/bmake/import.sh
+++ b/contrib/bmake/import.sh
@@ -68,19 +68,24 @@ VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'`
rm -f *~
mkdir -p ../tmp
+# new files are handled automatically
+# but we need to rm if needed
+# FILES are kept sorted so we can determine what was added and deleted
+# but we need to take care dealing with re-ordering
+(${GIT} diff FILES | sed -n '/^[+-][^+-]/p'; \
+ ${GIT} diff mk/FILES | sed -n '/^[+-][^+-]/s,.,&mk/,p' ) > $TF.diffs
+grep '^+' $TF.diffs | sed 's,^.,,' | sort > $TF.adds
+grep '^-' $TF.diffs | sed 's,^.,,' | sort > $TF.rms
+comm -13 $TF.adds $TF.rms > $TF.rm
+
if [ -z "$ECHO" ]; then
- # new files are handled automatically
- # but we need to rm if needed
- $GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
test -s $TF.rm && xargs rm -f < $TF.rm
$GIT add -A
$GIT diff --staged | tee ../tmp/bmake-import.diff
echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh
echo "After you commit, run $here/../tmp/bmake-post.sh"
else
- # FILES is kept sorted so we can determine what was added and deleted
- $GIT diff FILES | sed -n '/^+[^+]/s,^+,,p' > $TF.add
- $GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
+ comm -23 $TF.adds $TF.rms > $TF.add
test -s $TF.rm && { echo Removing:; cat $TF.rm; }
test -s $TF.add && { echo Adding:; cat $TF.add; }
$GIT diff
diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c
index eb5454cde574..c27c47d0b054 100644
--- a/contrib/bmake/job.c
+++ b/contrib/bmake/job.c
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $ */
+/* $NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -155,7 +155,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $");
+MAKE_RCSID("$NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $");
/*
* A shell defines how the commands are run. All commands for a target are
@@ -181,7 +181,7 @@ MAKE_RCSID("$NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $");
* printf template for executing the command while ignoring the return
* status. Finally runChkTmpl is a printf template for running the command and
* causing the shell to exit on error. If any of these strings are empty when
- * hasErrCtl is FALSE, the command will be executed anyway as is, and if it
+ * hasErrCtl is false, the command will be executed anyway as is, and if it
* causes an error, so be it. Any templates set up to echo the command will
* escape any '$ ` \ "' characters in the command string to avoid unwanted
* shell code injection, the escaped command is safe to use in double quotes.
@@ -201,14 +201,14 @@ typedef struct Shell {
*/
const char *name;
- Boolean hasEchoCtl; /* whether both echoOff and echoOn are there */
+ bool hasEchoCtl; /* whether both echoOff and echoOn are there */
const char *echoOff; /* command to turn echoing off */
const char *echoOn; /* command to turn echoing back on */
const char *noPrint; /* text to skip when printing output from the
* shell. This is usually the same as echoOff */
size_t noPrintLen; /* length of noPrint command */
- Boolean hasErrCtl; /* whether error checking can be controlled
+ bool hasErrCtl; /* whether error checking can be controlled
* for individual commands */
const char *errOn; /* command to turn on error checking */
const char *errOff; /* command to turn off error checking */
@@ -230,16 +230,16 @@ typedef struct Shell {
typedef struct CommandFlags {
/* Whether to echo the command before or instead of running it. */
- Boolean echo;
+ bool echo;
/* Run the command even in -n or -N mode. */
- Boolean always;
+ bool always;
/*
- * true if we turned error checking off before printing the command
- * and need to turn it back on
+ * true if we turned error checking off before writing the command to
+ * the commands file and need to turn it back on
*/
- Boolean ignerr;
+ bool ignerr;
} CommandFlags;
/*
@@ -252,7 +252,7 @@ typedef struct ShellWriter {
FILE *f;
/* we've sent 'set -x' */
- Boolean xtraced;
+ bool xtraced;
} ShellWriter;
@@ -276,14 +276,12 @@ static int Job_error_token = TRUE;
* error handling variables
*/
static int job_errors = 0; /* number of errors reported */
-typedef enum AbortReason { /* why is the make aborting? */
+static enum { /* Why is the make aborting? */
ABORT_NONE,
- ABORT_ERROR, /* Because of an error */
- ABORT_INTERRUPT, /* Because it was interrupted */
+ ABORT_ERROR, /* Aborted because of an error */
+ ABORT_INTERRUPT, /* Aborted because it was interrupted */
ABORT_WAIT /* Waiting for jobs to finish */
- /* XXX: "WAIT" is not a _reason_ for aborting, it's rather a status. */
-} AbortReason;
-static AbortReason aborting = ABORT_NONE;
+} aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
/*
@@ -338,12 +336,12 @@ static Shell shells[] = {
*/
{
DEFSHELL_CUSTOM, /* .name */
- FALSE, /* .hasEchoCtl */
+ false, /* .hasEchoCtl */
"", /* .echoOff */
"", /* .echoOn */
"", /* .noPrint */
0, /* .noPrintLen */
- FALSE, /* .hasErrCtl */
+ false, /* .hasErrCtl */
"", /* .errOn */
"", /* .errOff */
"echo \"%s\"\n", /* .echoTmpl */
@@ -361,12 +359,12 @@ static Shell shells[] = {
*/
{
"sh", /* .name */
- FALSE, /* .hasEchoCtl */
+ false, /* .hasEchoCtl */
"", /* .echoOff */
"", /* .echoOn */
"", /* .noPrint */
0, /* .noPrintLen */
- FALSE, /* .hasErrCtl */
+ false, /* .hasErrCtl */
"", /* .errOn */
"", /* .errOff */
"echo \"%s\"\n", /* .echoTmpl */
@@ -387,12 +385,12 @@ static Shell shells[] = {
*/
{
"ksh", /* .name */
- TRUE, /* .hasEchoCtl */
+ true, /* .hasEchoCtl */
"set +v", /* .echoOff */
"set -v", /* .echoOn */
"set +v", /* .noPrint */
6, /* .noPrintLen */
- FALSE, /* .hasErrCtl */
+ false, /* .hasErrCtl */
"", /* .errOn */
"", /* .errOff */
"echo \"%s\"\n", /* .echoTmpl */
@@ -410,12 +408,12 @@ static Shell shells[] = {
*/
{
"csh", /* .name */
- TRUE, /* .hasEchoCtl */
+ true, /* .hasEchoCtl */
"unset verbose", /* .echoOff */
"set verbose", /* .echoOn */
"unset verbose", /* .noPrint */
13, /* .noPrintLen */
- FALSE, /* .hasErrCtl */
+ false, /* .hasErrCtl */
"", /* .errOn */
"", /* .errOff */
"echo \"%s\"\n", /* .echoTmpl */
@@ -442,8 +440,8 @@ static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
static Job *job_table; /* The structures that describe them */
static Job *job_table_end; /* job_table + maxJobs */
static unsigned int wantToken; /* we want a token */
-static Boolean lurking_children = FALSE;
-static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */
+static bool lurking_children = false;
+static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */
/*
* Set of descriptors of pipes connected to
@@ -454,7 +452,7 @@ static Job **jobByFdIndex = NULL;
static nfds_t fdsLen = 0;
static void watchfd(Job *);
static void clearfd(Job *);
-static Boolean readyfd(Job *);
+static bool readyfd(Job *);
static char *targPrefix = NULL; /* To identify a job change in the output. */
static Job tokenWaitJob; /* token wait pseudo-job */
@@ -470,8 +468,8 @@ enum {
static sigset_t caught_signals; /* Set of signals we handle */
static volatile sig_atomic_t caught_sigchld;
-static void JobDoOutput(Job *, Boolean);
-static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD;
+static void CollectOutput(Job *, bool);
+static void JobInterrupt(bool, int) MAKE_ATTR_DEAD;
static void JobRestartJobs(void);
static void JobSigReset(void);
@@ -509,7 +507,7 @@ Job_FlagsToString(const Job *job, char *buf, size_t bufsize)
}
static void
-job_table_dump(const char *where)
+DumpJobs(const char *where)
{
Job *job;
char flags[4];
@@ -662,7 +660,7 @@ MAKE_ATTR_DEAD static void
JobPassSig_int(int signo)
{
/* Run .INTERRUPT target then exit */
- JobInterrupt(TRUE, signo);
+ JobInterrupt(true, signo);
}
/*
@@ -673,7 +671,7 @@ MAKE_ATTR_DEAD static void
JobPassSig_term(int signo)
{
/* Dont run .INTERRUPT target then exit */
- JobInterrupt(FALSE, signo);
+ JobInterrupt(false, signo);
}
static void
@@ -683,7 +681,7 @@ JobPassSig_suspend(int signo)
struct sigaction act;
/* Suppress job started/continued messages */
- make_suspended = TRUE;
+ make_suspended = true;
/* Pass the signal onto every job */
JobCondPassSig(signo);
@@ -732,7 +730,7 @@ JobPassSig_suspend(int signo)
}
static Job *
-JobFindPid(int pid, JobStatus status, Boolean isJobs)
+JobFindPid(int pid, JobStatus status, bool isJobs)
{
Job *job;
@@ -741,7 +739,7 @@ JobFindPid(int pid, JobStatus status, Boolean isJobs)
return job;
}
if (DEBUG(JOB) && isJobs)
- job_table_dump("no pid");
+ DumpJobs("no pid");
return NULL;
}
@@ -750,17 +748,17 @@ static void
ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags)
{
char *p = *pp;
- out_cmdFlags->echo = TRUE;
- out_cmdFlags->ignerr = FALSE;
- out_cmdFlags->always = FALSE;
+ out_cmdFlags->echo = true;
+ out_cmdFlags->ignerr = false;
+ out_cmdFlags->always = false;
for (;;) {
if (*p == '@')
out_cmdFlags->echo = DEBUG(LOUD);
else if (*p == '-')
- out_cmdFlags->ignerr = TRUE;
+ out_cmdFlags->ignerr = true;
else if (*p == '+')
- out_cmdFlags->always = TRUE;
+ out_cmdFlags->always = true;
else
break;
p++;
@@ -791,7 +789,7 @@ EscapeShellDblQuot(const char *cmd)
}
static void
-ShellWriter_PrintFmt(ShellWriter *wr, const char *fmt, const char *arg)
+ShellWriter_WriteFmt(ShellWriter *wr, const char *fmt, const char *arg)
{
DEBUG1(JOB, fmt, arg);
@@ -801,56 +799,56 @@ ShellWriter_PrintFmt(ShellWriter *wr, const char *fmt, const char *arg)
}
static void
-ShellWriter_Println(ShellWriter *wr, const char *line)
+ShellWriter_WriteLine(ShellWriter *wr, const char *line)
{
- ShellWriter_PrintFmt(wr, "%s\n", line);
+ ShellWriter_WriteFmt(wr, "%s\n", line);
}
static void
ShellWriter_EchoOff(ShellWriter *wr)
{
if (shell->hasEchoCtl)
- ShellWriter_Println(wr, shell->echoOff);
+ ShellWriter_WriteLine(wr, shell->echoOff);
}
static void
ShellWriter_EchoCmd(ShellWriter *wr, const char *escCmd)
{
- ShellWriter_PrintFmt(wr, shell->echoTmpl, escCmd);
+ ShellWriter_WriteFmt(wr, shell->echoTmpl, escCmd);
}
static void
ShellWriter_EchoOn(ShellWriter *wr)
{
if (shell->hasEchoCtl)
- ShellWriter_Println(wr, shell->echoOn);
+ ShellWriter_WriteLine(wr, shell->echoOn);
}
static void
ShellWriter_TraceOn(ShellWriter *wr)
{
if (!wr->xtraced) {
- ShellWriter_Println(wr, "set -x");
- wr->xtraced = TRUE;
+ ShellWriter_WriteLine(wr, "set -x");
+ wr->xtraced = true;
}
}
static void
-ShellWriter_ErrOff(ShellWriter *wr, Boolean echo)
+ShellWriter_ErrOff(ShellWriter *wr, bool echo)
{
if (echo)
ShellWriter_EchoOff(wr);
- ShellWriter_Println(wr, shell->errOff);
+ ShellWriter_WriteLine(wr, shell->errOff);
if (echo)
ShellWriter_EchoOn(wr);
}
static void
-ShellWriter_ErrOn(ShellWriter *wr, Boolean echo)
+ShellWriter_ErrOn(ShellWriter *wr, bool echo)
{
if (echo)
ShellWriter_EchoOff(wr);
- ShellWriter_Println(wr, shell->errOn);
+ ShellWriter_WriteLine(wr, shell->errOn);
if (echo)
ShellWriter_EchoOn(wr);
}
@@ -861,11 +859,11 @@ ShellWriter_ErrOn(ShellWriter *wr, Boolean echo)
* (configurable per shell).
*/
static void
-JobPrintSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
+JobWriteSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
const char *escCmd, const char **inout_cmdTemplate)
{
/* XXX: Why is the job modified at this point? */
- job->ignerr = TRUE;
+ job->ignerr = true;
if (job->echo && inout_cmdFlags->echo) {
ShellWriter_EchoOff(wr);
@@ -875,7 +873,7 @@ JobPrintSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
* Leave echoing off so the user doesn't see the commands
* for toggling the error checking.
*/
- inout_cmdFlags->echo = FALSE;
+ inout_cmdFlags->echo = false;
} else {
if (inout_cmdFlags->echo)
ShellWriter_EchoCmd(wr, escCmd);
@@ -887,11 +885,11 @@ JobPrintSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
* so pretend error checking is still on.
* XXX: What effects does this have, and why is it necessary?
*/
- inout_cmdFlags->ignerr = FALSE;
+ inout_cmdFlags->ignerr = false;
}
static void
-JobPrintSpecials(Job *job, ShellWriter *wr, const char *escCmd, Boolean run,
+JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run,
CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate)
{
if (!run) {
@@ -899,38 +897,42 @@ JobPrintSpecials(Job *job, ShellWriter *wr, const char *escCmd, Boolean run,
* If there is no command to run, there is no need to switch
* error checking off and on again for nothing.
*/
- inout_cmdFlags->ignerr = FALSE;
+ inout_cmdFlags->ignerr = false;
} else if (shell->hasErrCtl)
ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo);
else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') {
- JobPrintSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
+ JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
inout_cmdTemplate);
} else
- inout_cmdFlags->ignerr = FALSE;
+ inout_cmdFlags->ignerr = false;
}
/*
- * Put out another command for the given job.
+ * Write a shell command to the job's commands file, to be run later.
*
* If the command starts with '@' and neither the -s nor the -n flag was
- * given to make, we stick a shell-specific echoOff command in the script.
+ * given to make, stick a shell-specific echoOff command in the script.
*
* If the command starts with '-' and the shell has no error control (none
- * of the predefined shells has that), we ignore errors for the entire job.
- * XXX: Why ignore errors for the entire job?
- * XXX: Even ignore errors for the commands before this command?
+ * of the predefined shells has that), ignore errors for the entire job.
*
- * If the command is just "...", all further commands of this job are skipped
- * for now. They are attached to the .END node and will be run by Job_Finish
- * after all other targets have been made.
+ * XXX: Why ignore errors for the entire job? This is even documented in the
+ * manual page, but without any rationale since there is no known rationale.
+ *
+ * XXX: The manual page says the '-' "affects the entire job", but that's not
+ * accurate. The '-' does not affect the commands before the '-'.
+ *
+ * If the command is just "...", skip all further commands of this job. These
+ * commands are attached to the .END node instead and will be run by
+ * Job_Finish after all other targets have been made.
*/
static void
-JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
+JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
{
- Boolean run;
+ bool run;
CommandFlags cmdFlags;
- /* Template for printing a command to the shell file */
+ /* Template for writing a command to the shell file */
const char *cmdTemplate;
char *xcmd; /* The expanded command */
char *xcmdStart;
@@ -969,12 +971,12 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
ShellWriter_EchoOff(wr);
} else {
if (shell->hasErrCtl)
- cmdFlags.echo = TRUE;
+ cmdFlags.echo = true;
}
}
if (cmdFlags.ignerr) {
- JobPrintSpecials(job, wr, escCmd, run, &cmdFlags, &cmdTemplate);
+ JobWriteSpecials(job, wr, escCmd, run, &cmdFlags, &cmdTemplate);
} else {
/*
@@ -988,7 +990,7 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
if (job->echo && cmdFlags.echo) {
ShellWriter_EchoOff(wr);
ShellWriter_EchoCmd(wr, escCmd);
- cmdFlags.echo = FALSE;
+ cmdFlags.echo = false;
}
/*
* If it's a comment line or blank, avoid the possible
@@ -998,14 +1000,14 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
escCmd[0] == '\0'
? shell->runIgnTmpl
: shell->runChkTmpl;
- cmdFlags.ignerr = FALSE;
+ cmdFlags.ignerr = false;
}
}
if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0)
ShellWriter_TraceOn(wr);
- ShellWriter_PrintFmt(wr, cmdTemplate, xcmd);
+ ShellWriter_WriteFmt(wr, cmdTemplate, xcmd);
free(xcmdStart);
free(escCmd);
@@ -1017,19 +1019,22 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
}
/*
- * Print all commands to the shell file that is later executed.
+ * Write all commands to the shell file that is later executed.
*
- * The special command "..." stops printing and saves the remaining commands
+ * The special command "..." stops writing and saves the remaining commands
* to be executed later, when the target '.END' is made.
*
* Return whether at least one command was written to the shell file.
*/
-static Boolean
-JobPrintCommands(Job *job)
+static bool
+JobWriteCommands(Job *job)
{
StringListNode *ln;
- Boolean seen = FALSE;
- ShellWriter wr = { job->cmdFILE, FALSE };
+ bool seen = false;
+ ShellWriter wr;
+
+ wr.f = job->cmdFILE;
+ wr.xtraced = false;
for (ln = job->node->commands.first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum;
@@ -1040,8 +1045,8 @@ JobPrintCommands(Job *job)
break;
}
- JobPrintCommand(job, &wr, ln, ln->datum);
- seen = TRUE;
+ JobWriteCommand(job, &wr, ln, ln->datum);
+ seen = true;
}
return seen;
@@ -1079,12 +1084,26 @@ JobClosePipes(Job *job)
(void)close(job->outPipe);
job->outPipe = -1;
- JobDoOutput(job, TRUE);
+ CollectOutput(job, true);
(void)close(job->inPipe);
job->inPipe = -1;
}
static void
+DebugFailedJob(const Job *job)
+{
+ const StringListNode *ln;
+
+ if (!DEBUG(ERROR))
+ return;
+
+ debug_printf("\n*** Failed target: %s\n*** Failed commands:\n",
+ job->node->name);
+ for (ln = job->node->commands.first; ln != NULL; ln = ln->next)
+ debug_printf("\t%s\n", (const char *)ln->datum);
+}
+
+static void
JobFinishDoneExitedError(Job *job, WAIT_T *inout_status)
{
SwitchOutputTo(job->node);
@@ -1095,6 +1114,7 @@ JobFinishDoneExitedError(Job *job, WAIT_T *inout_status)
}
#endif
if (!shouldDieQuietly(job->node, -1)) {
+ DebugFailedJob(job);
(void)printf("*** [%s] Error code %d%s\n",
job->node->name, WEXITSTATUS(*inout_status),
job->ignerr ? " (ignored)" : "");
@@ -1127,6 +1147,7 @@ static void
JobFinishDoneSignaled(Job *job, WAIT_T status)
{
SwitchOutputTo(job->node);
+ DebugFailedJob(job);
(void)printf("*** [%s] Signal %d\n", job->node->name, WTERMSIG(status));
if (deleteOnError)
JobDeleteTarget(job->node);
@@ -1159,7 +1180,7 @@ JobFinishDone(Job *job, WAIT_T *inout_status)
static void
JobFinish (Job *job, WAIT_T status)
{
- Boolean done, return_job_token;
+ bool done, return_job_token;
DEBUG3(JOB, "JobFinish: %d [%s], status %d\n",
job->pid, job->node->name, status);
@@ -1174,7 +1195,7 @@ JobFinish (Job *job, WAIT_T status)
(void)fclose(job->cmdFILE);
job->cmdFILE = NULL;
}
- done = TRUE;
+ done = true;
} else if (WIFEXITED(status)) {
/*
@@ -1188,7 +1209,7 @@ JobFinish (Job *job, WAIT_T status)
} else {
/* No need to close things down or anything. */
- done = FALSE;
+ done = false;
}
if (done)
@@ -1202,13 +1223,13 @@ JobFinish (Job *job, WAIT_T status)
}
#endif
- return_job_token = FALSE;
+ return_job_token = false;
Trace_Log(JOBEND, job);
if (!job->special) {
if (WAIT_STATUS(status) != 0 ||
(aborting == ABORT_ERROR) || aborting == ABORT_INTERRUPT)
- return_job_token = TRUE;
+ return_job_token = true;
}
if (aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT &&
@@ -1221,7 +1242,7 @@ JobFinish (Job *job, WAIT_T status)
JobSaveCommands(job);
job->node->made = MADE;
if (!job->special)
- return_job_token = TRUE;
+ return_job_token = true;
Make_Update(job->node);
job->status = JOB_ST_FREE;
} else if (status != 0) {
@@ -1245,10 +1266,12 @@ static void
TouchRegular(GNode *gn)
{
const char *file = GNode_Path(gn);
- struct utimbuf times = { now, now };
+ struct utimbuf times;
int fd;
char c;
+ times.actime = now;
+ times.modtime = now;
if (utime(file, &times) >= 0)
return;
@@ -1278,7 +1301,7 @@ TouchRegular(GNode *gn)
* If the file did not exist, it is created.
*/
void
-Job_Touch(GNode *gn, Boolean echo)
+Job_Touch(GNode *gn, bool echo)
{
if (gn->type &
(OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC | OP_OPTIONAL |
@@ -1317,17 +1340,17 @@ Job_Touch(GNode *gn, Boolean echo)
* abortProc Function to abort with message
*
* Results:
- * TRUE if the commands list is/was ok.
+ * true if the commands list is/was ok.
*/
-Boolean
+bool
Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
{
if (GNode_IsTarget(gn))
- return TRUE;
+ return true;
if (!Lst_IsEmpty(&gn->commands))
- return TRUE;
+ return true;
if ((gn->type & OP_LIB) && !Lst_IsEmpty(&gn->children))
- return TRUE;
+ return true;
/*
* No commands. Look for .DEFAULT rule from which we might infer
@@ -1346,12 +1369,12 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
*/
Make_HandleUse(defaultNode, gn);
Var_Set(gn, IMPSRC, GNode_VarTarget(gn));
- return TRUE;
+ return true;
}
- Dir_UpdateMTime(gn, FALSE);
+ Dir_UpdateMTime(gn, false);
if (gn->mtime != 0 || (gn->type & OP_SPECIAL))
- return TRUE;
+ return true;
/*
* The node wasn't the target of an operator. We have no .DEFAULT
@@ -1367,25 +1390,25 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
"%s: %s, %d: ignoring stale %s for %s\n",
progname, gn->fname, gn->lineno, makeDependfile,
gn->name);
- return TRUE;
+ return true;
}
if (gn->type & OP_OPTIONAL) {
(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
progname, gn->name, "ignored");
(void)fflush(stdout);
- return TRUE;
+ return true;
}
if (opts.keepgoing) {
(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
progname, gn->name, "continuing");
(void)fflush(stdout);
- return FALSE;
+ return false;
}
abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
- return FALSE;
+ return false;
}
/*
@@ -1543,7 +1566,7 @@ JobExec(Job *job, char **argv)
if (DEBUG(JOB)) {
debug_printf("JobExec(%s): pid %d added to jobs table\n",
job->node->name, job->pid);
- job_table_dump("job started");
+ DumpJobs("job started");
}
JobSigUnlock(&mask);
}
@@ -1594,7 +1617,7 @@ JobMakeArgv(Job *job, char **argv)
}
static void
-JobWriteShellCommands(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
+JobWriteShellCommands(Job *job, GNode *gn, bool *out_run)
{
/*
* tfile is the name of a file into which all shell commands
@@ -1604,15 +1627,6 @@ JobWriteShellCommands(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
char tfile[MAXPATHLEN];
int tfd; /* File descriptor to the temp file */
- /*
- * We're serious here, but if the commands were bogus, we're
- * also dead...
- */
- if (!cmdsOK) {
- PrintOnError(gn, NULL); /* provide some clue */
- DieHorribly();
- }
-
tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile);
job->cmdFILE = fdopen(tfd, "w+");
@@ -1625,40 +1639,34 @@ JobWriteShellCommands(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
if (useMeta) {
meta_job_start(job, gn);
if (gn->type & OP_SILENT) /* might have changed */
- job->echo = FALSE;
+ job->echo = false;
}
#endif
- *out_run = JobPrintCommands(job);
+ *out_run = JobWriteCommands(job);
}
/*
- * Start a target-creation process going for the target described by the
- * graph node gn.
- *
- * Input:
- * gn target to create
- * flags flags for the job to override normal ones.
- * previous The previous Job structure for this node, if any.
+ * Start a target-creation process going for the target described by gn.
*
* Results:
* JOB_ERROR if there was an error in the commands, JOB_FINISHED
* if there isn't actually anything left to do for the job and
* JOB_RUNNING if the job has been started.
*
- * Side Effects:
+ * Details:
* A new Job node is created and added to the list of running
* jobs. PMake is forked and a child shell created.
*
* NB: The return value is ignored by everyone.
*/
static JobStartResult
-JobStart(GNode *gn, Boolean special)
+JobStart(GNode *gn, bool special)
{
Job *job; /* new job descriptor */
char *argv[10]; /* Argument vector to shell */
- Boolean cmdsOK; /* true if the nodes commands were all right */
- Boolean run;
+ bool cmdsOK; /* true if the nodes commands were all right */
+ bool run;
for (job = job_table; job < job_table_end; job++) {
if (job->status == JOB_ST_FREE)
@@ -1686,7 +1694,16 @@ JobStart(GNode *gn, Boolean special)
if (Lst_IsEmpty(&gn->commands)) {
job->cmdFILE = stdout;
- run = FALSE;
+ run = false;
+
+ /*
+ * We're serious here, but if the commands were bogus, we're
+ * also dead...
+ */
+ if (!cmdsOK) {
+ PrintOnError(gn, NULL); /* provide some clue */
+ DieHorribly();
+ }
} else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
(!opts.noExecute && !opts.touchFlag)) {
/*
@@ -1696,22 +1713,31 @@ JobStart(GNode *gn, Boolean special)
* virtual targets.
*/
- JobWriteShellCommands(job, gn, cmdsOK, &run);
+ /*
+ * We're serious here, but if the commands were bogus, we're
+ * also dead...
+ */
+ if (!cmdsOK) {
+ PrintOnError(gn, NULL); /* provide some clue */
+ DieHorribly();
+ }
+
+ JobWriteShellCommands(job, gn, &run);
(void)fflush(job->cmdFILE);
} else if (!GNode_ShouldExecute(gn)) {
/*
- * Just print all the commands to stdout in one fell swoop.
+ * Just write all the commands to stdout in one fell swoop.
* This still sets up job->tailCmds correctly.
*/
SwitchOutputTo(gn);
job->cmdFILE = stdout;
if (cmdsOK)
- JobPrintCommands(job);
- run = FALSE;
+ JobWriteCommands(job);
+ run = false;
(void)fflush(job->cmdFILE);
} else {
Job_Touch(gn, job->echo);
- run = FALSE;
+ run = false;
}
/* If we're not supposed to execute a shell, don't. */
@@ -1751,12 +1777,15 @@ JobStart(GNode *gn, Boolean special)
}
/*
- * Print the output of the shell command, skipping the noPrint text of the
- * shell, if any. The default shell does not have noPrint though, which means
- * that in all practical cases, handling the output is left to the caller.
+ * If the shell has an output filter (which only csh and ksh have by default),
+ * print the output of the child process, skipping the noPrint text of the
+ * shell.
+ *
+ * Return the part of the output that the calling function needs to output by
+ * itself.
*/
static char *
-JobOutput(char *cp, char *endp) /* XXX: should all be const */
+PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
{
char *ecp; /* XXX: should be const */
@@ -1803,14 +1832,14 @@ JobOutput(char *cp, char *endp) /* XXX: should all be const */
*
* Input:
* job the job whose output needs printing
- * finish TRUE if this is the last time we'll be called
+ * finish true if this is the last time we'll be called
* for this job
*/
static void
-JobDoOutput(Job *job, Boolean finish)
+CollectOutput(Job *job, bool finish)
{
- Boolean gotNL; /* true if got a newline */
- Boolean fbuf; /* true if our buffer filled up */
+ bool gotNL; /* true if got a newline */
+ bool fbuf; /* true if our buffer filled up */
size_t nr; /* number of bytes read */
size_t i; /* auxiliary index into outBuf */
size_t max; /* limit for i (end of current data) */
@@ -1818,8 +1847,8 @@ JobDoOutput(Job *job, Boolean finish)
/* Read as many bytes as will fit in the buffer. */
again:
- gotNL = FALSE;
- fbuf = FALSE;
+ gotNL = false;
+ fbuf = false;
nRead = read(job->inPipe, &job->outBuf[job->curPos],
JOB_BUFSIZE - job->curPos);
@@ -1827,7 +1856,7 @@ again:
if (errno == EAGAIN)
return;
if (DEBUG(JOB)) {
- perror("JobDoOutput(piperead)");
+ perror("CollectOutput(piperead)");
}
nr = 0;
} else {
@@ -1843,25 +1872,27 @@ again:
if (nr == 0 && job->curPos != 0) {
job->outBuf[job->curPos] = '\n';
nr = 1;
- finish = FALSE;
+ finish = false;
} else if (nr == 0) {
- finish = FALSE;
+ finish = false;
}
/*
* Look for the last newline in the bytes we just got. If there is
* one, break out of the loop with 'i' as its index and gotNL set
- * TRUE.
+ * true.
*/
max = job->curPos + nr;
for (i = job->curPos + nr - 1;
i >= job->curPos && i != (size_t)-1; i--) {
if (job->outBuf[i] == '\n') {
- gotNL = TRUE;
+ gotNL = true;
break;
} else if (job->outBuf[i] == '\0') {
/*
- * Why?
+ * FIXME: The null characters are only replaced with
+ * space _after_ the last '\n'. Everywhere else they
+ * hide the rest of the command output.
*/
job->outBuf[i] = ' ';
}
@@ -1874,7 +1905,7 @@ again:
* If we've run out of buffer space, we have no choice
* but to print the stuff. sigh.
*/
- fbuf = TRUE;
+ fbuf = true;
i = job->curPos;
}
}
@@ -1893,10 +1924,16 @@ again:
if (i >= job->curPos) {
char *cp;
- cp = JobOutput(job->outBuf, &job->outBuf[i]);
+ /*
+ * FIXME: SwitchOutputTo should be here, according to
+ * the comment above. But since PrintOutput does not
+ * do anything in the default shell, this bug has gone
+ * unnoticed until now.
+ */
+ cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
/*
- * There's still more in that thar buffer. This time,
+ * There's still more in the output buffer. This time,
* though, we know there's no newline at the end, so
* we add one of our own free will.
*/
@@ -1934,7 +1971,7 @@ again:
* end-of-file on the pipe. This is guaranteed to happen
* eventually since the other end of the pipe is now closed
* (we closed it explicitly and the child has exited). When
- * we do get an EOF, finish will be set FALSE and we'll fall
+ * we do get an EOF, finish will be set false and we'll fall
* through and out.
*/
goto again;
@@ -1957,7 +1994,7 @@ JobRun(GNode *targ)
Lst_Append(&lst, targ);
(void)Make_Run(&lst);
Lst_Done(&lst);
- JobStart(targ, TRUE);
+ JobStart(targ, true);
while (jobTokensRunning != 0) {
Job_CatchOutput();
}
@@ -1999,7 +2036,7 @@ Job_CatchChildren(void)
while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) {
DEBUG2(JOB, "Process %d exited/stopped status %x.\n",
pid, WAIT_STATUS(status));
- JobReapChild(pid, status, TRUE);
+ JobReapChild(pid, status, true);
}
}
@@ -2008,7 +2045,7 @@ Job_CatchChildren(void)
* this lets us reap jobs regardless.
*/
void
-JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
+JobReapChild(pid_t pid, WAIT_T status, bool isJobs)
{
Job *job; /* job descriptor for dead child */
@@ -2042,7 +2079,7 @@ JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
(void)printf("*** [%s] Stopped -- signal %d\n",
job->node->name, WSTOPSIG(status));
}
- job->suspended = TRUE;
+ job->suspended = true;
}
(void)fflush(stdout);
return;
@@ -2105,7 +2142,7 @@ Job_CatchOutput(void)
continue;
job = jobByFdIndex[i];
if (job->status == JOB_ST_RUNNING)
- JobDoOutput(job, FALSE);
+ CollectOutput(job, false);
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
/*
* With meta mode, we may have activity on the job's filemon
@@ -2130,7 +2167,7 @@ Job_CatchOutput(void)
void
Job_Make(GNode *gn)
{
- (void)JobStart(gn, FALSE);
+ (void)JobStart(gn, false);
}
static void
@@ -2248,7 +2285,7 @@ Job_Init(void)
if (rval > 0)
continue;
if (rval == 0)
- lurking_children = TRUE;
+ lurking_children = true;
break;
}
@@ -2346,7 +2383,7 @@ FindShellByName(const char *name)
* line The shell spec
*
* Results:
- * FALSE if the specification was incorrect.
+ * false if the specification was incorrect.
*
* Side Effects:
* 'shell' points to a Shell structure (either predefined or
@@ -2373,15 +2410,15 @@ FindShellByName(const char *name)
* hasErrCtl True if shell has error checking control
* newline String literal to represent a newline char
* check Command to turn on error checking if hasErrCtl
- * is TRUE or template of command to echo a command
+ * is true or template of command to echo a command
* for which error checking is off if hasErrCtl is
- * FALSE.
+ * false.
* ignore Command to turn off error checking if hasErrCtl
- * is TRUE or template of command to execute a
+ * is true or template of command to execute a
* command so as to ignore any errors it returns if
- * hasErrCtl is FALSE.
+ * hasErrCtl is false.
*/
-Boolean
+bool
Job_ParseShell(char *line)
{
Words wordsList;
@@ -2390,7 +2427,7 @@ Job_ParseShell(char *line)
size_t argc;
char *path;
Shell newShell;
- Boolean fullSpec = FALSE;
+ bool fullSpec = false;
Shell *sh;
/* XXX: don't use line as an iterator variable */
@@ -2403,13 +2440,13 @@ Job_ParseShell(char *line)
/*
* Parse the specification by keyword
*/
- wordsList = Str_Words(line, TRUE);
+ wordsList = Str_Words(line, true);
words = wordsList.words;
argc = wordsList.len;
path = wordsList.freeIt;
if (words == NULL) {
Error("Unterminated quoted string [%s]", line);
- return FALSE;
+ return false;
}
shell_freeIt = path;
@@ -2455,9 +2492,9 @@ Job_ParseShell(char *line)
Parse_Error(PARSE_FATAL,
"Unknown keyword \"%s\"", arg);
free(words);
- return FALSE;
+ return false;
}
- fullSpec = TRUE;
+ fullSpec = true;
}
}
@@ -2472,13 +2509,13 @@ Job_ParseShell(char *line)
Parse_Error(PARSE_FATAL,
"Neither path nor name specified");
free(words);
- return FALSE;
+ return false;
} else {
if ((sh = FindShellByName(newShell.name)) == NULL) {
Parse_Error(PARSE_WARNING,
"%s: No matching shell", newShell.name);
free(words);
- return FALSE;
+ return false;
}
shell = sh;
shellName = newShell.name;
@@ -2495,7 +2532,7 @@ Job_ParseShell(char *line)
} else {
/*
* The user provided a path. If s/he gave nothing else
- * (fullSpec is FALSE), try and find a matching shell in the
+ * (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.
@@ -2517,7 +2554,7 @@ Job_ParseShell(char *line)
Parse_Error(PARSE_WARNING,
"%s: No matching shell", shellName);
free(words);
- return FALSE;
+ return false;
}
shell = sh;
} else {
@@ -2529,7 +2566,7 @@ Job_ParseShell(char *line)
}
if (shell->echoOn != NULL && shell->echoOff != NULL)
- shell->hasEchoCtl = TRUE;
+ shell->hasEchoCtl = true;
if (!shell->hasErrCtl) {
if (shell->echoTmpl == NULL)
@@ -2543,7 +2580,7 @@ Job_ParseShell(char *line)
* by the shell specification.
*/
free(words);
- return TRUE;
+ return true;
}
/*
@@ -2558,7 +2595,7 @@ Job_ParseShell(char *line)
* signo signal received
*/
static void
-JobInterrupt(Boolean runINTERRUPT, int signo)
+JobInterrupt(bool runINTERRUPT, int signo)
{
Job *job; /* job descriptor in that element */
GNode *interrupt; /* the node describing the .INTERRUPT target */
@@ -2589,7 +2626,7 @@ JobInterrupt(Boolean runINTERRUPT, int signo)
if (runINTERRUPT && !opts.touchFlag) {
interrupt = Targ_FindNode(".INTERRUPT");
if (interrupt != NULL) {
- opts.ignoreErrors = FALSE;
+ opts.ignoreErrors = false;
JobRun(interrupt);
}
}
@@ -2694,7 +2731,7 @@ JobRestartJobs(void)
job->node->name);
(void)fflush(stdout);
}
- job->suspended = FALSE;
+ job->suspended = false;
if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
debug_printf("Failed to send SIGCONT to %d\n",
job->pid);
@@ -2708,7 +2745,7 @@ JobRestartJobs(void)
JobFinish(job, job->exit_status);
}
}
- make_suspended = FALSE;
+ make_suspended = false;
}
static void
@@ -2769,7 +2806,7 @@ clearfd(Job *job)
job->inPollfd = NULL;
}
-static Boolean
+static bool
readyfd(Job *job)
{
if (job->inPollfd == NULL)
@@ -2871,10 +2908,10 @@ Job_TokenReturn(void)
* If pool is empty, set wantToken so that we wake up when a token is
* released.
*
- * Returns TRUE if a token was withdrawn, and FALSE if the pool is currently
+ * Returns true if a token was withdrawn, and false if the pool is currently
* empty.
*/
-Boolean
+bool
Job_TokenWithdraw(void)
{
char tok, tok1;
@@ -2885,7 +2922,7 @@ Job_TokenWithdraw(void)
getpid(), aborting, jobTokensRunning);
if (aborting != ABORT_NONE || (jobTokensRunning >= opts.maxJobs))
- return FALSE;
+ return false;
count = read(tokenWaitJob.inPipe, &tok, 1);
if (count == 0)
@@ -2896,7 +2933,7 @@ Job_TokenWithdraw(void)
}
DEBUG1(JOB, "(%d) blocked for token\n", getpid());
wantToken = 1;
- return FALSE;
+ return false;
}
if (count == 1 && tok != '+') {
@@ -2922,7 +2959,7 @@ Job_TokenWithdraw(void)
jobTokensRunning++;
DEBUG1(JOB, "(%d) withdrew token\n", getpid());
- return TRUE;
+ return true;
}
/*
@@ -2931,12 +2968,12 @@ Job_TokenWithdraw(void)
*
* Exits if the target fails.
*/
-Boolean
+bool
Job_RunTarget(const char *target, const char *fname)
{
GNode *gn = Targ_FindNode(target);
if (gn == NULL)
- return FALSE;
+ return false;
if (fname != NULL)
Var_Set(gn, ALLSRC, fname);
@@ -2947,7 +2984,7 @@ Job_RunTarget(const char *target, const char *fname)
PrintOnError(gn, "\n\nStop.");
exit(1);
}
- return TRUE;
+ return true;
}
#ifdef USE_SELECT
diff --git a/contrib/bmake/job.h b/contrib/bmake/job.h
index 0d7d7313b9d4..ef66602518d7 100644
--- a/contrib/bmake/job.h
+++ b/contrib/bmake/job.h
@@ -1,4 +1,4 @@
-/* $NetBSD: job.h,v 1.72 2021/02/05 19:19:17 sjg Exp $ */
+/* $NetBSD: job.h,v 1.73 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -157,14 +157,14 @@ typedef struct Job {
JobStatus status;
- Boolean suspended;
+ bool suspended;
/* Ignore non-zero exits */
- Boolean ignerr;
+ bool ignerr;
/* Output the command before or instead of running it. */
- Boolean echo;
+ bool echo;
/* Target is a special one. */
- Boolean special;
+ bool special;
int inPipe; /* Pipe for reading output from job */
int outPipe; /* Pipe for writing control commands */
@@ -188,22 +188,22 @@ extern int jobTokensRunning; /* tokens currently "out" */
void Shell_Init(void);
const char *Shell_GetNewline(void);
-void Job_Touch(GNode *, Boolean);
-Boolean Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
+void Job_Touch(GNode *, bool);
+bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
void Job_CatchChildren(void);
void Job_CatchOutput(void);
void Job_Make(GNode *);
void Job_Init(void);
-Boolean Job_ParseShell(char *);
+bool Job_ParseShell(char *);
int Job_Finish(void);
void Job_End(void);
void Job_Wait(void);
void Job_AbortAll(void);
void Job_TokenReturn(void);
-Boolean Job_TokenWithdraw(void);
+bool Job_TokenWithdraw(void);
void Job_ServerStart(int, int, int);
void Job_SetPrefix(void);
-Boolean Job_RunTarget(const char *, const char *);
+bool Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t);
int Job_TempFile(const char *, char *, size_t);
diff --git a/contrib/bmake/lst.c b/contrib/bmake/lst.c
index 8f3c32ef72e6..372973112783 100644
--- a/contrib/bmake/lst.c
+++ b/contrib/bmake/lst.c
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $ */
+/* $NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -34,7 +34,7 @@
#include "make.h"
-MAKE_RCSID("$NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $");
+MAKE_RCSID("$NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $");
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@@ -205,7 +205,7 @@ Lst_FindDatum(List *list, const void *datum)
/*
* Move all nodes from src to the end of dst.
- * The source list becomes empty but is not freed.
+ * The source list becomes indeterminate.
*/
void
Lst_MoveAll(List *dst, List *src)
@@ -219,6 +219,10 @@ Lst_MoveAll(List *dst, List *src)
dst->last = src->last;
}
+#ifdef CLEANUP
+ src->first = NULL;
+ src->last = NULL;
+#endif
}
/* Copy the element data from src to the start of dst. */
diff --git a/contrib/bmake/lst.h b/contrib/bmake/lst.h
index fe854a7647a8..cddd6439f611 100644
--- a/contrib/bmake/lst.h
+++ b/contrib/bmake/lst.h
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.h,v 1.96 2021/02/01 18:55:15 rillig Exp $ */
+/* $NetBSD: lst.h,v 1.98 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -123,15 +123,17 @@ void Lst_Free(List *);
MAKE_INLINE void
Lst_Init(List *list)
{
- list->first = NULL;
- list->last = NULL;
+ list->first = NULL;
+ list->last = NULL;
}
/* Get information about a list */
-MAKE_INLINE Boolean
+MAKE_INLINE bool
Lst_IsEmpty(List *list)
-{ return list->first == NULL; }
+{
+ return list->first == NULL;
+}
/* Find the first node that contains the given datum, or NULL. */
ListNode *Lst_FindDatum(List *, const void *);
diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c
index d25bb00d3716..85a8a1cce7a1 100644
--- a/contrib/bmake/main.c
+++ b/contrib/bmake/main.c
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $ */
+/* $NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -111,7 +111,7 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $");
+MAKE_RCSID("$NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
@@ -125,20 +125,20 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
CmdOpts opts;
time_t now; /* Time at start of make */
GNode *defaultNode; /* .DEFAULT node */
-Boolean allPrecious; /* .PRECIOUS given on line by itself */
-Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
+bool allPrecious; /* .PRECIOUS given on line by itself */
+bool deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */
-Boolean enterFlagObj; /* -w and objdir != srcdir */
+bool enterFlagObj; /* -w and objdir != srcdir */
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
-Boolean doing_depend; /* Set while reading .depend */
-static Boolean jobsRunning; /* TRUE if the jobs might be running */
+bool doing_depend; /* Set while reading .depend */
+static bool jobsRunning; /* true if the jobs might be running */
static const char *tracefile;
static int ReadMakefile(const char *);
static void purge_relative_cached_realpaths(void);
-static Boolean ignorePWD; /* if we use -C, PWD is meaningless */
+static bool ignorePWD; /* if we use -C, PWD is meaningless */
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
char curdir[MAXPATHLEN + 1]; /* Startup directory */
const char *progname;
@@ -146,7 +146,7 @@ char *makeDependfile;
pid_t myPid;
int makelevel;
-Boolean forceJobs = FALSE;
+bool forceJobs = false;
static int main_errors = 0;
static HashTable cached_realpaths;
@@ -293,7 +293,7 @@ MainParseArgDebug(const char *argvalue)
debug |= DEBUG_JOB;
break;
case 'L':
- opts.strict = TRUE;
+ opts.strict = true;
break;
case 'l':
debug |= DEBUG_LOUD;
@@ -317,7 +317,7 @@ MainParseArgDebug(const char *argvalue)
debug |= DEBUG_TARG;
break;
case 'V':
- opts.debugVflag = TRUE;
+ opts.debugVflag = true;
break;
case 'v':
debug |= DEBUG_VAR;
@@ -350,22 +350,22 @@ debug_setbuf:
}
/* Is path relative, or does it contain any relative component "." or ".."? */
-static Boolean
+static bool
IsRelativePath(const char *path)
{
- const char *cp;
+ const char *p;
if (path[0] != '/')
- return TRUE;
- cp = path;
- while ((cp = strstr(cp, "/.")) != NULL) {
- cp += 2;
- if (*cp == '.')
- cp++;
- if (cp[0] == '/' || cp[0] == '\0')
- return TRUE;
- }
- return FALSE;
+ return true;
+ p = path;
+ while ((p = strstr(p, "/.")) != NULL) {
+ p += 2;
+ if (*p == '.')
+ p++;
+ if (*p == '/' || *p == '\0')
+ return true;
+ }
+ return false;
}
static void
@@ -388,7 +388,7 @@ MainParseArgChdir(const char *argvalue)
sa.st_ino == sb.st_ino &&
sa.st_dev == sb.st_dev)
strncpy(curdir, argvalue, MAXPATHLEN);
- ignorePWD = TRUE;
+ ignorePWD = true;
}
static void
@@ -411,7 +411,7 @@ MainParseArgJobsInternal(const char *argvalue)
#endif
jp_0 = -1;
jp_1 = -1;
- opts.compatMake = TRUE;
+ opts.compatMake = true;
} else {
Global_Append(MAKEFLAGS, "-J");
Global_Append(MAKEFLAGS, argvalue);
@@ -423,7 +423,7 @@ MainParseArgJobs(const char *argvalue)
{
char *p;
- forceJobs = TRUE;
+ forceJobs = true;
opts.maxJobs = (int)strtol(argvalue, &p, 0);
if (*p != '\0' || opts.maxJobs < 1) {
(void)fprintf(stderr,
@@ -454,14 +454,14 @@ MainParseArgSysInc(const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
}
-static Boolean
+static bool
MainParseArg(char c, const char *argvalue)
{
switch (c) {
case '\0':
break;
case 'B':
- opts.compatMake = TRUE;
+ opts.compatMake = true;
Global_Append(MAKEFLAGS, "-B");
Global_Set(MAKE_MODE, "compat");
break;
@@ -469,7 +469,7 @@ MainParseArg(char c, const char *argvalue)
MainParseArgChdir(argvalue);
break;
case 'D':
- if (argvalue[0] == '\0') return FALSE;
+ if (argvalue[0] == '\0') return false;
Global_SetExpand(argvalue, "1");
Global_Append(MAKEFLAGS, "-D");
Global_Append(MAKEFLAGS, argvalue);
@@ -483,12 +483,12 @@ MainParseArg(char c, const char *argvalue)
MainParseArgJobsInternal(argvalue);
break;
case 'N':
- opts.noExecute = TRUE;
- opts.noRecursiveExecute = TRUE;
+ opts.noExecute = true;
+ opts.noRecursiveExecute = true;
Global_Append(MAKEFLAGS, "-N");
break;
case 'S':
- opts.keepgoing = FALSE;
+ opts.keepgoing = false;
Global_Append(MAKEFLAGS, "-S");
break;
case 'T':
@@ -505,11 +505,11 @@ MainParseArg(char c, const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
break;
case 'W':
- opts.parseWarnFatal = TRUE;
+ opts.parseWarnFatal = true;
/* XXX: why no Var_Append? */
break;
case 'X':
- opts.varNoExportEnv = TRUE;
+ opts.varNoExportEnv = true;
Global_Append(MAKEFLAGS, "-X");
break;
case 'd':
@@ -523,21 +523,21 @@ MainParseArg(char c, const char *argvalue)
MainParseArgDebug(argvalue);
break;
case 'e':
- opts.checkEnvFirst = TRUE;
+ opts.checkEnvFirst = true;
Global_Append(MAKEFLAGS, "-e");
break;
case 'f':
Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
break;
case 'i':
- opts.ignoreErrors = TRUE;
+ opts.ignoreErrors = true;
Global_Append(MAKEFLAGS, "-i");
break;
case 'j':
MainParseArgJobs(argvalue);
break;
case 'k':
- opts.keepgoing = TRUE;
+ opts.keepgoing = true;
Global_Append(MAKEFLAGS, "-k");
break;
case 'm':
@@ -545,35 +545,35 @@ MainParseArg(char c, const char *argvalue)
/* XXX: why no Var_Append? */
break;
case 'n':
- opts.noExecute = TRUE;
+ opts.noExecute = true;
Global_Append(MAKEFLAGS, "-n");
break;
case 'q':
- opts.queryFlag = TRUE;
+ opts.queryFlag = true;
/* Kind of nonsensical, wot? */
Global_Append(MAKEFLAGS, "-q");
break;
case 'r':
- opts.noBuiltins = TRUE;
+ opts.noBuiltins = true;
Global_Append(MAKEFLAGS, "-r");
break;
case 's':
- opts.beSilent = TRUE;
+ opts.beSilent = true;
Global_Append(MAKEFLAGS, "-s");
break;
case 't':
- opts.touchFlag = TRUE;
+ opts.touchFlag = true;
Global_Append(MAKEFLAGS, "-t");
break;
case 'w':
- opts.enterFlag = TRUE;
+ opts.enterFlag = true;
Global_Append(MAKEFLAGS, "-w");
break;
default:
case '?':
usage();
}
- return TRUE;
+ return true;
}
/*
@@ -592,13 +592,13 @@ MainParseArgs(int argc, char **argv)
int arginc;
char *argvalue;
char *optscan;
- Boolean inOption, dashDash = FALSE;
+ bool inOption, dashDash = false;
const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w";
/* Can't actually use getopt(3) because rescanning is not portable */
rearg:
- inOption = FALSE;
+ inOption = false;
optscan = NULL;
while (argc > 1) {
const char *optspec;
@@ -610,20 +610,20 @@ rearg:
if (c == '\0') {
argv++;
argc--;
- inOption = FALSE;
+ inOption = false;
continue;
}
} else {
if (c != '-' || dashDash)
break;
- inOption = TRUE;
+ inOption = true;
c = *optscan++;
}
/* '-' found at some earlier point */
optspec = strchr(optspecs, c);
if (c != '\0' && optspec != NULL && optspec[1] == ':') {
/* -<something> found, and <something> should have an arg */
- inOption = FALSE;
+ inOption = false;
arginc = 1;
argvalue = optscan;
if (*argvalue == '\0') {
@@ -638,10 +638,10 @@ rearg:
switch (c) {
case '\0':
arginc = 1;
- inOption = FALSE;
+ inOption = false;
break;
case '-':
- dashDash = TRUE;
+ dashDash = true;
break;
default:
if (!MainParseArg(c, argvalue))
@@ -659,7 +659,7 @@ rearg:
for (; argc > 1; argv++, argc--) {
VarAssign var;
if (Parse_IsVar(argv[1], &var)) {
- Parse_DoVar(&var, SCOPE_CMDLINE);
+ Parse_Var(&var, SCOPE_CMDLINE);
} else {
if (argv[1][0] == '\0')
Punt("illegal (null) argument.");
@@ -716,7 +716,7 @@ Main_ParseArgLine(const char *line)
FStr_Done(&argv0);
}
- words = Str_Words(buf, TRUE);
+ words = Str_Words(buf, true);
if (words.words == NULL) {
Error("Unterminated quoted string [%s]", buf);
free(buf);
@@ -728,14 +728,14 @@ Main_ParseArgLine(const char *line)
Words_Free(words);
}
-Boolean
-Main_SetObjdir(Boolean writable, const char *fmt, ...)
+bool
+Main_SetObjdir(bool writable, const char *fmt, ...)
{
struct stat sb;
char *path;
char buf[MAXPATHLEN + 1];
char buf2[MAXPATHLEN + 1];
- Boolean rc = FALSE;
+ bool rc = false;
va_list ap;
va_start(ap, fmt);
@@ -759,24 +759,24 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
setenv("PWD", objdir, 1);
Dir_InitDot();
purge_relative_cached_realpaths();
- rc = TRUE;
+ rc = true;
if (opts.enterFlag && strcmp(objdir, curdir) != 0)
- enterFlagObj = TRUE;
+ enterFlagObj = true;
}
}
return rc;
}
-static Boolean
-SetVarObjdir(Boolean writable, const char *var, const char *suffix)
+static bool
+SetVarObjdir(bool writable, const char *var, const char *suffix)
{
FStr path = Var_Value(SCOPE_CMDLINE, var);
FStr xpath;
if (path.str == NULL || path.str[0] == '\0') {
FStr_Done(&path);
- return FALSE;
+ return false;
}
/* expand variable substitutions */
@@ -792,7 +792,7 @@ SetVarObjdir(Boolean writable, const char *var, const char *suffix)
FStr_Done(&xpath);
FStr_Done(&path);
- return TRUE;
+ return true;
}
/*
@@ -841,8 +841,8 @@ MakeMode(void)
if (mode[0] != '\0') {
if (strstr(mode, "compat") != NULL) {
- opts.compatMake = TRUE;
- forceJobs = FALSE;
+ opts.compatMake = true;
+ forceJobs = false;
}
#if USE_META
if (strstr(mode, "meta") != NULL)
@@ -854,7 +854,7 @@ MakeMode(void)
}
static void
-PrintVar(const char *varname, Boolean expandVars)
+PrintVar(const char *varname, bool expandVars)
{
if (strchr(varname, '$') != NULL) {
char *evalue;
@@ -880,24 +880,22 @@ PrintVar(const char *varname, Boolean expandVars)
}
/*
- * Return a Boolean based on a variable.
+ * Return a bool based on a variable.
*
* If the knob is not set, return the fallback.
* If set, anything that looks or smells like "No", "False", "Off", "0", etc.
- * is FALSE, otherwise TRUE.
+ * is false, otherwise true.
*/
-Boolean
-GetBooleanVar(const char *varname, Boolean fallback)
+bool
+GetBooleanExpr(const char *expr, bool fallback)
{
- char *expr = str_concat3("${", varname, ":U}");
char *value;
- Boolean res;
+ bool res;
(void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value);
/* TODO: handle errors */
res = ParseBoolean(value, fallback);
free(value);
- free(expr);
return res;
}
@@ -905,14 +903,15 @@ static void
doPrintVars(void)
{
StringListNode *ln;
- Boolean expandVars;
+ bool expandVars;
if (opts.printVars == PVM_EXPANDED)
- expandVars = TRUE;
+ expandVars = true;
else if (opts.debugVflag)
- expandVars = FALSE;
+ expandVars = false;
else
- expandVars = GetBooleanVar(".MAKE.EXPAND_VARIABLES", FALSE);
+ expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}",
+ false);
for (ln = opts.variables.first; ln != NULL; ln = ln->next) {
const char *varname = ln->datum;
@@ -920,11 +919,11 @@ doPrintVars(void)
}
}
-static Boolean
+static bool
runTargets(void)
{
GNodeList targs = LST_INIT; /* target nodes to create */
- Boolean outOfDate; /* FALSE if all targets up to date */
+ bool outOfDate; /* false if all targets up to date */
/*
* Have now read the entire graph and need to make a list of
@@ -947,7 +946,7 @@ runTargets(void)
*/
if (!opts.queryFlag) {
Job_Init();
- jobsRunning = TRUE;
+ jobsRunning = true;
}
/* Traverse the graph, checking on all the targets */
@@ -958,7 +957,7 @@ runTargets(void)
* targets as well as initializing the module.
*/
Compat_Run(&targs);
- outOfDate = FALSE;
+ outOfDate = false;
}
Lst_Done(&targs); /* Don't free the targets themselves. */
return outOfDate;
@@ -1110,11 +1109,11 @@ ignore_pwd:
static void
InitObjdir(const char *machine, const char *machine_arch)
{
- Boolean writable;
+ bool writable;
Dir_InitCur(curdir);
- writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE);
- (void)Main_SetObjdir(FALSE, "%s", curdir);
+ writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true);
+ (void)Main_SetObjdir(false, "%s", curdir);
if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) &&
!SetVarObjdir(writable, "MAKEOBJDIR", "") &&
@@ -1141,27 +1140,27 @@ UnlimitFiles(void)
static void
CmdOpts_Init(void)
{
- opts.compatMake = FALSE;
+ opts.compatMake = false;
opts.debug = DEBUG_NONE;
/* opts.debug_file has already been initialized earlier */
- opts.strict = FALSE;
- opts.debugVflag = FALSE;
- opts.checkEnvFirst = FALSE;
+ opts.strict = false;
+ opts.debugVflag = false;
+ opts.checkEnvFirst = false;
Lst_Init(&opts.makefiles);
- opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */
+ opts.ignoreErrors = false; /* Pay attention to non-zero returns */
opts.maxJobs = 1;
- opts.keepgoing = FALSE; /* Stop on error */
- opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
- opts.noExecute = FALSE; /* Execute all commands */
- opts.queryFlag = FALSE;
- opts.noBuiltins = FALSE; /* Read the built-in rules */
- opts.beSilent = FALSE; /* Print commands as executed */
- opts.touchFlag = FALSE;
+ opts.keepgoing = false; /* Stop on error */
+ opts.noRecursiveExecute = false; /* Execute all .MAKE targets */
+ opts.noExecute = false; /* Execute all commands */
+ opts.queryFlag = false;
+ opts.noBuiltins = false; /* Read the built-in rules */
+ opts.beSilent = false; /* Print commands as executed */
+ opts.touchFlag = false;
opts.printVars = PVM_NONE;
Lst_Init(&opts.variables);
- opts.parseWarnFatal = FALSE;
- opts.enterFlag = FALSE;
- opts.varNoExportEnv = FALSE;
+ opts.parseWarnFatal = false;
+ opts.enterFlag = false;
+ opts.varNoExportEnv = false;
Lst_Init(&opts.create);
}
@@ -1286,7 +1285,7 @@ InitMaxJobs(void)
opts.maxJobs = n;
maxJobTokens = opts.maxJobs;
- forceJobs = TRUE;
+ forceJobs = true;
free(value);
}
@@ -1427,12 +1426,12 @@ main_Init(int argc, char **argv)
Global_Set(MAKE_DEPENDFILE, ".depend");
CmdOpts_Init();
- allPrecious = FALSE; /* Remove targets when interrupted */
- deleteOnError = FALSE; /* Historical default behavior */
- jobsRunning = FALSE;
+ allPrecious = false; /* Remove targets when interrupted */
+ deleteOnError = false; /* Historical default behavior */
+ jobsRunning = false;
maxJobTokens = opts.maxJobs;
- ignorePWD = FALSE;
+ ignorePWD = false;
/*
* Initialize the parsing, directory and variable modules to prepare
@@ -1575,9 +1574,9 @@ main_PrepareMaking(void)
SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile);
if (makeDependfile[0] != '\0') {
/* TODO: handle errors */
- doing_depend = TRUE;
+ doing_depend = true;
(void)ReadMakefile(makeDependfile);
- doing_depend = FALSE;
+ doing_depend = false;
}
}
@@ -1599,7 +1598,7 @@ main_PrepareMaking(void)
* turn compatibility on.
*/
if (!opts.compatMake && !forceJobs)
- opts.compatMake = TRUE;
+ opts.compatMake = true;
if (!opts.compatMake)
Job_ServerStart(maxJobTokens, jp_0, jp_1);
@@ -1607,7 +1606,7 @@ main_PrepareMaking(void)
jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0);
if (opts.printVars == PVM_NONE)
- Main_ExportMAKEFLAGS(TRUE); /* initial export */
+ Main_ExportMAKEFLAGS(true); /* initial export */
InitVpath();
@@ -1615,7 +1614,7 @@ main_PrepareMaking(void)
* Now that all search paths have been read for suffixes et al, it's
* time to add the default search path to their lists...
*/
- Suff_DoPaths();
+ Suff_ExtendPaths();
/*
* Propagate attributes through :: dependency lists.
@@ -1632,13 +1631,13 @@ main_PrepareMaking(void)
* If the -v or -V options are given, print variables instead.
* Return whether any of the targets is out-of-date.
*/
-static Boolean
+static bool
main_Run(void)
{
if (opts.printVars != PVM_NONE) {
/* print the values of any variables requested by the user */
doPrintVars();
- return FALSE;
+ return false;
} else {
return runTargets();
}
@@ -1684,7 +1683,7 @@ main_CleanUp(void)
/* Determine the exit code. */
static int
-main_Exit(Boolean outOfDate)
+main_Exit(bool outOfDate)
{
if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0))
return 2; /* Not 1 so -q can distinguish error */
@@ -1694,7 +1693,7 @@ main_Exit(Boolean outOfDate)
int
main(int argc, char **argv)
{
- Boolean outOfDate;
+ bool outOfDate;
main_Init(argc, argv);
main_ReadFiles();
@@ -1862,7 +1861,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
/* Wait for the process to exit. */
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
- JobReapChild(pid, status, FALSE);
+ JobReapChild(pid, status, false);
res_len = buf.len;
res = Buf_DoneData(&buf);
@@ -2107,13 +2106,14 @@ cached_realpath(const char *pathname, char *resolved)
* Return true if we should die without noise.
* For example our failing child was a sub-make or failure happened elsewhere.
*/
-Boolean
+bool
shouldDieQuietly(GNode *gn, int bf)
{
static int quietly = -1;
if (quietly < 0) {
- if (DEBUG(JOB) || !GetBooleanVar(".MAKE.DIE_QUIETLY", TRUE))
+ if (DEBUG(JOB) ||
+ !GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true))
quietly = 0;
else if (bf >= 0)
quietly = bf;
@@ -2193,15 +2193,15 @@ PrintOnError(GNode *gn, const char *msg)
}
void
-Main_ExportMAKEFLAGS(Boolean first)
+Main_ExportMAKEFLAGS(bool first)
{
- static Boolean once = TRUE;
+ static bool once = true;
const char *expr;
char *s;
if (once != first)
return;
- once = FALSE;
+ once = false;
expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
(void)Var_Subst(expr, SCOPE_CMDLINE, VARE_WANTRES, &s);
@@ -2225,7 +2225,7 @@ getTmpdir(void)
return tmpdir;
/* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */
- (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/",
+ (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/",
SCOPE_GLOBAL, VARE_WANTRES, &tmpdir);
/* TODO: handle errors */
@@ -2272,18 +2272,18 @@ mkTempFile(const char *pattern, char *tfile, size_t tfile_sz)
/*
* Convert a string representation of a boolean into a boolean value.
- * Anything that looks like "No", "False", "Off", "0" etc. is FALSE,
- * the empty string is the fallback, everything else is TRUE.
+ * Anything that looks like "No", "False", "Off", "0" etc. is false,
+ * the empty string is the fallback, everything else is true.
*/
-Boolean
-ParseBoolean(const char *s, Boolean fallback)
+bool
+ParseBoolean(const char *s, bool fallback)
{
char ch = ch_tolower(s[0]);
if (ch == '\0')
return fallback;
if (ch == '0' || ch == 'f' || ch == 'n')
- return FALSE;
+ return false;
if (ch == 'o')
return ch_tolower(s[1]) != 'f';
- return TRUE;
+ return true;
}
diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c
index 1cd7299a76c5..a85a497be32d 100644
--- a/contrib/bmake/make.c
+++ b/contrib/bmake/make.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -72,7 +72,7 @@
* Examination of targets and their suitability for creation.
*
* Interface:
- * Make_Run Initialize things for the module. Returns TRUE if
+ * Make_Run Initialize things for the module. Returns true if
* work was (or would have been) done.
*
* Make_Update After a target is made, update all its parents.
@@ -85,7 +85,8 @@
* Update the node's youngestChild field based on the
* child's modification time.
*
- * Make_DoAllVar Set up the various local variables for a
+ * GNode_SetLocalVars
+ * Set up the various local variables for a
* target, including the .ALLSRC variable, making
* sure that any variable that needs to exist
* at the very least has the empty value.
@@ -103,7 +104,7 @@
#include "job.h"
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@@ -168,7 +169,7 @@ GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
suffix);
}
-Boolean
+bool
GNode_ShouldExecute(GNode *gn)
{
return !((gn->type & OP_MAKE)
@@ -184,7 +185,7 @@ GNode_UpdateYoungestChild(GNode *gn, GNode *cgn)
gn->youngestChild = cgn;
}
-static Boolean
+static bool
IsOODateRegular(GNode *gn)
{
/* These rules are inherited from the original Make. */
@@ -193,22 +194,22 @@ IsOODateRegular(GNode *gn)
if (gn->mtime < gn->youngestChild->mtime) {
DEBUG1(MAKE, "modified before source \"%s\"...",
GNode_Path(gn->youngestChild));
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
if (gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) {
DEBUG0(MAKE, "nonexistent and no sources...");
- return TRUE;
+ return true;
}
if (gn->type & OP_DOUBLEDEP) {
DEBUG0(MAKE, ":: operator and no sources...");
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -223,17 +224,17 @@ IsOODateRegular(GNode *gn)
* The mtime field of the node and the youngestChild field of its parents
* may be changed.
*/
-Boolean
+bool
GNode_IsOODate(GNode *gn)
{
- Boolean oodate;
+ bool oodate;
/*
* Certain types of targets needn't even be sought as their datedness
* doesn't depend on their modification time...
*/
if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) {
- Dir_UpdateMTime(gn, TRUE);
+ Dir_UpdateMTime(gn, true);
if (DEBUG(MAKE)) {
if (gn->mtime != 0)
debug_printf("modified %s...",
@@ -267,7 +268,7 @@ GNode_IsOODate(GNode *gn)
* no matter *what*.
*/
DEBUG0(MAKE, ".USE node...");
- oodate = FALSE;
+ oodate = false;
} else if ((gn->type & OP_LIB) && (gn->mtime == 0 || Arch_IsLib(gn))) {
DEBUG0(MAKE, "library...");
@@ -302,9 +303,9 @@ GNode_IsOODate(GNode *gn)
debug_printf(".EXEC node...");
}
}
- oodate = TRUE;
+ oodate = true;
} else if (IsOODateRegular(gn)) {
- oodate = TRUE;
+ oodate = true;
} else {
/*
* When a nonexistent child with no sources
@@ -351,7 +352,7 @@ PretendAllChildrenAreMade(GNode *pgn)
GNode *cgn = ln->datum;
/* This may also update cgn->path. */
- Dir_UpdateMTime(cgn, FALSE);
+ Dir_UpdateMTime(cgn, false);
GNode_UpdateYoungestChild(pgn, cgn);
pgn->unmade--;
}
@@ -443,7 +444,7 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
static void
MakeHandleUse(GNode *cgn, GNode *pgn, GNodeListNode *ln)
{
- Boolean unmarked;
+ bool unmarked;
unmarked = !(cgn->type & OP_MARK);
cgn->type |= OP_MARK;
@@ -485,7 +486,7 @@ Make_Recheck(GNode *gn)
{
time_t mtime;
- Dir_UpdateMTime(gn, TRUE);
+ Dir_UpdateMTime(gn, true);
mtime = gn->mtime;
#ifndef RECHECK
@@ -576,7 +577,7 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname)
}
/* See if a .ORDER rule stops us from building this node. */
-static Boolean
+static bool
IsWaitingForOrder(GNode *gn)
{
GNodeListNode *ln;
@@ -590,9 +591,9 @@ IsWaitingForOrder(GNode *gn)
DEBUG2(MAKE,
"IsWaitingForOrder: Waiting for .ORDER node \"%s%s\"\n",
ogn->name, ogn->cohort_num);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static void MakeBuildParent(GNode *, GNodeListNode *);
@@ -868,7 +869,7 @@ MakeAddAllSrc(GNode *cgn, GNode *pgn)
* match its ALLSRC variable.
*/
void
-Make_DoAllVar(GNode *gn)
+GNode_SetLocalVars(GNode *gn)
{
GNodeListNode *ln;
@@ -889,7 +890,7 @@ Make_DoAllVar(GNode *gn)
gn->flags |= DONE_ALLSRC;
}
-static Boolean
+static bool
MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
{
@@ -899,13 +900,13 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
GNode_FprintDetails(opts.debug_file, "", cn, "\n");
}
if (GNode_IsReady(cn))
- return FALSE;
+ return false;
/* If this node is on the RHS of a .ORDER, check LHSs. */
if (IsWaitingForOrder(cn)) {
/* Can't build this (or anything else in this child list) yet */
cn->made = DEFERRED;
- return FALSE; /* but keep looking */
+ return false; /* but keep looking */
}
DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n",
@@ -961,13 +962,13 @@ MakeChildren(GNode *gn)
*
* If the -q option was given, no job will be started,
* but as soon as an out-of-date target is found, this function
- * returns TRUE. In all other cases, this function returns FALSE.
+ * returns true. In all other cases, this function returns false.
*/
-static Boolean
+static bool
MakeStartJobs(void)
{
GNode *gn;
- Boolean have_token = FALSE;
+ bool have_token = false;
while (!Lst_IsEmpty(&toBeMade)) {
/*
@@ -976,7 +977,7 @@ MakeStartJobs(void)
*/
if (!have_token && !Job_TokenWithdraw())
break;
- have_token = TRUE;
+ have_token = true;
gn = Lst_Dequeue(&toBeMade);
DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num);
@@ -1022,10 +1023,10 @@ MakeStartJobs(void)
if (GNode_IsOODate(gn)) {
DEBUG0(MAKE, "out-of-date\n");
if (opts.queryFlag)
- return TRUE;
- Make_DoAllVar(gn);
+ return true;
+ GNode_SetLocalVars(gn);
Job_Make(gn);
- have_token = FALSE;
+ have_token = false;
} else {
DEBUG0(MAKE, "up-to-date\n");
gn->made = UPTODATE;
@@ -1037,7 +1038,7 @@ MakeStartJobs(void)
* for .TARGET when building up the local
* variables of its parent(s)...
*/
- Make_DoAllVar(gn);
+ GNode_SetLocalVars(gn);
}
Make_Update(gn);
}
@@ -1046,7 +1047,7 @@ MakeStartJobs(void)
if (have_token)
Job_TokenReturn();
- return FALSE;
+ return false;
}
/* Print the status of a .ORDER node. */
@@ -1081,7 +1082,7 @@ static void MakePrintStatusList(GNodeList *, int *);
* Print the status of a top-level node, viz. it being up-to-date already
* or not created due to an error in a lower level.
*/
-static Boolean
+static bool
MakePrintStatus(GNode *gn, int *errors)
{
if (gn->flags & DONECYCLE) {
@@ -1089,7 +1090,7 @@ MakePrintStatus(GNode *gn, int *errors)
* We've completely processed this node before, don't do
* it again.
*/
- return FALSE;
+ return false;
}
if (gn->unmade == 0) {
@@ -1128,7 +1129,7 @@ MakePrintStatus(GNode *gn, int *errors)
gn->name, gn->cohort_num);
break;
}
- return FALSE;
+ return false;
}
DEBUG3(MAKE, "MakePrintStatus: %s%s has %d unmade children\n",
@@ -1143,7 +1144,7 @@ MakePrintStatus(GNode *gn, int *errors)
MakePrintStatusList(&gn->children, errors);
/* Mark that this node needn't be processed again */
gn->flags |= DONECYCLE;
- return FALSE;
+ return false;
}
/* Only output the error once per node */
@@ -1151,11 +1152,11 @@ MakePrintStatus(GNode *gn, int *errors)
Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num);
if ((*errors)++ > 100)
/* Abandon the whole error report */
- return TRUE;
+ return true;
/* Reporting for our children will give the rest of the loop */
MakePrintStatusList(&gn->children, errors);
- return FALSE;
+ return false;
}
static void
@@ -1243,7 +1244,7 @@ Make_ExpandUse(GNodeList *targs)
*eon = ')';
}
- Dir_UpdateMTime(gn, FALSE);
+ Dir_UpdateMTime(gn, false);
Var_Set(gn, TARGET, GNode_Path(gn));
UnmarkChildren(gn);
HandleUseNodes(gn);
@@ -1364,14 +1365,14 @@ Make_ProcessWait(GNodeList *targs)
* targs the initial list of targets
*
* Results:
- * TRUE if work was done. FALSE otherwise.
+ * True if work was done, false otherwise.
*
* Side Effects:
* The make field of all nodes involved in the creation of the given
* targets is set to 1. The toBeMade list is set to contain all the
* 'leaves' of these subgraphs.
*/
-Boolean
+bool
Make_Run(GNodeList *targs)
{
int errors; /* Number of errors the Job module reports */
diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h
index da5f60534f31..a074923c643e 100644
--- a/contrib/bmake/make.h
+++ b/contrib/bmake/make.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.256 2021/02/05 19:19:17 sjg Exp $ */
+/* $NetBSD: make.h,v 1.263 2021/06/21 10:33:11 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -136,55 +136,30 @@
#endif
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
+#define MAKE_STATIC static MAKE_ATTR_UNUSED
-/*
- * A boolean type is defined as an integer, not an enum, for historic reasons.
- * The only allowed values are the constants TRUE and FALSE (1 and 0).
- */
-#if defined(lint) || defined(USE_C99_BOOLEAN)
+#if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN)
#include <stdbool.h>
-typedef bool Boolean;
-#define FALSE false
-#define TRUE true
-#elif defined(USE_DOUBLE_BOOLEAN)
-/* During development, to find type mismatches in function declarations. */
-typedef double Boolean;
-#define TRUE 1.0
-#define FALSE 0.0
-#elif defined(USE_UCHAR_BOOLEAN)
-/*
- * During development, to find code that depends on the exact value of TRUE or
- * that stores other values in Boolean variables.
- */
-typedef unsigned char Boolean;
-#define TRUE ((unsigned char)0xFF)
-#define FALSE ((unsigned char)0x00)
-#elif defined(USE_CHAR_BOOLEAN)
-/*
- * During development, to find code that uses a boolean as array index, via
- * -Wchar-subscripts.
- */
-typedef char Boolean;
-#define TRUE ((char)-1)
-#define FALSE ((char)0x00)
-#elif defined(USE_ENUM_BOOLEAN)
-typedef enum Boolean { FALSE, TRUE } Boolean;
#else
-typedef int Boolean;
-#ifndef TRUE
-#define TRUE 1
+#ifndef bool
+typedef unsigned int Boolean;
+#define bool Boolean
+#endif
+#ifndef true
+#define true 1
#endif
-#ifndef FALSE
-#define FALSE 0
+#ifndef false
+#define false 0
#endif
#endif
#include "lst.h"
#include "enum.h"
+#include "make_malloc.h"
+#include "str.h"
#include "hash.h"
#include "make-conf.h"
#include "buf.h"
-#include "make_malloc.h"
/*
* some vendors don't have this --sjg
@@ -247,6 +222,8 @@ typedef enum GNodeMade {
* should be made.
*
* Some of the OP_ constants can be combined, others cannot.
+ *
+ * See the tests depsrc-*.mk and deptgt-*.mk.
*/
typedef enum GNodeType {
OP_NONE = 0,
@@ -503,11 +480,11 @@ typedef enum CondEvalResult {
*/
/* True if every target is precious */
-extern Boolean allPrecious;
+extern bool allPrecious;
/* True if failed targets should be deleted */
-extern Boolean deleteOnError;
-/* TRUE while processing .depend */
-extern Boolean doing_depend;
+extern bool deleteOnError;
+/* true while processing .depend */
+extern bool doing_depend;
/* .DEFAULT rule */
extern GNode *defaultNode;
@@ -606,7 +583,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
do { \
if (DEBUG(module)) \
debug_printf args; \
- } while (/*CONSTCOND*/FALSE)
+ } while (/*CONSTCOND*/false)
#define DEBUG0(module, text) \
DEBUG_IMPL(module, ("%s", text))
@@ -630,7 +607,7 @@ typedef enum PrintVarsMode {
/* Command line options */
typedef struct CmdOpts {
/* -B: whether we are make compatible */
- Boolean compatMake;
+ bool compatMake;
/* -d: debug control: There is one bit per module. It is up to the
* module what debug information to print. */
@@ -643,19 +620,19 @@ typedef struct CmdOpts {
*
* Runs make in strict mode, with additional checks and better error
* handling. */
- Boolean strict;
+ bool strict;
/* -dV: for the -V option, print unexpanded variable values */
- Boolean debugVflag;
+ bool debugVflag;
/* -e: check environment variables before global variables */
- Boolean checkEnvFirst;
+ bool checkEnvFirst;
/* -f: the makefiles to read */
StringList makefiles;
/* -i: if true, ignore all errors from shell commands */
- Boolean ignoreErrors;
+ bool ignoreErrors;
/* -j: the maximum number of jobs that can run in parallel;
* this is coordinated with the submakes */
@@ -663,29 +640,29 @@ typedef struct CmdOpts {
/* -k: if true and an error occurs while making a node, continue
* making nodes that do not depend on the erroneous node */
- Boolean keepgoing;
+ bool keepgoing;
/* -N: execute no commands from the targets */
- Boolean noRecursiveExecute;
+ bool noRecursiveExecute;
/* -n: execute almost no commands from the targets */
- Boolean noExecute;
+ bool noExecute;
/*
* -q: if true, do not really make anything, just see if the targets
* are out-of-date
*/
- Boolean queryFlag;
+ bool queryFlag;
/* -r: raw mode, do not load the builtin rules. */
- Boolean noBuiltins;
+ bool noBuiltins;
/* -s: don't echo the shell commands before executing them */
- Boolean beSilent;
+ bool beSilent;
/* -t: touch the targets if they are out-of-date, but don't actually
* make them */
- Boolean touchFlag;
+ bool touchFlag;
/* -[Vv]: print expanded or unexpanded selected variables */
PrintVarsMode printVars;
@@ -693,14 +670,14 @@ typedef struct CmdOpts {
StringList variables;
/* -W: if true, makefile parsing warnings are treated as errors */
- Boolean parseWarnFatal;
+ bool parseWarnFatal;
/* -w: print 'Entering' and 'Leaving' for submakes */
- Boolean enterFlag;
+ bool enterFlag;
/* -X: if true, do not export variables set on the command line to the
* environment. */
- Boolean varNoExportEnv;
+ bool varNoExportEnv;
/* The target names specified on the command line.
* Used to resolve .if make(...) statements. */
@@ -713,24 +690,24 @@ extern CmdOpts opts;
#include "nonints.h"
void GNode_UpdateYoungestChild(GNode *, GNode *);
-Boolean GNode_IsOODate(GNode *);
+bool GNode_IsOODate(GNode *);
void Make_ExpandUse(GNodeList *);
time_t Make_Recheck(GNode *);
void Make_HandleUse(GNode *, GNode *);
void Make_Update(GNode *);
-void Make_DoAllVar(GNode *);
-Boolean Make_Run(GNodeList *);
-Boolean shouldDieQuietly(GNode *, int);
+void GNode_SetLocalVars(GNode *);
+bool Make_Run(GNodeList *);
+bool shouldDieQuietly(GNode *, int);
void PrintOnError(GNode *, const char *);
-void Main_ExportMAKEFLAGS(Boolean);
-Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
+void Main_ExportMAKEFLAGS(bool);
+bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
int mkTempFile(const char *, char *, size_t);
int str2Lst_Append(StringList *, char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
-Boolean GNode_ShouldExecute(GNode *gn);
+bool GNode_ShouldExecute(GNode *gn);
/* See if the node was seen on the left-hand side of a dependency operator. */
-MAKE_INLINE Boolean
+MAKE_INLINE bool
GNode_IsTarget(const GNode *gn)
{
return (gn->type & OP_OPMASK) != 0;
@@ -742,25 +719,25 @@ GNode_Path(const GNode *gn)
return gn->path != NULL ? gn->path : gn->name;
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
GNode_IsWaitingFor(const GNode *gn)
{
return (gn->flags & REMAKE) && gn->made <= REQUESTED;
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
GNode_IsReady(const GNode *gn)
{
return gn->made > DEFERRED;
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
GNode_IsDone(const GNode *gn)
{
return gn->made >= MADE;
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
GNode_IsError(const GNode *gn)
{
return gn->made == ERROR || gn->made == ABORTED;
@@ -781,7 +758,7 @@ GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
MAKE_INLINE const char *
GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
-#ifdef __GNUC__
+#if defined(__GNUC__) && __STDC_VERSION__ >= 199901L
#define UNCONST(ptr) ({ \
union __unconst { \
const void *__cp; \
@@ -809,15 +786,15 @@ GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#define KILLPG(pid, sig) killpg((pid), (sig))
#endif
-MAKE_INLINE Boolean
+MAKE_INLINE bool
ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
-MAKE_INLINE Boolean
+MAKE_INLINE bool
ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
-MAKE_INLINE Boolean
+MAKE_INLINE bool
ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
-MAKE_INLINE Boolean
+MAKE_INLINE bool
ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
-MAKE_INLINE Boolean
+MAKE_INLINE bool
ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
MAKE_INLINE char
ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }
diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c
index b7f1831b3cdf..c1f78136fb7c 100644
--- a/contrib/bmake/meta.c
+++ b/contrib/bmake/meta.c
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.c,v 1.177 2021/02/05 19:19:17 sjg Exp $ */
+/* $NetBSD: meta.c,v 1.181 2021/04/04 10:05:08 rillig Exp $ */
/*
* Implement 'meta' mode.
@@ -70,20 +70,20 @@ static char *metaIgnorePathsStr; /* string storage for the list */
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
#endif
-Boolean useMeta = FALSE;
-static Boolean useFilemon = FALSE;
-static Boolean writeMeta = FALSE;
-static Boolean metaMissing = FALSE; /* oodate if missing */
-static Boolean filemonMissing = FALSE; /* oodate if missing */
-static Boolean metaEnv = FALSE; /* don't save env unless asked */
-static Boolean metaVerbose = FALSE;
-static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
-static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
-static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
-static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
-static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
-
-extern Boolean forceJobs;
+bool useMeta = false;
+static bool useFilemon = false;
+static bool writeMeta = false;
+static bool metaMissing = false; /* oodate if missing */
+static bool filemonMissing = false; /* oodate if missing */
+static bool metaEnv = false; /* don't save env unless asked */
+static bool metaVerbose = false;
+static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */
+static bool metaIgnorePatterns = false; /* do we need to do pattern matches */
+static bool metaIgnoreFilter = false; /* do we have more complex filtering? */
+static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
+static bool metaSilent = false; /* if we have a .meta be SILENT */
+
+extern bool forceJobs;
extern char **environ;
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
@@ -133,7 +133,7 @@ meta_open_filemon(BuildMon *pbm)
pbm->filemon = filemon_open();
if (pbm->filemon == NULL) {
- useFilemon = FALSE;
+ useFilemon = false;
warn("Could not open filemon %s", filemon_path());
return;
}
@@ -319,7 +319,7 @@ meta_name(char *mname, size_t mnamelen,
* Return true if running ${.MAKE}
* Bypassed if target is flagged .MAKE
*/
-static Boolean
+static bool
is_submake(const char *cmd, GNode *gn)
{
static const char *p_make = NULL;
@@ -327,7 +327,7 @@ is_submake(const char *cmd, GNode *gn)
char *mp = NULL;
char *cp;
char *cp2;
- Boolean rc = FALSE;
+ bool rc = false;
if (p_make == NULL) {
p_make = Var_Value(gn, ".MAKE").str;
@@ -346,7 +346,7 @@ is_submake(const char *cmd, GNode *gn)
case ' ':
case '\t':
case '\n':
- rc = TRUE;
+ rc = true;
break;
}
if (cp2 > cmd && rc) {
@@ -356,7 +356,7 @@ is_submake(const char *cmd, GNode *gn)
case '\n':
break;
default:
- rc = FALSE; /* no match */
+ rc = false; /* no match */
break;
}
}
@@ -365,29 +365,31 @@ is_submake(const char *cmd, GNode *gn)
return rc;
}
-static Boolean
+static bool
any_is_submake(GNode *gn)
{
StringListNode *ln;
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
if (is_submake(ln->datum, gn))
- return TRUE;
- return FALSE;
+ return true;
+ return false;
}
static void
-printCMD(const char *cmd, FILE *fp, GNode *gn)
+printCMD(const char *ucmd, FILE *fp, GNode *gn)
{
- char *cmd_freeIt = NULL;
+ FStr xcmd = FStr_InitRefer(ucmd);
- if (strchr(cmd, '$') != NULL) {
- (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmd_freeIt);
+ if (strchr(ucmd, '$') != NULL) {
+ char *expanded;
+ (void)Var_Subst(ucmd, gn, VARE_WANTRES, &expanded);
/* TODO: handle errors */
- cmd = cmd_freeIt;
+ xcmd = FStr_InitOwn(expanded);
}
- fprintf(fp, "CMD %s\n", cmd);
- free(cmd_freeIt);
+
+ fprintf(fp, "CMD %s\n", xcmd.str);
+ FStr_Done(&xcmd);
}
static void
@@ -408,17 +410,17 @@ printCMDs(GNode *gn, FILE *fp)
debug_printf("Skipping meta for %s: .%s\n", \
gn->name, __STRING(_type)); \
} \
- return FALSE; \
+ return false; \
} \
-} while (/*CONSTCOND*/FALSE)
+} while (/*CONSTCOND*/false)
/*
* Do we need/want a .meta file ?
*/
-static Boolean
+static bool
meta_needed(GNode *gn, const char *dname,
- char *objdir_realpath, Boolean verbose)
+ char *objdir_realpath, bool verbose)
{
struct cached_stat cst;
@@ -440,13 +442,13 @@ meta_needed(GNode *gn, const char *dname,
if (Lst_IsEmpty(&gn->commands)) {
if (verbose)
debug_printf("Skipping meta for %s: no commands\n", gn->name);
- return FALSE;
+ return false;
}
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
/* OP_SUBMAKE is a bit too aggressive */
if (any_is_submake(gn)) {
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
- return FALSE;
+ return false;
}
}
@@ -454,7 +456,7 @@ meta_needed(GNode *gn, const char *dname,
if (cached_stat(dname, &cst) != 0) {
if (verbose)
debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
- return FALSE;
+ return false;
}
/* make sure these are canonical */
@@ -466,9 +468,9 @@ meta_needed(GNode *gn, const char *dname,
if (verbose)
debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
gn->name);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
@@ -490,7 +492,7 @@ meta_create(BuildMon *pbm, GNode *gn)
tname = GNode_VarTarget(gn);
/* if this succeeds objdir_realpath is realpath of dname */
- if (!meta_needed(gn, dname.str, objdir_realpath, TRUE))
+ if (!meta_needed(gn, dname.str, objdir_realpath, true))
goto out;
dname.str = objdir_realpath;
@@ -554,7 +556,7 @@ meta_create(BuildMon *pbm, GNode *gn)
return fp;
}
-static Boolean
+static bool
boolValue(char *s)
{
switch(*s) {
@@ -563,9 +565,9 @@ boolValue(char *s)
case 'n':
case 'F':
case 'f':
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/*
@@ -591,25 +593,25 @@ meta_init(void)
void
meta_mode_init(const char *make_mode)
{
- static Boolean once = FALSE;
+ static bool once = false;
char *cp;
FStr value;
- useMeta = TRUE;
- useFilemon = TRUE;
- writeMeta = TRUE;
+ useMeta = true;
+ useFilemon = true;
+ writeMeta = true;
if (make_mode != NULL) {
if (strstr(make_mode, "env") != NULL)
- metaEnv = TRUE;
+ metaEnv = true;
if (strstr(make_mode, "verb") != NULL)
- metaVerbose = TRUE;
+ metaVerbose = true;
if (strstr(make_mode, "read") != NULL)
- writeMeta = FALSE;
+ writeMeta = false;
if (strstr(make_mode, "nofilemon") != NULL)
- useFilemon = FALSE;
+ useFilemon = false;
if (strstr(make_mode, "ignore-cmd") != NULL)
- metaIgnoreCMDs = TRUE;
+ metaIgnoreCMDs = true;
if (useFilemon)
get_mode_bf(filemonMissing, "missing-filemon=");
get_mode_bf(metaCurdirOk, "curdirok=");
@@ -628,7 +630,7 @@ meta_mode_init(const char *make_mode)
}
if (once)
return;
- once = TRUE;
+ once = true;
memset(&Mybm, 0, sizeof Mybm);
/*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
@@ -652,12 +654,12 @@ meta_mode_init(const char *make_mode)
*/
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
if (value.str != NULL) {
- metaIgnorePatterns = TRUE;
+ metaIgnorePatterns = true;
FStr_Done(&value);
}
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
if (value.str != NULL) {
- metaIgnoreFilter = TRUE;
+ metaIgnoreFilter = true;
FStr_Done(&value);
}
}
@@ -774,7 +776,7 @@ meta_job_event(Job *job)
}
void
-meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status)
+meta_job_error(Job *job, GNode *gn, bool ignerr, int status)
{
char cwd[MAXPATHLEN];
BuildMon *pbm;
@@ -944,7 +946,7 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
return 0;
}
-static Boolean
+static bool
prefix_match(const char *prefix, const char *path)
{
size_t n = strlen(prefix);
@@ -952,35 +954,35 @@ prefix_match(const char *prefix, const char *path)
return strncmp(path, prefix, n) == 0;
}
-static Boolean
+static bool
has_any_prefix(const char *path, StringList *prefixes)
{
StringListNode *ln;
for (ln = prefixes->first; ln != NULL; ln = ln->next)
if (prefix_match(ln->datum, path))
- return TRUE;
- return FALSE;
+ return true;
+ return false;
}
/* See if the path equals prefix or starts with "prefix/". */
-static Boolean
+static bool
path_starts_with(const char *path, const char *prefix)
{
size_t n = strlen(prefix);
if (strncmp(path, prefix, n) != 0)
- return FALSE;
+ return false;
return path[n] == '\0' || path[n] == '/';
}
-static Boolean
+static bool
meta_ignore(GNode *gn, const char *p)
{
char fname[MAXPATHLEN];
if (p == NULL)
- return TRUE;
+ return true;
if (*p == '/') {
cached_realpath(p, fname); /* clean it up */
@@ -988,7 +990,7 @@ meta_ignore(GNode *gn, const char *p)
#ifdef DEBUG_META_MODE
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
#endif
- return TRUE;
+ return true;
}
}
@@ -1011,7 +1013,7 @@ meta_ignore(GNode *gn, const char *p)
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
#endif
free(pm);
- return TRUE;
+ return true;
}
free(pm);
}
@@ -1030,11 +1032,11 @@ meta_ignore(GNode *gn, const char *p)
DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
#endif
free(fm);
- return TRUE;
+ return true;
}
free(fm);
}
- return FALSE;
+ return false;
}
/*
@@ -1048,11 +1050,11 @@ meta_ignore(GNode *gn, const char *p)
/*
* It is possible that a .meta file is corrupted,
* if we detect this we want to reproduce it.
- * Setting oodate TRUE will have that effect.
+ * Setting oodate true will have that effect.
*/
#define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
warnx("%s: %d: malformed", fname, lineno); \
- oodate = TRUE; \
+ oodate = true; \
continue; \
}
@@ -1074,8 +1076,8 @@ append_if_new(StringList *list, const char *str)
Lst_Append(list, bmake_strdup(str));
}
-Boolean
-meta_oodate(GNode *gn, Boolean oodate)
+bool
+meta_oodate(GNode *gn, bool oodate)
{
static char *tmpdir = NULL;
static char cwd[MAXPATHLEN];
@@ -1095,9 +1097,9 @@ meta_oodate(GNode *gn, Boolean oodate)
static size_t cwdlen = 0;
static size_t tmplen = 0;
FILE *fp;
- Boolean needOODATE = FALSE;
+ bool needOODATE = false;
StringList missingFiles;
- Boolean have_filemon = FALSE;
+ bool have_filemon = false;
if (oodate)
return oodate; /* we're done */
@@ -1106,7 +1108,7 @@ meta_oodate(GNode *gn, Boolean oodate)
tname = GNode_VarTarget(gn);
/* if this succeeds fname3 is realpath of dname */
- if (!meta_needed(gn, dname.str, fname3, FALSE))
+ if (!meta_needed(gn, dname.str, fname3, false))
goto oodate_out;
dname.str = fname3;
@@ -1118,7 +1120,7 @@ meta_oodate(GNode *gn, Boolean oodate)
* requires that all variables are set in the same way that they
* would be if the target needs to be re-built.
*/
- Make_DoAllVar(gn);
+ GNode_SetLocalVars(gn);
meta_name(fname, sizeof fname, dname.str, tname, dname.str);
@@ -1164,7 +1166,7 @@ meta_oodate(GNode *gn, Boolean oodate)
buf[x - 1] = '\0';
else {
warnx("%s: %d: line truncated at %u", fname, lineno, x);
- oodate = TRUE;
+ oodate = true;
break;
}
link_src = NULL;
@@ -1172,11 +1174,11 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Find the start of the build monitor section. */
if (!have_filemon) {
if (strncmp(buf, "-- filemon", 10) == 0) {
- have_filemon = TRUE;
+ have_filemon = true;
continue;
}
if (strncmp(buf, "# buildmon", 10) == 0) {
- have_filemon = TRUE;
+ have_filemon = true;
continue;
}
}
@@ -1424,7 +1426,7 @@ meta_oodate(GNode *gn, Boolean oodate)
char *sdirs[4];
char **sdp;
int sdx = 0;
- Boolean found = FALSE;
+ bool found = false;
if (*p == '/') {
sdirs[sdx++] = p; /* done */
@@ -1455,7 +1457,7 @@ meta_oodate(GNode *gn, Boolean oodate)
fname, lineno, *sdp);
#endif
if (cached_stat(*sdp, &cst) == 0) {
- found = TRUE;
+ found = true;
p = *sdp;
}
}
@@ -1468,7 +1470,7 @@ meta_oodate(GNode *gn, Boolean oodate)
cst.cst_mtime > gn->mtime) {
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
fname, lineno, p);
- oodate = TRUE;
+ oodate = true;
} else if (S_ISDIR(cst.cst_mode)) {
/* Update the latest directory. */
cached_realpath(p, latestdir);
@@ -1500,25 +1502,25 @@ meta_oodate(GNode *gn, Boolean oodate)
if (cmdNode == NULL) {
DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
fname, lineno);
- oodate = TRUE;
+ oodate = true;
} else {
const char *cp;
char *cmd = cmdNode->datum;
- Boolean hasOODATE = FALSE;
+ bool hasOODATE = false;
if (strstr(cmd, "$?") != NULL)
- hasOODATE = TRUE;
+ hasOODATE = true;
else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
/* check for $[{(].OODATE[:)}] */
if (cp > cmd + 2 && cp[-2] == '$')
- hasOODATE = TRUE;
+ hasOODATE = true;
}
if (hasOODATE) {
- needOODATE = TRUE;
+ needOODATE = true;
DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
fname, lineno);
}
- (void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
+ (void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd);
/* TODO: handle errors */
if ((cp = strchr(cmd, '\n')) != NULL) {
@@ -1553,7 +1555,7 @@ meta_oodate(GNode *gn, Boolean oodate)
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
fname, lineno, p, cmd);
if (!metaIgnoreCMDs)
- oodate = TRUE;
+ oodate = true;
}
free(cmd);
cmdNode = cmdNode->next;
@@ -1566,13 +1568,13 @@ meta_oodate(GNode *gn, Boolean oodate)
if (!oodate && cmdNode != NULL) {
DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
fname, lineno);
- oodate = TRUE;
+ oodate = true;
}
CHECK_VALID_META(p);
if (strcmp(p, cwd) != 0) {
DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
fname, lineno, p, curdir);
- oodate = TRUE;
+ oodate = true;
}
}
}
@@ -1581,11 +1583,11 @@ meta_oodate(GNode *gn, Boolean oodate)
if (!Lst_IsEmpty(&missingFiles)) {
DEBUG2(META, "%s: missing files: %s...\n",
fname, (char *)missingFiles.first->datum);
- oodate = TRUE;
+ oodate = true;
}
if (!oodate && !have_filemon && filemonMissing) {
DEBUG1(META, "%s: missing filemon data\n", fname);
- oodate = TRUE;
+ oodate = true;
}
} else {
if (writeMeta && (metaMissing || (gn->type & OP_META))) {
@@ -1600,8 +1602,8 @@ meta_oodate(GNode *gn, Boolean oodate)
}
if (cp == NULL) {
DEBUG1(META, "%s: required but missing\n", fname);
- oodate = TRUE;
- needOODATE = TRUE; /* assume the worst */
+ oodate = true;
+ needOODATE = true; /* assume the worst */
}
}
}
@@ -1708,7 +1710,7 @@ meta_compat_parent(pid_t child)
fflush(stdout);
buf[nread] = '\0';
meta_job_output(NULL, buf, "");
- } while (/*CONSTCOND*/FALSE);
+ } while (/*CONSTCOND*/false);
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0)
metafd = -1;
diff --git a/contrib/bmake/meta.h b/contrib/bmake/meta.h
index 1fc8910d3b65..d4f02da4e78b 100644
--- a/contrib/bmake/meta.h
+++ b/contrib/bmake/meta.h
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.h,v 1.9 2020/12/10 20:49:11 rillig Exp $ */
+/* $NetBSD: meta.h,v 1.10 2021/04/03 11:08:40 rillig Exp $ */
/*
* Things needed for 'meta' mode.
@@ -48,13 +48,13 @@ void meta_job_child(struct Job *);
void meta_job_parent(struct Job *, pid_t);
int meta_job_fd(struct Job *);
int meta_job_event(struct Job *);
-void meta_job_error(struct Job *, GNode *, Boolean, int);
+void meta_job_error(struct Job *, GNode *, bool, int);
void meta_job_output(struct Job *, char *, const char *);
int meta_cmd_finish(void *);
int meta_job_finish(struct Job *);
-Boolean meta_oodate(GNode *, Boolean);
+bool meta_oodate(GNode *, bool);
void meta_compat_start(void);
void meta_compat_child(void);
void meta_compat_parent(pid_t);
-extern Boolean useMeta;
+extern bool useMeta;
diff --git a/contrib/bmake/metachar.h b/contrib/bmake/metachar.h
index 04f967229109..1fd1397cfe63 100644
--- a/contrib/bmake/metachar.h
+++ b/contrib/bmake/metachar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: metachar.h,v 1.15 2021/01/19 20:51:46 rillig Exp $ */
+/* $NetBSD: metachar.h,v 1.16 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@ extern unsigned char _metachar[];
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
-MAKE_INLINE Boolean
+MAKE_INLINE bool
needshell(const char *cmd)
{
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog
index fa6ea9b9d337..f73c4fb68c6b 100644
--- a/contrib/bmake/mk/ChangeLog
+++ b/contrib/bmake/mk/ChangeLog
@@ -1,3 +1,38 @@
+2021-06-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210616
+
+ * dirdeps.mk: when using .MAKE.DEPENDFILE_PREFERENCE to find
+ depend files to read, anchor MACHINE at , or end of string
+ to avoid prefix match.
+
+2021-05-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210504
+
+ * dirdeps.mk: re-implement ALL_MACHINES support to better
+ cater for local complexities, when ONLY_TARGET_SPEC_LIST
+ is not set. local.dirdeps.mk can set
+ DIRDEPS_ALL_MACHINES_FILTER and/or
+ DIRDEPS_ALL_MACHINES_FILTER_XTRAS to filter the results we get
+ from listing all existing Makefile.depend.*
+
+2021-04-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210420
+
+ * dirdeps.mk: revert previous - not always safe.
+
+2021-03-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210321
+
+ * dirdeps.mk: when generating dirdeps.cache
+ we only need to hook the initial DIRDEPS to the
+ dirdeps target. That and any _build_xtra_dirs (like tests which
+ should not be hooked directly to the dependency graph - to avoid
+ cycles)
+
2021-01-30 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210130
diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk
index 4eb20cedfec7..38ead3de37cd 100644
--- a/contrib/bmake/mk/dirdeps.mk
+++ b/contrib/bmake/mk/dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: dirdeps.mk,v 1.133 2021/01/31 04:39:22 sjg Exp $
+# $Id: dirdeps.mk,v 1.140 2021/06/20 23:42:38 sjg Exp $
# Copyright (c) 2010-2021, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
@@ -409,23 +409,72 @@ _DIRDEP_USE: .USE .MAKE
done
.ifdef ALL_MACHINES
-# this is how you limit it to only the machines we have been built for
-# previously.
.if empty(ONLY_TARGET_SPEC_LIST) && empty(ONLY_MACHINE_LIST)
-.if !empty(ALL_MACHINE_LIST)
-# ALL_MACHINE_LIST is the list of all legal machines - ignore anything else
-_machine_list != cd ${_CURDIR} && 'ls' -1 ${ALL_MACHINE_LIST:O:u:@m@${.MAKE.DEPENDFILE:T:R}.$m@} 2> /dev/null; echo
+# we start with everything
+_machine_list != echo; 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}* 2> /dev/null
+
+# some things we know we want to ignore
+DIRDEPS_TARGETS_SKIP_LIST += \
+ *~ \
+ *.bak \
+ *.inc \
+ *.old \
+ *.options \
+ *.orig \
+ *.rej \
+
+# first trim things we know we want to skip
+# and provide canonical form
+_machine_list := ${_machine_list:${DIRDEPS_TARGETS_SKIP_LIST:${M_ListToSkip}}:T:E}
+
+# cater for local complexities
+# local.dirdeps.mk can set
+# DIRDEPS_ALL_MACHINES_FILTER and
+# DIRDEPS_ALL_MACHINES_FILTER_XTRAS for final tweaks
+
+.if !empty(ALL_TARGET_SPEC_LIST)
+.if ${_debug_reldir}
+.info ALL_TARGET_SPEC_LIST=${ALL_TARGET_SPEC_LIST}
+.endif
+DIRDEPS_ALL_MACHINES_FILTER += \
+ @x@$${ALL_TARGET_SPEC_LIST:@s@$${x:M$$s}@}@
+.elif !empty(ALL_MACHINE_LIST)
+.if ${_debug_reldir}
+.info ALL_MACHINE_LIST=${ALL_MACHINE_LIST}
+.endif
+.if ${TARGET_SPEC_VARS:[#]} > 1
+# the space below can result in extraneous ':'
+DIRDEPS_ALL_MACHINES_FILTER += \
+ @x@$${ALL_MACHINE_LIST:@m@$${x:M$$m,*} $${x:M$$m}@}@
.else
-_machine_list != 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.* 2> /dev/null; echo
+DIRDEPS_ALL_MACHINES_FILTER += \
+ @x@$${ALL_MACHINE_LIST:@m@$${x:M$$m}@}@
.endif
-_only_machines := ${_machine_list:${NIgnoreFiles:UN*.bak}:E:O:u}
+.endif
+# add local XTRAS - default to something benign
+DIRDEPS_ALL_MACHINES_FILTER += \
+ ${DIRDEPS_ALL_MACHINES_FILTER_XTRAS:UNbak}
+
+.if ${_debug_reldir}
+.info _machine_list=${_machine_list}
+.info DIRDEPS_ALL_MACHINES_FILTER=${DIRDEPS_ALL_MACHINES_FILTER}
+.endif
+
+_only_machines := ${_machine_list:${DIRDEPS_ALL_MACHINES_FILTER:ts:}:S,:, ,g}
.else
_only_machines := ${ONLY_TARGET_SPEC_LIST:U} ${ONLY_MACHINE_LIST:U}
.endif
.if empty(_only_machines)
# we must be boot-strapping
-_only_machines := ${TARGET_MACHINE:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}}
+_only_machines := ${TARGET_MACHINE:U${ALL_TARGET_SPEC_LIST:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}}}
+.endif
+
+# cleanup the result
+_only_machines := ${_only_machines:O:u}
+
+.if ${_debug_reldir}
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: ALL_MACHINES _only_machines=${_only_machines}
.endif
.else # ! ALL_MACHINES
@@ -452,6 +501,10 @@ _only_machines := ${_only_machines:${NOT_TARGET_SPEC_LIST:${M_ListToSkip}}}
# clean up
_only_machines := ${_only_machines:O:u}
+.if ${_debug_reldir}
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _only_machines=${_only_machines}
+.endif
+
# make sure we have a starting place?
DIRDEPS ?= ${RELDIR}
.endif # target
@@ -697,6 +750,7 @@ _cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m}
.export _cache_deps
x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
.endif
+# anything in _build_xtra_dirs is hooked to dirdeps: only
x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \
@@ -735,7 +789,7 @@ DEP_MACHINE := ${_DEP_MACHINE}
# Warning: there is an assumption here that MACHINE is always
# the first entry in TARGET_SPEC_VARS.
# If TARGET_SPEC and MACHINE are insufficient, you have a problem.
-_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:S;${MACHINE};${d:E:C/,.*//};:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]}
+_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:C;${MACHINE}((,.+)?)$;${d:E:C/,.*//}\1;:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]}
.if !empty(_m)
# M_dep_qual_fixes isn't geared to Makefile.depend
_qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}}
@@ -801,6 +855,7 @@ _reldir_failed: .NOMETA
.endif
# bootstrapping new dependencies made easy?
+.if !target(bootstrap-empty)
.if !target(bootstrap) && (make(bootstrap) || \
make(bootstrap-this) || \
make(bootstrap-recurse) || \
@@ -856,3 +911,4 @@ bootstrap-empty: .NOTMAIN .NOMETA
@{ echo DIRDEPS=; echo ".include <dirdeps.mk>"; } > ${_want}
.endif
+.endif
diff --git a/contrib/bmake/mk/dpadd.mk b/contrib/bmake/mk/dpadd.mk
index 45585ce76d9b..aef12528f163 100644
--- a/contrib/bmake/mk/dpadd.mk
+++ b/contrib/bmake/mk/dpadd.mk
@@ -1,4 +1,4 @@
-# $Id: dpadd.mk,v 1.28 2020/08/19 17:51:53 sjg Exp $
+# $Id: dpadd.mk,v 1.29 2021/04/20 02:30:44 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
#
@@ -211,8 +211,9 @@ OBJ_${__lib:T:R} ?= ${__lib:H:S,${OBJTOP},${RELOBJTOP},}
# If INCLUDES_libfoo is not set, then we'll use ${SRC_libfoo}/h if it exists,
# else just ${SRC_libfoo}.
#
-INCLUDES_${__lib:T:R}?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h:${SRC_${__lib:T:R}}}
-
+.if !empty(SRC_${__lib:T:R})
+INCLUDES_${__lib:T:R} ?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h:${SRC_${__lib:T:R}}}
+.endif
.endfor
# even for staged libs we sometimes
diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk
index b014728ca450..96c35b1052ec 100755
--- a/contrib/bmake/mk/install-mk
+++ b/contrib/bmake/mk/install-mk
@@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: install-mk,v 1.191 2021/01/30 23:16:42 sjg Exp $
+# $Id: install-mk,v 1.196 2021/06/19 15:30:41 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@@ -70,7 +70,7 @@
# sjg@crufty.net
#
-MK_VERSION=20210130
+MK_VERSION=20210616
OWNER=
GROUP=
MODE=444
diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk
index b5cb39a30855..5d09dbd88e81 100644
--- a/contrib/bmake/mk/meta.autodep.mk
+++ b/contrib/bmake/mk/meta.autodep.mk
@@ -1,4 +1,4 @@
-# $Id: meta.autodep.mk,v 1.53 2020/11/08 05:47:56 sjg Exp $
+# $Id: meta.autodep.mk,v 1.54 2021/03/06 17:03:18 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -298,16 +298,20 @@ start_utc := ${now_utc}
meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \
created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}}
+.if !target(_reldir_finish)
#.END: _reldir_finish
.if target(gendirdeps)
_reldir_finish: gendirdeps
.endif
_reldir_finish: .NOMETA
@echo "${TIME_STAMP} Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
+.endif
+.if !target(_reldir_failed)
#.ERROR: _reldir_failed
_reldir_failed: .NOMETA
@echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
+.endif
.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0
.END: _reldir_finish
diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py
index 4627e08d7c11..193e303de3da 100755
--- a/contrib/bmake/mk/meta2deps.py
+++ b/contrib/bmake/mk/meta2deps.py
@@ -37,7 +37,7 @@ We only pay attention to a subset of the information in the
"""
RCSid:
- $Id: meta2deps.py,v 1.34 2020/10/02 03:11:17 sjg Exp $
+ $Id: meta2deps.py,v 1.38 2021/06/17 05:20:08 sjg Exp $
Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@@ -68,12 +68,6 @@ RCSid:
import os, re, sys
-def getv(dict, key, d=None):
- """Lookup key in dict and return value or the supplied default."""
- if key in dict:
- return dict[key]
- return d
-
def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
"""
Return an absolute path, resolving via cwd or last_dir if needed.
@@ -152,7 +146,10 @@ def abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
return path
def sort_unique(list, cmp=None, key=None, reverse=False):
- list.sort(cmp, key, reverse)
+ if sys.version_info[0] == 2:
+ list.sort(cmp, key, reverse)
+ else:
+ list.sort(reverse=reverse)
nl = []
le = None
for e in list:
@@ -224,22 +221,22 @@ class MetaFile:
"""
self.name = name
- self.debug = getv(conf, 'debug', 0)
- self.debug_out = getv(conf, 'debug_out', sys.stderr)
-
- self.machine = getv(conf, 'MACHINE', '')
- self.machine_arch = getv(conf, 'MACHINE_ARCH', '')
- self.target_spec = getv(conf, 'TARGET_SPEC', '')
- self.curdir = getv(conf, 'CURDIR')
- self.reldir = getv(conf, 'RELDIR')
- self.dpdeps = getv(conf, 'DPDEPS')
+ self.debug = conf.get('debug', 0)
+ self.debug_out = conf.get('debug_out', sys.stderr)
+
+ self.machine = conf.get('MACHINE', '')
+ self.machine_arch = conf.get('MACHINE_ARCH', '')
+ self.target_spec = conf.get('TARGET_SPEC', '')
+ self.curdir = conf.get('CURDIR')
+ self.reldir = conf.get('RELDIR')
+ self.dpdeps = conf.get('DPDEPS')
self.line = 0
if not self.conf:
# some of the steps below we want to do only once
self.conf = conf
- self.host_target = getv(conf, 'HOST_TARGET')
- for srctop in getv(conf, 'SRCTOPS', []):
+ self.host_target = conf.get('HOST_TARGET')
+ for srctop in conf.get('SRCTOPS', []):
if srctop[-1] != '/':
srctop += '/'
if not srctop in self.srctops:
@@ -256,7 +253,7 @@ class MetaFile:
if self.target_spec:
trim_list += add_trims(self.target_spec)
- for objroot in getv(conf, 'OBJROOTS', []):
+ for objroot in conf.get('OBJROOTS', []):
for e in trim_list:
if objroot.endswith(e):
# this is not what we want - fix it
@@ -276,7 +273,7 @@ class MetaFile:
self.srctops.sort(reverse=True)
self.objroots.sort(reverse=True)
- self.excludes = getv(conf, 'EXCLUDES', [])
+ self.excludes = conf.get('EXCLUDES', [])
if self.debug:
print("host_target=", self.host_target, file=self.debug_out)
@@ -468,8 +465,8 @@ class MetaFile:
if pid != last_pid:
if last_pid:
pid_last_dir[last_pid] = self.last_dir
- cwd = getv(pid_cwd, pid, self.cwd)
- self.last_dir = getv(pid_last_dir, pid, self.cwd)
+ cwd = pid_cwd.get(pid, self.cwd)
+ self.last_dir = pid_last_dir.get(pid, self.cwd)
last_pid = pid
# process operations
@@ -557,7 +554,10 @@ class MetaFile:
# to the src dir, we may need to add dependencies for each
rdir = dir
dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out)
- rdir = os.path.realpath(dir)
+ if dir:
+ rdir = os.path.realpath(dir)
+ else:
+ dir = rdir
if rdir == dir:
rdir = None
# now put path back together
@@ -725,7 +725,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
for a in eaten:
args.remove(a)
- debug_out = getv(conf, 'debug_out', sys.stderr)
+ debug_out = conf.get('debug_out', sys.stderr)
if debug:
print("config:", file=debug_out)
@@ -752,9 +752,9 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
print(m.src_dirdeps('\nsrc:'))
- dpdeps = getv(conf, 'DPDEPS')
+ dpdeps = conf.get('DPDEPS')
if dpdeps:
- m.file_depends(open(dpdeps, 'wb'))
+ m.file_depends(open(dpdeps, 'w'))
return m
diff --git a/contrib/bmake/mk/rst2htm.mk b/contrib/bmake/mk/rst2htm.mk
index 1db9792f4127..66eb8552f875 100644
--- a/contrib/bmake/mk/rst2htm.mk
+++ b/contrib/bmake/mk/rst2htm.mk
@@ -1,4 +1,4 @@
-# $Id: rst2htm.mk,v 1.11 2020/08/19 17:51:53 sjg Exp $
+# $Id: rst2htm.mk,v 1.12 2021/05/26 04:20:31 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@@ -21,7 +21,9 @@ TXTSRCS != 'ls' -1t ${.CURDIR}/*.txt ${.CURDIR}/*.rst 2>/dev/null; echo
.endif
RSTSRCS ?= ${TXTSRCS}
HTMFILES ?= ${RSTSRCS:R:T:O:u:%=%.htm}
-RST2HTML ?= rst2html.py
+# can be empty, 4 or 5
+HTML_VERSION ?=
+RST2HTML ?= rst2html${HTML_VERSION}.py
RST2PDF ?= rst2pdf
RST2S5 ?= rst2s5.py
# the following will run RST2S5 if the target name contains the word 'slides'
diff --git a/contrib/bmake/nonints.h b/contrib/bmake/nonints.h
index 38ac0c85518b..7119d798432b 100644
--- a/contrib/bmake/nonints.h
+++ b/contrib/bmake/nonints.h
@@ -1,4 +1,4 @@
-/* $NetBSD: nonints.h,v 1.202 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: nonints.h,v 1.213 2021/04/11 13:35:56 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -76,14 +76,14 @@
void Arch_Init(void);
void Arch_End(void);
-Boolean Arch_ParseArchive(char **, GNodeList *, GNode *);
+bool Arch_ParseArchive(char **, GNodeList *, GNode *);
void Arch_Touch(GNode *);
void Arch_TouchLib(GNode *);
void Arch_UpdateMTime(GNode *gn);
void Arch_UpdateMemberMTime(GNode *gn);
void Arch_FindLib(GNode *, SearchPath *);
-Boolean Arch_LibOODate(GNode *);
-Boolean Arch_IsLib(GNode *);
+bool Arch_LibOODate(GNode *);
+bool Arch_IsLib(GNode *);
/* compat.c */
int Compat_RunCommand(const char *, GNode *, StringListNode *);
@@ -91,7 +91,7 @@ void Compat_Run(GNodeList *);
void Compat_Make(GNode *, GNode *);
/* cond.c */
-CondEvalResult Cond_EvalCondition(const char *, Boolean *);
+CondEvalResult Cond_EvalCondition(const char *, bool *);
CondEvalResult Cond_EvalLine(const char *);
void Cond_restore_depth(unsigned int);
unsigned int Cond_save_depth(void);
@@ -117,16 +117,16 @@ void SearchPath_Free(SearchPath *);
/* for.c */
int For_Eval(const char *);
-Boolean For_Accum(const char *);
+bool For_Accum(const char *);
void For_Run(int);
/* job.c */
#ifdef WAIT_T
-void JobReapChild(pid_t, WAIT_T, Boolean);
+void JobReapChild(pid_t, WAIT_T, bool);
#endif
/* main.c */
-Boolean GetBooleanVar(const char *, Boolean);
+bool GetBooleanExpr(const char *, bool);
void Main_ParseArgLine(const char *);
char *Cmd_Exec(const char *, const char **);
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
@@ -137,7 +137,7 @@ void Finish(int) MAKE_ATTR_DEAD;
int eunlink(const char *);
void execDie(const char *, const char *);
char *getTmpdir(void);
-Boolean ParseBoolean(const char *, Boolean);
+bool ParseBoolean(const char *, bool);
char *cached_realpath(const char *, char *);
/* parse.c */
@@ -161,86 +161,14 @@ typedef struct VarAssign {
typedef char *(*ReadMoreProc)(void *, size_t *);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
-Boolean Parse_IsVar(const char *, VarAssign *out_var);
-void Parse_DoVar(VarAssign *, GNode *);
+bool Parse_IsVar(const char *, VarAssign *out_var);
+void Parse_Var(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
void Parse_MainName(GNodeList *);
int Parse_GetFatals(void);
-/* str.c */
-
-/* A read-only string that may need to be freed after use. */
-typedef struct FStr {
- const char *str;
- void *freeIt;
-} FStr;
-
-/* A modifiable string that may need to be freed after use. */
-typedef struct MFStr {
- char *str;
- void *freeIt;
-} MFStr;
-
-typedef struct Words {
- char **words;
- size_t len;
- void *freeIt;
-} Words;
-
-/* Return a string that is the sole owner of str. */
-MAKE_INLINE FStr
-FStr_InitOwn(char *str)
-{
- return (FStr){ str, str };
-}
-
-/* Return a string that refers to the shared str. */
-MAKE_INLINE FStr
-FStr_InitRefer(const char *str)
-{
- return (FStr){ str, NULL };
-}
-
-MAKE_INLINE void
-FStr_Done(FStr *fstr)
-{
- free(fstr->freeIt);
-}
-
-/* Return a string that is the sole owner of str. */
-MAKE_INLINE MFStr
-MFStr_InitOwn(char *str)
-{
- return (MFStr){ str, str };
-}
-
-/* Return a string that refers to the shared str. */
-MAKE_INLINE MFStr
-MFStr_InitRefer(char *str)
-{
- return (MFStr){ str, NULL };
-}
-
-MAKE_INLINE void
-MFStr_Done(MFStr *mfstr)
-{
- free(mfstr->freeIt);
-}
-
-Words Str_Words(const char *, Boolean);
-MAKE_INLINE void
-Words_Free(Words w)
-{
- free(w.words);
- free(w.freeIt);
-}
-
-char *str_concat2(const char *, const char *);
-char *str_concat3(const char *, const char *, const char *);
-char *str_concat4(const char *, const char *, const char *, const char *);
-Boolean Str_Match(const char *, const char *);
#ifndef HAVE_STRLCPY
/* strlcpy.c */
@@ -252,12 +180,12 @@ void Suff_Init(void);
void Suff_End(void);
void Suff_ClearSuffixes(void);
-Boolean Suff_IsTransform(const char *);
+bool Suff_IsTransform(const char *);
GNode *Suff_AddTransform(const char *);
void Suff_EndTransform(GNode *);
void Suff_AddSuffix(const char *, GNode **);
SearchPath *Suff_GetPath(const char *);
-void Suff_DoPaths(void);
+void Suff_ExtendPaths(void);
void Suff_AddInclude(const char *);
void Suff_AddLib(const char *);
void Suff_FindDeps(GNode *);
@@ -277,7 +205,7 @@ GNode *Targ_GetNode(const char *);
GNode *Targ_NewInternalNode(const char *);
GNode *Targ_GetEndNode(void);
void Targ_FindList(GNodeList *, StringList *);
-Boolean Targ_Precious(const GNode *);
+bool Targ_Precious(const GNode *);
void Targ_SetMain(GNode *);
void Targ_PrintCmds(GNode *);
void Targ_PrintNode(GNode *, int);
@@ -292,36 +220,40 @@ const char *GNodeMade_Name(GNodeMade);
void Var_Init(void);
void Var_End(void);
-typedef enum VarEvalFlags {
- VARE_NONE = 0,
+typedef enum VarEvalMode {
/*
- * Expand and evaluate variables during parsing.
+ * Only parse the expression but don't evaluate any part of it.
*
- * TODO: Document what Var_Parse and Var_Subst return when this flag
- * is not set.
+ * TODO: Document what Var_Parse and Var_Subst return in this mode.
+ * As of 2021-03-15, they return unspecified, inconsistent results.
*/
- VARE_WANTRES = 1 << 0,
+ VARE_PARSE_ONLY,
+
+ /* Parse and evaluate the expression. */
+ VARE_WANTRES,
/*
- * Treat undefined variables as errors.
- * Must only be used in combination with VARE_WANTRES.
+ * Parse and evaluate the expression. It is an error if a
+ * subexpression evaluates to undefined.
*/
- VARE_UNDEFERR = 1 << 1,
+ VARE_UNDEFERR,
/*
- * Keep '$$' as '$$' instead of reducing it to a single '$'.
+ * Parse and evaluate the expression. Keep '$$' as '$$' instead of
+ * reducing it to a single '$'. Subexpressions that evaluate to
+ * undefined expand to an empty string.
*
* Used in variable assignments using the ':=' operator. It allows
* multiple such assignments to be chained without accidentally
* expanding '$$file' to '$file' in the first assignment and
* interpreting it as '${f}' followed by 'ile' in the next assignment.
*/
- VARE_KEEP_DOLLAR = 1 << 2,
+ VARE_EVAL_KEEP_DOLLAR,
/*
- * Keep undefined variables as-is instead of expanding them to an
- * empty string.
+ * Parse and evaluate the expression. Keep undefined variables as-is
+ * instead of expanding them to an empty string.
*
* Example for a ':=' assignment:
* CFLAGS = $(.INCLUDES)
@@ -330,8 +262,14 @@ typedef enum VarEvalFlags {
* # way) is still undefined, the updated CFLAGS becomes
* # "-I.. $(.INCLUDES)".
*/
- VARE_KEEP_UNDEF = 1 << 3
-} VarEvalFlags;
+ VARE_EVAL_KEEP_UNDEF,
+
+ /*
+ * Parse and evaluate the expression. Keep '$$' as '$$' and preserve
+ * undefined subexpressions.
+ */
+ VARE_KEEP_DOLLAR_UNDEF
+} VarEvalMode;
typedef enum VarSetFlags {
VAR_SET_NONE = 0,
@@ -361,7 +299,8 @@ typedef enum VarParseResult {
* Some callers handle this case differently, so return this
* information to them, for now.
*
- * TODO: Replace this with a new flag VARE_KEEP_UNDEFINED.
+ * TODO: Instead of having this special return value, rather ensure
+ * that VARE_EVAL_KEEP_UNDEF is processed properly.
*/
VPR_UNDEF
@@ -385,18 +324,18 @@ void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags);
void Var_Append(GNode *, const char *, const char *);
void Var_AppendExpand(GNode *, const char *, const char *);
-Boolean Var_Exists(GNode *, const char *);
-Boolean Var_ExistsExpand(GNode *, const char *);
+bool Var_Exists(GNode *, const char *);
+bool Var_ExistsExpand(GNode *, const char *);
FStr Var_Value(GNode *, const char *);
const char *GNode_ValueDirect(GNode *, const char *);
-VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *);
-VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
+VarParseResult Var_Parse(const char **, GNode *, VarEvalMode, FStr *);
+VarParseResult Var_Subst(const char *, GNode *, VarEvalMode, char **);
void Var_Stats(void);
void Var_Dump(GNode *);
void Var_ReexportVars(void);
void Var_Export(VarExportMode, const char *);
void Var_ExportVars(const char *);
-void Var_UnExport(Boolean, const char *);
+void Var_UnExport(bool, const char *);
void Global_Set(const char *, const char *);
void Global_SetExpand(const char *, const char *);
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index d14c7c46547a..3c2d75acdf9a 100644
--- a/contrib/bmake/parse.c
+++ b/contrib/bmake/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -86,7 +86,7 @@
* Parse_File Parse a top-level makefile. Included files are
* handled by IncludeFile instead.
*
- * Parse_IsVar Return TRUE if the given line is a variable
+ * Parse_IsVar Return true if the given line is a variable
* assignment. Used by MainParseArgs to determine if
* an argument is a target or a variable assignment.
* Used internally for pretty much the same thing.
@@ -124,7 +124,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $");
/* types and constants */
@@ -133,11 +133,11 @@ MAKE_RCSID("$NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $");
*/
typedef struct IFile {
char *fname; /* name of file (relative? absolute?) */
- Boolean fromForLoop; /* simulated .include by the .for loop */
+ bool fromForLoop; /* simulated .include by the .for loop */
int lineno; /* current line number in file */
int first_lineno; /* line number of start of text */
unsigned int cond_depth; /* 'if' nesting when file opened */
- Boolean depending; /* state of doing_depend on EOF */
+ bool depending; /* state of doing_depend on EOF */
/* The buffer from which the file's content is read. */
char *buf_freeIt;
@@ -333,7 +333,7 @@ struct loadedfile {
const char *path; /* name, for error reports */
char *buf; /* contents buffer */
size_t len; /* length of contents */
- Boolean used; /* XXX: have we used the data yet */
+ bool used; /* XXX: have we used the data yet */
};
/* XXX: What is the lifetime of the path? Who manages the memory? */
@@ -346,7 +346,7 @@ loadedfile_create(const char *path, char *buf, size_t buflen)
lf->path = path == NULL ? "(stdin)" : path;
lf->buf = buf;
lf->len = buflen;
- lf->used = FALSE;
+ lf->used = false;
return lf;
}
@@ -369,7 +369,7 @@ loadedfile_readMore(void *x, size_t *len)
if (lf->used)
return NULL;
- lf->used = TRUE;
+ lf->used = true;
*len = lf->len;
return lf->buf;
}
@@ -377,16 +377,16 @@ loadedfile_readMore(void *x, size_t *len)
/*
* Try to get the size of a file.
*/
-static Boolean
+static bool
load_getsize(int fd, size_t *ret)
{
struct stat st;
if (fstat(fd, &st) < 0)
- return FALSE;
+ return false;
if (!S_ISREG(st.st_mode))
- return FALSE;
+ return false;
/*
* st_size is an off_t, which is 64 bits signed; *ret is
@@ -398,10 +398,10 @@ load_getsize(int fd, size_t *ret)
* While we're at it reject negative sizes too, just in case.
*/
if (st.st_size < 0 || st.st_size > 0x3fffffff)
- return FALSE;
+ return false;
*ret = (size_t)st.st_size;
- return TRUE;
+ return true;
}
/*
@@ -507,7 +507,7 @@ PrintStackTrace(void)
for (i = n; i-- > 0;) {
const IFile *entry = entries + i;
const char *fname = entry->fname;
- Boolean printLineno;
+ bool printLineno;
char dirbuf[MAXPATHLEN + 1];
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
@@ -527,10 +527,10 @@ PrintStackTrace(void)
}
/* Check if the current character is escaped on the current line. */
-static Boolean
+static bool
ParseIsEscaped(const char *line, const char *c)
{
- Boolean active = FALSE;
+ bool active = false;
for (;;) {
if (line == c)
return active;
@@ -612,7 +612,7 @@ static void
ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
ParseErrorLevel type, const char *fmt, va_list ap)
{
- static Boolean fatal_warning_error_printed = FALSE;
+ static bool fatal_warning_error_printed = false;
(void)fprintf(f, "%s: ", progname);
@@ -631,7 +631,7 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
fatals++;
if (type == PARSE_WARNING && !fatal_warning_error_printed) {
Error("parsing warnings being treated as errors");
- fatal_warning_error_printed = TRUE;
+ fatal_warning_error_printed = true;
}
print_stack_trace:
@@ -730,7 +730,7 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
* which does not need to be informed once the child target has been made.
*/
static void
-LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial)
+LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
{
if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts))
pgn = pgn->cohorts.last->datum;
@@ -752,7 +752,7 @@ LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial)
/* Add the node to each target from the current dependency group. */
static void
-LinkToTargets(GNode *gn, Boolean isSpecial)
+LinkToTargets(GNode *gn, bool isSpecial)
{
GNodeListNode *ln;
@@ -760,7 +760,7 @@ LinkToTargets(GNode *gn, Boolean isSpecial)
LinkSource(ln->datum, gn, isSpecial);
}
-static Boolean
+static bool
TryApplyDependencyOperator(GNode *gn, GNodeType op)
{
/*
@@ -771,7 +771,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
((op & OP_OPMASK) != (gn->type & OP_OPMASK))) {
Parse_Error(PARSE_FATAL, "Inconsistent operator for %s",
gn->name);
- return FALSE;
+ return false;
}
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
@@ -821,7 +821,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
gn->type |= op;
}
- return TRUE;
+ return true;
}
static void
@@ -843,7 +843,7 @@ ApplyDependencyOperator(GNodeType op)
* We give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
-ParseDependencySourceWait(Boolean isSpecial)
+ParseDependencySourceWait(bool isSpecial)
{
static int wait_number = 0;
char wait_src[16];
@@ -858,29 +858,29 @@ ParseDependencySourceWait(Boolean isSpecial)
}
-static Boolean
+static bool
ParseDependencySourceKeyword(const char *src, ParseSpecial specType)
{
int keywd;
GNodeType op;
if (*src != '.' || !ch_isupper(src[1]))
- return FALSE;
+ return false;
keywd = ParseFindKeyword(src);
if (keywd == -1)
- return FALSE;
+ return false;
op = parseKeywords[keywd].op;
if (op != OP_NONE) {
ApplyDependencyOperator(op);
- return TRUE;
+ return true;
}
if (parseKeywords[keywd].spec == SP_WAIT) {
ParseDependencySourceWait(specType != SP_NOT);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static void
@@ -891,7 +891,7 @@ ParseDependencySourceMain(const char *src)
* list of things to create, but only if the user didn't specify a
* target on the command line and .MAIN occurs for the first time.
*
- * See ParseDoDependencyTargetSpecial, branch SP_MAIN.
+ * See ParseDependencyTargetSpecial, branch SP_MAIN.
* See unit-tests/cond-func-make-main.mk.
*/
Lst_Append(&opts.create, bmake_strdup(src));
@@ -1057,8 +1057,8 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
const char *nested_p = cp;
FStr nested_val;
- (void)Var_Parse(&nested_p, SCOPE_CMDLINE, VARE_NONE,
- &nested_val);
+ (void)Var_Parse(&nested_p, SCOPE_CMDLINE,
+ VARE_PARSE_ONLY, &nested_val);
/* TODO: handle errors */
FStr_Done(&nested_val);
cp += nested_p - cp;
@@ -1069,11 +1069,15 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
*pp = cp;
}
-/* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */
+/*
+ * Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER.
+ *
+ * See the tests deptgt-*.mk.
+ */
static void
-ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
- const char *targetName,
- SearchPathList **inout_paths)
+ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
+ const char *targetName,
+ SearchPathList **inout_paths)
{
switch (*inout_specType) {
case SP_PATH:
@@ -1116,13 +1120,13 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
break;
}
case SP_DELETE_ON_ERROR:
- deleteOnError = TRUE;
+ deleteOnError = true;
break;
case SP_NOTPARALLEL:
opts.maxJobs = 1;
break;
case SP_SINGLESHELL:
- opts.compatMake = TRUE;
+ opts.compatMake = true;
break;
case SP_ORDER:
order_pred = NULL;
@@ -1136,9 +1140,9 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
* .PATH<suffix> has to be handled specially.
* Call on the suffix module to give us a path to modify.
*/
-static Boolean
-ParseDoDependencyTargetPath(const char *suffixName,
- SearchPathList **inout_paths)
+static bool
+ParseDependencyTargetPath(const char *suffixName,
+ SearchPathList **inout_paths)
{
SearchPath *path;
@@ -1146,28 +1150,28 @@ ParseDoDependencyTargetPath(const char *suffixName,
if (path == NULL) {
Parse_Error(PARSE_FATAL,
"Suffix '%s' not defined (yet)", suffixName);
- return FALSE;
+ return false;
}
if (*inout_paths == NULL)
*inout_paths = Lst_New();
Lst_Append(*inout_paths, path);
- return TRUE;
+ return true;
}
/*
* See if it's a special target and if so set specType to match it.
*/
-static Boolean
-ParseDoDependencyTarget(const char *targetName,
- ParseSpecial *inout_specType,
- GNodeType *out_tOp, SearchPathList **inout_paths)
+static bool
+ParseDependencyTarget(const char *targetName,
+ ParseSpecial *inout_specType,
+ GNodeType *out_tOp, SearchPathList **inout_paths)
{
int keywd;
if (!(targetName[0] == '.' && ch_isupper(targetName[1])))
- return TRUE;
+ return true;
/*
* See if the target is a special target that must have it
@@ -1178,25 +1182,25 @@ ParseDoDependencyTarget(const char *targetName,
if (*inout_specType == SP_PATH &&
parseKeywords[keywd].spec != SP_PATH) {
Parse_Error(PARSE_FATAL, "Mismatched special targets");
- return FALSE;
+ return false;
}
*inout_specType = parseKeywords[keywd].spec;
*out_tOp = parseKeywords[keywd].op;
- ParseDoDependencyTargetSpecial(inout_specType, targetName,
+ ParseDependencyTargetSpecial(inout_specType, targetName,
inout_paths);
} else if (strncmp(targetName, ".PATH", 5) == 0) {
*inout_specType = SP_PATH;
- if (!ParseDoDependencyTargetPath(targetName + 5, inout_paths))
- return FALSE;
+ if (!ParseDependencyTargetPath(targetName + 5, inout_paths))
+ return false;
}
- return TRUE;
+ return true;
}
static void
-ParseDoDependencyTargetMundane(char *targetName, StringList *curTargs)
+ParseDependencyTargetMundane(char *targetName, StringList *curTargs)
{
if (Dir_HasWildcards(targetName)) {
/*
@@ -1233,16 +1237,16 @@ ParseDoDependencyTargetMundane(char *targetName, StringList *curTargs)
}
static void
-ParseDoDependencyTargetExtraWarn(char **pp, const char *lstart)
+ParseDependencyTargetExtraWarn(char **pp, const char *lstart)
{
- Boolean warning = FALSE;
+ bool warning = false;
char *cp = *pp;
while (*cp != '\0') {
if (!ParseIsEscaped(lstart, cp) && (*cp == '!' || *cp == ':'))
break;
if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t'))
- warning = TRUE;
+ warning = true;
cp++;
}
if (warning)
@@ -1252,7 +1256,7 @@ ParseDoDependencyTargetExtraWarn(char **pp, const char *lstart)
}
static void
-ParseDoDependencyCheckSpec(ParseSpecial specType)
+ParseDependencyCheckSpec(ParseSpecial specType)
{
switch (specType) {
default:
@@ -1276,15 +1280,19 @@ ParseDoDependencyCheckSpec(ParseSpecial specType)
}
}
-static Boolean
-ParseDoDependencyParseOp(char **pp, const char *lstart, GNodeType *out_op)
+/*
+ * In a dependency line like 'targets: sources' or 'targets! sources', parse
+ * the operator ':', '::' or '!' from between the targets and the sources.
+ */
+static bool
+ParseDependencyOp(char **pp, const char *lstart, GNodeType *out_op)
{
const char *cp = *pp;
if (*cp == '!') {
*out_op = OP_FORCE;
(*pp)++;
- return TRUE;
+ return true;
}
if (*cp == ':') {
@@ -1295,14 +1303,14 @@ ParseDoDependencyParseOp(char **pp, const char *lstart, GNodeType *out_op)
*out_op = OP_DEPENDS;
(*pp)++;
}
- return TRUE;
+ return true;
}
{
const char *msg = lstart[0] == '.'
? "Unknown directive" : "Missing dependency operator";
Parse_Error(PARSE_FATAL, "%s", msg);
- return FALSE;
+ return false;
}
}
@@ -1318,21 +1326,30 @@ ClearPaths(SearchPathList *paths)
Dir_SetPATH();
}
+/*
+ * Several special targets take different actions if present with no
+ * sources:
+ * a .SUFFIXES line with no sources clears out all old suffixes
+ * a .PRECIOUS line makes all targets precious
+ * a .IGNORE line ignores errors for all targets
+ * a .SILENT line creates silence when making all targets
+ * a .PATH removes all directories from the search path(s).
+ */
static void
-ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
+ParseDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
{
switch (specType) {
case SP_SUFFIXES:
Suff_ClearSuffixes();
break;
case SP_PRECIOUS:
- allPrecious = TRUE;
+ allPrecious = true;
break;
case SP_IGNORE:
- opts.ignoreErrors = TRUE;
+ opts.ignoreErrors = true;
break;
case SP_SILENT:
- opts.beSilent = TRUE;
+ opts.beSilent = true;
break;
case SP_PATH:
ClearPaths(paths);
@@ -1385,8 +1402,8 @@ AddToPaths(const char *dir, SearchPathList *paths)
* and will cause make to do a new chdir to that path.
*/
static void
-ParseDoDependencySourceSpecial(ParseSpecial specType, char *word,
- SearchPathList *paths)
+ParseDependencySourceSpecial(ParseSpecial specType, char *word,
+ SearchPathList *paths)
{
switch (specType) {
case SP_SUFFIXES:
@@ -1405,21 +1422,21 @@ ParseDoDependencySourceSpecial(ParseSpecial specType, char *word,
Suff_SetNull(word);
break;
case SP_OBJDIR:
- Main_SetObjdir(FALSE, "%s", word);
+ Main_SetObjdir(false, "%s", word);
break;
default:
break;
}
}
-static Boolean
-ParseDoDependencyTargets(char **inout_cp,
- char **inout_line,
- const char *lstart,
- ParseSpecial *inout_specType,
- GNodeType *inout_tOp,
- SearchPathList **inout_paths,
- StringList *curTargs)
+static bool
+ParseDependencyTargets(char **inout_cp,
+ char **inout_line,
+ const char *lstart,
+ ParseSpecial *inout_specType,
+ GNodeType *inout_tOp,
+ SearchPathList **inout_paths,
+ StringList *curTargs)
{
char *cp;
char *tgt = *inout_line;
@@ -1452,7 +1469,7 @@ ParseDoDependencyTargets(char **inout_cp,
* Arch_ParseArchive will set 'line' to be the first
* non-blank after the archive-spec. It creates/finds
* nodes for the members and places them on the given
- * list, returning TRUE if all went well and FALSE if
+ * list, returning true if all went well and false if
* there was an error in the specification. On error,
* line should remain untouched.
*/
@@ -1460,7 +1477,7 @@ ParseDoDependencyTargets(char **inout_cp,
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"",
tgt);
- return FALSE;
+ return false;
}
cp = tgt;
@@ -1469,23 +1486,23 @@ ParseDoDependencyTargets(char **inout_cp,
if (*cp == '\0') {
ParseErrorNoDependency(lstart);
- return FALSE;
+ return false;
}
/* Insert a null terminator. */
savec = *cp;
*cp = '\0';
- if (!ParseDoDependencyTarget(tgt, inout_specType, inout_tOp,
+ if (!ParseDependencyTarget(tgt, inout_specType, inout_tOp,
inout_paths))
- return FALSE;
+ return false;
/*
* Have word in line. Get or create its node and stick it at
* the end of the targets list
*/
if (*inout_specType == SP_NOT && *tgt != '\0')
- ParseDoDependencyTargetMundane(tgt, curTargs);
+ ParseDependencyTargetMundane(tgt, curTargs);
else if (*inout_specType == SP_PATH && *tgt != '.' &&
*tgt != '\0')
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
@@ -1499,7 +1516,7 @@ ParseDoDependencyTargets(char **inout_cp,
* we allow on this line.
*/
if (*inout_specType != SP_NOT && *inout_specType != SP_PATH)
- ParseDoDependencyTargetExtraWarn(&cp, lstart);
+ ParseDependencyTargetExtraWarn(&cp, lstart);
else
pp_skip_whitespace(&cp);
@@ -1513,12 +1530,12 @@ ParseDoDependencyTargets(char **inout_cp,
*inout_cp = cp;
*inout_line = tgt;
- return TRUE;
+ return true;
}
static void
-ParseDoDependencySourcesSpecial(char *start, char *end,
- ParseSpecial specType, SearchPathList *paths)
+ParseDependencySourcesSpecial(char *start, char *end,
+ ParseSpecial specType, SearchPathList *paths)
{
char savec;
@@ -1527,7 +1544,7 @@ ParseDoDependencySourcesSpecial(char *start, char *end,
end++;
savec = *end;
*end = '\0';
- ParseDoDependencySourceSpecial(specType, start, paths);
+ ParseDependencySourceSpecial(specType, start, paths);
*end = savec;
if (savec != '\0')
end++;
@@ -1536,9 +1553,9 @@ ParseDoDependencySourcesSpecial(char *start, char *end,
}
}
-static Boolean
-ParseDoDependencySourcesMundane(char *start, char *end,
- ParseSpecial specType, GNodeType tOp)
+static bool
+ParseDependencySourcesMundane(char *start, char *end,
+ ParseSpecial specType, GNodeType tOp)
{
while (*start != '\0') {
/*
@@ -1566,7 +1583,7 @@ ParseDoDependencySourcesMundane(char *start, char *end,
Parse_Error(PARSE_FATAL,
"Error in source archive spec \"%s\"",
start);
- return FALSE;
+ return false;
}
while (!Lst_IsEmpty(&sources)) {
@@ -1586,7 +1603,60 @@ ParseDoDependencySourcesMundane(char *start, char *end,
pp_skip_whitespace(&end);
start = end;
}
- return TRUE;
+ return true;
+}
+
+/*
+ * In a dependency line like 'targets: sources', parse the sources.
+ *
+ * See the tests depsrc-*.mk.
+ */
+static void
+ParseDependencySources(char *const line, char *const cp,
+ GNodeType const tOp,
+ ParseSpecial const specType,
+ SearchPathList ** inout_paths)
+{
+ if (line[0] == '\0') {
+ ParseDependencySourcesEmpty(specType, *inout_paths);
+ } else if (specType == SP_MFLAGS) {
+ Main_ParseArgLine(line);
+ /*
+ * Set the initial character to a null-character so the loop
+ * to get sources won't get anything.
+ */
+ *line = '\0';
+ } else if (specType == SP_SHELL) {
+ if (!Job_ParseShell(line)) {
+ Parse_Error(PARSE_FATAL,
+ "improper shell specification");
+ return;
+ }
+ *line = '\0';
+ } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
+ specType == SP_DELETE_ON_ERROR) {
+ *line = '\0';
+ }
+
+ /* Now go for the sources. */
+ if (specType == SP_SUFFIXES || specType == SP_PATH ||
+ specType == SP_INCLUDES || specType == SP_LIBS ||
+ specType == SP_NULL || specType == SP_OBJDIR) {
+ ParseDependencySourcesSpecial(line, cp, specType,
+ *inout_paths);
+ if (*inout_paths != NULL) {
+ Lst_Free(*inout_paths);
+ *inout_paths = NULL;
+ }
+ if (specType == SP_PATH)
+ Dir_SetPATH();
+ } else {
+ assert(*inout_paths == NULL);
+ if (!ParseDependencySourcesMundane(line, cp, specType, tOp))
+ return;
+ }
+
+ FindMainTarget();
}
/*
@@ -1598,7 +1668,7 @@ ParseDoDependencySourcesMundane(char *start, char *end,
*
* The operator is applied to each node in the global 'targets' list,
* which is where the nodes found for the targets are kept, by means of
- * the ParseDoOp function.
+ * the ParseOp function.
*
* The sources are parsed in much the same way as the targets, except
* that they are expanded using the wildcarding scheme of the C-Shell,
@@ -1617,7 +1687,7 @@ ParseDoDependencySourcesMundane(char *start, char *end,
* Upon return, the value of the line is unspecified.
*/
static void
-ParseDoDependency(char *line)
+ParseDependency(char *line)
{
char *cp; /* our current position */
GNodeType op; /* the operator on the line */
@@ -1635,7 +1705,7 @@ ParseDoDependency(char *line)
*/
ParseSpecial specType = SP_NOT;
- DEBUG1(PARSE, "ParseDoDependency(%s)\n", line);
+ DEBUG1(PARSE, "ParseDependency(%s)\n", line);
tOp = OP_NONE;
paths = NULL;
@@ -1643,8 +1713,8 @@ ParseDoDependency(char *line)
/*
* First, grind through the targets.
*/
- /* XXX: don't use line as an iterator variable */
- if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp,
+ /* XXX: don't use 'line' as an iterator variable */
+ if (!ParseDependencyTargets(&cp, &line, lstart, &specType, &tOp,
&paths, &curTargs))
goto out;
@@ -1656,12 +1726,12 @@ ParseDoDependency(char *line)
Lst_Init(&curTargs);
if (!Lst_IsEmpty(targets))
- ParseDoDependencyCheckSpec(specType);
+ ParseDependencyCheckSpec(specType);
/*
* Have now parsed all the target names. Must parse the operator next.
*/
- if (!ParseDoDependencyParseOp(&cp, lstart, &op))
+ if (!ParseDependencyOp(&cp, lstart, &op))
goto out;
/*
@@ -1680,55 +1750,7 @@ ParseDoDependency(char *line)
pp_skip_whitespace(&cp);
line = cp; /* XXX: 'line' is an inappropriate name */
- /*
- * Several special targets take different actions if present with no
- * sources:
- * a .SUFFIXES line with no sources clears out all old suffixes
- * a .PRECIOUS line makes all targets precious
- * a .IGNORE line ignores errors for all targets
- * a .SILENT line creates silence when making all targets
- * a .PATH removes all directories from the search path(s).
- */
- if (line[0] == '\0') {
- ParseDoDependencySourcesEmpty(specType, paths);
- } else if (specType == SP_MFLAGS) {
- /*
- * Call on functions in main.c to deal with these arguments and
- * set the initial character to a null-character so the loop to
- * get sources won't get anything
- */
- Main_ParseArgLine(line);
- *line = '\0';
- } else if (specType == SP_SHELL) {
- if (!Job_ParseShell(line)) {
- Parse_Error(PARSE_FATAL,
- "improper shell specification");
- goto out;
- }
- *line = '\0';
- } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
- specType == SP_DELETE_ON_ERROR) {
- *line = '\0';
- }
-
- /* Now go for the sources. */
- if (specType == SP_SUFFIXES || specType == SP_PATH ||
- specType == SP_INCLUDES || specType == SP_LIBS ||
- specType == SP_NULL || specType == SP_OBJDIR) {
- ParseDoDependencySourcesSpecial(line, cp, specType, paths);
- if (paths != NULL) {
- Lst_Free(paths);
- paths = NULL;
- }
- if (specType == SP_PATH)
- Dir_SetPATH();
- } else {
- assert(paths == NULL);
- if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp))
- goto out;
- }
-
- FindMainTarget();
+ ParseDependencySources(line, cp, tOp, specType, &paths);
out:
if (paths != NULL)
@@ -1805,7 +1827,7 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
*
* Used for both lines in a file and command line arguments.
*/
-Boolean
+bool
Parse_IsVar(const char *p, VarAssign *out_var)
{
VarAssignParsed pvar;
@@ -1861,7 +1883,7 @@ Parse_IsVar(const char *p, VarAssign *out_var)
pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1;
cpp_skip_whitespace(&p);
AdjustVarassignOp(&pvar, p, out_var);
- return TRUE;
+ return true;
}
if (*p == '=' &&
(ch == '+' || ch == ':' || ch == '?' || ch == '!')) {
@@ -1870,13 +1892,13 @@ Parse_IsVar(const char *p, VarAssign *out_var)
p++;
cpp_skip_whitespace(&p);
AdjustVarassignOp(&pvar, p, out_var);
- return TRUE;
+ return true;
}
if (firstSpace != NULL)
- return FALSE;
+ return false;
}
- return FALSE;
+ return false;
}
/*
@@ -1889,7 +1911,7 @@ VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
char *expandedValue;
- (void)Var_Subst(uvalue, scope, VARE_NONE,
+ (void)Var_Subst(uvalue, scope, VARE_PARSE_ONLY,
&expandedValue);
/* TODO: handle errors */
free(expandedValue);
@@ -1913,8 +1935,7 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
if (!Var_ExistsExpand(scope, name))
Var_SetExpand(scope, name, "");
- (void)Var_Subst(uvalue, scope,
- VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue);
+ (void)Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF, &evalue);
/* TODO: handle errors */
Var_SetExpand(scope, name, evalue);
@@ -1933,8 +1954,8 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
cmd = FStr_InitRefer(uvalue);
if (strchr(cmd.str, '$') != NULL) {
char *expanded;
- (void)Var_Subst(cmd.str, SCOPE_CMDLINE,
- VARE_WANTRES | VARE_UNDEFERR, &expanded);
+ (void)Var_Subst(cmd.str, SCOPE_CMDLINE, VARE_UNDEFERR,
+ &expanded);
/* TODO: handle errors */
cmd = FStr_InitOwn(expanded);
}
@@ -1952,7 +1973,7 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
/*
* Perform a variable assignment.
*
- * The actual value of the variable is returned in *out_TRUE_avalue.
+ * The actual value of the variable is returned in *out_true_avalue.
* Especially for VAR_SUBST and VAR_SHELL this can differ from the literal
* value.
*
@@ -1960,9 +1981,9 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
* the case. It is only skipped if the operator is '?=' and the variable
* already exists.
*/
-static Boolean
+static bool
VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
- GNode *scope, FStr *out_TRUE_avalue)
+ GNode *scope, FStr *out_true_avalue)
{
FStr avalue = FStr_InitRefer(uvalue);
@@ -1974,21 +1995,21 @@ VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
VarAssign_EvalShell(name, uvalue, scope, &avalue);
else {
if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name))
- return FALSE;
+ return false;
/* Normal assignment -- just do it. */
Var_SetExpand(scope, name, uvalue);
}
- *out_TRUE_avalue = avalue;
- return TRUE;
+ *out_true_avalue = avalue;
+ return true;
}
static void
VarAssignSpecial(const char *name, const char *avalue)
{
if (strcmp(name, MAKEOVERRIDES) == 0)
- Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */
+ Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
else if (strcmp(name, ".CURDIR") == 0) {
/*
* Someone is being (too?) clever...
@@ -2005,7 +2026,7 @@ VarAssignSpecial(const char *name, const char *avalue)
/* Perform the variable variable assignment in the given scope. */
void
-Parse_DoVar(VarAssign *var, GNode *scope)
+Parse_Var(VarAssign *var, GNode *scope)
{
FStr avalue; /* actual value (maybe expanded) */
@@ -2023,7 +2044,7 @@ Parse_DoVar(VarAssign *var, GNode *scope)
* See if the command possibly calls a sub-make by using the variable
* expressions ${.MAKE}, ${MAKE} or the plain word "make".
*/
-static Boolean
+static bool
MaybeSubMake(const char *cmd)
{
const char *start;
@@ -2036,7 +2057,7 @@ MaybeSubMake(const char *cmd)
if (p[0] == 'm' && p[1] == 'a' && p[2] == 'k' && p[3] == 'e')
if (start == cmd || !ch_isalnum(p[-1]))
if (!ch_isalnum(p[4]))
- return TRUE;
+ return true;
if (*p != '$')
continue;
@@ -2055,9 +2076,9 @@ MaybeSubMake(const char *cmd)
if (p[0] == 'M' && p[1] == 'A' && p[2] == 'K' && p[3] == 'E')
if (p[4] == endc)
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -2119,7 +2140,7 @@ Parse_AddIncludeDir(const char *dir)
* line options.
*/
static void
-IncludeFile(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
+IncludeFile(char *file, bool isSystem, bool depinc, bool silent)
{
struct loadedfile *lf;
char *fullname; /* full pathname of file */
@@ -2225,11 +2246,11 @@ IncludeFile(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
}
static void
-ParseDoInclude(char *directive)
+ParseInclude(char *directive)
{
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
- Boolean silent = directive[0] != 'i';
+ bool silent = directive[0] != 'i';
char *file = directive + (silent ? 8 : 7);
/* Skip to delimiter character so we know where to look */
@@ -2281,25 +2302,24 @@ ParseDoInclude(char *directive)
static void
SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
{
- const char *slash, *dirname, *basename;
- void *freeIt;
+ const char *slash, *basename;
+ FStr dirname;
slash = strrchr(filename, '/');
if (slash == NULL) {
- dirname = curdir;
+ dirname = FStr_InitRefer(curdir);
basename = filename;
- freeIt = NULL;
} else {
- dirname = freeIt = bmake_strsedup(filename, slash);
+ dirname = FStr_InitOwn(bmake_strsedup(filename, slash));
basename = slash + 1;
}
- Global_SetExpand(dirvar, dirname);
+ Global_SetExpand(dirvar, dirname.str);
Global_SetExpand(filevar, basename);
DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
- __func__, dirvar, dirname, filevar, basename);
- free(freeIt);
+ __func__, dirvar, dirname.str, filevar, basename);
+ FStr_Done(&dirname);
}
/*
@@ -2338,7 +2358,7 @@ ParseSetParseFile(const char *filename)
}
}
-static Boolean
+static bool
StrContainsWord(const char *str, const char *word)
{
size_t strLen = strlen(str);
@@ -2346,20 +2366,20 @@ StrContainsWord(const char *str, const char *word)
const char *p, *end;
if (strLen < wordLen)
- return FALSE; /* str is too short to contain word */
+ return false; /* str is too short to contain word */
end = str + strLen - wordLen;
for (p = str; p != NULL; p = strchr(p, ' ')) {
if (*p == ' ')
p++;
if (p > end)
- return FALSE; /* cannot contain word */
+ return false; /* cannot contain word */
if (memcmp(p, word, wordLen) == 0 &&
(p[wordLen] == '\0' || p[wordLen] == ' '))
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -2368,11 +2388,11 @@ StrContainsWord(const char *str, const char *word)
*
* XXX: The paths in this list don't seem to be normalized in any way.
*/
-static Boolean
+static bool
VarContainsWord(const char *varname, const char *word)
{
FStr val = Var_Value(SCOPE_GLOBAL, varname);
- Boolean found = val.str != NULL && StrContainsWord(val.str, word);
+ bool found = val.str != NULL && StrContainsWord(val.str, word);
FStr_Done(&val);
return found;
}
@@ -2404,7 +2424,7 @@ Parse_SetInput(const char *name, int lineno, int fd,
IFile *curFile;
char *buf;
size_t len;
- Boolean fromForLoop = name == NULL;
+ bool fromForLoop = name == NULL;
if (fromForLoop)
name = CurFile()->fname;
@@ -2449,14 +2469,14 @@ Parse_SetInput(const char *name, int lineno, int fd,
}
/* Check if the directive is an include directive. */
-static Boolean
-IsInclude(const char *dir, Boolean sysv)
+static bool
+IsInclude(const char *dir, bool sysv)
{
if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv))
dir++;
if (strncmp(dir, "include", 7) != 0)
- return FALSE;
+ return false;
/* Space is not mandatory for BSD .include */
return !sysv || ch_isspace(dir[7]);
@@ -2465,26 +2485,26 @@ IsInclude(const char *dir, Boolean sysv)
#ifdef SYSVINCLUDE
/* Check if the line is a SYSV include directive. */
-static Boolean
+static bool
IsSysVInclude(const char *line)
{
const char *p;
- if (!IsInclude(line, TRUE))
- return FALSE;
+ if (!IsInclude(line, true))
+ return false;
/* Avoid interpreting a dependency line as an include */
for (p = line; (p = strchr(p, ':')) != NULL;) {
/* end of line -> it's a dependency */
if (*++p == '\0')
- return FALSE;
+ return false;
/* '::' operator or ': ' -> it's a dependency */
if (*p == ':' || ch_isspace(*p))
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/* Push to another file. The line points to the word "include". */
@@ -2492,8 +2512,8 @@ static void
ParseTraditionalInclude(char *line)
{
char *cp; /* current position in file spec */
- Boolean done = FALSE;
- Boolean silent = line[0] != 'i';
+ bool done = false;
+ bool silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
char *all_files;
@@ -2521,9 +2541,9 @@ ParseTraditionalInclude(char *line)
if (*cp != '\0')
*cp = '\0';
else
- done = TRUE;
+ done = true;
- IncludeFile(file, FALSE, FALSE, silent);
+ IncludeFile(file, false, false, silent);
}
out:
free(all_files);
@@ -2569,10 +2589,10 @@ ParseGmakeExport(char *line)
* up to go back to reading the previous file at the previous location.
*
* Results:
- * TRUE to continue parsing, i.e. it had only reached the end of an
- * included file, FALSE if the main file has been parsed completely.
+ * true to continue parsing, i.e. it had only reached the end of an
+ * included file, false if the main file has been parsed completely.
*/
-static Boolean
+static bool
ParseEOF(void)
{
char *ptr;
@@ -2589,7 +2609,7 @@ ParseEOF(void)
curFile->buf_end = ptr == NULL ? NULL : ptr + len;
curFile->lineno = curFile->first_lineno;
if (ptr != NULL)
- return TRUE; /* Iterate again */
+ return true; /* Iterate again */
/* Ensure the makefile (or loop) didn't have mismatched conditionals */
Cond_restore_depth(curFile->cond_depth);
@@ -2610,7 +2630,7 @@ ParseEOF(void)
Global_Delete(".PARSEFILE");
Global_Delete(".INCLUDEDFROMDIR");
Global_Delete(".INCLUDEDFROMFILE");
- return FALSE;
+ return false;
}
curFile = CurFile();
@@ -2618,7 +2638,7 @@ ParseEOF(void)
curFile->fname, curFile->lineno);
ParseSetParseFile(curFile->fname);
- return TRUE;
+ return true;
}
typedef enum ParseRawLineResult {
@@ -2839,7 +2859,7 @@ ParseGetLine(GetLineMode mode)
return line;
}
-static Boolean
+static bool
ParseSkippedBranches(void)
{
char *line;
@@ -2854,7 +2874,7 @@ ParseSkippedBranches(void)
* This check will probably duplicate some of
* the code in ParseLine. Most of the code
* there cannot apply, only ParseVarassign and
- * ParseDependency can, and to prevent code
+ * ParseDependencyLine can, and to prevent code
* duplication, these would need to be called
* with a flag called onlyCheckSyntax.
*
@@ -2865,7 +2885,7 @@ ParseSkippedBranches(void)
return line != NULL;
}
-static Boolean
+static bool
ParseForLoop(const char *line)
{
int rval;
@@ -2873,9 +2893,9 @@ ParseForLoop(const char *line)
rval = For_Eval(line);
if (rval == 0)
- return FALSE; /* Not a .for line */
+ return false; /* Not a .for line */
if (rval < 0)
- return TRUE; /* Syntax error - error printed, ignore line */
+ return true; /* Syntax error - error printed, ignore line */
firstLineno = CurFile()->lineno;
@@ -2891,7 +2911,7 @@ ParseForLoop(const char *line)
For_Run(firstLineno); /* Stash each iteration as a new 'input file' */
- return TRUE; /* Read next line from for-loop buffer */
+ return true; /* Read next line from for-loop buffer */
}
/*
@@ -2992,7 +3012,7 @@ ParseLine_ShellCommand(const char *p)
}
}
-MAKE_INLINE Boolean
+MAKE_INLINE bool
IsDirective(const char *dir, size_t dirlen, const char *name)
{
return dirlen == strlen(name) && memcmp(dir, name, dirlen) == 0;
@@ -3002,7 +3022,7 @@ IsDirective(const char *dir, size_t dirlen, const char *name)
* See if the line starts with one of the known directives, and if so, handle
* the directive.
*/
-static Boolean
+static bool
ParseDirective(char *line)
{
char *cp = line + 1;
@@ -3010,9 +3030,9 @@ ParseDirective(char *line)
size_t dirlen;
pp_skip_whitespace(&cp);
- if (IsInclude(cp, FALSE)) {
- ParseDoInclude(cp);
- return TRUE;
+ if (IsInclude(cp, false)) {
+ ParseInclude(cp);
+ return true;
}
dir = cp;
@@ -3021,53 +3041,53 @@ ParseDirective(char *line)
dirlen = (size_t)(cp - dir);
if (*cp != '\0' && !ch_isspace(*cp))
- return FALSE;
+ return false;
pp_skip_whitespace(&cp);
arg = cp;
if (IsDirective(dir, dirlen, "undef")) {
Var_Undef(cp);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "export")) {
Var_Export(VEM_PLAIN, arg);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "export-env")) {
Var_Export(VEM_ENV, arg);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "export-literal")) {
Var_Export(VEM_LITERAL, arg);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "unexport")) {
- Var_UnExport(FALSE, arg);
- return TRUE;
+ Var_UnExport(false, arg);
+ return true;
} else if (IsDirective(dir, dirlen, "unexport-env")) {
- Var_UnExport(TRUE, arg);
- return TRUE;
+ Var_UnExport(true, arg);
+ return true;
} else if (IsDirective(dir, dirlen, "info")) {
ParseMessage(PARSE_INFO, "info", arg);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "warning")) {
ParseMessage(PARSE_WARNING, "warning", arg);
- return TRUE;
+ return true;
} else if (IsDirective(dir, dirlen, "error")) {
ParseMessage(PARSE_FATAL, "error", arg);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
-static Boolean
+static bool
ParseVarassign(const char *line)
{
VarAssign var;
if (!Parse_IsVar(line, &var))
- return FALSE;
+ return false;
FinishDependencyGroup();
- Parse_DoVar(&var, SCOPE_GLOBAL);
- return TRUE;
+ Parse_Var(&var, SCOPE_GLOBAL);
+ return true;
}
static char *
@@ -3092,13 +3112,13 @@ FindSemicolon(char *p)
}
/*
- * dependency -> target... op [source...]
+ * dependency -> target... op [source...] [';' command]
* op -> ':' | '::' | '!'
*/
static void
-ParseDependency(char *line)
+ParseDependencyLine(char *line)
{
- VarEvalFlags eflags;
+ VarEvalMode emode;
char *expanded_line;
const char *shellcmd = NULL;
@@ -3147,8 +3167,8 @@ ParseDependency(char *line)
* 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 = opts.strict ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR;
- (void)Var_Subst(line, SCOPE_CMDLINE, eflags, &expanded_line);
+ emode = opts.strict ? VARE_WANTRES : VARE_UNDEFERR;
+ (void)Var_Subst(line, SCOPE_CMDLINE, emode, &expanded_line);
/* TODO: handle errors */
/* Need a fresh list for the target nodes */
@@ -3156,7 +3176,7 @@ ParseDependency(char *line)
Lst_Free(targets);
targets = Lst_New();
- ParseDoDependency(expanded_line);
+ ParseDependency(expanded_line);
free(expanded_line);
if (shellcmd != NULL)
@@ -3207,7 +3227,7 @@ ParseLine(char *line)
FinishDependencyGroup();
- ParseDependency(line);
+ ParseDependencyLine(line);
}
/*
diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c
index c486df6d3d84..b4baede4d417 100644
--- a/contrib/bmake/str.c
+++ b/contrib/bmake/str.c
@@ -1,4 +1,4 @@
-/* $NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $ */
+/* $NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -71,7 +71,7 @@
#include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
-MAKE_RCSID("$NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $");
+MAKE_RCSID("$NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
@@ -99,39 +99,23 @@ str_concat3(const char *s1, const char *s2, const char *s3)
return result;
}
-/* Return the concatenation of s1, s2, s3 and s4, freshly allocated. */
-char *
-str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
-{
- size_t len1 = strlen(s1);
- size_t len2 = strlen(s2);
- size_t len3 = strlen(s3);
- size_t len4 = strlen(s4);
- char *result = bmake_malloc(len1 + len2 + len3 + len4 + 1);
- memcpy(result, s1, len1);
- memcpy(result + len1, s2, len2);
- memcpy(result + len1 + len2, s3, len3);
- memcpy(result + len1 + len2 + len3, s4, len4 + 1);
- return result;
-}
-
/*
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account.
*
- * If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
+ * If expand is true, quotes are removed and escape sequences such as \r, \t,
* etc... are expanded. In this case, return NULL on parse errors.
*
* Returns the fractured words, which must be freed later using Words_Free,
* unless the returned Words.words was NULL.
*/
-Words
-Str_Words(const char *str, Boolean expand)
+SubstringWords
+Substring_Words(const char *str, bool expand)
{
size_t str_len;
char *words_buf;
size_t words_cap;
- char **words;
+ Substring *words;
size_t words_len;
char inquote;
char *word_start;
@@ -146,7 +130,7 @@ Str_Words(const char *str, Boolean expand)
words_buf = bmake_malloc(str_len + 1);
words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
- words = bmake_malloc((words_cap + 1) * sizeof(char *));
+ words = bmake_malloc((words_cap + 1) * sizeof(words[0]));
/*
* copy the string; at the same time, parse backslashes,
@@ -205,17 +189,24 @@ Str_Words(const char *str, Boolean expand)
*word_end++ = '\0';
if (words_len == words_cap) {
size_t new_size;
- words_cap *= 2; /* ramp up fast */
- new_size = (words_cap + 1) * sizeof(char *);
+ words_cap *= 2;
+ new_size = (words_cap + 1) * sizeof(words[0]);
words = bmake_realloc(words, new_size);
}
- words[words_len++] = word_start;
+ words[words_len++] =
+ Substring_Init(word_start, word_end - 1);
word_start = NULL;
if (ch == '\n' || ch == '\0') {
if (expand && inquote != '\0') {
+ SubstringWords res;
+
free(words);
free(words_buf);
- return (Words){ NULL, 0, NULL };
+
+ res.words = NULL;
+ res.len = 0;
+ res.freeIt = NULL;
+ return res;
}
goto done;
}
@@ -262,8 +253,40 @@ Str_Words(const char *str, Boolean expand)
*word_end++ = ch;
}
done:
- words[words_len] = NULL; /* useful for argv */
- return (Words){ words, words_len, words_buf };
+ words[words_len] = Substring_Init(NULL, NULL); /* useful for argv */
+
+ {
+ SubstringWords result;
+
+ result.words = words;
+ result.len = words_len;
+ result.freeIt = words_buf;
+ return result;
+ }
+}
+
+Words
+Str_Words(const char *str, bool expand)
+{
+ SubstringWords swords;
+ Words words;
+ size_t i;
+
+ swords = Substring_Words(str, expand);
+ if (swords.words == NULL) {
+ words.words = NULL;
+ words.len = 0;
+ words.freeIt = NULL;
+ return words;
+ }
+
+ words.words = bmake_malloc((swords.len + 1) * sizeof(words.words[0]));
+ words.len = swords.len;
+ words.freeIt = swords.freeIt;
+ for (i = 0; i < swords.len + 1; i++)
+ words.words[i] = UNCONST(swords.words[i].start);
+ free(swords.words);
+ return words;
}
/*
@@ -272,7 +295,7 @@ done:
*
* XXX: this function does not detect or report malformed patterns.
*/
-Boolean
+bool
Str_Match(const char *str, const char *pat)
{
for (;;) {
@@ -284,7 +307,7 @@ Str_Match(const char *str, const char *pat)
if (*pat == '\0')
return *str == '\0';
if (*str == '\0' && *pat != '*')
- return FALSE;
+ return false;
/*
* A '*' in the pattern matches any substring. We handle this
@@ -295,13 +318,13 @@ Str_Match(const char *str, const char *pat)
while (*pat == '*')
pat++;
if (*pat == '\0')
- return TRUE;
+ return true;
while (*str != '\0') {
if (Str_Match(str, pat))
- return TRUE;
+ return true;
str++;
}
- return FALSE;
+ return false;
}
/* A '?' in the pattern matches any single character. */
@@ -315,14 +338,14 @@ Str_Match(const char *str, const char *pat)
* character lists, the backslash is an ordinary character.
*/
if (*pat == '[') {
- Boolean neg = pat[1] == '^';
+ bool neg = pat[1] == '^';
pat += neg ? 2 : 1;
for (;;) {
if (*pat == ']' || *pat == '\0') {
if (neg)
break;
- return FALSE;
+ return false;
}
/*
* XXX: This naive comparison makes the
@@ -347,7 +370,7 @@ Str_Match(const char *str, const char *pat)
pat++;
}
if (neg && *pat != ']' && *pat != '\0')
- return FALSE;
+ return false;
while (*pat != ']' && *pat != '\0')
pat++;
if (*pat == '\0')
@@ -362,11 +385,11 @@ Str_Match(const char *str, const char *pat)
if (*pat == '\\') {
pat++;
if (*pat == '\0')
- return FALSE;
+ return false;
}
if (*pat != *str)
- return FALSE;
+ return false;
thisCharOK:
pat++;
diff --git a/contrib/bmake/str.h b/contrib/bmake/str.h
new file mode 100644
index 000000000000..ce0bb5ad82bc
--- /dev/null
+++ b/contrib/bmake/str.h
@@ -0,0 +1,366 @@
+/* $NetBSD: str.h,v 1.9 2021/05/30 21:16:54 rillig Exp $ */
+
+/*
+ Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Memory-efficient string handling.
+ */
+
+
+/* A read-only string that may need to be freed after use. */
+typedef struct FStr {
+ const char *str;
+ void *freeIt;
+} FStr;
+
+/* A modifiable string that may need to be freed after use. */
+typedef struct MFStr {
+ char *str;
+ void *freeIt;
+} MFStr;
+
+/* A read-only range of a character array, NOT null-terminated. */
+typedef struct Substring {
+ const char *start;
+ const char *end;
+} Substring;
+
+/*
+ * Builds a string, only allocating memory if the string is different from the
+ * expected string.
+ */
+typedef struct LazyBuf {
+ char *data;
+ size_t len;
+ size_t cap;
+ const char *expected;
+ void *freeIt;
+} LazyBuf;
+
+/* The result of splitting a string into words. */
+typedef struct Words {
+ char **words;
+ size_t len;
+ void *freeIt;
+} Words;
+
+/* The result of splitting a string into words. */
+typedef struct SubstringWords {
+ Substring *words;
+ size_t len;
+ void *freeIt;
+} SubstringWords;
+
+
+MAKE_INLINE FStr
+FStr_Init(const char *str, void *freeIt)
+{
+ FStr fstr;
+ fstr.str = str;
+ fstr.freeIt = freeIt;
+ return fstr;
+}
+
+/* Return a string that is the sole owner of str. */
+MAKE_INLINE FStr
+FStr_InitOwn(char *str)
+{
+ return FStr_Init(str, str);
+}
+
+/* Return a string that refers to the shared str. */
+MAKE_INLINE FStr
+FStr_InitRefer(const char *str)
+{
+ return FStr_Init(str, NULL);
+}
+
+MAKE_INLINE void
+FStr_Done(FStr *fstr)
+{
+ free(fstr->freeIt);
+#ifdef CLEANUP
+ fstr->str = NULL;
+ fstr->freeIt = NULL;
+#endif
+}
+
+
+MAKE_INLINE MFStr
+MFStr_Init(char *str, void *freeIt)
+{
+ MFStr mfstr;
+ mfstr.str = str;
+ mfstr.freeIt = freeIt;
+ return mfstr;
+}
+
+/* Return a string that is the sole owner of str. */
+MAKE_INLINE MFStr
+MFStr_InitOwn(char *str)
+{
+ return MFStr_Init(str, str);
+}
+
+/* Return a string that refers to the shared str. */
+MAKE_INLINE MFStr
+MFStr_InitRefer(char *str)
+{
+ return MFStr_Init(str, NULL);
+}
+
+MAKE_INLINE void
+MFStr_Done(MFStr *mfstr)
+{
+ free(mfstr->freeIt);
+#ifdef CLEANUP
+ mfstr->str = NULL;
+ mfstr->freeIt = NULL;
+#endif
+}
+
+
+MAKE_STATIC Substring
+Substring_Init(const char *start, const char *end)
+{
+ Substring sub;
+
+ sub.start = start;
+ sub.end = end;
+ return sub;
+}
+
+MAKE_INLINE Substring
+Substring_InitStr(const char *str)
+{
+ return Substring_Init(str, str + strlen(str));
+}
+
+MAKE_STATIC size_t
+Substring_Length(Substring sub)
+{
+ return (size_t)(sub.end - sub.start);
+}
+
+MAKE_STATIC bool
+Substring_IsEmpty(Substring sub)
+{
+ return sub.start == sub.end;
+}
+
+MAKE_INLINE bool
+Substring_Equals(Substring sub, const char *str)
+{
+ size_t len = strlen(str);
+ return Substring_Length(sub) == len &&
+ memcmp(sub.start, str, len) == 0;
+}
+
+MAKE_STATIC Substring
+Substring_Sub(Substring sub, size_t start, size_t end)
+{
+ assert(start <= Substring_Length(sub));
+ assert(end <= Substring_Length(sub));
+ return Substring_Init(sub.start + start, sub.start + end);
+}
+
+MAKE_STATIC bool
+Substring_HasPrefix(Substring sub, Substring prefix)
+{
+ return Substring_Length(sub) >= Substring_Length(prefix) &&
+ memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
+}
+
+MAKE_STATIC bool
+Substring_HasSuffix(Substring sub, Substring suffix)
+{
+ size_t suffixLen = Substring_Length(suffix);
+ return Substring_Length(sub) >= suffixLen &&
+ memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
+}
+
+/* Returns an independent, null-terminated copy of the substring. */
+MAKE_STATIC FStr
+Substring_Str(Substring sub)
+{
+ if (Substring_IsEmpty(sub))
+ return FStr_InitRefer("");
+ return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
+}
+
+MAKE_STATIC const char *
+Substring_SkipFirst(Substring sub, char ch)
+{
+ const char *p;
+
+ for (p = sub.start; p != sub.end; p++)
+ if (*p == ch)
+ return p + 1;
+ return sub.start;
+}
+
+MAKE_STATIC const char *
+Substring_LastIndex(Substring sub, char ch)
+{
+ const char *p;
+
+ for (p = sub.end; p != sub.start; p--)
+ if (p[-1] == ch)
+ return p - 1;
+ return NULL;
+}
+
+MAKE_STATIC Substring
+Substring_Dirname(Substring pathname)
+{
+ const char *p;
+
+ for (p = pathname.end; p != pathname.start; p--)
+ if (p[-1] == '/')
+ return Substring_Init(pathname.start, p - 1);
+ return Substring_InitStr(".");
+}
+
+MAKE_STATIC Substring
+Substring_Basename(Substring pathname)
+{
+ const char *p;
+
+ for (p = pathname.end; p != pathname.start; p--)
+ if (p[-1] == '/')
+ return Substring_Init(p, pathname.end);
+ return pathname;
+}
+
+
+MAKE_STATIC void
+LazyBuf_Init(LazyBuf *buf, const char *expected)
+{
+ buf->data = NULL;
+ buf->len = 0;
+ buf->cap = 0;
+ buf->expected = expected;
+ buf->freeIt = NULL;
+}
+
+MAKE_INLINE void
+LazyBuf_Done(LazyBuf *buf)
+{
+ free(buf->freeIt);
+}
+
+MAKE_STATIC void
+LazyBuf_Add(LazyBuf *buf, char ch)
+{
+
+ if (buf->data != NULL) {
+ if (buf->len == buf->cap) {
+ buf->cap *= 2;
+ buf->data = bmake_realloc(buf->data, buf->cap);
+ }
+ buf->data[buf->len++] = ch;
+
+ } else if (ch == buf->expected[buf->len]) {
+ buf->len++;
+ return;
+
+ } else {
+ buf->cap = buf->len + 16;
+ buf->data = bmake_malloc(buf->cap);
+ memcpy(buf->data, buf->expected, buf->len);
+ buf->data[buf->len++] = ch;
+ }
+}
+
+MAKE_STATIC void
+LazyBuf_AddStr(LazyBuf *buf, const char *str)
+{
+ const char *p;
+
+ for (p = str; *p != '\0'; p++)
+ LazyBuf_Add(buf, *p);
+}
+
+MAKE_STATIC void
+LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end)
+{
+ const char *p;
+
+ for (p = start; p != end; p++)
+ LazyBuf_Add(buf, *p);
+}
+
+MAKE_INLINE void
+LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
+{
+ LazyBuf_AddBytesBetween(buf, sub.start, sub.end);
+}
+
+MAKE_STATIC Substring
+LazyBuf_Get(const LazyBuf *buf)
+{
+ const char *start = buf->data != NULL ? buf->data : buf->expected;
+ return Substring_Init(start, start + buf->len);
+}
+
+MAKE_STATIC FStr
+LazyBuf_DoneGet(LazyBuf *buf)
+{
+ if (buf->data != NULL) {
+ LazyBuf_Add(buf, '\0');
+ return FStr_InitOwn(buf->data);
+ }
+ return Substring_Str(LazyBuf_Get(buf));
+}
+
+
+Words Str_Words(const char *, bool);
+
+MAKE_INLINE void
+Words_Free(Words w)
+{
+ free(w.words);
+ free(w.freeIt);
+}
+
+
+SubstringWords Substring_Words(const char *, bool);
+
+MAKE_INLINE void
+SubstringWords_Free(SubstringWords w)
+{
+ free(w.words);
+ free(w.freeIt);
+}
+
+
+char *str_concat2(const char *, const char *);
+char *str_concat3(const char *, const char *, const char *);
+
+bool Str_Match(const char *, const char *);
diff --git a/contrib/bmake/suff.c b/contrib/bmake/suff.c
index 91e8bc613eb8..b2c926a2b8bc 100644
--- a/contrib/bmake/suff.c
+++ b/contrib/bmake/suff.c
@@ -1,4 +1,4 @@
-/* $NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -77,7 +77,8 @@
*
* Suff_End Clean up the module.
*
- * Suff_DoPaths Extend the search path of each suffix to include the
+ * Suff_ExtendPaths
+ * Extend the search path of each suffix to include the
* default search path.
*
* Suff_ClearSuffixes
@@ -114,7 +115,7 @@
#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
-MAKE_RCSID("$NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@@ -328,7 +329,7 @@ Suffix_TrimSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
suff->name, suff->nameLen);
}
-static Boolean
+static bool
Suffix_IsSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
{
return Suffix_TrimSuffix(suff, nameLen, nameEnd) != NULL;
@@ -509,9 +510,9 @@ Suff_ClearSuffixes(void)
* suffixes (the source ".c" and the target ".o"). If there are no such
* suffixes, try a single-suffix transformation as well.
*
- * Return TRUE if the string is a valid transformation.
+ * Return true if the string is a valid transformation.
*/
-static Boolean
+static bool
ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
{
SuffixListNode *ln;
@@ -536,7 +537,7 @@ ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
if (targ != NULL) {
*out_src = src;
*out_targ = targ;
- return TRUE;
+ return true;
}
}
}
@@ -554,17 +555,17 @@ ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
*/
*out_src = single;
*out_targ = nullSuff;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
- * Return TRUE if the given string is a transformation rule, that is, a
+ * Return true if the given string is a transformation rule, that is, a
* concatenation of two known suffixes such as ".c.o" or a single suffix
* such as ".o".
*/
-Boolean
+bool
Suff_IsTransform(const char *str)
{
Suffix *src, *targ;
@@ -616,7 +617,7 @@ Suff_AddTransform(const char *name)
{
/* TODO: Avoid the redundant parsing here. */
- Boolean ok = ParseTransform(name, &srcSuff, &targSuff);
+ bool ok = ParseTransform(name, &srcSuff, &targSuff);
assert(ok);
(void)ok;
}
@@ -725,11 +726,11 @@ RebuildGraph(GNode *transform, Suffix *suff)
* becomes the main target.
*
* Results:
- * TRUE iff a new main target has been selected.
+ * true iff a new main target has been selected.
*/
-static Boolean
+static bool
UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
- Boolean *inout_removedMain)
+ bool *inout_removedMain)
{
Suffix *srcSuff, *targSuff;
char *ptr;
@@ -740,20 +741,20 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
*inout_main = target;
Targ_SetMain(target);
/*
- * XXX: Why could it be a good idea to return TRUE here?
+ * XXX: Why could it be a good idea to return true here?
* The main task of this function is to turn ordinary nodes
* into transformations, no matter whether or not a new .MAIN
* node has been found.
*/
/*
- * XXX: Even when changing this to FALSE, none of the existing
+ * XXX: Even when changing this to false, none of the existing
* unit tests fails.
*/
- return TRUE;
+ return true;
}
if (target->type == OP_TRANSFORM)
- return FALSE;
+ return false;
/*
* XXX: What about a transformation ".cpp.c"? If ".c" is added as
@@ -762,7 +763,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
*/
ptr = strstr(target->name, suff->name);
if (ptr == NULL)
- return FALSE;
+ return false;
/*
* XXX: In suff-rebuild.mk, in the line '.SUFFIXES: .c .b .a', this
@@ -773,14 +774,14 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
* amounts of memory.
*/
if (ptr == target->name)
- return FALSE;
+ return false;
if (ParseTransform(target->name, &srcSuff, &targSuff)) {
if (*inout_main == target) {
DEBUG1(MAKE,
"Setting main node from \"%s\" back to null\n",
target->name);
- *inout_removedMain = TRUE;
+ *inout_removedMain = true;
*inout_main = NULL;
Targ_SetMain(NULL);
}
@@ -795,7 +796,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
srcSuff->name, targSuff->name);
Relate(srcSuff, targSuff);
}
- return FALSE;
+ return false;
}
/*
@@ -808,7 +809,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
static void
UpdateTargets(GNode **inout_main, Suffix *suff)
{
- Boolean removedMain = FALSE;
+ bool removedMain = false;
GNodeListNode *ln;
for (ln = Targ_List()->first; ln != NULL; ln = ln->next) {
@@ -876,7 +877,7 @@ Suff_GetPath(const char *sname)
* ".LIBS" and the flag is '-L'.
*/
void
-Suff_DoPaths(void)
+Suff_ExtendPaths(void)
{
SuffixListNode *ln;
char *flags;
@@ -1061,7 +1062,7 @@ CandidateList_AddCandidatesFor(CandidateList *list, Candidate *cand)
* Free the first candidate in the list that is not referenced anymore.
* Return whether a candidate was removed.
*/
-static Boolean
+static bool
RemoveCandidate(CandidateList *srcs)
{
CandidateListNode *ln;
@@ -1097,7 +1098,7 @@ RemoveCandidate(CandidateList *srcs)
Lst_Remove(srcs, ln);
free(src->file);
free(src);
- return TRUE;
+ return true;
}
#ifdef DEBUG_SRC
else {
@@ -1108,7 +1109,7 @@ RemoveCandidate(CandidateList *srcs)
#endif
}
- return FALSE;
+ return false;
}
/* Find the first existing file/target in srcs. */
@@ -1282,7 +1283,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* add those nodes to the members list.
*
* Unfortunately, we can't use Str_Words because it doesn't understand about
- * variable specifications with spaces in them.
+ * variable expressions with spaces in them.
*/
static void
ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
@@ -1309,7 +1310,7 @@ ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
const char *nested_p = cp;
FStr junk;
- (void)Var_Parse(&nested_p, pgn, VARE_NONE, &junk);
+ (void)Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY, &junk);
/* TODO: handle errors */
if (junk.str == var_Error) {
Parse_Error(PARSE_FATAL,
@@ -1380,7 +1381,7 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
}
DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
- (void)Var_Subst(cgn->name, pgn, VARE_WANTRES | VARE_UNDEFERR, &cp);
+ (void)Var_Subst(cgn->name, pgn, VARE_UNDEFERR, &cp);
/* TODO: handle errors */
{
@@ -1494,9 +1495,9 @@ Suff_FindPath(GNode *gn)
* the sources for the transformation rule.
*
* Results:
- * TRUE if successful, FALSE if not.
+ * true if successful, false if not.
*/
-static Boolean
+static bool
ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
{
GNodeListNode *ln;
@@ -1515,7 +1516,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
/* This can happen when linking an OP_MEMBER and OP_ARCHV node. */
if (gn == NULL)
- return FALSE;
+ return false;
DEBUG3(SUFF, "\tapplying %s -> %s to \"%s\"\n",
ssuff->name, tsuff->name, tgn->name);
@@ -1540,7 +1541,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
*/
Lst_Append(&sgn->implicitParents, tgn);
- return TRUE;
+ return true;
}
/*
diff --git a/contrib/bmake/targ.c b/contrib/bmake/targ.c
index 2b02f233ac48..68573644ff35 100644
--- a/contrib/bmake/targ.c
+++ b/contrib/bmake/targ.c
@@ -1,4 +1,4 @@
-/* $NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $ */
+/* $NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -93,7 +93,7 @@
* Targ_FindList Given a list of names, find nodes for all
* of them, creating them as necessary.
*
- * Targ_Precious Return TRUE if the target is precious and
+ * Targ_Precious Return true if the target is precious and
* should not be removed if we are interrupted.
*
* Targ_Propagate Propagate information between related nodes.
@@ -113,7 +113,7 @@
#include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $");
+MAKE_RCSID("$NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $");
/*
* All target nodes that appeared on the left-hand side of one of the
@@ -246,7 +246,7 @@ GNode_Free(void *gnp)
* SCOPE_GLOBAL), it should be safe to free the variables as well,
* since each node manages the memory for all its variables itself.
*
- * XXX: The GNodes that are only used as variable scopes (VAR_CMD,
+ * XXX: The GNodes that are only used as variable scopes (SCOPE_CMD,
* SCOPE_GLOBAL, SCOPE_INTERNAL) are not freed at all (see Var_End,
* where they are not mentioned). These might be freed at all, if
* their variable values are indeed not used anywhere else (see
@@ -283,7 +283,7 @@ Targ_FindNode(const char *name)
GNode *
Targ_GetNode(const char *name)
{
- Boolean isNew;
+ bool isNew;
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
if (!isNew)
return HashEntry_Get(he);
@@ -347,7 +347,7 @@ Targ_FindList(GNodeList *gns, StringList *names)
}
/* See if the given target is precious. */
-Boolean
+bool
Targ_Precious(const GNode *gn)
{
/* XXX: Why are '::' targets precious? */
@@ -410,7 +410,7 @@ Targ_FmtTime(time_t tm)
static char buf[128];
struct tm *parts = localtime(&tm);
- (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
+ (void)strftime(buf, sizeof buf, "%H:%M:%S %b %d, %Y", parts);
return buf;
}
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile
index d649c552a03a..784223a56652 100644
--- a/contrib/bmake/unit-tests/Makefile
+++ b/contrib/bmake/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.143 2021/02/06 18:31:30 sjg Exp $
+# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $
#
-# $NetBSD: Makefile,v 1.269 2021/02/06 18:26:03 sjg Exp $
+# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $
#
# Unit tests for make(1)
#
@@ -203,7 +203,9 @@ TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
+TESTS+= job-output-null
TESTS+= jobs-empty-commands
+TESTS+= jobs-empty-commands-error
TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make
@@ -228,6 +230,7 @@ TESTS+= opt-debug-curdir
TESTS+= opt-debug-cond
TESTS+= opt-debug-dir
TESTS+= opt-debug-errors
+TESTS+= opt-debug-errors-jobs
TESTS+= opt-debug-file
TESTS+= opt-debug-for
TESTS+= opt-debug-graph1
@@ -321,6 +324,7 @@ TESTS+= var-class-env
TESTS+= var-class-global
TESTS+= var-class-local
TESTS+= var-class-local-legacy
+TESTS+= var-eval-short
TESTS+= var-op
TESTS+= var-op-append
TESTS+= var-op-assign
@@ -347,6 +351,7 @@ TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime
TESTS+= varmod-loop
+TESTS+= varmod-loop-varname
TESTS+= varmod-match
TESTS+= varmod-match-escape
TESTS+= varmod-no-match
@@ -363,6 +368,7 @@ TESTS+= varmod-select-words
TESTS+= varmod-shell
TESTS+= varmod-subst
TESTS+= varmod-subst-regex
+TESTS+= varmod-sun-shell
TESTS+= varmod-sysv
TESTS+= varmod-tail
TESTS+= varmod-to-abs
@@ -484,6 +490,7 @@ SED_CMDS.job-output-long-lines= \
${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d'
+SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,'
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2}
SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3}
@@ -494,11 +501,12 @@ SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
# The "-q" may be there or not, see jobs.c, variable shells.
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
+SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex}
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
-# For Compat_RunCommand, useShell == FALSE.
+# For Compat_RunCommand, useShell == false.
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
-# For Compat_RunCommand, useShell == TRUE.
+# For Compat_RunCommand, useShell == true.
SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
@@ -509,8 +517,7 @@ SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell}
SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
-SED_CMDS.varmod-subst-regex+= \
- -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
@@ -590,6 +597,11 @@ STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,'
+# The actual error messages for a failed regcomp or regexec differ between the
+# implementations.
+STD_SED_CMDS.regex= \
+ -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+
# End of the configuration helpers section.
.-include "Makefile.inc"
@@ -639,8 +651,10 @@ _MKMSG_TEST= :
.if ${.OBJDIR} != ${.CURDIR}
# easy
TMPDIR:= ${.OBJDIR}/tmp
+.elif defined(TMPDIR)
+TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
.else
-TMPDIR:= ${TMPDIR:U/tmp}/uid${.MAKE.UID}
+TMPDIR:= /tmp/uid${.MAKE.UID}
.endif
# make sure it exists
.if !exist(${TMPDIR})
diff --git a/contrib/bmake/unit-tests/archive.mk b/contrib/bmake/unit-tests/archive.mk
index f8815cf40a40..2cd43a99e9ad 100644
--- a/contrib/bmake/unit-tests/archive.mk
+++ b/contrib/bmake/unit-tests/archive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: archive.mk,v 1.11 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: archive.mk,v 1.12 2021/04/09 14:42:00 christos Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
@@ -8,11 +8,11 @@
# several other tests.
ARCHIVE= libprog.a
-FILES= archive.mk modmisc.mk varmisc.mk
+FILES= archive.mk archive-suffix.mk modmisc.mk ternary.mk varmisc.mk
all:
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
- @cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
+ @cd ${MAKEFILE:H} && cp ${FILES} ${.CURDIR}
.endif
# The following targets create and remove files. The filesystem cache in
# dir.c would probably not handle this correctly, therefore each of the
diff --git a/contrib/bmake/unit-tests/cmd-errors-jobs.exp b/contrib/bmake/unit-tests/cmd-errors-jobs.exp
index 6d9c6bb7f890..9ed0557975b3 100644
--- a/contrib/bmake/unit-tests/cmd-errors-jobs.exp
+++ b/contrib/bmake/unit-tests/cmd-errors-jobs.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0
diff --git a/contrib/bmake/unit-tests/cmd-errors-lint.exp b/contrib/bmake/unit-tests/cmd-errors-lint.exp
index 09924c538de0..90b63bbcb08e 100644
--- a/contrib/bmake/unit-tests/cmd-errors-lint.exp
+++ b/contrib/bmake/unit-tests/cmd-errors-lint.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier
: end
exit status 2
diff --git a/contrib/bmake/unit-tests/cmd-errors.exp b/contrib/bmake/unit-tests/cmd-errors.exp
index 6d9c6bb7f890..9ed0557975b3 100644
--- a/contrib/bmake/unit-tests/cmd-errors.exp
+++ b/contrib/bmake/unit-tests/cmd-errors.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk
index 5094924f1c8d..11a990cbbce1 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.mk
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
@@ -42,7 +42,7 @@ WORD= word
.endif
# The :U modifier modifies expressions based on undefined variables
-# (VAR_JUNK) by adding the VAR_KEEP flag, which marks the expression
+# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression
# as "being interesting enough to be further processed".
#
.if empty(UNDEF:S,^$,value,W:Ufallback)
@@ -93,8 +93,8 @@ WORD= word
# neither leading nor trailing spaces are trimmed in the argument of the
# function. If the spaces were trimmed, the variable name would be "" and
# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse
-# without VARE_UNDEFERR, the value of the undefined variable is returned as
-# an empty string.
+# without VARE_UNDEFERR, the value of the undefined variable is
+# returned as an empty string.
${:U }= space
.if empty( )
. error
@@ -168,7 +168,7 @@ ${:U WORD }= variable name with spaces
# parsing it, this unrealistic variable name should have done no harm.
#
# The variable expression was expanded though, and this was wrong. The
-# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
+# expansion was done without VARE_WANTRES (called VARF_WANTRES back
# then) though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be
diff --git a/contrib/bmake/unit-tests/cond-func-make-main.mk b/contrib/bmake/unit-tests/cond-func-make-main.mk
index 31b370afabde..97b91f869991 100644
--- a/contrib/bmake/unit-tests/cond-func-make-main.mk
+++ b/contrib/bmake/unit-tests/cond-func-make-main.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
+# $NetBSD: cond-func-make-main.mk,v 1.2 2021/04/04 10:13:09 rillig Exp $
#
# Test how accurately the make() function in .if conditions reflects
# what is actually made.
@@ -33,7 +33,7 @@ first-main-target:
# the line. This implies that several main targets can be set at the name
# time, but they have to be in the same dependency group.
#
-# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
+# See ParseDependencyTargetSpecial, branch SP_MAIN.
.MAIN: dot-main-target-1a dot-main-target-1b
.if !make(dot-main-target-1a)
@@ -47,7 +47,7 @@ dot-main-target-{1,2}{a,b}:
: Making ${.TARGET}.
# At this point, the list of targets to be made (opts.create) is not empty
-# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
+# anymore. ParseDependencyTargetSpecial therefore treats the .MAIN as if
# it were an ordinary target. Since .MAIN is not listed as a dependency
# anywhere, it is not made.
.if target(.MAIN)
diff --git a/contrib/bmake/unit-tests/cond-late.exp b/contrib/bmake/unit-tests/cond-late.exp
index 46c4aa2f4230..e179e8c74cc4 100644
--- a/contrib/bmake/unit-tests/cond-late.exp
+++ b/contrib/bmake/unit-tests/cond-late.exp
@@ -1,4 +1,4 @@
-make: Bad conditional expression ` != "no"' in != "no"?:
+make: Bad conditional expression ' != "no"' in ' != "no"?:'
yes
no
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk
index 46c7ea26a97b..113c3fd08fed 100644
--- a/contrib/bmake/unit-tests/cond-short.mk
+++ b/contrib/bmake/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -13,8 +13,11 @@
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
# expressions, undefined variables were allowed.
+#
+# See also:
+# var-eval-short.mk, for short-circuited variable modifiers
-# The && operator.
+# The && operator:
.if 0 && ${echo "unexpected and" 1>&2 :L:sh}
.endif
@@ -86,7 +89,7 @@ VAR= # empty again, for the following tests
. warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
.endif
-# The || operator.
+# The || operator:
.if 1 || ${echo "unexpected or" 1>&2 :L:sh}
.endif
@@ -208,9 +211,4 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
. error
.endif
-# TODO: Test each modifier to make sure it is skipped when it is irrelevant
-# for the result. Since this test is already quite long, do that in another
-# test.
-
all:
- @:;:
diff --git a/contrib/bmake/unit-tests/cond-token-string.exp b/contrib/bmake/unit-tests/cond-token-string.exp
index 07b318caa81a..45f9993457d3 100644
--- a/contrib/bmake/unit-tests/cond-token-string.exp
+++ b/contrib/bmake/unit-tests/cond-token-string.exp
@@ -1,4 +1,4 @@
-make: "cond-token-string.mk" line 13: Unknown modifier 'Z'
+make: "cond-token-string.mk" line 13: Unknown modifier "Z"
make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 22: xvalue is not defined.
make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "")
diff --git a/contrib/bmake/unit-tests/cond-token-var.mk b/contrib/bmake/unit-tests/cond-token-var.mk
index 30eba87ad4d2..168c63c46ac1 100644
--- a/contrib/bmake/unit-tests/cond-token-var.mk
+++ b/contrib/bmake/unit-tests/cond-token-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-var.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
+# $NetBSD: cond-token-var.mk,v 1.6 2021/04/25 21:05:38 rillig Exp $
#
# Tests for variable expressions in .if conditions.
#
@@ -46,3 +46,24 @@ DEF= defined
# Since the expression is defined now, it doesn't generate any parse error.
.if ${UNDEF:U}
.endif
+
+# If the value of the variable expression is a number, it is compared against
+# zero.
+.if ${:U0}
+. error
+.endif
+.if !${:U1}
+. error
+.endif
+
+# If the value of the variable expression is not a number, any non-empty
+# value evaluates to true, even if there is only whitespace.
+.if ${:U}
+. error
+.endif
+.if !${:U }
+. error
+.endif
+.if !${:Uanything}
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/cond1.exp b/contrib/bmake/unit-tests/cond1.exp
index 0acd935780a0..8b65d782524d 100644
--- a/contrib/bmake/unit-tests/cond1.exp
+++ b/contrib/bmake/unit-tests/cond1.exp
@@ -17,7 +17,7 @@ Passed:
5 is prime
make: String comparison operator must be either == or !=
-make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
+make: Bad conditional expression '"0" > 0' in '"0" > 0?OK:No'
OK
exit status 0
diff --git a/contrib/bmake/unit-tests/counter-append.mk b/contrib/bmake/unit-tests/counter-append.mk
index 1c4e00d6118c..d234835e5ec3 100755
--- a/contrib/bmake/unit-tests/counter-append.mk
+++ b/contrib/bmake/unit-tests/counter-append.mk
@@ -1,4 +1,4 @@
-# $NetBSD: counter-append.mk,v 1.4 2020/10/17 16:57:17 rillig Exp $
+# $NetBSD: counter-append.mk,v 1.5 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::+= variable modifier.
@@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::+=a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
-# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
+# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}
diff --git a/contrib/bmake/unit-tests/counter.mk b/contrib/bmake/unit-tests/counter.mk
index 3c75d7a5032a..7cf8fba72876 100644
--- a/contrib/bmake/unit-tests/counter.mk
+++ b/contrib/bmake/unit-tests/counter.mk
@@ -1,4 +1,4 @@
-# $NetBSD: counter.mk,v 1.5 2020/10/17 16:57:17 rillig Exp $
+# $NetBSD: counter.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::= variable modifier.
@@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
-# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
+# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}
diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk
index 438a8a84a60d..4503424e31ab 100755
--- a/contrib/bmake/unit-tests/dep-var.mk
+++ b/contrib/bmake/unit-tests/dep-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dep-var.mk,v 1.5 2020/09/13 20:04:26 rillig Exp $
+# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
@@ -79,7 +79,7 @@ all: $$$$)
# undefined.
#
# Since 2020-09-13, this generates a parse error in lint mode (-dL), but not
-# in normal mode since ParseDoDependency does not handle any errors after
+# in normal mode since ParseDependency does not handle any errors after
# calling Var_Parse.
undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
@echo ${.TARGET:Q}
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.exp b/contrib/bmake/unit-tests/deptgt-makeflags.exp
index 7eb54eba7f30..11043bc5110c 100644
--- a/contrib/bmake/unit-tests/deptgt-makeflags.exp
+++ b/contrib/bmake/unit-tests/deptgt-makeflags.exp
@@ -1,10 +1,10 @@
Global:delete DOLLAR (not found)
-Command:DOLLAR = $$$$
-Global:.MAKEOVERRIDES = VAR DOLLAR
+Command: DOLLAR = $$$$
+Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"
-Var_Parse: ${DOLLAR} != "\$\$" with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${DOLLAR} != "\$\$" (eval-defined)
lhs = "$$", rhs = "$$", op = !=
-Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
-Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
+Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
+Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
make: Unterminated quoted string [make VAR=initial UNBALANCED=']
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-order.exp b/contrib/bmake/unit-tests/deptgt-order.exp
index 39a9383953dd..5f7dde0ac69d 100644
--- a/contrib/bmake/unit-tests/deptgt-order.exp
+++ b/contrib/bmake/unit-tests/deptgt-order.exp
@@ -1 +1,4 @@
+: 'Making two out of one.'
+: 'Making three out of two.'
+: 'Making all out of three.'
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-order.mk b/contrib/bmake/unit-tests/deptgt-order.mk
index 003552f57a49..f241331ae1e1 100644
--- a/contrib/bmake/unit-tests/deptgt-order.mk
+++ b/contrib/bmake/unit-tests/deptgt-order.mk
@@ -1,8 +1,18 @@
-# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-order.mk,v 1.3 2021/06/17 15:25:33 rillig Exp $
#
# Tests for the special target .ORDER in dependency declarations.
-# TODO: Implementation
+all one two three: .PHONY
-all:
- @:;
+two: one
+ : 'Making $@ out of $>.'
+three: two
+ : 'Making $@ out of $>.'
+
+# This .ORDER creates a circular dependency since 'three' depends on 'one'
+# but 'one' is supposed to be built after 'three'.
+.ORDER: three one
+
+# XXX: The circular dependency should be detected here.
+all: three
+ : 'Making $@ out of $>.'
diff --git a/contrib/bmake/unit-tests/deptgt.exp b/contrib/bmake/unit-tests/deptgt.exp
index b2aeaa5a2850..bdac2aee3e6c 100644
--- a/contrib/bmake/unit-tests/deptgt.exp
+++ b/contrib/bmake/unit-tests/deptgt.exp
@@ -1,14 +1,14 @@
make: "deptgt.mk" line 10: warning: Extra target ignored
make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL"
ParseReadLine (34): '${:U}: empty-source'
-ParseDoDependency(: empty-source)
+ParseDependency(: empty-source)
ParseReadLine (35): ' : command for empty targets list'
ParseReadLine (36): ': empty-source'
-ParseDoDependency(: empty-source)
+ParseDependency(: empty-source)
ParseReadLine (37): ' : command for empty targets list'
ParseReadLine (38): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-make: "deptgt.mk" line 46: Unknown modifier 'Z'
+ParseDependency(.MAKEFLAGS: -d0)
+make: "deptgt.mk" line 46: Unknown modifier "Z"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/deptgt.mk b/contrib/bmake/unit-tests/deptgt.mk
index 09f381715e6d..15d7e59aeced 100644
--- a/contrib/bmake/unit-tests/deptgt.mk
+++ b/contrib/bmake/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.11 2021/04/04 10:13:09 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -12,7 +12,7 @@
# The following lines demonstrate how 'targets' is set and reset during
# parsing of dependencies. To see it in action, set breakpoints in:
#
-# ParseDoDependency at the beginning
+# ParseDependency at the beginning
# FinishDependencyGroup at "targets = NULL"
# Parse_File at "Lst_Free(targets)"
# Parse_File at "targets = Lst_New()"
diff --git a/contrib/bmake/unit-tests/directive-export-impl.exp b/contrib/bmake/unit-tests/directive-export-impl.exp
index 1a5cf34dbfb8..740daa605129 100644
--- a/contrib/bmake/unit-tests/directive-export-impl.exp
+++ b/contrib/bmake/unit-tests/directive-export-impl.exp
@@ -1,56 +1,56 @@
ParseReadLine (21): 'UT_VAR= <${REF}>'
-Global:UT_VAR = <${REF}>
+Global: UT_VAR = <${REF}>
ParseReadLine (28): '.export UT_VAR'
-Global:.MAKE.EXPORTED = UT_VAR
+Global: .MAKE.EXPORTED = UT_VAR
ParseReadLine (32): ': ${UT_VAR:N*}'
-Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
-Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-Pattern[UT_VAR] for [<>] is [*]
+Var_Parse: ${UT_VAR:N*} (eval-defined)
+Var_Parse: ${REF}> (eval-defined)
+Evaluating modifier ${UT_VAR:N...} on value "<>"
+Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
-Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-ParseDoDependency(: )
+Result of ${UT_VAR:N*} is ""
+ParseDependency(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
-Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" (eval-defined)
+Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
-Var_Parse: ${UT_VAR} with VARE_WANTRES
-Var_Parse: ${REF}> with VARE_WANTRES
-Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
+Var_Parse: ${UT_VAR} (eval)
+Var_Parse: ${REF}> (eval)
+Result of ${:!echo "\$UT_VAR"!} is "<>" (eval-defined, defined)
lhs = "<>", rhs = "<>", op = !=
-ParseReadLine (49): ': ${UT_VAR:N*}'
-Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
-Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-Pattern[UT_VAR] for [<>] is [*]
+ParseReadLine (50): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} (eval-defined)
+Var_Parse: ${REF}> (eval-defined)
+Evaluating modifier ${UT_VAR:N...} on value "<>"
+Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
-Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-ParseDoDependency(: )
-ParseReadLine (53): 'REF= defined'
-Global:REF = defined
+Result of ${UT_VAR:N*} is ""
+ParseDependency(: )
+ParseReadLine (54): 'REF= defined'
+Global: REF = defined
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
-Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" (eval-defined)
+Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
-Var_Parse: ${UT_VAR} with VARE_WANTRES
-Var_Parse: ${REF}> with VARE_WANTRES
-Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
+Var_Parse: ${UT_VAR} (eval)
+Var_Parse: ${REF}> (eval)
+Result of ${:!echo "\$UT_VAR"!} is "<defined>" (eval-defined, defined)
lhs = "<defined>", rhs = "<defined>", op = !=
-ParseReadLine (61): 'all:'
-ParseDoDependency(all:)
-Global:.ALLTARGETS = all
-ParseReadLine (62): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d cpv -d
-Global:.MAKEFLAGS = -r -k -d cpv -d 0
+ParseReadLine (62): 'all:'
+ParseDependency(all:)
+Global: .ALLTARGETS = all
+ParseReadLine (63): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-export-impl.mk b/contrib/bmake/unit-tests/directive-export-impl.mk
index 556e5352d1c3..0ad290f653d4 100644
--- a/contrib/bmake/unit-tests/directive-export-impl.mk
+++ b/contrib/bmake/unit-tests/directive-export-impl.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-impl.mk,v 1.1 2020/12/29 01:45:06 rillig Exp $
+# $NetBSD: directive-export-impl.mk,v 1.3 2021/04/03 23:08:30 rillig Exp $
#
# Test for the implementation of exporting variables to child processes.
# This involves marking variables for export, actually exporting them,
@@ -8,8 +8,8 @@
# Var_Export
# ExportVar
# VarExportedMode (global)
-# VAR_EXPORTED (per variable)
-# VAR_REEXPORT (per variable)
+# VarFlags.exported (per variable)
+# VarFlags.reexport (per variable)
# VarExportMode (per call of Var_Export and ExportVar)
: ${:U:sh} # side effect: initialize .SHELL
@@ -22,13 +22,13 @@ UT_VAR= <${REF}>
# At this point, ExportVar("UT_VAR", VEM_PLAIN) is called. Since the
# variable value refers to another variable, ExportVar does not actually
-# export the variable but only marks it as VAR_EXPORTED and VAR_REEXPORT.
-# After that, ExportVars registers the variable name in .MAKE.EXPORTED.
-# That's all for now.
+# export the variable but only marks it as VarFlags.exported and
+# VarFlags.reexport. After that, ExportVars registers the variable name in
+# .MAKE.EXPORTED. That's all for now.
.export UT_VAR
-# Evaluating this expression shows the variable flags in the debug log,
-# which are VAR_EXPORTED|VAR_REEXPORT.
+# The following expression has both flags 'exported' and 'reexport' set.
+# These flags do not show up anywhere, not even in the debug log.
: ${UT_VAR:N*}
# At the last moment before actually forking off the child process for the
@@ -43,9 +43,10 @@ UT_VAR= <${REF}>
. error
.endif
-# Evaluating this expression shows the variable flags in the debug log,
-# which are still VAR_EXPORTED|VAR_REEXPORT, which means that the variable
-# is still marked as being re-exported for each child process.
+# The following expression still has 'exported' and 'reexport' set.
+# These flags do not show up anywhere though, not even in the debug log.
+# These flags means that the variable is still marked as being re-exported
+# for each child process.
: ${UT_VAR:N*}
# Now the referenced variable gets defined. This does not influence anything
diff --git a/contrib/bmake/unit-tests/directive-export.mk b/contrib/bmake/unit-tests/directive-export.mk
index 40fda0968cb0..942d4b371bbd 100644
--- a/contrib/bmake/unit-tests/directive-export.mk
+++ b/contrib/bmake/unit-tests/directive-export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export.mk,v 1.6 2020/12/13 01:07:54 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.8 2021/02/16 19:01:18 rillig Exp $
#
# Tests for the .export directive.
#
@@ -28,8 +28,17 @@ VAR= value $$ ${INDIRECT}
. error
.endif
-# No argument means to export all variables.
+# No syntactical argument means to export all variables.
.export
+# An empty argument means no additional variables to export.
+.export ${:U}
+
+
+# Trigger the "This isn't going to end well" in ExportVarEnv.
+EMPTY_SHELL= ${:sh}
+.export EMPTY_SHELL # only marked for export at this point
+_!= :;: # Force the variable to be actually exported.
+
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-for-errors.exp b/contrib/bmake/unit-tests/directive-for-errors.exp
index 6088a93c9a4a..da5eee473ec2 100644
--- a/contrib/bmake/unit-tests/directive-for-errors.exp
+++ b/contrib/bmake/unit-tests/directive-for-errors.exp
@@ -13,7 +13,7 @@ make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for subst
make: "directive-for-errors.mk" line 64: missing `in' in for
make: "directive-for-errors.mk" line 66: warning: Should not be reached.
make: "directive-for-errors.mk" line 67: for-less endfor
-make: "directive-for-errors.mk" line 73: Unknown modifier 'Z'
+make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
diff --git a/contrib/bmake/unit-tests/directive-for-errors.mk b/contrib/bmake/unit-tests/directive-for-errors.mk
index 7890e2375af4..602ecbf32e4e 100644
--- a/contrib/bmake/unit-tests/directive-for-errors.mk
+++ b/contrib/bmake/unit-tests/directive-for-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-errors.mk,v 1.1 2020/12/31 03:05:12 rillig Exp $
+# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $
#
# Tests for error handling in .for loops.
@@ -13,8 +13,8 @@
# XXX: The error message is misleading though. As of 2020-12-31, it says
# "Unknown directive "for"", but that directive is actually known. This is
# because ForEval does not detect the .for loop as such, so parsing
-# continues in ParseLine > ParseDependency > ParseDoDependency >
-# ParseDoDependencyTargets > ParseErrorNoDependency, and there the directive
+# continues in ParseLine > ParseDependencyLine > ParseDependency >
+# ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently.
.for/i in 1 2 3
. warning ${i}
diff --git a/contrib/bmake/unit-tests/directive-for-escape.exp b/contrib/bmake/unit-tests/directive-for-escape.exp
index 89a8cbc2e229..59d4c2324f15 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.exp
+++ b/contrib/bmake/unit-tests/directive-for-escape.exp
@@ -1,12 +1,12 @@
For: end for 1
For: loop body:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable specification (expecting '}') for "" (value "!"") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
For: loop body:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable specification (expecting '}') for "" (value "!"\\") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 29: !"\\
For: end for 1
For: loop body:
@@ -37,19 +37,19 @@ make: "directive-for-escape.mk" line 55: end}
For: end for 1
For: loop body:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
-make: "directive-for-escape.mk" line 66: begin<fallback>end
+make: "directive-for-escape.mk" line 67: begin<fallback>end
For: end for 1
For: loop body:
. info ${:U\$}
-make: "directive-for-escape.mk" line 74: $
+make: "directive-for-escape.mk" line 75: $
For: end for 1
For: loop body:
. info ${NUMBERS} ${:Ureplaced}
-make: "directive-for-escape.mk" line 82: one two three replaced
+make: "directive-for-escape.mk" line 83: one two three replaced
For: end for 1
For: loop body:
. info ${:Ureplaced}
-make: "directive-for-escape.mk" line 92: replaced
+make: "directive-for-escape.mk" line 93: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@@ -62,14 +62,14 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
-make: "directive-for-escape.mk" line 100: . $i: inner
-make: "directive-for-escape.mk" line 101: . ${i}: inner
-make: "directive-for-escape.mk" line 102: . ${i:M*}: inner
-make: "directive-for-escape.mk" line 103: . $(i): inner
-make: "directive-for-escape.mk" line 104: . $(i:M*): inner
-make: "directive-for-escape.mk" line 105: . ${i${:U}}: outer
-make: "directive-for-escape.mk" line 106: . ${i\}}: inner}
-make: "directive-for-escape.mk" line 107: . ${i2}: two
-make: "directive-for-escape.mk" line 108: . ${i,}: comma
-make: "directive-for-escape.mk" line 109: . adjacent: innerinnerinnerinner
+make: "directive-for-escape.mk" line 101: . $i: inner
+make: "directive-for-escape.mk" line 102: . ${i}: inner
+make: "directive-for-escape.mk" line 103: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 104: . $(i): inner
+make: "directive-for-escape.mk" line 105: . $(i:M*): inner
+make: "directive-for-escape.mk" line 106: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 107: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 108: . ${i2}: two
+make: "directive-for-escape.mk" line 109: . ${i,}: comma
+make: "directive-for-escape.mk" line 110: . adjacent: innerinnerinnerinner
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-for-escape.mk b/contrib/bmake/unit-tests/directive-for-escape.mk
index d61f05cc53cc..babc4b8c6e88 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.mk
+++ b/contrib/bmake/unit-tests/directive-for-escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-escape.mk,v 1.6 2021/01/25 19:05:39 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 rillig Exp $
#
# Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this
@@ -7,8 +7,8 @@
.MAKEFLAGS: -df
-# Even though the .for loops takes quotes into account when splitting the
-# string into words, the quotes don't need to be balances, as of 2020-12-31.
+# Even though the .for loops take quotes into account when splitting the
+# string into words, the quotes don't need to be balanced, as of 2020-12-31.
# This could be considered a bug.
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
@@ -33,7 +33,7 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
#
# XXX: It is unexpected that the variable V gets expanded in the loop body.
# The double '$$' should prevent exactly this. Probably nobody was
-# adventurous enough to use literal dollar signs in the values for a .for
+# adventurous enough to use literal dollar signs in the values of a .for
# loop.
V= value
VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
@@ -43,14 +43,14 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
# Try to cover the code for nested '{}' in for_var_len, without success.
#
-# The value of VALUES is not meant to be a variable expression. Instead, it
-# is meant to represent dollar, lbrace, "UNDEF:U", backslash, dollar,
-# backslash, dollar, space, nested braces, space, "end}".
+# The value of the variable VALUES is not meant to be a variable expression.
+# Instead, it is meant to represent literal text, the only escaping mechanism
+# being that each '$' is written as '$$'.
#
# The .for loop splits ${VALUES} into 3 words, at the space characters, since
# these are not escaped.
VALUES= $${UNDEF:U\$$\$$ {{}} end}
-# XXX: Where does the '\$$\$$' get converted into a single '\$'?
+# XXX: Where in the code does the '\$\$' get converted into a single '\$'?
.for i in ${VALUES}
. info $i
.endfor
@@ -59,8 +59,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end}
#
# XXX: It is wrong that for_var_len requires the braces to be balanced.
# Each variable modifier has its own inconsistent way of parsing nested
-# variable expressions, braces and parentheses. The only sensible thing
-# to do is therefore to let Var_Parse do all the parsing work.
+# variable expressions, braces and parentheses. (Compare ':M', ':S', and
+# ':D' for details.) The only sensible thing to do is therefore to let
+# Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES}
. info $i
diff --git a/contrib/bmake/unit-tests/directive-for.exp b/contrib/bmake/unit-tests/directive-for.exp
index bdaf4492baf0..4e882aad7b68 100755
--- a/contrib/bmake/unit-tests/directive-for.exp
+++ b/contrib/bmake/unit-tests/directive-for.exp
@@ -16,7 +16,7 @@ make: "directive-for.mk" line 140: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{
make: "directive-for.mk" line 148: outer value value
make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
-make: "directive-for.mk" line 154: Unknown modifier 'Z'
+make: "directive-for.mk" line 154: Unknown modifier "Z"
make: "directive-for.mk" line 155: XXX: Not reached word1
make: "directive-for.mk" line 155: XXX: Not reached word3
make: Fatal errors encountered -- cannot continue
diff --git a/contrib/bmake/unit-tests/directive-undef.exp b/contrib/bmake/unit-tests/directive-undef.exp
index d64cb8b5afe0..56c871429397 100644
--- a/contrib/bmake/unit-tests/directive-undef.exp
+++ b/contrib/bmake/unit-tests/directive-undef.exp
@@ -1,5 +1,6 @@
make: "directive-undef.mk" line 29: The .undef directive requires an argument
-make: "directive-undef.mk" line 86: Unknown modifier 'Z'
+make: "directive-undef.mk" line 86: Unknown modifier "Z"
+make: "directive-undef.mk" line 103: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-undef.mk b/contrib/bmake/unit-tests/directive-undef.mk
index b9a69f733517..41ea6b5bf8fa 100644
--- a/contrib/bmake/unit-tests/directive-undef.mk
+++ b/contrib/bmake/unit-tests/directive-undef.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-undef.mk,v 1.9 2020/12/22 20:10:21 rillig Exp $
+# $NetBSD: directive-undef.mk,v 1.10 2021/02/16 18:02:19 rillig Exp $
#
# Tests for the .undef directive.
#
@@ -86,5 +86,22 @@ ${DOLLAR}= dollar
.undef ${VARNAMES:L:Z}
+UT_EXPORTED= exported-value
+.export UT_EXPORTED
+.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "exported-value"
+. error
+.endif
+.if !${.MAKE.EXPORTED:MUT_EXPORTED}
+. error
+.endif
+.undef UT_EXPORTED # XXX: does not update .MAKE.EXPORTED
+.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "not-exported"
+. error
+.endif
+.if ${.MAKE.EXPORTED:MUT_EXPORTED}
+. warning UT_EXPORTED is still listed in .MAKE.EXPORTED even though $\
+ it is not exported anymore.
+.endif
+
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-unexport-env.exp b/contrib/bmake/unit-tests/directive-unexport-env.exp
index 677596ea4aa8..6d653e65fd32 100644
--- a/contrib/bmake/unit-tests/directive-unexport-env.exp
+++ b/contrib/bmake/unit-tests/directive-unexport-env.exp
@@ -1,18 +1,18 @@
make: "directive-unexport-env.mk" line 13: Unknown directive "unexport-en"
make: "directive-unexport-env.mk" line 15: Unknown directive "unexport-environment"
-Global:UT_EXPORTED = value
-Global:UT_UNEXPORTED = value
-Global:.MAKE.EXPORTED = UT_EXPORTED
+Global: UT_EXPORTED = value
+Global: UT_UNEXPORTED = value
+Global: .MAKE.EXPORTED = UT_EXPORTED
make: "directive-unexport-env.mk" line 21: The directive .unexport-env does not take arguments
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_EXPORTED" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_EXPORTED" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED" (VARE_WANTRES, none, none)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_EXPORTED"
+Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_EXPORTED"
+Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED"
Unexporting "UT_EXPORTED"
Global:delete .MAKE.EXPORTED
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive.exp b/contrib/bmake/unit-tests/directive.exp
index b93d768169ab..ee866b7ee2b3 100644
--- a/contrib/bmake/unit-tests/directive.exp
+++ b/contrib/bmake/unit-tests/directive.exp
@@ -2,11 +2,11 @@ make: "directive.mk" line 9: Unknown directive "indented"
make: "directive.mk" line 10: Unknown directive "indented"
make: "directive.mk" line 11: Unknown directive "indented"
make: "directive.mk" line 15: Unknown directive "info"
-Global:.info =
-Global:.info = value
+Global: .info =
+Global: .info = value
make: "directive.mk" line 26: := value
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/include-main.exp b/contrib/bmake/unit-tests/include-main.exp
index 61e716ad8ad7..c8a670a1c14a 100644
--- a/contrib/bmake/unit-tests/include-main.exp
+++ b/contrib/bmake/unit-tests/include-main.exp
@@ -9,7 +9,7 @@ make: "include-subsub.mk" line 5: subsub-ok
in .for loop from include-sub.mk:29
in .include from include-main.mk:27
ParseReadLine (6): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
+ParseDependency(.MAKEFLAGS: -d0)
make: "include-sub.mk" line 38: sub-after-ok
make: "include-sub.mk" line 45: sub-after-for-ok
make: "include-main.mk" line 30: main-after-ok
diff --git a/contrib/bmake/unit-tests/job-output-null.exp b/contrib/bmake/unit-tests/job-output-null.exp
new file mode 100644
index 000000000000..af9b4e64dba3
--- /dev/null
+++ b/contrib/bmake/unit-tests/job-output-null.exp
@@ -0,0 +1,4 @@
+hello
+hello
+hello world without newline, hello world without newline, hello world without newline.
+exit status 0
diff --git a/contrib/bmake/unit-tests/job-output-null.mk b/contrib/bmake/unit-tests/job-output-null.mk
new file mode 100644
index 000000000000..7620bdf6a7ba
--- /dev/null
+++ b/contrib/bmake/unit-tests/job-output-null.mk
@@ -0,0 +1,32 @@
+# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $
+#
+# Test how null bytes in the output of a command are handled. Make processes
+# them using null-terminated strings, which may cut off some of the output.
+#
+# As of 2021-04-15, make handles null bytes from the child process
+# inconsistently. It's an edge case though since typically the child
+# processes output text.
+
+.MAKEFLAGS: -j1 # force jobs mode
+
+all: .PHONY
+ # The null byte from the command output is kept as-is.
+ # See CollectOutput, which looks like it intended to replace these
+ # null bytes with simple spaces.
+ @printf 'hello\0world%s\n' ''
+
+ # Give the parent process a chance to see the above output, but not
+ # yet the output from the next printf command.
+ @sleep 1
+
+ # All null bytes from the command output are kept as-is.
+ @printf 'hello\0world%s\n' '' '' '' '' '' ''
+
+ @sleep 1
+
+ # The null bytes are replaced with spaces since they are not followed
+ # by a newline.
+ #
+ # The three null bytes in a row test whether this output is
+ # compressed to a single space like in DebugFailedTarget. It isn't.
+ @printf 'hello\0world\0without\0\0\0newline%s' ', ' ', ' '.'
diff --git a/contrib/bmake/unit-tests/jobs-empty-commands-error.exp b/contrib/bmake/unit-tests/jobs-empty-commands-error.exp
new file mode 100644
index 000000000000..1639425d9013
--- /dev/null
+++ b/contrib/bmake/unit-tests/jobs-empty-commands-error.exp
@@ -0,0 +1,5 @@
+: 'Making existing-target out of nothing.'
+make: don't know how to make nonexistent-target (continuing)
+
+make: stopped in unit-tests
+exit status 2
diff --git a/contrib/bmake/unit-tests/jobs-empty-commands-error.mk b/contrib/bmake/unit-tests/jobs-empty-commands-error.mk
new file mode 100644
index 000000000000..b9ba4403078e
--- /dev/null
+++ b/contrib/bmake/unit-tests/jobs-empty-commands-error.mk
@@ -0,0 +1,19 @@
+# $NetBSD: jobs-empty-commands-error.mk,v 1.1 2021/06/16 09:39:48 rillig Exp $
+#
+# In jobs mode, the shell commands for creating a target are written to a
+# temporary file first, which is then run by the shell. In chains of
+# dependencies, these files would end up empty. Since job.c 1.399 from
+# 2021-01-29, these empty files are no longer created.
+#
+# After 2021-01-29, before job.c 1.435 2021-06-16, targets that could not be
+# made led to longer error messages than necessary.
+
+.MAKEFLAGS: -j1
+
+all: existing-target
+
+existing-target:
+ : 'Making $@ out of nothing.'
+
+all: nonexistent-target
+ : 'Not reached'
diff --git a/contrib/bmake/unit-tests/moderrs.exp b/contrib/bmake/unit-tests/moderrs.exp
index 0ca1aa2aedd5..9d8bd308c36c 100644
--- a/contrib/bmake/unit-tests/moderrs.exp
+++ b/contrib/bmake/unit-tests/moderrs.exp
@@ -1,143 +1,136 @@
mod-unknown-direct:
want: Unknown modifier 'Z'
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
VAR:Z=before--after
mod-unknown-indirect:
want: Unknown modifier 'Z'
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
VAR:Z=before-inner}-after
unclosed-direct:
-want: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
-make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
+want: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+make: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
VAR:S,V,v,=Thevariable
unclosed-indirect:
-want: Unclosed variable specification after complex modifier (expecting '}') for VAR
-make: Unclosed variable specification after complex modifier (expecting '}') for VAR
+want: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
+make: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
VAR:S,V,v,=Thevariable
unfinished-indirect:
want: Unfinished modifier for VAR (',' missing)
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
VAR:S,V,v=
unfinished-loop:
want: Unfinished modifier for UNDEF ('@' missing)
-make: Unfinished modifier for UNDEF ('@' missing)
+make: Unfinished modifier for "UNDEF" ('@' missing)
want: Unfinished modifier for UNDEF ('@' missing)
-make: Unfinished modifier for UNDEF ('@' missing)
+make: Unfinished modifier for "UNDEF" ('@' missing)
1 2 3
loop-close:
-make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @
+make: Unclosed variable expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
1}... 2}... 3}...
1}... 2}... 3}...
words:
want: Unfinished modifier for UNDEF (']' missing)
-make: Unfinished modifier for UNDEF (']' missing)
+make: Unfinished modifier for "UNDEF" (']' missing)
want: Unfinished modifier for UNDEF (']' missing)
-make: Unfinished modifier for UNDEF (']' missing)
+make: Unfinished modifier for "UNDEF" (']' missing)
13=
-make: Bad modifier `:[123451234512345123451234512345]' for UNDEF
+make: Bad modifier ":[123451234512345123451234512345]" for variable "UNDEF"
12345=S,^ok,:S,^3ok,}
exclam:
want: Unfinished modifier for VARNAME ('!' missing)
-make: Unfinished modifier for VARNAME ('!' missing)
+make: Unfinished modifier for "VARNAME" ('!' missing)
want: Unfinished modifier for ! ('!' missing)
-make: Unfinished modifier for ! ('!' missing)
+make: Unfinished modifier for "!" ('!' missing)
mod-subst-delimiter:
-make: Missing delimiter for :S modifier
+make: Missing delimiter for modifier ':S'
1:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
2:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
3:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
4:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
+make: Unclosed variable expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
mod-regex-delimiter:
make: Missing delimiter for :C modifier
1:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
2:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
3:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
4:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
+make: Unclosed variable expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
-mod-regex-undefined-subexpression:
-one one 2 3 5 8 one3 2one 34
-make: No match for subexpression \2
-make: No match for subexpression \2
-make: No match for subexpression \1
-make: No match for subexpression \2
-make: No match for subexpression \1
-()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34
-
mod-ts-parse:
112358132134
15152535558513521534
-make: Bad modifier `:ts\65oct' for FIB
+make: Bad modifier ":ts\65oct" for variable "FIB"
+65oct}
+make: Bad modifier ":ts\65oct" for variable ""
65oct}
-make: Bad modifier `:tsxy' for FIB
+make: Bad modifier ":tsxy" for variable "FIB"
xy}
mod-t-parse:
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
-make: Bad modifier `:txy' for FIB
+make: Bad modifier ":txy" for variable "FIB"
y}
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
M*}
mod-ifelse-parse:
-make: Unfinished modifier for FIB (':' missing)
+make: Unfinished modifier for "FIB" (':' missing)
-make: Unfinished modifier for FIB (':' missing)
+make: Unfinished modifier for "FIB" (':' missing)
-make: Unfinished modifier for FIB ('}' missing)
+make: Unfinished modifier for "FIB" ('}' missing)
-make: Unfinished modifier for FIB ('}' missing)
+make: Unfinished modifier for "FIB" ('}' missing)
then
mod-remember-parse:
1 1 2 3 5 8 13 21 34
-make: Unknown modifier '_'
+make: Unknown modifier "__"
mod-sysv-parse:
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3"
+make: Unclosed variable expression, expecting '}' for modifier "3" of variable "FIB" with value ""
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3="
+make: Unclosed variable expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3=x3"
+make: Unclosed variable expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
1 1 2 x3 5 8 1x3 21 34
diff --git a/contrib/bmake/unit-tests/moderrs.mk b/contrib/bmake/unit-tests/moderrs.mk
index 8fdcb496ee29..ffd920314c5d 100644
--- a/contrib/bmake/unit-tests/moderrs.mk
+++ b/contrib/bmake/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: moderrs.mk,v 1.25 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: moderrs.mk,v 1.30 2021/06/21 08:28:37 rillig Exp $
#
# various modifier error tests
@@ -19,7 +19,6 @@ all: words
all: exclam
all: mod-subst-delimiter
all: mod-regex-delimiter
-all: mod-regex-undefined-subexpression
all: mod-ts-parse
all: mod-t-parse
all: mod-ifelse-parse
@@ -35,11 +34,11 @@ mod-unknown-indirect: print-header print-footer
@echo 'VAR:${MOD_UNKN}=before-${VAR:${MOD_UNKN}:inner}-after'
unclosed-direct: print-header print-footer
- @echo 'want: Unclosed variable specification (expecting $'}$') for "VAR" (value "Thevariable") modifier S'
+ @echo 'want: Unclosed variable expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
@echo VAR:S,V,v,=${VAR:S,V,v,
unclosed-indirect: print-header print-footer
- @echo 'want: Unclosed variable specification after complex modifier (expecting $'}$') for VAR'
+ @echo 'want: Unclosed variable expression after indirect modifier, expecting $'}$' for variable "VAR"'
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
unfinished-indirect: print-header print-footer
@@ -119,27 +118,11 @@ mod-regex-delimiter: print-header print-footer
@echo 6: ${VAR:C,from,to,
@echo 7: ${VAR:C,from,to,}
-# In regular expressions with alternatives, not all capturing groups are
-# always set; some may be missing. Warn about these.
-#
-# Since there is no way to turn off this warning, the combination of
-# alternative matches and capturing groups is seldom used, if at all.
-#
-# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added
-# for treating undefined capturing groups as empty, but that would create a
-# syntactical ambiguity since the :S and :C modifiers are open-ended (see
-# mod-subst-chain). Luckily the modifier :U does not make sense after :C,
-# therefore this case does not happen in practice.
-# The sub-modifier for the :S and :C modifiers would have to be chosen
-# wisely, to not create ambiguities while parsing.
-mod-regex-undefined-subexpression: print-header print-footer
- @echo ${FIB:C,1(.*),one\1,} # all ok
- @echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression
-
mod-ts-parse: print-header print-footer
@echo ${FIB:ts}
@echo ${FIB:ts\65} # octal 065 == U+0035 == '5'
@echo ${FIB:ts\65oct} # bad modifier
+ @echo ${:U${FIB}:ts\65oct} # bad modifier, variable name is ""
@echo ${FIB:tsxy} # modifier too long
mod-t-parse: print-header print-footer
diff --git a/contrib/bmake/unit-tests/modts.exp b/contrib/bmake/unit-tests/modts.exp
index 5db79fc96586..18837016add4 100644
--- a/contrib/bmake/unit-tests/modts.exp
+++ b/contrib/bmake/unit-tests/modts.exp
@@ -1,6 +1,6 @@
-make: Bad modifier `:tx' for LIST
+make: Bad modifier ":tx" for variable "LIST"
LIST:tx="}"
-make: Bad modifier `:ts\X' for LIST
+make: Bad modifier ":ts\X" for variable "LIST"
LIST:ts/x:tu="\X:tu}"
FU_mod-ts="a/b/cool"
FU_mod-ts:ts:T="cool" == cool?
diff --git a/contrib/bmake/unit-tests/modword.exp b/contrib/bmake/unit-tests/modword.exp
index 9fd7f1b494fe..02e9974c02d6 100644
--- a/contrib/bmake/unit-tests/modword.exp
+++ b/contrib/bmake/unit-tests/modword.exp
@@ -1,4 +1,4 @@
-make: Bad modifier `:[]' for LIST
+make: Bad modifier ":[]" for variable "LIST"
LIST:[]="" is an error
LIST:[0]="one two three four five six"
LIST:[0x0]="one two three four five six"
@@ -37,17 +37,17 @@ REALLYSPACE=" "
REALLYSPACE:[1]="" == "" ?
REALLYSPACE:[*]:[1]=" " == " " ?
LIST:[1]="one"
-make: Bad modifier `:[1.]' for LIST
+make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
-make: Bad modifier `:[1].' for LIST
+make: Bad modifier ":[1]." for variable "LIST"
LIST:[1].="}" is an error
LIST:[2]="two"
LIST:[6]="six"
LIST:[7]=""
LIST:[999]=""
-make: Bad modifier `:[-]' for LIST
+make: Bad modifier ":[-]" for variable "LIST"
LIST:[-]="" is an error
-make: Bad modifier `:[--]' for LIST
+make: Bad modifier ":[--]" for variable "LIST"
LIST:[--]="" is an error
LIST:[-1]="six"
LIST:[-2]="five"
@@ -67,20 +67,22 @@ LIST:[*]:C/ /,/:[2]=""
LIST:[*]:C/ /,/:[*]:[2]=""
LIST:[*]:C/ /,/:[@]:[2]="three"
LONGLIST:[012..0x12]="10 11 12 13 14 15 16 17 18"
-make: Bad modifier `:[1.]' for LIST
+make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
-make: Bad modifier `:[1..]' for LIST
+make: Bad modifier ":[1..]" for variable "LIST"
LIST:[1..]="" is an error
+make: Bad modifier ":[1.. ]" for variable "LIST"
+LIST:[1.. ]="" is an error
LIST:[1..1]="one"
-make: Bad modifier `:[1..1.]' for LIST
+make: Bad modifier ":[1..1.]" for variable "LIST"
LIST:[1..1.]="" is an error
LIST:[1..2]="one two"
LIST:[2..1]="two one"
LIST:[3..-2]="three four five"
LIST:[-4..4]="three four"
-make: Bad modifier `:[0..1]' for LIST
+make: Bad modifier ":[0..1]" for variable "LIST"
LIST:[0..1]="" is an error
-make: Bad modifier `:[-1..0]' for LIST
+make: Bad modifier ":[-1..0]" for variable "LIST"
LIST:[-1..0]="" is an error
LIST:[-1..1]="six five four three two one"
LIST:[0..0]="one two three four five six"
@@ -95,7 +97,7 @@ LIST:[${ONE}]="one"
LIST:[${MINUSONE}]="six"
LIST:[${STAR}]="one two three four five six"
LIST:[${AT}]="one two three four five six"
-make: Bad modifier `:[${EMPTY' for LIST
+make: Bad modifier ":[${EMPTY" for variable "LIST"
LIST:[${EMPTY}]="" is an error
LIST:[${LONGLIST:[21]:S/2//}]="one"
LIST:[${LIST:[#]}]="six"
diff --git a/contrib/bmake/unit-tests/modword.mk b/contrib/bmake/unit-tests/modword.mk
index 383c9dca975b..95bb1fec78c3 100644
--- a/contrib/bmake/unit-tests/modword.mk
+++ b/contrib/bmake/unit-tests/modword.mk
@@ -1,4 +1,4 @@
-# $NetBSD: modword.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: modword.mk,v 1.6 2021/03/14 16:00:07 rillig Exp $
#
# Test behaviour of new :[] modifier
# TODO: When was this modifier new?
@@ -99,6 +99,7 @@ mod-squarebrackets-n:
mod-squarebrackets-start-end:
@echo 'LIST:[1.]="${LIST:[1.]}" is an error'
@echo 'LIST:[1..]="${LIST:[1..]}" is an error'
+ @echo 'LIST:[1.. ]="${LIST:[1.. ]}" is an error'
@echo 'LIST:[1..1]="${LIST:[1..1]}"'
@echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error'
@echo 'LIST:[1..2]="${LIST:[1..2]}"'
diff --git a/contrib/bmake/unit-tests/opt-chdir.mk b/contrib/bmake/unit-tests/opt-chdir.mk
index 20241f02740e..a8806149f31c 100644
--- a/contrib/bmake/unit-tests/opt-chdir.mk
+++ b/contrib/bmake/unit-tests/opt-chdir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-chdir.mk,v 1.5 2020/11/15 05:43:56 sjg Exp $
+# $NetBSD: opt-chdir.mk,v 1.6 2021/05/18 17:05:45 sjg Exp $
#
# Tests for the -C command line option, which changes the directory at the
# beginning.
@@ -23,5 +23,7 @@ chdir-root: .PHONY .IGNORE
@MAKE_OBJDIR_CHECK_WRITABLE=no ${MAKE} -C / -V 'cwd: $${.CURDIR}'
# Trying to change to a nonexistent directory exits immediately.
+# Note: just because the whole point of /nonexistent is that it should
+# not exist - doesn't mean it doesn't.
chdir-nonexistent: .PHONY .IGNORE
- @${MAKE} -C /nonexistent
+ @${MAKE} -C /nonexistent.${.MAKE.PID}
diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
new file mode 100644
index 000000000000..25eb2b470b72
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
@@ -0,0 +1,48 @@
+echo '3 spaces'; false
+3 spaces
+
+*** Failed target: fail-spaces
+*** Failed commands:
+ echo '3 spaces'; false
+*** [fail-spaces] Error code 1
+
+make: stopped in unit-tests
+echo \ indented; false
+ indented
+
+*** Failed target: fail-escaped-space
+*** Failed commands:
+ echo \ indented; false
+*** [fail-escaped-space] Error code 1
+
+make: stopped in unit-tests
+echo 'line1
+line2'; false
+line1
+line2
+
+*** Failed target: fail-newline
+*** Failed commands:
+ echo 'line1${.newline}line2'; false
+*** [fail-newline] Error code 1
+
+make: stopped in unit-tests
+echo 'line1 line2'; false
+line1 line2
+
+*** Failed target: fail-multiline
+*** Failed commands:
+ echo 'line1 line2'; false
+*** [fail-multiline] Error code 1
+
+make: stopped in unit-tests
+echo 'word1' 'word2'; false
+word1 word2
+
+*** Failed target: fail-multiline-intention
+*** Failed commands:
+ echo 'word1' 'word2'; false
+*** [fail-multiline-intention] Error code 1
+
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk
new file mode 100644
index 000000000000..83b50987a752
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk
@@ -0,0 +1,36 @@
+# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $
+#
+# Tests for the -de command line option, which adds debug logging for
+# failed commands and targets; since 2021-04-27 also in jobs mode.
+
+.MAKEFLAGS: -de -j1
+
+all: fail-spaces
+all: fail-escaped-space
+all: fail-newline
+all: fail-multiline
+all: fail-multiline-intention
+
+fail-spaces:
+ echo '3 spaces'; false
+
+fail-escaped-space:
+ echo \ indented; false
+
+fail-newline:
+ echo 'line1${.newline}line2'; false
+
+# The line continuations in multiline commands are turned into an ordinary
+# space before the command is actually run.
+fail-multiline:
+ echo 'line1\
+ line2'; false
+
+# It is a common style to align the continuation backslashes at the right
+# of the lines, usually at column 73. All spaces before the continuation
+# backslash are preserved and are usually outside a shell word and thus
+# irrelevant. Since "usually" is not "always", these space characters are
+# not merged into a single space.
+fail-multiline-intention:
+ echo 'word1' \
+ 'word2'; false
diff --git a/contrib/bmake/unit-tests/opt-debug-lint.exp b/contrib/bmake/unit-tests/opt-debug-lint.exp
index f2123f20e37f..05b341b30dae 100644
--- a/contrib/bmake/unit-tests/opt-debug-lint.exp
+++ b/contrib/bmake/unit-tests/opt-debug-lint.exp
@@ -2,7 +2,7 @@ make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
-make: "opt-debug-lint.mk" line 69: Unknown modifier '$'
+make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/opt-debug-lint.mk b/contrib/bmake/unit-tests/opt-debug-lint.mk
index bb1b38feb717..155e1a3de3be 100644
--- a/contrib/bmake/unit-tests/opt-debug-lint.mk
+++ b/contrib/bmake/unit-tests/opt-debug-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-lint.mk,v 1.12 2020/12/20 19:10:53 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
# to catch common mistakes, such as unclosed variable expressions.
@@ -77,5 +77,19 @@ ${UNDEF}: ${UNDEF}
. error
.endif
-all:
- @:;
+# In lint mode, the whole variable text is evaluated to check for unclosed
+# expressions and unknown operators. During this check, the subexpression
+# '${:U2}' is not expanded, instead it is copied verbatim into the regular
+# expression, leading to '.*=.{1,${:U2}}$'.
+#
+# Before var.c 1.856 from 2021-03-14, this regular expression was then
+# compiled even though that was not necessary for checking the syntax at the
+# level of variable expressions. The unexpanded '$' then resulted in a wrong
+# error message.
+#
+# This only happened in lint mode since in default mode the early check for
+# unclosed expressions and unknown modifiers is skipped.
+#
+# See VarCheckSyntax, ApplyModifier_Regex.
+#
+VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g}
diff --git a/contrib/bmake/unit-tests/opt-debug.exp b/contrib/bmake/unit-tests/opt-debug.exp
index 52a36c71b4ee..6a5f7b4cb3e7 100644
--- a/contrib/bmake/unit-tests/opt-debug.exp
+++ b/contrib/bmake/unit-tests/opt-debug.exp
@@ -1,4 +1,4 @@
-Global:VAR = value
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: VAR = value
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-file.mk b/contrib/bmake/unit-tests/opt-file.mk
index 3ab8ef4e3c7d..b7a1c09e6d16 100644
--- a/contrib/bmake/unit-tests/opt-file.mk
+++ b/contrib/bmake/unit-tests/opt-file.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-file.mk,v 1.11 2020/12/22 08:57:23 rillig Exp $
+# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the -f command line option.
@@ -28,10 +28,10 @@ all: file-containing-null-byte
# ParseReadLine (1): 'VAR=value\<A5><A5><A5><A5><A5><A5>'
# Global:VAR = value\<A5><A5><A5><A5><A5><A5>value\<A5><A5><A5><A5><A5><A5>
# ParseReadLine (2): 'alue\<A5><A5><A5><A5><A5><A5>'
-# ParseDoDependency(alue\<A5><A5><A5><A5><A5><A5>)
+# ParseDependency(alue\<A5><A5><A5><A5><A5><A5>)
# make-2014.01.01.00.00.00: "(stdin)" line 2: Need an operator
# ParseReadLine (3): '<A5><A5><A5>ZZZZZZZZZZZZZZZZ'
-# ParseDoDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
+# ParseDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
#
file-ending-in-backslash: .PHONY
@printf '%s' 'VAR=value\' \
diff --git a/contrib/bmake/unit-tests/opt-jobs-no-action.mk b/contrib/bmake/unit-tests/opt-jobs-no-action.mk
index a75fc38cf2fa..19d82c5bf4b8 100644
--- a/contrib/bmake/unit-tests/opt-jobs-no-action.mk
+++ b/contrib/bmake/unit-tests/opt-jobs-no-action.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-jobs-no-action.mk,v 1.8 2020/12/10 23:54:41 rillig Exp $
+# $NetBSD: opt-jobs-no-action.mk,v 1.9 2021/04/04 09:58:51 rillig Exp $
#
# Tests for the combination of the options -j and -n, which prints the
# commands instead of actually running them.
@@ -23,7 +23,7 @@
# this is handled by the [0] != '\0' checks in Job_ParseShell.
# The '\#' is handled by ParseGetLine.
# The '\n' is handled by Str_Words in Job_ParseShell.
-# The '$$' is handled by Var_Subst in ParseDependency.
+# The '$$' is handled by Var_Subst in ParseDependencyLine.
.SHELL: \
name=sh \
path=${.SHELL} \
diff --git a/contrib/bmake/unit-tests/recursive.mk b/contrib/bmake/unit-tests/recursive.mk
index 73a8409fe030..5265cec59a2d 100644
--- a/contrib/bmake/unit-tests/recursive.mk
+++ b/contrib/bmake/unit-tests/recursive.mk
@@ -1,12 +1,12 @@
-# $NetBSD: recursive.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: recursive.mk,v 1.5 2021/03/15 12:15:03 rillig Exp $
#
# In -dL mode, a variable may get expanded before it makes sense.
# This would stop make from doing anything since the "recursive" error
# is fatal and exits immediately.
#
# The purpose of evaluating that variable early was just to detect
-# whether there are unclosed variables. It might be enough to parse the
-# variable value without VARE_WANTRES for that purpose.
+# whether there are unclosed variables. The variable value is therefore
+# parsed with VARE_PARSE_ONLY for that purpose.
#
# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
# GNU Automake.
@@ -36,4 +36,3 @@ MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE}
UNCLOSED= $(MISSING_PAREN
UNCLOSED= ${MISSING_BRACE
UNCLOSED= ${MISSING_BRACE_INDIRECT}
-
diff --git a/contrib/bmake/unit-tests/sh-jobs.mk b/contrib/bmake/unit-tests/sh-jobs.mk
index e8d4f976109a..de80de56040c 100644
--- a/contrib/bmake/unit-tests/sh-jobs.mk
+++ b/contrib/bmake/unit-tests/sh-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-jobs.mk,v 1.3 2020/12/11 01:06:10 rillig Exp $
+# $NetBSD: sh-jobs.mk,v 1.4 2021/04/16 16:49:27 rillig Exp $
#
# Tests for the "run in jobs mode" part of the "Shell Commands" section
# from the manual page.
@@ -14,14 +14,14 @@ all: .PHONY comment .WAIT comment-with-followup-line .WAIT no-comment
# would lead to a syntax error in the generated shell file, at least for
# bash and dash, but not for NetBSD sh and ksh.
#
-# See JobPrintCommand, cmdTemplate, runIgnTmpl
+# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment: .PHONY
@# comment
# If a shell command starts with a comment character after stripping the
# leading '@', it is run in ignore-errors mode.
#
-# See JobPrintCommand, cmdTemplate, runIgnTmpl
+# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment-with-followup-line: .PHONY
@# comment${.newline}echo '$@: This is printed.'; false
@true
@@ -29,7 +29,7 @@ comment-with-followup-line: .PHONY
# Without the comment, the commands are run in the default mode, which checks
# the exit status of every makefile line.
#
-# See JobPrintCommand, cmdTemplate, runChkTmpl
+# See JobWriteCommand, cmdTemplate, runChkTmpl
no-comment: .PHONY
@echo '$@: This is printed.'; false
@true
diff --git a/contrib/bmake/unit-tests/shell-csh.mk b/contrib/bmake/unit-tests/shell-csh.mk
index 99852e33ce16..47313563d22b 100644
--- a/contrib/bmake/unit-tests/shell-csh.mk
+++ b/contrib/bmake/unit-tests/shell-csh.mk
@@ -1,4 +1,4 @@
-# $NetBSD: shell-csh.mk,v 1.7 2020/12/13 02:09:55 sjg Exp $
+# $NetBSD: shell-csh.mk,v 1.8 2021/04/04 09:58:51 rillig Exp $
#
# Tests for using a C shell for running the commands.
@@ -12,7 +12,7 @@ CSH!= which csh 2> /dev/null || true
.endif
# In parallel mode, the shell->noPrint command is filtered from
-# the output, rather naively (in JobOutput).
+# the output, rather naively (in PrintOutput).
#
# Until 2020-10-03, the output in parallel mode was garbled because
# the definition of the csh had been wrong since 1993 at least.
diff --git a/contrib/bmake/unit-tests/suff-incomplete.exp b/contrib/bmake/unit-tests/suff-incomplete.exp
index 23b959d4b4e5..2331436d378e 100644
--- a/contrib/bmake/unit-tests/suff-incomplete.exp
+++ b/contrib/bmake/unit-tests/suff-incomplete.exp
@@ -1,19 +1,19 @@
ParseReadLine (9): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (11): '.SUFFIXES: .a .b .c'
-ParseDoDependency(.SUFFIXES: .a .b .c)
+ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (17): '.a.b:'
-ParseDoDependency(.a.b:)
+ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (21): '.a.c: ${.PREFIX}.dependency'
deleting incomplete transformation from `.a' to `.b'
-ParseDoDependency(.a.c: ${.PREFIX}.dependency)
+ParseDependency(.a.c: ${.PREFIX}.dependency)
defining transformation from `.a' to `.c'
inserting ".a" (1) at end of list
inserting ".c" (3) at end of list
@@ -22,7 +22,7 @@ inserting ".c" (3) at end of list
# ${.PREFIX}.dependency, unmade, type none, flags none
ParseReadLine (23): '.DEFAULT:'
transformation .a.c complete
-ParseDoDependency(.DEFAULT:)
+ParseDependency(.DEFAULT:)
ParseReadLine (24): ' : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.'
transformation .DEFAULT complete
Wildcard expanding "all"...
diff --git a/contrib/bmake/unit-tests/suff-main-several.exp b/contrib/bmake/unit-tests/suff-main-several.exp
index a494ddc68545..09fa6d63bffa 100644
--- a/contrib/bmake/unit-tests/suff-main-several.exp
+++ b/contrib/bmake/unit-tests/suff-main-several.exp
@@ -1,12 +1,12 @@
ParseReadLine (8): '.1.2 .1.3 .1.4:'
-ParseDoDependency(.1.2 .1.3 .1.4:)
+ParseDependency(.1.2 .1.3 .1.4:)
Setting main node to ".1.2"
ParseReadLine (9): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (14): 'next-main:'
-ParseDoDependency(next-main:)
+ParseDependency(next-main:)
ParseReadLine (15): ' : Making ${.TARGET}'
ParseReadLine (19): '.SUFFIXES: .1 .2 .3 .4'
-ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Setting main node from ".1.2" back to null
@@ -27,42 +27,42 @@ inserting ".1" (1) at end of list
inserting ".4" (4) at end of list
Setting main node to "next-main"
ParseReadLine (24): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (32): '.SUFFIXES: .4 .3 .2 .1'
-ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (33): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (34): '.SUFFIXES: .1 .2 .3 .4'
-ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Adding suffix ".3"
Adding suffix ".4"
ParseReadLine (35): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (36): '.SUFFIXES: .4 .3 .2 .1'
-ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (38): 'suff-main-several.1:'
-ParseDoDependency(suff-main-several.1:)
+ParseDependency(suff-main-several.1:)
ParseReadLine (39): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}'
-ParseDoDependency(next-main: suff-main-several.{2,3,4})
+ParseDependency(next-main: suff-main-several.{2,3,4})
# LinkSource: added child next-main - suff-main-several.{2,3,4}
# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, unmade, type none, flags none
ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
-ParseDoDependency(.MAKEFLAGS: -d0 -dg1)
+ParseDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph:
# .1.2, unmade, type OP_TRANSFORM, flags none
# .1.3, unmade, type OP_TRANSFORM, flags none
diff --git a/contrib/bmake/unit-tests/suff-rebuild.exp b/contrib/bmake/unit-tests/suff-rebuild.exp
index ccb423a6086a..7ef53ae2e151 100644
--- a/contrib/bmake/unit-tests/suff-rebuild.exp
+++ b/contrib/bmake/unit-tests/suff-rebuild.exp
@@ -1,38 +1,38 @@
ParseReadLine (10): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (12): '.SUFFIXES: .a .b .c'
-ParseDoDependency(.SUFFIXES: .a .b .c)
+ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (14): 'suff-rebuild-example.a:'
-ParseDoDependency(suff-rebuild-example.a:)
+ParseDependency(suff-rebuild-example.a:)
Adding "suff-rebuild-example.a" to all targets.
ParseReadLine (15): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (17): '.a.b:'
-ParseDoDependency(.a.b:)
+ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (18): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (19): '.b.c:'
transformation .a.b complete
-ParseDoDependency(.b.c:)
+ParseDependency(.b.c:)
defining transformation from `.b' to `.c'
inserting ".b" (2) at end of list
inserting ".c" (3) at end of list
ParseReadLine (20): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (21): '.c:'
transformation .b.c complete
-ParseDoDependency(.c:)
+ParseDependency(.c:)
defining transformation from `.c' to `'
inserting ".c" (3) at end of list
inserting "" (0) at end of list
ParseReadLine (22): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (44): '.SUFFIXES: .c .b .a'
transformation .c complete
-ParseDoDependency(.SUFFIXES: .c .b .a)
+ParseDependency(.SUFFIXES: .c .b .a)
Adding ".END" to all targets.
Wildcard expanding "all"...
SuffFindDeps "all"
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.exp b/contrib/bmake/unit-tests/var-class-cmdline.exp
index 39a9383953dd..6df2155ca7eb 100644
--- a/contrib/bmake/unit-tests/var-class-cmdline.exp
+++ b/contrib/bmake/unit-tests/var-class-cmdline.exp
@@ -1 +1,4 @@
+make: "var-class-cmdline.mk" line 67: global
+make: "var-class-cmdline.mk" line 76: makeflags
+makeflags
exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.mk b/contrib/bmake/unit-tests/var-class-cmdline.mk
index c43b5351c329..679e051bb242 100644
--- a/contrib/bmake/unit-tests/var-class-cmdline.mk
+++ b/contrib/bmake/unit-tests/var-class-cmdline.mk
@@ -1,8 +1,80 @@
-# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-class-cmdline.mk,v 1.5 2021/02/23 21:59:31 rillig Exp $
#
# Tests for variables specified on the command line.
+#
+# Variables that are specified on the command line override those from the
+# global scope.
+#
+# For performance reasons, the actual implementation is more complex than the
+# above single-sentence rule, in order to avoid unnecessary lookups in scopes,
+# which before var.c 1.586 from 2020-10-25 calculated the hash value of the
+# variable name once for each lookup. Instead, when looking up the value of
+# a variable, the search often starts in the global scope since that is where
+# most of the variables are stored. This conflicts with the statement that
+# variables from the cmdline scope override global variables, since after the
+# common case of finding a variable in the global scope, another lookup would
+# be needed in the cmdline scope to ensure that there is no overriding
+# variable there.
+#
+# Instead of this costly lookup scheme, make implements it in a different
+# way:
+#
+# Whenever a global variable is created, this creation is ignored if
+# there is a cmdline variable of the same name.
+#
+# Whenever a cmdline variable is created, any global variable of the
+# same name is deleted.
+#
+# Whenever a global variable is deleted, nothing special happens.
+#
+# Deleting a cmdline variable is not possible.
+#
+# These 4 rules provide the guarantee that whenever a global variable exists,
+# there cannot be a cmdline variable of the same name. Therefore, after
+# finding a variable in the global scope, no additional lookup is needed in
+# the cmdline scope.
+#
+# The above ruleset provides the same guarantees as the simple rule "cmdline
+# overrides global". Due to an implementation mistake, the actual behavior
+# was not entirely equivalent to the simple rule though. The mistake was
+# that when a cmdline variable with '$$' in its name was added, a global
+# variable was deleted, but not with the exact same name as the cmdline
+# variable. Instead, the name of the global variable was expanded one more
+# time than the name of the cmdline variable. For variable names that didn't
+# have a '$$' in their name, it was implemented correctly all the time.
+#
+# The bug was added in var.c 1.183 on 2013-07-16, when Var_Set called
+# Var_Delete to delete the global variable. Just two months earlier, in var.c
+# 1.174 from 2013-05-18, Var_Delete had started to expand the variable name.
+# Together, these two changes made the variable name be expanded twice in a
+# row. This bug was fixed in var.c 1.835 from 2021-02-22.
+#
+# Another bug was the wrong assumption that "deleting a cmdline variable is
+# not possible". Deleting such a variable has been possible since var.c 1.204
+# from 2016-02-19, when the variable modifier ':@' started to delete the
+# temporary loop variable after finishing the loop. It was probably not
+# intended back then that a side effect of this seemingly simple change was
+# that both global and cmdline variables could now be undefined at will as a
+# side effect of evaluating a variable expression. As of 2021-02-23, this is
+# still possible.
+#
+# Most cmdline variables are set at the very beginning, when parsing the
+# command line arguments. Using the special target '.MAKEFLAGS', it is
+# possible to set cmdline variables at any later time.
+
+# A normal global variable, without any cmdline variable nearby.
+VAR= global
+.info ${VAR}
-# TODO: Implementation
+# The global variable is "overridden" by simply deleting it and then
+# installing the cmdline variable instead. Since there is no obvious way to
+# undefine a cmdline variable, there is no need to remember the old value
+# of the global variable could become visible again.
+#
+# See varmod-loop.mk for a non-obvious way to undefine a cmdline variable.
+.MAKEFLAGS: VAR=makeflags
+.info ${VAR}
-all:
- @:;
+# If Var_SetWithFlags should ever forget to delete the global variable,
+# the below line would print "global" instead of the current "makeflags".
+.MAKEFLAGS: -V VAR
diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp
new file mode 100644
index 000000000000..ae0aff7d7c2c
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-eval-short.exp
@@ -0,0 +1,29 @@
+make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar.
+make: "var-eval-short.mk" line 41: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
+make: "var-eval-short.mk" line 79: Invalid time value: ${FAIL}}
+make: "var-eval-short.mk" line 79: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
+make: "var-eval-short.mk" line 93: Invalid time value: ${FAIL}}
+make: "var-eval-short.mk" line 93: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
+CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
+Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
+Parsing modifier ${0:?...}
+Modifier part: "${FAIL}then"
+Modifier part: "${FAIL}else"
+Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
+ParseReadLine (158): 'DEFINED= defined'
+Global: DEFINED = defined
+CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
+Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
+Parsing modifier ${DEFINED:L}
+Result of ${DEFINED:L} is "defined" (parse-only, regular)
+Parsing modifier ${DEFINED:?...}
+Modifier part: "${FAIL}then"
+Modifier part: "${FAIL}else"
+Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
+ParseReadLine (161): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/var-eval-short.mk b/contrib/bmake/unit-tests/var-eval-short.mk
new file mode 100644
index 000000000000..41782f0d7823
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-eval-short.mk
@@ -0,0 +1,163 @@
+# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $
+#
+# Tests for each variable modifier to ensure that they only do the minimum
+# necessary computations. If the result of the expression is not needed, they
+# should only parse the modifier but not actually evaluate it.
+#
+# See also:
+# var.c, the comment starting with 'The ApplyModifier functions'
+# ApplyModifier, for the order of the modifiers
+# ParseModifierPart, for evaluating nested expressions
+# cond-short.mk
+
+FAIL= ${:!echo unexpected 1>&2!}
+
+# The following tests only ensure that nested expressions are not evaluated.
+# They cannot ensure that any unexpanded text returned from ParseModifierPart
+# is ignored as well. To do that, it is necessary to step through the code of
+# each modifier.
+
+.if 0 && ${FAIL}
+.endif
+
+.if 0 && ${VAR::=${FAIL}}
+.elif defined(VAR)
+. error
+.endif
+
+.if 0 && ${${FAIL}:?then:else}
+.endif
+
+.if 0 && ${1:?${FAIL}:${FAIL}}
+.endif
+
+.if 0 && ${0:?${FAIL}:${FAIL}}
+.endif
+
+# Before var.c 1.870 from 2021-03-14, the expression ${FAIL} was evaluated
+# after the loop, when undefining the temporary global loop variable.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if 0 && ${:Uword:@${FAIL}@expr@}
+.endif
+
+.if 0 && ${:Uword:@var@${FAIL}@}
+.endif
+
+# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand
+# the nested expression ${FAIL} and then tried to parse the unexpanded text,
+# which failed since '$' is not a valid range character.
+.if 0 && ${:Uword:[${FAIL}]}
+.endif
+
+# Before var.c,v 1.867 from 2021-03-14, the modifier ':_' defined the variable
+# even though the whole expression should have only been parsed, not
+# evaluated.
+.if 0 && ${:Uword:_=VAR}
+.elif defined(VAR)
+. error
+.endif
+
+# Before var.c,v 1.856 from 2021-03-14, the modifier ':C' did not expand the
+# nested expression ${FAIL} and then tried to compile the unexpanded text as a
+# regular expression, which failed both because of the '{FAIL}', which is not
+# a valid repetition, and because of the '****', which are repeated
+# repetitions as well.
+# '${FAIL}'
+.if 0 && ${:Uword:C,${FAIL}****,,}
+.endif
+
+DEFINED= # defined
+.if 0 && ${DEFINED:D${FAIL}}
+.endif
+
+.if 0 && ${:Uword:E}
+.endif
+
+# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
+# ':gmtime' does not expand its argument.
+.if 0 && ${:Uword:gmtime=${FAIL}}
+.endif
+
+.if 0 && ${:Uword:H}
+.endif
+
+.if 0 && ${:Uword:hash}
+.endif
+
+.if 0 && ${value:L}
+.endif
+
+# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
+# ':localtime' does not expand its argument.
+.if 0 && ${:Uword:localtime=${FAIL}}
+.endif
+
+.if 0 && ${:Uword:M${FAIL}}
+.endif
+
+.if 0 && ${:Uword:N${FAIL}}
+.endif
+
+.if 0 && ${:Uword:O}
+.endif
+
+.if 0 && ${:Uword:Ox}
+.endif
+
+.if 0 && ${:Uword:P}
+.endif
+
+.if 0 && ${:Uword:Q}
+.endif
+
+.if 0 && ${:Uword:q}
+.endif
+
+.if 0 && ${:Uword:R}
+.endif
+
+.if 0 && ${:Uword:range}
+.endif
+
+.if 0 && ${:Uword:S,${FAIL},${FAIL},}
+.endif
+
+.if 0 && ${:Uword:sh}
+.endif
+
+.if 0 && ${:Uword:T}
+.endif
+
+.if 0 && ${:Uword:ts/}
+.endif
+
+.if 0 && ${:U${FAIL}}
+.endif
+
+.if 0 && ${:Uword:u}
+.endif
+
+.if 0 && ${:Uword:word=replacement}
+.endif
+
+# Before var.c 1.875 from 2021-03-14, Var_Parse returned "${FAIL}else" for the
+# irrelevant right-hand side of the condition, even though this was not
+# necessary. Since the return value from Var_Parse is supposed to be ignored
+# anyway, and since it is actually ignored in an overly complicated way,
+# an empty string suffices.
+.MAKEFLAGS: -dcpv
+.if 0 && ${0:?${FAIL}then:${FAIL}else}
+.endif
+
+# The ':L' is applied before the ':?' modifier, giving the expression a name
+# and a value, just to see whether this value gets passed through or whether
+# the parse-only mode results in an empty string (only visible in the debug
+# log). As of var.c 1.875 from 2021-03-14, the value of the variable gets
+# through, even though an empty string would suffice.
+DEFINED= defined
+.if 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
+.endif
+.MAKEFLAGS: -d0
+
+all:
diff --git a/contrib/bmake/unit-tests/var-op-append.exp b/contrib/bmake/unit-tests/var-op-append.exp
index 424ad37ccf63..32134be75a3d 100644
--- a/contrib/bmake/unit-tests/var-op-append.exp
+++ b/contrib/bmake/unit-tests/var-op-append.exp
@@ -1,7 +1,7 @@
-Var_Parse: ${:U\$\$\$\$\$\$\$\$} with VARE_WANTRES
-Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (VARE_WANTRES, none, VES_DEF)
-Global:VAR.$$$$$$$$ = dollars
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Var_Parse: ${:U\$\$\$\$\$\$\$\$} (eval)
+Evaluating modifier ${:U...} on value "" (eval, undefined)
+Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (eval, defined)
+Global: VAR.$$$$$$$$ = dollars
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-append.mk b/contrib/bmake/unit-tests/var-op-append.mk
index deb4af6a7384..420ee376b75d 100644
--- a/contrib/bmake/unit-tests/var-op-append.mk
+++ b/contrib/bmake/unit-tests/var-op-append.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-append.mk,v 1.8 2021/02/03 08:40:47 rillig Exp $
+# $NetBSD: var-op-append.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the += variable assignment operator, which appends to a variable,
# creating it if necessary.
@@ -26,7 +26,7 @@ VAR+= # empty
# '+=' assignment operator. As far as possible, the '+' is interpreted as
# part of the assignment operator.
#
-# See Parse_DoVar
+# See Parse_Var
C++= value
.if ${C+} != "value" || defined(C++)
. error
diff --git a/contrib/bmake/unit-tests/var-op-assign.mk b/contrib/bmake/unit-tests/var-op-assign.mk
index 3bcc3de0ba0e..18ecf8d0d5ed 100644
--- a/contrib/bmake/unit-tests/var-op-assign.mk
+++ b/contrib/bmake/unit-tests/var-op-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-assign.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: var-op-assign.mk,v 1.8 2021/03/15 19:15:04 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
@@ -42,7 +42,7 @@ VAR= new value and \# some $$ special characters # comment
# This alone would not produce any side-effects, therefore the variable has
# a :!...! modifier that executes a shell command. The :!...! modifier turns
# an undefined expression into a defined one, see ApplyModifier_ShellCommand,
-# the call to ApplyModifiersState_Define.
+# the call to Expr_Define.
#
# Since the right-hand side of a '=' assignment is not expanded at the time
# when the variable is defined, the first command is not run at all.
diff --git a/contrib/bmake/unit-tests/var-op-sunsh.mk b/contrib/bmake/unit-tests/var-op-sunsh.mk
index 0e16b2b42d34..0d15b8c88b92 100644
--- a/contrib/bmake/unit-tests/var-op-sunsh.mk
+++ b/contrib/bmake/unit-tests/var-op-sunsh.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-sunsh.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: var-op-sunsh.mk,v 1.8 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the :sh= variable assignment operator, which runs its right-hand
# side through the shell. It is a seldom-used alternative to the !=
@@ -50,7 +50,7 @@ VAR:shoe:shore= echo two-colons
# The variable modifier ':sh' and the assignment operator modifier ':sh'.
# Intuitively this variable name contains the variable modifier, but until
# 2020-10-04, the parser regarded it as an assignment operator modifier, in
-# Parse_DoVar.
+# Parse_Var.
VAR.${:Uecho 123:sh}= ok-123
.if ${VAR.123} != "ok-123"
. error
@@ -75,11 +75,11 @@ VAR.key:shift= Shift
# the ':sh' assignment operator modifier. Let's see what happens ...
#
# Well, the end result is correct but the way until there is rather
-# adventurous. This only works because the parser replaces each an every
-# whitespace character that is not nested with '\0' (see Parse_DoVar).
+# adventurous. This only works because the parser replaces each and every
+# whitespace character that is not nested with '\0' (see Parse_Var).
# The variable name therefore ends before the first ':sh', and the last
# ':sh' turns the assignment operator into the shell command evaluation.
-# Parse_DoVar completely trusts Parse_IsVar to properly verify the syntax.
+# Parse_Var completely trusts Parse_IsVar to properly verify the syntax.
#
# The ':sh' is the only word that may occur between the variable name and
# the assignment operator at nesting level 0. All other words would lead
@@ -102,7 +102,7 @@ VAR :sh(Put a comment here)= comment in parentheses
# The unintended comment can include multiple levels of nested braces and
# parentheses, they don't even need to be balanced since they are only
-# counted by Parse_IsVar and ignored by Parse_DoVar.
+# counted by Parse_IsVar and ignored by Parse_Var.
VAR :sh{Put}((((a}{comment}}}}{here}= comment in braces
.if ${VAR} != "comment in braces"
. error
diff --git a/contrib/bmake/unit-tests/varcmd.mk b/contrib/bmake/unit-tests/varcmd.mk
index 9ec4f4f9a21a..12739df30926 100644
--- a/contrib/bmake/unit-tests/varcmd.mk
+++ b/contrib/bmake/unit-tests/varcmd.mk
@@ -1,6 +1,17 @@
-# $NetBSD: varcmd.mk,v 1.5 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: varcmd.mk,v 1.6 2021/02/16 19:43:09 rillig Exp $
#
# Test behaviour of recursive make and vars set on command line.
+#
+# FIXME: The purpose of this test is unclear. The test uses six levels of
+# sub-makes, which makes it incredibly hard to understand. There must be at
+# least an introductory explanation about what _should_ happen here.
+# The variable names are terrible, as well as their values.
+#
+# This test produces different results if the large block with the condition
+# "scope == SCOPE_GLOBAL" in Var_SetWithFlags is removed. This test should
+# be rewritten to make it clear why there is a difference and why this is
+# actually intended. Removing that large block of code makes only this test
+# and vardebug.mk fail, which is not enough.
FU= fu
FOO?= foo
@@ -57,4 +68,3 @@ five: show show-v
six: show-v
@${.MAKE} -f ${MAKEFILE} V=override show-v
-
diff --git a/contrib/bmake/unit-tests/vardebug.exp b/contrib/bmake/unit-tests/vardebug.exp
index a9a00a11f5dd..6d00acc977af 100644
--- a/contrib/bmake/unit-tests/vardebug.exp
+++ b/contrib/bmake/unit-tests/vardebug.exp
@@ -1,86 +1,69 @@
Global:delete FROM_CMDLINE (not found)
-Command:FROM_CMDLINE =
-Global:.MAKEOVERRIDES = FROM_CMDLINE
-Global:VAR = added
-Global:VAR = overwritten
+Command: FROM_CMDLINE =
+Global: .MAKEOVERRIDES = FROM_CMDLINE
+Global: VAR = added
+Global: VAR = overwritten
Global:delete VAR
Global:delete VAR (not found)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored
-Global:FROM_CMDLINE = overwritten ignored!
-Global:VAR = 1
-Global:VAR = 1 2
-Global:VAR = 1 2 3
-Var_Parse: ${VAR:M[2]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:M...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Pattern[VAR] for [1 2 3] is [[2]]
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
+Var_AppendExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
+Global: FROM_CMDLINE = overwritten ignored!
+Global: VAR = 1
+Global: VAR = 1 2
+Global: VAR = 1 2 3
+Var_Parse: ${VAR:M[2]} (eval-defined)
+Evaluating modifier ${VAR:M...} on value "1 2 3"
+Pattern for ':M' is "[2]"
ModifyWords: split "1 2 3" into 3 words
-VarMatch [1] [[2]]
-VarMatch [2] [[2]]
-VarMatch [3] [[2]]
-Result of ${VAR:M[2]} is "2" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:N[2]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:N...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Pattern[VAR] for [1 2 3] is [[2]]
+Result of ${VAR:M[2]} is "2"
+Var_Parse: ${VAR:N[2]} (eval-defined)
+Evaluating modifier ${VAR:N...} on value "1 2 3"
+Pattern for ':N' is "[2]"
ModifyWords: split "1 2 3" into 3 words
-Result of ${VAR:N[2]} is "1 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:S,2,two,} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:S...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${VAR:N[2]} is "1 3"
+Var_Parse: ${VAR:S,2,two,} (eval-defined)
+Evaluating modifier ${VAR:S...} on value "1 2 3"
Modifier part: "2"
Modifier part: "two"
ModifyWords: split "1 2 3" into 3 words
-Result of ${VAR:S,2,two,} is "1 two 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:Q} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:Q} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:Q} is "1\ 2\ 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:tu:tl:Q} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:t...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:tu} is "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Applying ${VAR:t...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:tl} is "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Applying ${VAR:Q} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:Q} is "1\ 2\ 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Uvalue} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UM*e} is "M*e" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Result of ${VAR:S,2,two,} is "1 two 3"
+Var_Parse: ${VAR:Q} (eval-defined)
+Evaluating modifier ${VAR:Q} on value "1 2 3"
+Result of ${VAR:Q} is "1\ 2\ 3"
+Var_Parse: ${VAR:tu:tl:Q} (eval-defined)
+Evaluating modifier ${VAR:t...} on value "1 2 3"
+Result of ${VAR:tu} is "1 2 3"
+Evaluating modifier ${VAR:t...} on value "1 2 3"
+Result of ${VAR:tl} is "1 2 3"
+Evaluating modifier ${VAR:Q} on value "1 2 3"
+Result of ${VAR:Q} is "1\ 2\ 3"
+Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:Uvalue} is "value" (eval-defined, defined)
Indirect modifier "M*e" from "${:UM*e}"
-Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[] for [value] is [*e]
+Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
+Pattern for ':M' is "*e"
ModifyWords: split "value" into 1 words
-VarMatch [value] [*e]
-Result of ${:M*e} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[] for [value] is [valu[e]]
+Result of ${:M*e} is "value" (eval-defined, defined)
+Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
+Pattern for ':M' is "valu[e]"
ModifyWords: split "value" into 1 words
-VarMatch [value] [valu[e]]
-Result of ${:Mvalu[e]} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:UVAR} with VARE_WANTRES
-Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR} is "VAR" (VARE_WANTRES, none, VES_DEF)
+Result of ${:Mvalu[e]} is "value" (eval-defined, defined)
Global:delete VAR
-Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Uvariable} is "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Applying ${:u...} to "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-make: "vardebug.mk" line 44: Unknown modifier 'u'
-Result of ${:unknown} is error (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${:Uvariable:unknown} (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:Uvariable} is "variable" (eval-defined, defined)
+Evaluating modifier ${:u...} on value "variable" (eval-defined, defined)
+make: "vardebug.mk" line 44: Unknown modifier "unknown"
+Result of ${:unknown} is error (eval-defined, defined)
make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
-Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${UNDEFINED} (eval-defined)
make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED})
Global:delete .SHELL (not found)
-Command:.SHELL = </path/to/shell>
-Command:.SHELL = overwritten ignored (read-only)
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Command: .SHELL = </path/to/shell>
+Command: .SHELL = overwritten ignored (read-only)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmisc.exp b/contrib/bmake/unit-tests/varmisc.exp
index e8f88d9ca51f..f56f72d0ab9c 100644
--- a/contrib/bmake/unit-tests/varmisc.exp
+++ b/contrib/bmake/unit-tests/varmisc.exp
@@ -54,7 +54,7 @@ make: Unclosed variable "UNCLOSED"
make: Unclosed variable "UNCLOSED"
make: Unclosed variable "PATTERN"
-make: Unclosed variable specification (expecting '}') for "UNCLOSED" (value "") modifier M
+make: Unclosed variable expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
make: Unclosed variable "param"
make: Unclosed variable "UNCLOSED."
diff --git a/contrib/bmake/unit-tests/varmod-assign.exp b/contrib/bmake/unit-tests/varmod-assign.exp
index 743ef2fb4082..1e43714d500b 100644
--- a/contrib/bmake/unit-tests/varmod-assign.exp
+++ b/contrib/bmake/unit-tests/varmod-assign.exp
@@ -1,3 +1,17 @@
+Global: param = twice
+Global: VARNAME = VAR.$${param}
+Var_Parse: ${VARNAME} (eval)
+Global: VAR.${param} = initial-value
+Var_Parse: ${${VARNAME}::=assigned-value} (eval-defined)
+Var_Parse: ${VARNAME}::=assigned-value} (eval-defined)
+Evaluating modifier ${VAR.${param}::...} on value "initial-value"
+Modifier part: "assigned-value"
+Global: VAR.${param} = assigned-value
+Result of ${VAR.${param}::=assigned-value} is ""
+Var_Parse: ${${VARNAME}} != "assigned-value" (eval-defined)
+Var_Parse: ${VARNAME}} != "assigned-value" (eval-defined)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
mod-assign: first=1.
mod-assign: last=3.
mod-assign: appended=1 2 3.
@@ -10,15 +24,15 @@ mod-assign-nested: then1t1
mod-assign-nested: else2e2
mod-assign-nested: then3t3
mod-assign-nested: else4e4
-make: Bad modifier `:' for
+make: Bad modifier ":" for variable ""
mod-assign-empty: value}
-make: Bad modifier `:' for
+make: Bad modifier ":" for variable ""
mod-assign-empty: overwritten}
mod-assign-empty: VAR=overwritten
-make: Unknown modifier ':'
+make: Unknown modifier ":x"
sysv:y
-make: Unfinished modifier for ASSIGN ('}' missing)
+make: Unfinished modifier for "ASSIGN" ('}' missing)
ok=word
make: " echo word; false " returned non-zero status
diff --git a/contrib/bmake/unit-tests/varmod-assign.mk b/contrib/bmake/unit-tests/varmod-assign.mk
index e4cbc249df88..f50c654f5bcf 100644
--- a/contrib/bmake/unit-tests/varmod-assign.mk
+++ b/contrib/bmake/unit-tests/varmod-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-assign.mk,v 1.9 2021/01/22 22:54:53 rillig Exp $
+# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $
#
# Tests for the obscure ::= variable modifiers, which perform variable
# assignments during evaluation, just like the = operator in C.
@@ -91,7 +91,7 @@ mod-assign-shell-error:
@${SH_ERR::=previous}
@${SH_ERR::!= echo word; false } echo err=${SH_ERR}
-# XXX: The ::= modifier expands its right-hand side, exactly once.
+# XXX: The ::= modifier expands its right-hand side exactly once.
# This differs subtly from normal assignments such as '+=' or '=', which copy
# their right-hand side literally.
APPEND.prev= previous
@@ -104,3 +104,38 @@ APPEND.dollar= $${APPEND.indirect}
.if ${APPEND.var} != "previous indirect \${:Unot expanded}"
. error
.endif
+
+
+# The assignment modifier can be used in a variable expression that is
+# enclosed in parentheses. In such a case, parsing stops at the first ')',
+# not at the first '}'.
+VAR= previous
+_:= $(VAR::=current})
+.if ${VAR} != "current}"
+. error
+.endif
+
+
+# Before var.c 1.888 from 2021-03-15, an expression using the modifier '::='
+# expanded its variable name once too often during evaluation. This was only
+# relevant for variable names containing a '$' sign in their actual name, not
+# the usual VAR.${param}.
+.MAKEFLAGS: -dv
+param= twice
+VARNAME= VAR.$${param} # Indirect variable name because of the '$',
+ # to avoid difficult escaping rules.
+
+${VARNAME}= initial-value # Sets 'VAR.${param}' to 'expanded'.
+.if defined(VAR.twice) # At this point, the '$$' is not expanded.
+. error
+.endif
+.if ${${VARNAME}::=assigned-value} # Here the variable name gets expanded once
+. error # too often.
+.endif
+.if defined(VAR.twice)
+. error The variable name in the '::=' modifier is expanded once too often.
+.endif
+.if ${${VARNAME}} != "assigned-value"
+. error
+.endif
+.MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/varmod-defined.exp b/contrib/bmake/unit-tests/varmod-defined.exp
index 15f40226f1db..2f7d4dbf4baa 100644
--- a/contrib/bmake/unit-tests/varmod-defined.exp
+++ b/contrib/bmake/unit-tests/varmod-defined.exp
@@ -1,23 +1,23 @@
-Global:8_DOLLARS = $$$$$$$$
-Global:VAR =
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:D${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${VAR:D...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Var_Parse: ${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:@var@${8_DOLLARS}@} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${VAR:@...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
+Global: 8_DOLLARS = $$$$$$$$
+Global: VAR =
+Var_Parse: ${8_DOLLARS} (eval-keep-dollar-and-undefined)
+Global: VAR = $$$$$$$$
+Var_Parse: ${VAR:D${8_DOLLARS}} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${VAR:D...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
+Var_Parse: ${8_DOLLARS}} (eval-keep-dollar-and-undefined)
+Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
+Global: VAR = $$$$$$$$
+Var_Parse: ${VAR:@var@${8_DOLLARS}@} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${VAR:@...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
Modifier part: "var"
Modifier part: "${8_DOLLARS}"
ModifyWords: split "$$$$$$$$" into 1 words
-Global:var = $$$$$$$$
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_UNDEF
+Global: var = $$$$$$$$
+Var_Parse: ${8_DOLLARS} (eval-keep-undefined)
ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$"
Global:delete var
-Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Global:VAR = $$$$
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (eval-keep-dollar-and-undefined, regular)
+Global: VAR = $$$$
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-defined.mk b/contrib/bmake/unit-tests/varmod-defined.mk
index 59b9d79d754b..a44b9f993146 100644
--- a/contrib/bmake/unit-tests/varmod-defined.mk
+++ b/contrib/bmake/unit-tests/varmod-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-defined.mk,v 1.9 2020/11/12 00:40:55 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.11 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the :D variable modifier, which returns the given string
# if the variable is defined. It is closely related to the :U modifier.
@@ -89,9 +89,9 @@ DEF= defined
# The :D and :U modifiers behave differently from the :@var@ modifier in
# that they preserve dollars in a ':=' assignment. This is because
-# ApplyModifier_Defined passes the eflags unmodified to Var_Parse, unlike
+# ApplyModifier_Defined passes the emode unmodified to Var_Parse, unlike
# ApplyModifier_Loop, which uses ParseModifierPart, which in turn removes
-# VARE_KEEP_DOLLAR from eflags.
+# the keepDollar flag from emode.
#
# XXX: This inconsistency is documented nowhere.
.MAKEFLAGS: -dv
diff --git a/contrib/bmake/unit-tests/varmod-edge.exp b/contrib/bmake/unit-tests/varmod-edge.exp
index c90eef2756c6..d9db72b2e2ef 100644
--- a/contrib/bmake/unit-tests/varmod-edge.exp
+++ b/contrib/bmake/unit-tests/varmod-edge.exp
@@ -1,7 +1,7 @@
make: "varmod-edge.mk" line 166: ok M-paren
make: "varmod-edge.mk" line 166: ok M-mixed
make: "varmod-edge.mk" line 166: ok M-unescape
-make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
make: "varmod-edge.mk" line 166: ok M-nest-mix
make: "varmod-edge.mk" line 166: ok M-nest-brk
make: "varmod-edge.mk" line 166: ok M-pat-err
@@ -12,12 +12,16 @@ make: "varmod-edge.mk" line 166: ok M-128
make: "varmod-edge.mk" line 166: ok eq-ext
make: "varmod-edge.mk" line 166: ok eq-q
make: "varmod-edge.mk" line 166: ok eq-bs
-make: Unfinished modifier for INP.eq-esc ('=' missing)
+make: Unfinished modifier for "INP.eq-esc" ('=' missing)
make: "varmod-edge.mk" line 166: ok eq-esc
make: "varmod-edge.mk" line 166: ok colon
-make: "varmod-edge.mk" line 165: Unknown modifier ':'
-make: "varmod-edge.mk" line 165: Unknown modifier ':'
+make: "varmod-edge.mk" line 165: Unknown modifier ":"
+make: "varmod-edge.mk" line 165: Unknown modifier ":"
make: "varmod-edge.mk" line 166: ok colons
+make: "varmod-edge.mk" line 175: Unknown modifier "Z"
+make: "varmod-edge.mk" line 175: Malformed conditional (${:Z})
+make: Unfinished modifier for "" (',' missing)
+make: "varmod-edge.mk" line 188: Malformed conditional (${:S,})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-edge.mk b/contrib/bmake/unit-tests/varmod-edge.mk
index a0b6d9342ef6..762053d281a3 100644
--- a/contrib/bmake/unit-tests/varmod-edge.mk
+++ b/contrib/bmake/unit-tests/varmod-edge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-edge.mk,v 1.13 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-edge.mk,v 1.16 2021/02/23 15:56:30 rillig Exp $
#
# Tests for edge cases in variable modifiers.
#
@@ -51,7 +51,7 @@ TESTS+= M-nest-mix
INP.M-nest-mix= (parentheses)
MOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}}
EXP.M-nest-mix= (parentheses)}
-# make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
+# make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
# In contrast to parentheses and braces, the brackets are not counted
# when the :M modifier is parsed since Makefile variables only take the
@@ -169,5 +169,27 @@ EXP.colons= # empty
. endif
.endfor
+# Even in expressions based on an unnamed variable, there may be errors.
+# XXX: The error message should mention the variable name of the expression,
+# even though that name is empty in this case.
+.if ${:Z}
+. error
+.else
+. error
+.endif
+
+# Even in expressions based on an unnamed variable, there may be errors.
+#
+# Before var.c 1.842 from 2021-02-23, the error message did not surround the
+# variable name with quotes, leading to the rather confusing "Unfinished
+# modifier for (',' missing)", having two spaces in a row.
+#
+# XXX: The error message should report the filename:lineno.
+.if ${:S,}
+. error
+.else
+. error
+.endif
+
all:
@echo ok
diff --git a/contrib/bmake/unit-tests/varmod-hash.exp b/contrib/bmake/unit-tests/varmod-hash.exp
index f16f30903539..1286b456c6c2 100644
--- a/contrib/bmake/unit-tests/varmod-hash.exp
+++ b/contrib/bmake/unit-tests/varmod-hash.exp
@@ -1,9 +1,9 @@
-make: Unknown modifier 'h'
+make: Unknown modifier "has"
26bb0f5f
12345
-make: Unknown modifier 'h'
+make: Unknown modifier "hasX"
-make: Unknown modifier 'h'
+make: Unknown modifier "hashed"
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp
index 17d4d8afcbeb..e42e39525f1c 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.exp
+++ b/contrib/bmake/unit-tests/varmod-ifelse.exp
@@ -1,20 +1,32 @@
-make: Bad conditional expression `variable expression == "literal"' in variable expression == "literal"?bad:bad
+make: Bad conditional expression 'variable expression == "literal"' in 'variable expression == "literal"?bad:bad'
make: "varmod-ifelse.mk" line 27: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad})
-make: Bad conditional expression ` == ""' in == ""?bad-assign:bad-assign
-make: Bad conditional expression ` == ""' in == ""?bad-cond:bad-cond
+make: Bad conditional expression ' == ""' in ' == ""?bad-assign:bad-assign'
+make: Bad conditional expression ' == ""' in ' == ""?bad-cond:bad-cond'
make: "varmod-ifelse.mk" line 44: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
-make: Bad conditional expression `1 == == 2' in 1 == == 2?yes:no
+make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
make: "varmod-ifelse.mk" line 66: Malformed conditional (${1 == == 2:?yes:no} != "")
CondParser_Eval: "${1 == == 2:?yes:no}" != ""
CondParser_Eval: 1 == == 2
lhs = 1.000000, rhs = 0.000000, op = ==
-make: Bad conditional expression `1 == == 2' in 1 == == 2?yes:no
+make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
lhs = "", rhs = "", op = !=
make: "varmod-ifelse.mk" line 92: warning: Oops, the parse error should have been propagated.
CondParser_Eval: ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
CondParser_Eval: ${VAR} == value
lhs = "value", rhs = "value", op = ==
lhs = "ok", rhs = "ok", op = !=
+make: "varmod-ifelse.mk" line 153: no.
+make: "varmod-ifelse.mk" line 154: String comparison operator must be either == or !=
+make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no'
+make: "varmod-ifelse.mk" line 154: .
+make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no'
+make: "varmod-ifelse.mk" line 159: .
+make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no'
+make: "varmod-ifelse.mk" line 160: .
+make: "varmod-ifelse.mk" line 167: true
+make: "varmod-ifelse.mk" line 169: false
+make: Bad conditional expression ' ' in ' ?true:false'
+make: "varmod-ifelse.mk" line 171:
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk
index ec6acdb2ee2f..0e16032a6543 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.mk
+++ b/contrib/bmake/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.9 2021/01/25 19:05:39 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.17 2021/06/11 13:01:28 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -74,7 +74,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# conditional expression".
#
# XXX: The left-hand side is enclosed in quotes. This results in Var_Parse
-# being called without VARE_UNDEFERR being set. When ApplyModifier_IfElse
+# being called without VARE_UNDEFERR. When ApplyModifier_IfElse
# returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the
# value of the variable expression is still undefined. CondParser_String is
# then supposed to do proper error handling, but since varUndefined is local
@@ -111,5 +111,61 @@ VAR= value
.endif
.MAKEFLAGS: -d0
-all:
- @:;
+# On 2021-04-19, when building external/bsd/tmux with HAVE_LLVM=yes and
+# HAVE_GCC=no, the following conditional generated this error message:
+#
+# make: Bad conditional expression 'string == "literal" && no >= 10'
+# in 'string == "literal" && no >= 10?yes:no'
+#
+# Despite the error message (which was not clearly marked with "error:"),
+# the build continued, for historical reasons, see main_Exit.
+#
+# The tricky detail here is that the condition that looks so obvious in the
+# form written in the makefile becomes tricky when it is actually evaluated.
+# This is because the condition is written in the place of the variable name
+# of the expression, and in an expression, the variable name is always
+# expanded first, before even looking at the modifiers. This happens for the
+# modifier ':?' as well, so when CondEvalExpression gets to see the
+# expression, it already looks like this:
+#
+# string == "literal" && no >= 10
+#
+# When parsing such an expression, the parser used to be strict. It first
+# evaluated the left-hand side of the operator '&&' and then started parsing
+# the right-hand side 'no >= 10'. The word 'no' is obviously a string
+# literal, not enclosed in quotes, which is ok, even on the left-hand side of
+# the comparison operator, but only because this is a condition in the
+# modifier ':?'. In an ordinary directive '.if', this would be a parse error.
+# For strings, only the comparison operators '==' and '!=' are defined,
+# therefore parsing stopped at the '>', producing the 'Bad conditional
+# expression'.
+#
+# Ideally, the conditional expression would not be expanded before parsing
+# it. This would allow to write the conditions exactly as seen below. That
+# change has a high chance of breaking _some_ existing code and would need
+# to be thoroughly tested.
+#
+# Since cond.c 1.262 from 2021-04-20, make reports a more specific error
+# message in situations like these, pointing directly to the specific problem
+# instead of just saying that the whole condition is bad.
+STRING= string
+NUMBER= no # not really a number
+.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
+
+# The following situation occasionally occurs with MKINET6 or similar
+# variables.
+NUMBER= # empty, not really a number either
+.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
+
+# CondParser_LeafToken handles [0-9-+] specially, treating them as a number.
+PLUS= +
+ASTERISK= *
+EMPTY= # empty
+# "true" since "+" is not the empty string.
+.info ${${PLUS} :?true:false}
+# "false" since the variable named "*" is not defined.
+.info ${${ASTERISK} :?true:false}
+# syntax error since the condition is completely blank.
+.info ${${EMPTY} :?true:false}
diff --git a/contrib/bmake/unit-tests/varmod-indirect.exp b/contrib/bmake/unit-tests/varmod-indirect.exp
index 860da7781979..63ed988d0c0e 100644
--- a/contrib/bmake/unit-tests/varmod-indirect.exp
+++ b/contrib/bmake/unit-tests/varmod-indirect.exp
@@ -1,59 +1,43 @@
-make: "varmod-indirect.mk" line 13: Unknown modifier '$'
-make: "varmod-indirect.mk" line 108: before
-make: "varmod-indirect.mk" line 108: after
-make: "varmod-indirect.mk" line 114: before
-make: "varmod-indirect.mk" line 114: after
-make: "varmod-indirect.mk" line 120: before
-make: "varmod-indirect.mk" line 120: after
-make: "varmod-indirect.mk" line 124: Unknown modifier 'Z'
-make: "varmod-indirect.mk" line 125: before
-make: "varmod-indirect.mk" line 125: after
-ParseReadLine (134): '_:= before ${UNDEF} after'
-Global:_ =
-Var_Parse: ${UNDEF} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Global:_ = before ${UNDEF} after
-ParseReadLine (137): '_:= before ${UNDEF:${:US,a,a,}} after'
-Var_Parse: ${UNDEF:${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+make: "varmod-indirect.mk" line 19: Unknown modifier "${"
+make: "varmod-indirect.mk" line 52: Unknown modifier "${"
+make: "varmod-indirect.mk" line 55: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
+make: "varmod-indirect.mk" line 140: before
+make: "varmod-indirect.mk" line 140: after
+make: "varmod-indirect.mk" line 146: before
+make: "varmod-indirect.mk" line 146: after
+make: "varmod-indirect.mk" line 152: before
+make: "varmod-indirect.mk" line 152: after
+make: "varmod-indirect.mk" line 156: Unknown modifier "Z"
+make: "varmod-indirect.mk" line 157: before
+make: "varmod-indirect.mk" line 157: after
+ParseReadLine (166): '_:= before ${UNDEF} after'
+Global: _ =
+Var_Parse: ${UNDEF} after (eval-keep-dollar-and-undefined)
+Global: _ = before ${UNDEF} after
+ParseReadLine (169): '_:= before ${UNDEF:${:US,a,a,}} after'
+Var_Parse: ${UNDEF:${:US,a,a,}} after (eval-keep-dollar-and-undefined)
Indirect modifier "S,a,a," from "${:US,a,a,}"
-Applying ${UNDEF:S...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
+Evaluating modifier ${UNDEF:S...} on value "" (eval-keep-dollar-and-undefined, undefined)
Modifier part: "a"
Modifier part: "a"
ModifyWords: split "" into 1 words
-Result of ${UNDEF:S,a,a,} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:S,a,a,} after
-ParseReadLine (147): '_:= before ${UNDEF:${:U}} after'
-Var_Parse: ${UNDEF:${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+Result of ${UNDEF:S,a,a,} is "" (eval-keep-dollar-and-undefined, undefined)
+Global: _ = before ${UNDEF:S,a,a,} after
+ParseReadLine (179): '_:= before ${UNDEF:${:U}} after'
+Var_Parse: ${UNDEF:${:U}} after (eval-keep-dollar-and-undefined)
Indirect modifier "" from "${:U}"
-Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:} after
-ParseReadLine (152): '_:= before ${UNDEF:${:UZ}} after'
-Var_Parse: ${UNDEF:${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+Global: _ = before ${UNDEF:} after
+ParseReadLine (184): '_:= before ${UNDEF:${:UZ}} after'
+Var_Parse: ${UNDEF:${:UZ}} after (eval-keep-dollar-and-undefined)
Indirect modifier "Z" from "${:UZ}"
-Applying ${UNDEF:Z} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-make: "varmod-indirect.mk" line 152: Unknown modifier 'Z'
-Result of ${UNDEF:Z} is error (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:Z} after
-ParseReadLine (154): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d 0 -d pv -d
-Global:.MAKEFLAGS = -r -k -d 0 -d pv -d 0
+Evaluating modifier ${UNDEF:Z} on value "" (eval-keep-dollar-and-undefined, undefined)
+make: "varmod-indirect.mk" line 184: Unknown modifier "Z"
+Result of ${UNDEF:Z} is error (eval-keep-dollar-and-undefined, undefined)
+Global: _ = before ${UNDEF:Z} after
+ParseReadLine (186): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d 0 -d pv -d
+Global: .MAKEFLAGS = -r -k -d 0 -d pv -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-indirect.mk b/contrib/bmake/unit-tests/varmod-indirect.mk
index d130c7cae76d..fa58997cc849 100644
--- a/contrib/bmake/unit-tests/varmod-indirect.mk
+++ b/contrib/bmake/unit-tests/varmod-indirect.mk
@@ -1,15 +1,21 @@
-# $NetBSD: varmod-indirect.mk,v 1.5 2020/12/27 17:32:25 rillig Exp $
+# $NetBSD: varmod-indirect.mk,v 1.9 2021/03/15 20:00:50 rillig Exp $
#
# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
# These can be used for very basic purposes like converting a string to either
# uppercase or lowercase, as well as for fairly advanced modifiers that first
# look like line noise and are hard to decipher.
#
-# TODO: Since when are indirect modifiers supported?
+# Initial support for indirect modifiers was added in var.c 1.101 from
+# 2006-02-18. Since var.c 1.108 from 2006-05-11 it is possible to use
+# indirect modifiers for all but the very first modifier as well.
# To apply a modifier indirectly via another variable, the whole
# modifier must be put into a single variable expression.
+# The following expression generates a parse error since its indirect
+# modifier contains more than a sole variable expression.
+#
+# expect+1: Unknown modifier '$'
.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
. warning unexpected
.endif
@@ -28,13 +34,39 @@
.endif
-# An indirect variable that evaluates to the empty string is allowed though.
+# An indirect variable that evaluates to the empty string is allowed.
+# It is even allowed to write another modifier directly afterwards.
+# There is no practical use case for this feature though, as demonstrated
+# in the test case directly below.
+.if ${value:L:${:Dempty}S,value,replaced,} != "replaced"
+. warning unexpected
+.endif
+
+# If an expression for an indirect modifier evaluates to anything else than an
+# empty string and is neither followed by a ':' nor '}', this produces a parse
+# error. Because of this parse error, this feature cannot be used reasonably
+# in practice.
+#
+# expect+1: Unknown modifier '$'
+#.MAKEFLAGS: -dvc
+.if ${value:L:${:UM*}S,value,replaced,} == "M*S,value,replaced,}"
+. warning FIXME: this expression should have resulted in a parse $\
+ error rather than returning the unparsed portion of the $\
+ expression.
+.else
+. error
+.endif
+#.MAKEFLAGS: -d0
+
+# An indirect modifier can be followed by other modifiers, no matter if the
+# indirect modifier evaluates to an empty string or not.
+#
# This makes it possible to define conditional modifiers, like this:
#
# M.little-endian= S,1234,4321,
# M.big-endian= # none
-.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
-. warning unexpected
+.if ${value:L:${:D empty }:S,value,replaced,} != "replaced"
+. error
.endif
@@ -154,4 +186,62 @@ _:= before ${UNDEF:${:UZ}} after
.MAKEFLAGS: -d0
.undef _
+
+# When evaluating indirect modifiers, these modifiers may expand to ':tW',
+# which modifies the interpretation of the expression value. This modified
+# interpretation only lasts until the end of the indirect modifier, it does
+# not influence the outer variable expression.
+.if ${1 2 3:L:tW:[#]} != 1 # direct :tW applies to the :[#]
+. error
+.endif
+.if ${1 2 3:L:${:UtW}:[#]} != 3 # indirect :tW does not apply to :[#]
+. error
+.endif
+
+
+# When evaluating indirect modifiers, these modifiers may expand to ':ts*',
+# which modifies the interpretation of the expression value. This modified
+# interpretation only lasts until the end of the indirect modifier, it does
+# not influence the outer variable expression.
+#
+# In this first expression, the direct ':ts*' has no effect since ':U' does not
+# treat the expression value as a list of words but as a single word. It has
+# to be ':U', not ':D', since the "expression name" is "1 2 3" and there is no
+# variable of that name.
+#.MAKEFLAGS: -dcpv
+.if ${1 2 3:L:ts*:Ua b c} != "a b c"
+. error
+.endif
+# In this expression, the direct ':ts*' affects the ':M' at the end.
+.if ${1 2 3:L:ts*:Ua b c:M*} != "a*b*c"
+. error
+.endif
+# In this expression, the ':ts*' is indirect, therefore the changed separator
+# only applies to the modifiers from the indirect text. It does not affect
+# the ':M' since that is not part of the text from the indirect modifier.
+#
+# Implementation detail: when ApplyModifiersIndirect calls ApplyModifiers
+# (which creates a new ModChain containing a fresh separator),
+# the outer separator character is not passed by reference to the inner
+# evaluation, therefore the scope of the inner separator ends after applying
+# the modifier ':ts*'.
+.if ${1 2 3:L:${:Uts*}:Ua b c:M*} != "a b c"
+. error
+.endif
+
+# A direct modifier ':U' turns the expression from undefined to defined.
+# An indirect modifier ':U' has the same effect, unlike the separator from
+# ':ts*' or the single-word marker from ':tW'.
+#
+# This is because when ApplyModifiersIndirect calls ApplyModifiers, it passes
+# the definedness of the outer expression by reference. If that weren't the
+# case, the first condition below would result in a parse error because its
+# left-hand side would be undefined.
+.if ${UNDEF:${:UUindirect-fallback}} != "indirect-fallback"
+. error
+.endif
+.if ${UNDEF:${:UUindirect-fallback}:Uouter-fallback} != "outer-fallback"
+. error
+.endif
+
all:
diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.exp b/contrib/bmake/unit-tests/varmod-loop-varname.exp
new file mode 100644
index 000000000000..9170307bd2a0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-varname.exp
@@ -0,0 +1,11 @@
+make: "varmod-loop-varname.mk" line 13: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 13: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
+make: "varmod-loop-varname.mk" line 80: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 80: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
+make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
+make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.mk b/contrib/bmake/unit-tests/varmod-loop-varname.mk
new file mode 100644
index 000000000000..d51e2ba76a42
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-varname.mk
@@ -0,0 +1,127 @@
+# $NetBSD: varmod-loop-varname.mk,v 1.2 2021/04/04 13:35:26 rillig Exp $
+#
+# Tests for the first part of the variable modifier ':@var@...@', which
+# contains the variable name to use during the loop.
+
+.MAKE.SAVE_DOLLARS= yes
+
+
+# Before 2021-04-04, the name of the loop variable could be generated
+# dynamically. There was no practical use-case for this.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"
+. error
+.endif
+
+
+# ":::" is a very creative variable name, unlikely to occur in practice.
+# The expression ${\:\:\:} would not work since backslashes can only
+# be escaped in the modifiers, but not in the variable name, therefore
+# the extra indirection via the modifier ':U'.
+.if ${:U1 2 3:@:::@x${${:U\:\:\:}}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# "@@" is another creative variable name.
+.if ${:U1 2 3:@\@\@@x${@@}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# In extreme cases, even the backslash can be used as variable name.
+# It needs to be doubled though.
+.if ${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# The variable name can technically be empty, and in this situation
+# the variable value cannot be accessed since the empty "variable"
+# is protected to always return an empty string.
+.if ${:U1 2 3:@@x${}y@} != "xy xy xy"
+. error
+.endif
+
+
+# The :@ modifier resolves the variables from the replacement text once more
+# than expected. In particular, it resolves _all_ variables from the scope,
+# and not only the loop variable (in this case v).
+SRCS= source
+CFLAGS.source= before
+ALL_CFLAGS:= ${SRCS:@src@${CFLAGS.${src}}@} # note the ':='
+CFLAGS.source+= after
+.if ${ALL_CFLAGS} != "before"
+. error
+.endif
+
+
+# In the following example, the modifier ':@' expands the '$$' to '$'. This
+# means that when the resulting expression is evaluated, these resulting '$'
+# will be interpreted as starting a subexpression.
+#
+# The d means direct reference, the i means indirect reference.
+RESOLVE= ${RES1} $${RES1}
+RES1= 1d${RES2} 1i$${RES2}
+RES2= 2d${RES3} 2i$${RES3}
+RES3= 3
+
+.if ${RESOLVE:@v@w${v}w@} != "w1d2d3w w2i3w w1i2d3 2i\${RES3}w w1d2d3 2i\${RES3} 1i\${RES2}w"
+. error
+.endif
+
+
+# Until 2020-07-20, the variable name of the :@ modifier could end with one
+# or two dollar signs, which were silently ignored.
+# There's no point in allowing a dollar sign in that position.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if ${1 2 3:L:@v$@($v)@} != "(1) (2) (3)"
+. error
+.else
+. error
+.endif
+.if ${1 2 3:L:@v$$@($v)@} != "() () ()"
+. error
+.else
+. error
+.endif
+.if ${1 2 3:L:@v$$$@($v)@} != "() () ()"
+. error
+.else
+. error
+.endif
+
+
+# It may happen that there are nested :@ modifiers that use the same name for
+# for the loop variable. These modifiers influence each other.
+#
+# As of 2020-10-18, the :@ modifier is implemented by actually setting a
+# variable in the scope of the expression and deleting it again after the
+# loop. This is different from the .for loops, which substitute the variable
+# expression with ${:Uvalue}, leading to different unwanted side effects.
+#
+# To make the behavior more predictable, the :@ modifier should restore the
+# loop variable to the value it had before the loop. This would result in
+# the string "1a b c1 2a b c2 3a b c3", making the two loops independent.
+.if ${:U1 2 3:@i@$i${:Ua b c:@i@$i@}${i:Uu}@} != "1a b cu 2a b cu 3a b cu"
+. error
+.endif
+
+# During the loop, the variable is actually defined and nonempty.
+# If the loop were implemented in the same way as the .for loop, the variable
+# would be neither defined nor nonempty since all expressions of the form
+# ${var} would have been replaced with ${:Uword} before evaluating them.
+.if defined(var)
+. error
+.endif
+.if ${:Uword:@var@${defined(var):?def:undef} ${empty(var):?empty:nonempty}@} \
+ != "def nonempty"
+. error
+.endif
+.if defined(var)
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp
index 66cfd6f51e16..a4704973f6e2 100644
--- a/contrib/bmake/unit-tests/varmod-loop.exp
+++ b/contrib/bmake/unit-tests/varmod-loop.exp
@@ -1,21 +1,12 @@
-ParseReadLine (117): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
+ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = !=
-ParseReadLine (122): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
+ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = !=
-ParseReadLine (147): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-:+one+ +two+ +three+:
-:x1y x2y x3y:
-:x1y x2y x3y:
-:mod-loop-varname: :x1y x2y x3y: ::
-:x1y x2y x3y:
-empty: :xy xy xy:
-mod-loop-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w:
-mod-loop-varname-dollar:(1) (2) (3).
-mod-loop-varname-dollar:() () ().
-mod-loop-varname-dollar:() () ().
+ParseReadLine (105): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+:varname-overwriting-target: :x1y x2y x3y: ::
mod-loop-dollar:1:
mod-loop-dollar:${word}$:
mod-loop-dollar:$3$:
diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk
index c109c775a492..4fdaa3ff4e61 100644
--- a/contrib/bmake/unit-tests/varmod-loop.mk
+++ b/contrib/bmake/unit-tests/varmod-loop.mk
@@ -1,63 +1,21 @@
-# $NetBSD: varmod-loop.mk,v 1.9 2021/02/04 21:42:47 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the :@var@...${var}...@ variable modifier.
.MAKE.SAVE_DOLLARS= yes
-all: mod-loop-varname
-all: mod-loop-resolve
-all: mod-loop-varname-dollar
+all: varname-overwriting-target
all: mod-loop-dollar
-# In the :@ modifier, the name of the loop variable can even be generated
-# dynamically. There's no practical use-case for this, and hopefully nobody
-# will ever depend on this, but technically it's possible.
-# Therefore, in -dL mode, this is forbidden, see lint.mk.
-mod-loop-varname:
- @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}:
-
- # ":::" is a very creative variable name, unlikely in practice.
- # The expression ${\:\:\:} would not work since backslashes can only
- # be escaped in the modifiers, but not in the variable name.
- @echo :${:U1 2 3:@:::@x${${:U\:\:\:}}y@}:
-
- # "@@" is another creative variable name.
- @echo :${:U1 2 3:@\@\@@x${@@}y@}:
-
+varname-overwriting-target:
# Even "@" works as a variable name since the variable is installed
# in the "current" scope, which in this case is the one from the
- # target.
+ # target. Because of this, after the loop has finished, '$@' is
+ # undefined. This is something that make doesn't expect, this may
+ # even trigger an assertion failure somewhere.
@echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@:
- # In extreme cases, even the backslash can be used as variable name.
- # It needs to be doubled though.
- @echo :${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@}:
-
- # The variable name can technically be empty, and in this situation
- # the variable value cannot be accessed since the empty variable is
- # protected to always return an empty string.
- @echo empty: :${:U1 2 3:@@x${}y@}:
-# The :@ modifier resolves the variables a little more often than expected.
-# In particular, it resolves _all_ variables from the scope, and not only
-# the loop variable (in this case v).
-#
-# The d means direct reference, the i means indirect reference.
-RESOLVE= ${RES1} $${RES1}
-RES1= 1d${RES2} 1i$${RES2}
-RES2= 2d${RES3} 2i$${RES3}
-RES3= 3
-
-mod-loop-resolve:
- @echo $@:${RESOLVE:@v@w${v}w@:Q}:
-
-# Until 2020-07-20, the variable name of the :@ modifier could end with one
-# or two dollar signs, which were silently ignored.
-# There's no point in allowing a dollar sign in that position.
-mod-loop-varname-dollar:
- @echo $@:${1 2 3:L:@v$@($v)@:Q}.
- @echo $@:${1 2 3:L:@v$$@($v)@:Q}.
- @echo $@:${1 2 3:L:@v$$$@($v)@:Q}.
# Demonstrate that it is possible to generate dollar signs using the
# :@ modifier.
@@ -109,9 +67,9 @@ mod-loop-dollar:
# This string literal is written with 8 dollars, and this is saved as the
# variable value. But as soon as this value is evaluated, it goes through
# Var_Subst, which replaces each '$$' with a single '$'. This could be
-# prevented by VARE_KEEP_DOLLAR, but that flag is usually removed before
-# expanding subexpressions. See ApplyModifier_Loop and ParseModifierPart
-# for examples.
+# prevented by VARE_EVAL_KEEP_DOLLAR, but that flag is usually removed
+# before expanding subexpressions. See ApplyModifier_Loop and
+# ParseModifierPart for examples.
#
.MAKEFLAGS: -dcp
USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
@@ -120,20 +78,20 @@ USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
.endif
#
SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
-# The ':=' assignment operator evaluates the variable value using the flag
-# VARE_KEEP_DOLLAR, which means that some dollar signs are preserved, but not
-# all. The dollar signs in the top-level expression and in the indirect
-# ${8_DOLLARS} are preserved.
+# The ':=' assignment operator evaluates the variable value using the mode
+# VARE_KEEP_DOLLAR_UNDEF, which means that some dollar signs are preserved,
+# but not all. The dollar signs in the top-level expression and in the
+# indirect ${8_DOLLARS} are preserved.
#
# The variable modifier :@var@ does not preserve the dollar signs though, no
# matter in which context it is evaluated. What happens in detail is:
# First, the modifier part "${8_DOLLARS}" is parsed without expanding it.
# Next, each word of the value is expanded on its own, and at this moment
-# in ApplyModifier_Loop, the VARE_KEEP_DOLLAR flag is not passed down to
+# in ApplyModifier_Loop, the flag keepDollar is not passed down to
# ModifyWords, resulting in "$$$$" for the first word of USE_8_DOLLARS.
#
# The remaining words of USE_8_DOLLARS are not affected by any variable
-# modifier and are thus expanded with the flag VARE_KEEP_DOLLAR in action.
+# modifier and are thus expanded with the flag keepDollar in action.
# The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value
# "$$$$ $$$$$$$$ $$$$$$$$".
#
@@ -145,3 +103,87 @@ SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
. error
.endif
.MAKEFLAGS: -d0
+
+# After looping over the words of the expression, the loop variable gets
+# undefined. The modifier ':@' uses an ordinary global variable for this,
+# which is different from the '.for' loop, which replaces ${var} with
+# ${:Uvalue} in the body of the loop. This choice of implementation detail
+# can be used for a nasty side effect. The expression ${:U:@VAR@@} evaluates
+# to an empty string, plus it undefines the variable 'VAR'. This is the only
+# possibility to undefine a global variable during evaluation.
+GLOBAL= before-global
+RESULT:= ${:U${GLOBAL} ${:U:@GLOBAL@@} ${GLOBAL:Uundefined}}
+.if ${RESULT} != "before-global undefined"
+. error
+.endif
+
+# The above side effect of undefining a variable from a certain scope can be
+# further combined with the otherwise undocumented implementation detail that
+# the argument of an '.if' directive is evaluated in cmdline scope. Putting
+# these together makes it possible to undefine variables from the cmdline
+# scope, something that is not possible in a straight-forward way.
+.MAKEFLAGS: CMDLINE=cmdline
+.if ${:U${CMDLINE}${:U:@CMDLINE@@}} != "cmdline"
+. error
+.endif
+# Now the cmdline variable got undefined.
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# At this point, it still looks as if the cmdline variable were defined,
+# since the value of CMDLINE is still "cmdline". That impression is only
+# superficial though, the cmdline variable is actually deleted. To
+# demonstrate this, it is now possible to override its value using a global
+# variable, something that was not possible before:
+CMDLINE= global
+.if ${CMDLINE} != "global"
+. error
+.endif
+# Now undefine that global variable again, to get back to the original value.
+.undef CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# What actually happened is that when CMDLINE was set by the '.MAKEFLAGS'
+# target in the cmdline scope, that same variable was exported to the
+# environment, see Var_SetWithFlags.
+.unexport CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# The above '.unexport' has no effect since UnexportVar requires a global
+# variable of the same name to be defined, otherwise nothing is unexported.
+CMDLINE= global
+.unexport CMDLINE
+.undef CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# This still didn't work since there must not only be a global variable, the
+# variable must be marked as exported as well, which it wasn't before.
+CMDLINE= global
+.export CMDLINE
+.unexport CMDLINE
+.undef CMDLINE
+.if ${CMDLINE:Uundefined} != "undefined"
+. error
+.endif
+# Finally the variable 'CMDLINE' from the cmdline scope is gone, and all its
+# traces from the environment are gone as well. To do that, a global variable
+# had to be defined and exported, something that is far from obvious. To
+# recap, here is the essence of the above story:
+.MAKEFLAGS: CMDLINE=cmdline # have a cmdline + environment variable
+.if ${:U:@CMDLINE@@}} # undefine cmdline, keep environment
+.endif
+CMDLINE= global # needed for deleting the environment
+.export CMDLINE # needed for deleting the environment
+.unexport CMDLINE # delete the environment
+.undef CMDLINE # delete the global helper variable
+.if ${CMDLINE:Uundefined} != "undefined"
+. error # 'CMDLINE' is gone now from all scopes
+.endif
+
+
+# TODO: Actually trigger the undefined behavior (use after free) that was
+# already suspected in Var_Parse, in the comment 'the value of the variable
+# must not change'.
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.exp b/contrib/bmake/unit-tests/varmod-match-escape.exp
index 30c148075524..42cdd7a87ac9 100755
--- a/contrib/bmake/unit-tests/varmod-match-escape.exp
+++ b/contrib/bmake/unit-tests/varmod-match-escape.exp
@@ -1,60 +1,38 @@
-Global:SPECIALS = \: : \\ * \*
+Global: SPECIALS = \: : \\ * \*
CondParser_Eval: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}}
-Var_Parse: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U}\: with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[SPECIALS] for [\: : \\ * \*] is [\:]
+Var_Parse: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} (eval-defined)
+Evaluating modifier ${SPECIALS:M...} on value "\: : \\ * \*"
+Pattern for ':M' is "\:"
ModifyWords: split "\: : \\ * \*" into 5 words
-VarMatch [\:] [\:]
-VarMatch [:] [\:]
-VarMatch [\\] [\:]
-VarMatch [*] [\:]
-VarMatch [\*] [\:]
-Result of ${SPECIALS:M${:U}\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[SPECIALS] for [\: : \\ * \*] is [:]
+Result of ${SPECIALS:M${:U}\:} is ":"
+Var_Parse: ${SPECIALS:M\:${:U}} (eval-defined)
+Evaluating modifier ${SPECIALS:M...} on value "\: : \\ * \*"
+Pattern for ':M' is ":"
ModifyWords: split "\: : \\ * \*" into 5 words
-VarMatch [\:] [:]
-VarMatch [:] [:]
-VarMatch [\\] [:]
-VarMatch [*] [:]
-VarMatch [\*] [:]
-Result of ${SPECIALS:M\:${:U}} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${SPECIALS:M\:${:U}} is ":"
lhs = ":", rhs = ":", op = !=
-Global:VALUES = : :: :\:
+Global: VALUES = : :: :\:
CondParser_Eval: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:}
-Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[VALUES] for [: :: :\:] is [:]
+Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} (eval-defined)
+Evaluating modifier ${VALUES:M...} on value ": :: :\:"
+Var_Parse: ${:U:} (eval-defined)
+Evaluating modifier ${:U} on value "" (eval-defined, undefined)
+Result of ${:U} is "" (eval-defined, defined)
+Pattern for ':M' is ":"
ModifyWords: split ": :: :\:" into 3 words
-VarMatch [:] [:]
-VarMatch [::] [:]
-VarMatch [:\:] [:]
-Result of ${VALUES:M\:${:U\:}} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U\:}\: with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[VALUES] for [: :: :\:] is [:\:]
+Result of ${VALUES:M\:${:U\:}} is ":"
+Var_Parse: ${VALUES:M${:U\:}\:} (eval-defined)
+Evaluating modifier ${VALUES:M...} on value ": :: :\:"
+Var_Parse: ${:U\:}\: (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:U\:} is ":" (eval-defined, defined)
+Pattern for ':M' is ":\:"
ModifyWords: split ": :: :\:" into 3 words
-VarMatch [:] [:\:]
-VarMatch [::] [:\:]
-VarMatch [:\:] [:\:]
-Result of ${VALUES:M${:U\:}\:} is "::" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${VALUES:M${:U\:}\:} is "::"
lhs = ":", rhs = "::", op = !=
make: "varmod-match-escape.mk" line 42: warning: XXX: Oops
-Global:.MAKEFLAGS = -r -k -d cv -d
-Global:.MAKEFLAGS = -r -k -d cv -d 0
+Global: .MAKEFLAGS = -r -k -d cv -d
+Global: .MAKEFLAGS = -r -k -d cv -d 0
make: "varmod-match-escape.mk" line 67: Dollar followed by nothing
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.mk b/contrib/bmake/unit-tests/varmod-match-escape.mk
index e62fbe8352b7..5ac69f964a68 100755
--- a/contrib/bmake/unit-tests/varmod-match-escape.mk
+++ b/contrib/bmake/unit-tests/varmod-match-escape.mk
@@ -1,8 +1,8 @@
-# $NetBSD: varmod-match-escape.mk,v 1.6 2021/02/01 22:36:28 rillig Exp $
+# $NetBSD: varmod-match-escape.mk,v 1.7 2021/04/03 11:08:40 rillig Exp $
#
# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
# depending on whether there was a variable expression somewhere before the
-# first backslash or not. See ApplyModifier_Match, "copy = TRUE".
+# first backslash or not. See ApplyModifier_Match, "copy = true".
#
# Apart from the different and possibly confusing debug output, there is no
# difference in behavior. When parsing the modifier text, only \{, \} and \:
@@ -29,8 +29,8 @@ SPECIALS= \: : \\ * \*
#
# XXX: As of 2020-11-01, the modifier on the right-hand side of the
# comparison is parsed differently though. First, the variable expression
-# is parsed, resulting in ':' and needSubst=TRUE. After that, the escaped
-# ':' is seen, and this time, copy=TRUE is not executed but stays copy=FALSE.
+# is parsed, resulting in ':' and needSubst=true. After that, the escaped
+# ':' is seen, and this time, copy=true is not executed but stays copy=false.
# Therefore the escaped ':' is kept as-is, and the final pattern becomes
# ':\:'.
#
diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp
index 99d1d6ef164c..94c3cb694886 100644
--- a/contrib/bmake/unit-tests/varmod-order.exp
+++ b/contrib/bmake/unit-tests/varmod-order.exp
@@ -1,6 +1,6 @@
-make: Bad modifier `:OX' for NUMBERS
+make: Bad modifier ":OX" for variable "NUMBERS"
make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX"
-make: Bad modifier `:OxXX' for NUMBERS
+make: Bad modifier ":OxXX" for variable "NUMBERS"
make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-range.exp b/contrib/bmake/unit-tests/varmod-range.exp
index 3a9d4d032c3a..f4ada11ebde6 100644
--- a/contrib/bmake/unit-tests/varmod-range.exp
+++ b/contrib/bmake/unit-tests/varmod-range.exp
@@ -1,12 +1,12 @@
-make: "varmod-range.mk" line 53: Invalid number: x}Rest" != "Rest"
+make: "varmod-range.mk" line 53: Invalid number "x}Rest" != "Rest"" for ':range' modifier
make: "varmod-range.mk" line 53: Malformed conditional ("${:U:range=x}Rest" != "Rest")
-make: "varmod-range.mk" line 62: Unknown modifier 'x'
+make: "varmod-range.mk" line 62: Unknown modifier "x0"
make: "varmod-range.mk" line 62: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
-make: "varmod-range.mk" line 78: Unknown modifier 'r'
+make: "varmod-range.mk" line 78: Unknown modifier "rang"
make: "varmod-range.mk" line 78: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
-make: "varmod-range.mk" line 85: Unknown modifier 'r'
+make: "varmod-range.mk" line 85: Unknown modifier "rango"
make: "varmod-range.mk" line 85: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
-make: "varmod-range.mk" line 92: Unknown modifier 'r'
+make: "varmod-range.mk" line 92: Unknown modifier "ranger"
make: "varmod-range.mk" line 92: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-remember.exp b/contrib/bmake/unit-tests/varmod-remember.exp
index 448f817d8969..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/varmod-remember.exp
+++ b/contrib/bmake/unit-tests/varmod-remember.exp
@@ -1,3 +1 @@
-1 2 3 1 2 3 1 2 3
-1 2 3, SAVED=3
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-remember.mk b/contrib/bmake/unit-tests/varmod-remember.mk
index 68eb96a122c4..403811759672 100644
--- a/contrib/bmake/unit-tests/varmod-remember.mk
+++ b/contrib/bmake/unit-tests/varmod-remember.mk
@@ -1,12 +1,35 @@
-# $NetBSD: varmod-remember.mk,v 1.3 2020/08/23 15:18:43 rillig Exp $
+# $NetBSD: varmod-remember.mk,v 1.6 2021/03/14 17:27:27 rillig Exp $
#
# Tests for the :_ modifier, which saves the current variable value
# in the _ variable or another, to be used later again.
+.if ${1 2 3:L:_:@var@${_}@} != "1 2 3 1 2 3 1 2 3"
+. error
+.endif
+
# In the parameterized form, having the variable name on the right side of
# the = assignment operator is confusing. In almost all other situations
# the variable name is on the left-hand side of the = operator. Luckily
# this modifier is only rarely needed.
+.if ${1 2 3:L:@var@${var:_=SAVED:}@} != "1 2 3"
+. error
+.elif ${SAVED} != "3"
+. error
+.endif
+
+# The ':_' modifier takes a variable name as optional argument. This variable
+# name can refer to other variables, though this was rather an implementation
+# oversight than an intended feature. The variable name stops at the first
+# '}' or ')' and thus cannot use the usual form ${VARNAME} of long variable
+# names.
+#
+# Because of all these edge-casey conditions, this "feature" has been removed
+# in var.c 1.867 from 2021-03-14.
+S= INDIRECT_VARNAME
+.if ${value:L:@var@${var:_=$S}@} != "value"
+. error
+.elif defined(INDIRECT_VARNAME)
+. error
+.endif
+
all:
- @echo ${1 2 3:L:_:@var@${_}@}
- @echo ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED}
diff --git a/contrib/bmake/unit-tests/varmod-shell.mk b/contrib/bmake/unit-tests/varmod-shell.mk
index db82e302f2a8..c736042f80a0 100644
--- a/contrib/bmake/unit-tests/varmod-shell.mk
+++ b/contrib/bmake/unit-tests/varmod-shell.mk
@@ -1,15 +1,13 @@
-# $NetBSD: varmod-shell.mk,v 1.5 2020/11/17 20:11:02 rillig Exp $
+# $NetBSD: varmod-shell.mk,v 1.6 2021/02/14 20:16:17 rillig Exp $
#
-# Tests for the :sh variable modifier, which runs the shell command
-# given by the variable value and returns its output.
+# Tests for the ':!cmd!' variable modifier, which runs the shell command
+# given by the variable modifier and returns its output.
#
# This modifier has been added on 2000-04-29.
#
# See also:
# ApplyModifier_ShellCommand
-# TODO: Implementation
-
# The command to be run is enclosed between exclamation marks.
# The previous value of the expression is irrelevant for this modifier.
# The :!cmd! modifier turns an undefined expression into a defined one.
@@ -32,4 +30,3 @@
.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.exp b/contrib/bmake/unit-tests/varmod-subst-regex.exp
index 207a97fc25e8..a09046ef764c 100644
--- a/contrib/bmake/unit-tests/varmod-subst-regex.exp
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.exp
@@ -20,6 +20,27 @@ mod-regex-limits:22-ok:1 33 556
mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest
make: Regex compilation error: (details omitted)
mod-regex-errors:
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
mod-regex-errors: xy
-exit status 0
+unmatched-subexpression.ok: one one 2 3 5 8 one3 2one 34
+make: No match for subexpression \2
+unmatched-subexpression.1: ()()
+make: No match for subexpression \2
+unmatched-subexpression.1: ()()
+make: No match for subexpression \1
+unmatched-subexpression.2: ()()
+unmatched-subexpression.3: 3
+unmatched-subexpression.5: 5
+unmatched-subexpression.8: 8
+make: No match for subexpression \2
+unmatched-subexpression.13: (3)()
+make: No match for subexpression \1
+unmatched-subexpression.21: ()(1)
+unmatched-subexpression.34: 34
+make: No match for subexpression \2
+make: No match for subexpression \2
+make: No match for subexpression \1
+make: No match for subexpression \2
+make: No match for subexpression \1
+unmatched-subexpression.all: ()() ()() ()() 3 5 8 (3)() ()(1) 34
+exit status 2
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.mk b/contrib/bmake/unit-tests/varmod-subst-regex.mk
index 91b2f0e6a2f9..197691d73aad 100644
--- a/contrib/bmake/unit-tests/varmod-subst-regex.mk
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.mk
@@ -1,10 +1,14 @@
-# $NetBSD: varmod-subst-regex.mk,v 1.6 2020/12/05 18:13:44 rillig Exp $
+# $NetBSD: varmod-subst-regex.mk,v 1.7 2021/06/21 08:17:39 rillig Exp $
#
# Tests for the :C,from,to, variable modifier.
+# report unmatched subexpressions
+.MAKEFLAGS: -dL
+
all: mod-regex-compile-error
all: mod-regex-limits
all: mod-regex-errors
+all: unmatched-subexpression
# The variable expression expands to 4 words. Of these words, none matches
# the regular expression "a b" since these words don't contain any
@@ -107,3 +111,51 @@ mod-regex-errors:
# unknown modifier, the parse error is ignored in ParseModifierPart
# and the faulty variable expression expands to "".
@echo $@: ${word:L:C,.*,x${:U:Z}y,W}
+
+# In regular expressions with alternatives, not all capturing groups are
+# always set; some may be missing. Make calls these "unmatched
+# subexpressions".
+#
+# Between var.c 1.16 from 1996-12-24 until before var.c 1.933 from 2021-06-21,
+# unmatched subexpressions produced an "error message" but did not have any
+# further effect since the "error handling" didn't influence the exit status.
+#
+# Before 2021-06-21 there was no way to turn off this warning, thus the
+# combination of alternative matches and capturing groups was seldom used, if
+# at all.
+#
+# Since var.c 1.933 from 2021-06-21, the error message is only printed in lint
+# mode (-dL), but not in default mode.
+#
+# As an alternative to the change from var.c 1.933 from 2021-06-21, a possible
+# mitigation would have been to add a new modifier 'U' to the already existing
+# '1Wg' modifiers of the ':C' modifier. That modifier could have been used in
+# the modifier ':C,(a.)|(b.),\1\2,U' to treat unmatched subexpressions as
+# empty. This approach would have created a syntactical ambiguity since the
+# modifiers ':S' and ':C' are open-ended (see mod-subst-chain), that is, they
+# do not need to be followed by a ':' to separate them from the next modifier.
+# Luckily the modifier :U does not make sense after :C, therefore this case
+# does not happen in practice.
+unmatched-subexpression:
+ # In each of the following cases, if the regular expression matches at
+ # all, the subexpression \1 matches as well.
+ @echo $@.ok: ${:U1 1 2 3 5 8 13 21 34:C,1(.*),one\1,}
+
+ # In the following cases:
+ # * The subexpression \1 is only defined for 1 and 13.
+ # * The subexpression \2 is only defined for 2 and 21.
+ # * If the regular expression does not match at all, the
+ # replacement string is not analyzed, thus no error messages.
+ # In total, there are 5 error messages about unmatched subexpressions.
+ @echo $@.1: ${:U 1:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.1: ${:U 1:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.2: ${:U 2:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \1
+ @echo $@.3: ${:U 3:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.5: ${:U 5:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.8: ${:U 8:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.13: ${:U 13:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.21: ${:U 21:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \1
+ @echo $@.34: ${:U 34:C,1(.*)|2(.*),(\1)(\2),:Q}
+
+ # And now all together: 5 error messages for 1, 1, 2, 13, 21.
+ @echo $@.all: ${:U1 1 2 3 5 8 13 21 34:C,1(.*)|2(.*),(\1)(\2),:Q}
diff --git a/contrib/bmake/unit-tests/varmod-subst.exp b/contrib/bmake/unit-tests/varmod-subst.exp
index 3122c17b1ed3..97fa2e4f1491 100644
--- a/contrib/bmake/unit-tests/varmod-subst.exp
+++ b/contrib/bmake/unit-tests/varmod-subst.exp
@@ -45,7 +45,7 @@ mod-subst-delimiter:
1 two 3 tilde
mod-subst-chain:
A B c.
-make: Unknown modifier 'i'
+make: Unknown modifier "i"
.
mod-subst-dollar:$1:
mod-subst-dollar:$2:
diff --git a/contrib/bmake/unit-tests/varmod-subst.mk b/contrib/bmake/unit-tests/varmod-subst.mk
index 3c3ee673c07a..85f41e499ab7 100644
--- a/contrib/bmake/unit-tests/varmod-subst.mk
+++ b/contrib/bmake/unit-tests/varmod-subst.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-subst.mk,v 1.8 2021/05/14 19:37:16 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -78,6 +78,14 @@ WORDS= sequences of letters
. warning The :S modifier matches a too long suffix anchored at both ends.
.endif
+.if ${WORDS:S,*,replacement,} != ${WORDS}
+. error The '*' seems to be interpreted as a wildcard of some kind.
+.endif
+
+.if ${WORDS:S,.,replacement,} != ${WORDS}
+. error The '.' seems to be interpreted as a wildcard of some kind.
+.endif
+
mod-subst:
@echo $@:
@echo :${:Ua b b c:S,a b,,:Q}:
diff --git a/contrib/bmake/unit-tests/varmod-sun-shell.exp b/contrib/bmake/unit-tests/varmod-sun-shell.exp
new file mode 100644
index 000000000000..5087bc66d943
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-sun-shell.exp
@@ -0,0 +1,2 @@
+make: "echo word; false" returned non-zero status
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-sun-shell.mk b/contrib/bmake/unit-tests/varmod-sun-shell.mk
new file mode 100644
index 000000000000..712b36bc7030
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-sun-shell.mk
@@ -0,0 +1,21 @@
+# $NetBSD: varmod-sun-shell.mk,v 1.1 2021/02/14 20:16:17 rillig Exp $
+#
+# Tests for the :sh variable modifier, which runs the shell command
+# given by the variable value and returns its output.
+#
+# This modifier has been added on 1996-05-29.
+#
+# See also:
+# ApplyModifier_SunShell
+
+.if ${echo word:L:sh} != "word"
+. error
+.endif
+
+# If the command exits with non-zero, an error message is printed.
+# XXX: Processing continues as usual though.
+.if ${echo word; false:L:sh} != "word"
+. error
+.endif
+
+all:
diff --git a/contrib/bmake/unit-tests/varmod-sysv.exp b/contrib/bmake/unit-tests/varmod-sysv.exp
index 57e69a667281..59275857f98a 100644
--- a/contrib/bmake/unit-tests/varmod-sysv.exp
+++ b/contrib/bmake/unit-tests/varmod-sysv.exp
@@ -1,5 +1,150 @@
-make: Unfinished modifier for word214 ('=' missing)
+make: Unfinished modifier for "word214" ('=' missing)
make: "varmod-sysv.mk" line 214: Malformed conditional (${word214:L:from${:D=}to})
+word modifier result
+'' = ""
+suffix = "suffix"
+prefix = "prefix"
+pre-middle-suffix = "pre-middle-suffix"
+'' =NS ""
+suffix =NS "suffixNS"
+prefix =NS "prefixNS"
+pre-middle-suffix =NS "pre-middle-suffixNS"
+'' =% ""
+suffix =% "suffix%"
+prefix =% "prefix%"
+pre-middle-suffix =% "pre-middle-suffix%"
+'' =%NS ""
+suffix =%NS "suffix%NS"
+prefix =%NS "prefix%NS"
+pre-middle-suffix =%NS "pre-middle-suffix%NS"
+'' =NPre% ""
+suffix =NPre% "suffixNPre%"
+prefix =NPre% "prefixNPre%"
+pre-middle-suffix =NPre% "pre-middle-suffixNPre%"
+'' =NPre%NS ""
+suffix =NPre%NS "suffixNPre%NS"
+prefix =NPre%NS "prefixNPre%NS"
+pre-middle-suffix =NPre%NS "pre-middle-suffixNPre%NS"
+'' ffix= ""
+suffix ffix= "su"
+prefix ffix= "prefix"
+pre-middle-suffix ffix= "pre-middle-su"
+'' ffix=NS ""
+suffix ffix=NS "suNS"
+prefix ffix=NS "prefix"
+pre-middle-suffix ffix=NS "pre-middle-suNS"
+'' ffix=% ""
+suffix ffix=% "su%"
+prefix ffix=% "prefix"
+pre-middle-suffix ffix=% "pre-middle-su%"
+'' ffix=%NS ""
+suffix ffix=%NS "su%NS"
+prefix ffix=%NS "prefix"
+pre-middle-suffix ffix=%NS "pre-middle-su%NS"
+'' ffix=NPre% ""
+suffix ffix=NPre% "suNPre%"
+prefix ffix=NPre% "prefix"
+pre-middle-suffix ffix=NPre% "pre-middle-suNPre%"
+'' ffix=NPre%NS ""
+suffix ffix=NPre%NS "suNPre%NS"
+prefix ffix=NPre%NS "prefix"
+pre-middle-suffix ffix=NPre%NS "pre-middle-suNPre%NS"
+'' %= ""
+suffix %= ""
+prefix %= ""
+pre-middle-suffix %= ""
+'' %=NS ""
+suffix %=NS "NS"
+prefix %=NS "NS"
+pre-middle-suffix %=NS "NS"
+'' %=% ""
+suffix %=% "suffix"
+prefix %=% "prefix"
+pre-middle-suffix %=% "pre-middle-suffix"
+'' %=%NS ""
+suffix %=%NS "suffixNS"
+prefix %=%NS "prefixNS"
+pre-middle-suffix %=%NS "pre-middle-suffixNS"
+'' %=NPre% ""
+suffix %=NPre% "NPresuffix"
+prefix %=NPre% "NPreprefix"
+pre-middle-suffix %=NPre% "NPrepre-middle-suffix"
+'' %=NPre%NS ""
+suffix %=NPre%NS "NPresuffixNS"
+prefix %=NPre%NS "NPreprefixNS"
+pre-middle-suffix %=NPre%NS "NPrepre-middle-suffixNS"
+'' pre%= ""
+suffix pre%= "suffix"
+prefix pre%= ""
+pre-middle-suffix pre%= ""
+'' pre%=NS ""
+suffix pre%=NS "suffix"
+prefix pre%=NS "NS"
+pre-middle-suffix pre%=NS "NS"
+'' pre%=% ""
+suffix pre%=% "suffix"
+prefix pre%=% "fix"
+pre-middle-suffix pre%=% "-middle-suffix"
+'' pre%=%NS ""
+suffix pre%=%NS "suffix"
+prefix pre%=%NS "fixNS"
+pre-middle-suffix pre%=%NS "-middle-suffixNS"
+'' pre%=NPre% ""
+suffix pre%=NPre% "suffix"
+prefix pre%=NPre% "NPrefix"
+pre-middle-suffix pre%=NPre% "NPre-middle-suffix"
+'' pre%=NPre%NS ""
+suffix pre%=NPre%NS "suffix"
+prefix pre%=NPre%NS "NPrefixNS"
+pre-middle-suffix pre%=NPre%NS "NPre-middle-suffixNS"
+'' %ffix= ""
+suffix %ffix= ""
+prefix %ffix= "prefix"
+pre-middle-suffix %ffix= ""
+'' %ffix=NS ""
+suffix %ffix=NS "NS"
+prefix %ffix=NS "prefix"
+pre-middle-suffix %ffix=NS "NS"
+'' %ffix=% ""
+suffix %ffix=% "su"
+prefix %ffix=% "prefix"
+pre-middle-suffix %ffix=% "pre-middle-su"
+'' %ffix=%NS ""
+suffix %ffix=%NS "suNS"
+prefix %ffix=%NS "prefix"
+pre-middle-suffix %ffix=%NS "pre-middle-suNS"
+'' %ffix=NPre% ""
+suffix %ffix=NPre% "NPresu"
+prefix %ffix=NPre% "prefix"
+pre-middle-suffix %ffix=NPre% "NPrepre-middle-su"
+'' %ffix=NPre%NS ""
+suffix %ffix=NPre%NS "NPresuNS"
+prefix %ffix=NPre%NS "prefix"
+pre-middle-suffix %ffix=NPre%NS "NPrepre-middle-suNS"
+'' pre%ffix= ""
+suffix pre%ffix= "suffix"
+prefix pre%ffix= "prefix"
+pre-middle-suffix pre%ffix= ""
+'' pre%ffix=NS ""
+suffix pre%ffix=NS "suffix"
+prefix pre%ffix=NS "prefix"
+pre-middle-suffix pre%ffix=NS "NS"
+'' pre%ffix=% ""
+suffix pre%ffix=% "suffix"
+prefix pre%ffix=% "prefix"
+pre-middle-suffix pre%ffix=% "-middle-su"
+'' pre%ffix=%NS ""
+suffix pre%ffix=%NS "suffix"
+prefix pre%ffix=%NS "prefix"
+pre-middle-suffix pre%ffix=%NS "-middle-suNS"
+'' pre%ffix=NPre% ""
+suffix pre%ffix=NPre% "suffix"
+prefix pre%ffix=NPre% "prefix"
+pre-middle-suffix pre%ffix=NPre% "NPre-middle-su"
+'' pre%ffix=NPre%NS ""
+suffix pre%ffix=NPre%NS "suffix"
+prefix pre%ffix=NPre%NS "prefix"
+pre-middle-suffix pre%ffix=NPre%NS "NPre-middle-suNS"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-sysv.mk b/contrib/bmake/unit-tests/varmod-sysv.mk
index 751736ceaf74..712c1731717b 100644
--- a/contrib/bmake/unit-tests/varmod-sysv.mk
+++ b/contrib/bmake/unit-tests/varmod-sysv.mk
@@ -1,13 +1,13 @@
-# $NetBSD: varmod-sysv.mk,v 1.12 2020/12/05 13:01:33 rillig Exp $
+# $NetBSD: varmod-sysv.mk,v 1.14 2021/04/12 16:09:57 rillig Exp $
#
-# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix
+# Tests for the variable modifier ':from=to', which replaces the suffix
# "from" with "to". It can also use '%' as a wildcard.
#
# This modifier is applied when the other modifiers don't match exactly.
#
# See ApplyModifier_SysV.
-# A typical use case for the :from=to modifier is conversion of filename
+# A typical use case for the modifier ':from=to' is conversion of filename
# extensions.
.if ${src.c:L:.c=.o} != "src.o"
. error
@@ -23,43 +23,43 @@
. error
.endif
-# The :from=to modifier is therefore often combined with the :M modifier.
+# The modifier ':from=to' is therefore often combined with the modifier ':M'.
.if ${src.c src.h:L:M*.c:.c=.o} != "src.o"
. error
.endif
-# Another use case for the :from=to modifier is to append a suffix to each
+# Another use case for the modifier ':from=to' is to append a suffix to each
# word. In this case, the "from" string is empty, therefore it always
-# matches. The same effect can be achieved with the :S,$,teen, modifier.
+# matches. The same effect can be achieved with the modifier ':S,$,teen,'.
.if ${four six seven nine:L:=teen} != "fourteen sixteen seventeen nineteen"
. error
.endif
-# The :from=to modifier can also be used to surround each word by strings.
+# The modifier ':from=to' can also be used to surround each word by strings.
# It might be tempting to use this for enclosing a string in quotes for the
-# shell, but that's the job of the :Q modifier.
+# shell, but that's the job of the modifier ':Q'.
.if ${one two three:L:%=(%)} != "(one) (two) (three)"
. error
.endif
-# When the :from=to modifier is parsed, it lasts until the closing brace
-# or parenthesis. The :Q in the below expression may look like a modifier
-# but isn't. It is part of the replacement string.
+# When the modifier ':from=to' is parsed, it lasts until the closing brace
+# or parenthesis. The ':Q' in the below expression may look like a modifier
+# but it isn't. It is part of the replacement string.
.if ${a b c d e:L:%a=x:Q} != "x:Q b c d e"
. error
.endif
-# In the :from=to modifier, both parts can contain variable expressions.
+# In the modifier ':from=to', both parts can contain variable expressions.
.if ${one two:L:${:Uone}=${:U1}} != "1 two"
. error
.endif
-# In the :from=to modifier, the "from" part is expanded exactly once.
+# In the modifier ':from=to', the "from" part is expanded exactly once.
.if ${:U\$ \$\$ \$\$\$\$:${:U\$\$\$\$}=4} != "\$ \$\$ 4"
. error
.endif
-# In the :from=to modifier, the "to" part is expanded exactly twice.
+# In the modifier ':from=to', the "to" part is expanded exactly twice.
# XXX: The right-hand side should be expanded only once.
# XXX: It's hard to get the escaping correct here, and to read that.
# XXX: It's not intuitive why the closing brace must be escaped but not
@@ -75,7 +75,7 @@
.endif
# If the variable value is empty, it is debatable whether it consists of a
-# single empty word, or no word at all. The :from=to modifier treats it as
+# single empty word, or no word at all. The modifier ':from=to' treats it as
# no word at all.
#
# See SysVMatch, which doesn't handle w_len == p_len specially.
@@ -93,10 +93,10 @@
# Before 2020-07-19, an ampersand could be used in the replacement part
# of a SysV substitution modifier, and it was replaced with the whole match,
-# just like in the :S modifier.
+# just like in the modifier ':S'.
#
# This was probably a copy-and-paste mistake since the code for the SysV
-# modifier looked a lot like the code for the :S and :C modifiers.
+# modifier looked a lot like the code for the modifiers ':S' and ':C'.
# The ampersand is not mentioned in the manual page.
.if ${a.bcd.e:L:a.%=%} != "bcd.e"
. error
@@ -109,14 +109,14 @@
# Before 2020-07-20, when a SysV modifier was parsed, a single dollar
# before the '=' was parsed (but not interpreted) as an anchor.
# Parsing something without then evaluating it accordingly doesn't make
-# sense.
+# sense, so this has been fixed.
.if ${value:L:e$=x} != "value"
. error
.endif
-# Before 2020-07-20, the modifier ":e$=x" was parsed as having a left-hand
-# side "e" and a right-hand side "x". The dollar was parsed (but not
+# Before 2020-07-20, the modifier ':e$=x' was parsed as having a left-hand
+# side 'e' and a right-hand side 'x'. The dollar was parsed (but not
# interpreted) as 'anchor at the end'. Therefore the modifier was equivalent
-# to ":e=x", which doesn't match the string "value$". Therefore the whole
+# to ':e=x', which doesn't match the string "value$". Therefore the whole
# expression evaluated to "value$".
.if ${${:Uvalue\$}:L:e$=x} != "valux"
. error
@@ -198,7 +198,7 @@
. error
.endif
-# The :from=to modifier can be used to replace both the prefix and a suffix
+# The modifier ':from=to' can be used to replace both the prefix and a suffix
# of a word with other strings. This is not possible with a single :S
# modifier, and using a :C modifier for the same task looks more complicated
# in many cases.
@@ -238,4 +238,17 @@ INDIRECT= 1:${VALUE} 2:$${VALUE} 4:$$$${VALUE}
. error
.endif
+# Test all relevant combinations of prefix, '%' and suffix in both the pattern
+# and the replacement.
+!=1>&2 printf '%-24s %-24s %-24s\n' 'word' 'modifier' 'result'
+.for from in '' ffix % pre% %ffix pre%ffix
+. for to in '' NS % %NS NPre% NPre%NS
+. for word in '' suffix prefix pre-middle-suffix
+. for mod in ${from:N''}=${to:N''}
+!=1>&2 printf '%-24s %-24s "%s"\n' ''${word:Q} ''${mod:Q} ''${word:N'':${mod}:Q}
+. endfor
+. endfor
+. endfor
+.endfor
+
all:
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.exp b/contrib/bmake/unit-tests/varmod-to-separator.exp
index 44c9f0973ed9..c6e8ce98a21a 100644
--- a/contrib/bmake/unit-tests/varmod-to-separator.exp
+++ b/contrib/bmake/unit-tests/varmod-to-separator.exp
@@ -2,17 +2,17 @@ make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu}
make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu}
make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
-make: Bad modifier `:ts\-300' for WORDS
+make: Bad modifier ":ts\-300" for variable "WORDS"
make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
-make: Bad modifier `:ts\8' for 1 2 3
+make: Bad modifier ":ts\8" for variable "1 2 3"
make: "varmod-to-separator.mk" line 136: Malformed conditional (${1 2 3:L:ts\8:tu})
-make: Bad modifier `:ts\100L' for 1 2 3
+make: Bad modifier ":ts\100L" for variable "1 2 3"
make: "varmod-to-separator.mk" line 143: Malformed conditional (${1 2 3:L:ts\100L})
-make: Bad modifier `:ts\x40g' for 1 2 3
+make: Bad modifier ":ts\x40g" for variable "1 2 3"
make: "varmod-to-separator.mk" line 150: Malformed conditional (${1 2 3:L:ts\x40g})
-make: Bad modifier `:tx' for WORDS
+make: Bad modifier ":tx" for variable "WORDS"
make: "varmod-to-separator.mk" line 158: Malformed conditional (${WORDS:tx} != "anything")
-make: Bad modifier `:t\X' for WORDS
+make: Bad modifier ":t\X" for variable "WORDS"
make: "varmod-to-separator.mk" line 165: Malformed conditional (${WORDS:t\X} != "anything")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-unique.mk b/contrib/bmake/unit-tests/varmod-unique.mk
index ea4698764947..04d04a575af1 100644
--- a/contrib/bmake/unit-tests/varmod-unique.mk
+++ b/contrib/bmake/unit-tests/varmod-unique.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-unique.mk,v 1.4 2020/08/31 17:41:38 rillig Exp $
+# $NetBSD: varmod-unique.mk,v 1.5 2021/05/30 20:26:41 rillig Exp $
#
# Tests for the :u variable modifier, which discards adjacent duplicate
# words.
@@ -15,10 +15,18 @@
. warning The :u modifier must do nothing with an empty word list.
.endif
-.if ${:U1:u} != "1"
+.if ${:U :u} != ""
+. warning The modifier ':u' must normalize the whitespace.
+.endif
+
+.if ${:Uword:u} != "word"
. warning The :u modifier must do nothing with a single-element word list.
.endif
+.if ${:U word :u} != "word"
+. warning The modifier ':u' must normalize the whitespace.
+.endif
+
.if ${:U1 1 1 1 1 1 1 1:u} != "1"
. warning The :u modifier must merge _all_ adjacent duplicate words.
.endif
diff --git a/contrib/bmake/unit-tests/varname-dot-shell.exp b/contrib/bmake/unit-tests/varname-dot-shell.exp
index 46a1b2127c98..bfbcfc960182 100755
--- a/contrib/bmake/unit-tests/varname-dot-shell.exp
+++ b/contrib/bmake/unit-tests/varname-dot-shell.exp
@@ -1,32 +1,32 @@
ParseReadLine (10): 'ORIG_SHELL:= ${.SHELL}'
-Global:ORIG_SHELL =
-Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Global: ORIG_SHELL =
+Var_Parse: ${.SHELL} (eval-keep-dollar-and-undefined)
Global:delete .SHELL (not found)
-Command:.SHELL = (details omitted)
-Global:ORIG_SHELL = (details omitted)
+Command: .SHELL = (details omitted)
+Global: ORIG_SHELL = (details omitted)
ParseReadLine (12): '.SHELL= overwritten'
-Global:.SHELL = overwritten
+Global: .SHELL = overwritten
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (19): '.MAKEFLAGS: .SHELL+=appended'
-ParseDoDependency(.MAKEFLAGS: .SHELL+=appended)
+ParseDependency(.MAKEFLAGS: .SHELL+=appended)
Ignoring append to .SHELL since it is read-only
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (27): '.undef .SHELL'
Global:delete .SHELL
ParseReadLine (28): '.SHELL= newly overwritten'
-Global:.SHELL = newly overwritten
+Global: .SHELL = newly overwritten
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (33): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d cpv -d
-Global:.MAKEFLAGS = -r -k -d cpv -d 0
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-empty.exp b/contrib/bmake/unit-tests/varname-empty.exp
index 28f55368fd19..ec225c6973c8 100644
--- a/contrib/bmake/unit-tests/varname-empty.exp
+++ b/contrib/bmake/unit-tests/varname-empty.exp
@@ -1,47 +1,27 @@
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored
-Var_Set("", "cmdline-plain", ...) name expands to empty string - ignored
-Global:.CURDIR = <curdir>
-Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE:U} with VARE_WANTRES
-Applying ${MAKE_OBJDIR_CHECK_WRITABLE:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${MAKE_OBJDIR_CHECK_WRITABLE:U} is "" (VARE_WANTRES, none, VES_DEF)
-Global:.OBJDIR = <curdir>
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdline-u" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored
+Global: .CURDIR = <curdir>
+Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval)
+Global: .OBJDIR = <curdir>
Global:delete .PATH (not found)
-Global:.PATH = .
-Global:.PATH = . <curdir>
-Global:.TARGETS =
-Internal:MAKEFILE = varname-empty.mk
-Global:.MAKE.MAKEFILES = varname-empty.mk
-Global:.PARSEFILE = varname-empty.mk
+Global: .PATH = .
+Global: .PATH = . <curdir>
+Global: .TARGETS =
+Internal: MAKEFILE = varname-empty.mk
+Global: .MAKE.MAKEFILES = varname-empty.mk
+Global: .PARSEFILE = varname-empty.mk
Global:delete .INCLUDEDFROMDIR (not found)
Global:delete .INCLUDEDFROMFILE (not found)
-Var_Set("", "default", ...) name expands to empty string - ignored
-Var_Set("", "assigned", ...) name expands to empty string - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "default" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "assigned" - ignored
SetVar: variable name is empty - ignored
-Var_Set("", "", ...) name expands to empty string - ignored
-Var_Set("", "subst", ...) name expands to empty string - ignored
-Var_Set("", "shell-output", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "assigned indirectly", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Append("${:U}", "appended indirectly", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.MAKEFLAGS = -r -d v -d
-Global:.MAKEFLAGS = -r -d v -d 0
+Var_SetExpand: variable name "" expands to empty string, with value "" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "subst" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "shell-output" - ignored
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "assigned indirectly" - ignored
+Var_AppendExpand: variable name "${:U}" expands to empty string, with value "appended indirectly" - ignored
+Global: .MAKEFLAGS = -r -d v -d
+Global: .MAKEFLAGS = -r -d v -d 0
out: fallback
out: 1 2 3
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-empty.mk b/contrib/bmake/unit-tests/varname-empty.mk
index 492f9f2618ba..f077d2ec07b4 100755
--- a/contrib/bmake/unit-tests/varname-empty.mk
+++ b/contrib/bmake/unit-tests/varname-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-empty.mk,v 1.8 2021/02/03 08:34:15 rillig Exp $
+# $NetBSD: varname-empty.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the special variable with the empty name.
#
@@ -49,7 +49,7 @@ ${:U}+= appended indirectly
.MAKEFLAGS: -d0
# Before 2020-08-22, the simple assignment operator '=' after an empty
-# variable name had an off-by-one bug in Parse_DoVar. The code that was
+# variable name had an off-by-one bug in Parse_Var. The code that was
# supposed to "skip to operator character" started its search _after_ the
# assignment operator, assuming that the variable name would be at least
# one character long. It then looked for the next occurrence of a '=', which
diff --git a/contrib/bmake/unit-tests/varname.exp b/contrib/bmake/unit-tests/varname.exp
index 84f878a9f742..942532b654d5 100644
--- a/contrib/bmake/unit-tests/varname.exp
+++ b/contrib/bmake/unit-tests/varname.exp
@@ -1,24 +1,21 @@
-Global:VAR{{{}}} = 3 braces
-Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES
-Global:VARNAME = VAR(((
-Var_Parse: ${VARNAME} with VARE_WANTRES
-Global:VAR((( = 3 open parentheses
-Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES
-Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.ALLTARGETS = VAR(((=)
+Global: VAR{{{}}} = 3 braces
+Var_Parse: ${VAR{{{}}}}" != "3 braces" (eval)
+Global: VARNAME = VAR(((
+Var_Parse: ${VARNAME} (eval)
+Global: VAR((( = 3 open parentheses
+Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" (eval)
+Global: .ALLTARGETS = VAR(((=)
make: "varname.mk" line 30: No closing parenthesis in archive specification
make: "varname.mk" line 30: Error in archive specification: "VAR"
-Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.ALLTARGETS = VAR(((=) VAR\(\(\(=
+Var_Parse: ${:UVAR\(\(\(}= try2 (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (eval-defined, defined)
+Global: .ALLTARGETS = VAR(((=) VAR\(\(\(=
make: "varname.mk" line 35: Invalid line type
-Var_Parse: ${VARNAME} with VARE_WANTRES
-Global:VAR((( = try3
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Var_Parse: ${VARNAME} (eval)
+Global: VAR((( = try3
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varparse-dynamic.mk b/contrib/bmake/unit-tests/varparse-dynamic.mk
index c65ba12e6149..d4d165017a7f 100644
--- a/contrib/bmake/unit-tests/varparse-dynamic.mk
+++ b/contrib/bmake/unit-tests/varparse-dynamic.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-dynamic.mk,v 1.4 2021/02/04 21:42:47 rillig Exp $
+# $NetBSD: varparse-dynamic.mk,v 1.5 2021/02/22 20:38:55 rillig Exp $
# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped
# the last character in the variable name.
@@ -15,8 +15,8 @@
# expression is returned as the variable value, hoping that it can be
# resolved at a later point.
#
-# This test covers the code in Var_Parse that deals with VAR_JUNK but not
-# VAR_KEEP for dynamic variables.
+# This test covers the code in Var_Parse that deals with DEF_UNDEF but not
+# DEF_DEFINED for dynamic variables.
.if ${.TARGET:S,^,,} != "\${.TARGET:S,^,,}"
. error
.endif
diff --git a/contrib/bmake/unit-tests/varparse-errors.exp b/contrib/bmake/unit-tests/varparse-errors.exp
index 50a0766c7d70..27589e0b21af 100644
--- a/contrib/bmake/unit-tests/varparse-errors.exp
+++ b/contrib/bmake/unit-tests/varparse-errors.exp
@@ -1,5 +1,5 @@
-make: "varparse-errors.mk" line 38: Unknown modifier 'Z'
-make: "varparse-errors.mk" line 46: Unknown modifier 'Z'
+make: "varparse-errors.mk" line 38: Unknown modifier "Z"
+make: "varparse-errors.mk" line 46: Unknown modifier "Z"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varparse-errors.mk b/contrib/bmake/unit-tests/varparse-errors.mk
index 113c7a292a79..f0947bb9410a 100644
--- a/contrib/bmake/unit-tests/varparse-errors.mk
+++ b/contrib/bmake/unit-tests/varparse-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-errors.mk,v 1.3 2020/12/20 19:47:34 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.4 2021/03/15 12:15:03 rillig Exp $
# Tests for parsing and evaluating all kinds of variable expressions.
#
@@ -25,7 +25,7 @@ ERR_BAD_MOD= An ${:Uindirect:Z} expression with an unknown modifier.
ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
# In a conditional, a variable expression that is not enclosed in quotes is
-# expanded using the flags VARE_UNDEFERR and VARE_WANTRES.
+# expanded using the mode VARE_UNDEFERR.
# The variable itself must be defined.
# It may refer to undefined variables though.
.if ${REF_UNDEF} != "A reference to an undefined variable."
diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c
index eddca9ef6daa..6e5148bba968 100644
--- a/contrib/bmake/var.c
+++ b/contrib/bmake/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $ */
+/* $NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -120,7 +120,8 @@
*
* Var_Dump Print out all variables defined in the given scope.
*
- * XXX: There's a lot of duplication in these functions.
+ * XXX: There's a lot of almost duplicate code in these functions that only
+ * differs in subtle details that are not mentioned in the manual page.
*/
#include <sys/stat.h>
@@ -147,48 +148,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $");
-
-typedef enum VarFlags {
- VAR_NONE = 0,
-
- /*
- * The variable's value is currently being used by Var_Parse or
- * Var_Subst. This marker is used to avoid endless recursion.
- */
- VAR_IN_USE = 0x01,
-
- /*
- * The variable comes from the environment.
- * These variables are not registered in any GNode, therefore they
- * must be freed as soon as they are not used anymore.
- */
- VAR_FROM_ENV = 0x02,
-
- /*
- * The variable is exported to the environment, to be used by child
- * processes.
- */
- VAR_EXPORTED = 0x10,
-
- /*
- * At the point where this variable was exported, it contained an
- * unresolved reference to another variable. Before any child
- * process is started, it needs to be exported again, in the hope
- * that the referenced variable can then be resolved.
- */
- VAR_REEXPORT = 0x20,
-
- /* The variable came from the command line. */
- VAR_FROM_CMD = 0x40,
-
- /*
- * The variable value cannot be changed anymore, and the variable
- * cannot be deleted. Any attempts to do so are silently ignored,
- * they are logged with -dv though.
- */
- VAR_READONLY = 0x80
-} VarFlags;
+MAKE_RCSID("$NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -219,12 +179,53 @@ typedef struct Var {
/* The unexpanded value of the variable. */
Buffer val;
- /* Miscellaneous status flags. */
- VarFlags flags;
+
+ /* The variable came from the command line. */
+ bool fromCmd: 1;
+
+ /*
+ * The variable comes from the environment.
+ * These variables are not registered in any GNode, therefore they
+ * must be freed as soon as they are not used anymore.
+ */
+ bool fromEnv: 1;
+
+ /*
+ * The variable value cannot be changed anymore, and the variable
+ * cannot be deleted. Any attempts to do so are silently ignored,
+ * they are logged with -dv though.
+ *
+ * See VAR_SET_READONLY.
+ */
+ bool readOnly: 1;
+
+ /*
+ * The variable's value is currently being used by Var_Parse or
+ * Var_Subst. This marker is used to avoid endless recursion.
+ */
+ bool inUse: 1;
+
+ /*
+ * The variable is exported to the environment, to be used by child
+ * processes.
+ */
+ bool exported: 1;
+
+ /*
+ * At the point where this variable was exported, it contained an
+ * unresolved reference to another variable. Before any child
+ * process is started, it needs to be exported again, in the hope
+ * that the referenced variable can then be resolved.
+ */
+ bool reexport: 1;
} Var;
/*
- * Exporting vars is expensive so skip it if we can
+ * Exporting variables is expensive and may leak memory, so skip it if we
+ * can.
+ *
+ * To avoid this, it might be worth encapsulating the environment variables
+ * in a separate data structure called EnvVars.
*/
typedef enum VarExportedMode {
VAR_EXPORTED_NONE,
@@ -233,37 +234,37 @@ typedef enum VarExportedMode {
} VarExportedMode;
typedef enum UnexportWhat {
+ /* Unexport the variables given by name. */
UNEXPORT_NAMED,
+ /*
+ * Unexport all globals previously exported, but keep the environment
+ * inherited from the parent.
+ */
UNEXPORT_ALL,
+ /*
+ * Unexport all globals previously exported and clear the environment
+ * inherited from the parent.
+ */
UNEXPORT_ENV
} UnexportWhat;
/* Flags for pattern matching in the :S and :C modifiers */
-typedef struct VarPatternFlags {
-
- /* Replace as often as possible ('g') */
- Boolean subGlobal: 1;
- /* Replace only once ('1') */
- Boolean subOnce: 1;
- /* Match at start of word ('^') */
- Boolean anchorStart: 1;
- /* Match at end of word ('$') */
- Boolean anchorEnd: 1;
-} VarPatternFlags;
-
-/* SepBuf is a string being built from words, interleaved with separators. */
+typedef struct PatternFlags {
+ bool subGlobal: 1; /* 'g': replace as often as possible */
+ bool subOnce: 1; /* '1': replace only once */
+ bool anchorStart: 1; /* '^': match only at start of word */
+ bool anchorEnd: 1; /* '$': match only at end of word */
+} PatternFlags;
+
+/* SepBuf builds a string from words interleaved with separators. */
typedef struct SepBuf {
Buffer buf;
- Boolean needSep;
+ bool needSep;
/* Usually ' ', but see the ':ts' modifier. */
char sep;
} SepBuf;
-ENUM_FLAGS_RTTI_4(VarEvalFlags,
- VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR,
- VARE_KEEP_UNDEF);
-
/*
* This lets us tell if we have replaced the original environ
* (which we cannot free).
@@ -282,6 +283,8 @@ char var_Error[] = "";
* a case where VARE_UNDEFERR is not set. This undefined variable is
* typically a dynamic variable such as ${.TARGET}, whose expansion needs to
* be deferred until it is defined in an actual target.
+ *
+ * See VARE_EVAL_KEEP_UNDEF.
*/
static char varUndefined[] = "";
@@ -289,12 +292,12 @@ static char varUndefined[] = "";
* Traditionally this make consumed $$ during := like any other expansion.
* Other make's do not, and this make follows straight since 2016-01-09.
*
- * This knob allows controlling the behavior.
- * FALSE to consume $$ during := assignment.
- * TRUE to preserve $$ during := assignment.
+ * This knob allows controlling the behavior:
+ * false to consume $$ during := assignment.
+ * true to preserve $$ during := assignment.
*/
#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
-static Boolean save_dollars = FALSE;
+static bool save_dollars = false;
/*
* A scope collects variable names and their values.
@@ -321,64 +324,59 @@ GNode *SCOPE_CMDLINE;
GNode *SCOPE_GLOBAL;
GNode *SCOPE_INTERNAL;
-ENUM_FLAGS_RTTI_6(VarFlags,
- VAR_IN_USE, VAR_FROM_ENV,
- VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY);
-
static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
+static const char *VarEvalMode_Name[] = {
+ "parse-only",
+ "eval",
+ "eval-defined",
+ "eval-keep-dollar",
+ "eval-keep-undefined",
+ "eval-keep-dollar-and-undefined",
+};
+
static Var *
-VarNew(FStr name, const char *value, VarFlags flags)
+VarNew(FStr name, const char *value, bool fromEnv, bool readOnly)
{
size_t value_len = strlen(value);
Var *var = bmake_malloc(sizeof *var);
var->name = name;
Buf_InitSize(&var->val, value_len + 1);
Buf_AddBytes(&var->val, value, value_len);
- var->flags = flags;
+ var->fromCmd = false;
+ var->fromEnv = fromEnv;
+ var->readOnly = readOnly;
+ var->inUse = false;
+ var->exported = false;
+ var->reexport = false;
return var;
}
-static const char *
-CanonicalVarname(const char *name)
-{
- if (*name == '.' && ch_isupper(name[1])) {
- switch (name[1]) {
- case 'A':
- if (strcmp(name, ".ALLSRC") == 0)
- name = ALLSRC;
- if (strcmp(name, ".ARCHIVE") == 0)
- name = ARCHIVE;
- break;
- case 'I':
- if (strcmp(name, ".IMPSRC") == 0)
- name = IMPSRC;
- break;
- case 'M':
- if (strcmp(name, ".MEMBER") == 0)
- name = MEMBER;
- break;
- case 'O':
- if (strcmp(name, ".OODATE") == 0)
- name = OODATE;
- break;
- case 'P':
- if (strcmp(name, ".PREFIX") == 0)
- name = PREFIX;
- break;
- case 'S':
- if (strcmp(name, ".SHELL") == 0) {
- if (shellPath == NULL)
- Shell_Init();
- }
- break;
- case 'T':
- if (strcmp(name, ".TARGET") == 0)
- name = TARGET;
- break;
- }
- }
+static Substring
+CanonicalVarname(Substring name)
+{
+
+ if (!(Substring_Length(name) > 0 && name.start[0] == '.'))
+ return name;
+
+ if (Substring_Equals(name, ".ALLSRC"))
+ return Substring_InitStr(ALLSRC);
+ if (Substring_Equals(name, ".ARCHIVE"))
+ return Substring_InitStr(ARCHIVE);
+ if (Substring_Equals(name, ".IMPSRC"))
+ return Substring_InitStr(IMPSRC);
+ if (Substring_Equals(name, ".MEMBER"))
+ return Substring_InitStr(MEMBER);
+ if (Substring_Equals(name, ".OODATE"))
+ return Substring_InitStr(OODATE);
+ if (Substring_Equals(name, ".PREFIX"))
+ return Substring_InitStr(PREFIX);
+ if (Substring_Equals(name, ".TARGET"))
+ return Substring_InitStr(TARGET);
+
+ if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
+ Shell_Init();
/* GNU make has an additional alias $^ == ${.ALLSRC}. */
@@ -386,9 +384,9 @@ CanonicalVarname(const char *name)
}
static Var *
-GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
+GNode_FindVar(GNode *scope, Substring varname, unsigned int hash)
{
- return HashTable_FindValueHash(&scope->vars, varname, hash);
+ return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash);
}
/*
@@ -397,7 +395,7 @@ GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
* Input:
* name name to find, is not expanded any further
* scope scope in which to look first
- * elsewhere TRUE to look in other scopes as well
+ * elsewhere true to look in other scopes as well
*
* Results:
* The found variable, or NULL if the variable does not exist.
@@ -405,29 +403,19 @@ GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
* VarFreeEnv after use.
*/
static Var *
-VarFind(const char *name, GNode *scope, Boolean elsewhere)
+VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
{
Var *var;
unsigned int nameHash;
- /*
- * If the variable name begins with a '.', it could very well be
- * one of the local ones. We check the name against all the local
- * variables and substitute the short version in for 'name' if it
- * matches one of them.
- */
+ /* Replace '.TARGET' with '@', likewise for other local variables. */
name = CanonicalVarname(name);
- nameHash = Hash_Hash(name);
+ nameHash = Hash_Substring(name);
- /* First look for the variable in the given scope. */
var = GNode_FindVar(scope, name, nameHash);
if (!elsewhere)
return var;
- /*
- * The variable was not found in the given scope.
- * Now look for it in the other scopes as well.
- */
if (var == NULL && scope != SCOPE_CMDLINE)
var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
@@ -440,12 +428,19 @@ VarFind(const char *name, GNode *scope, Boolean elsewhere)
}
if (var == NULL) {
- char *env;
+ FStr envName;
+ const char *envValue;
- if ((env = getenv(name)) != NULL) {
- char *varname = bmake_strdup(name);
- return VarNew(FStr_InitOwn(varname), env, VAR_FROM_ENV);
- }
+ /*
+ * TODO: try setting an environment variable with the empty
+ * name, which should be technically possible, just to see
+ * how make reacts. All .for loops should be broken then.
+ */
+ envName = Substring_Str(name);
+ envValue = getenv(envName.str);
+ if (envValue != NULL)
+ return VarNew(envName, envValue, true, false);
+ FStr_Done(&envName);
if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
@@ -461,43 +456,35 @@ VarFind(const char *name, GNode *scope, Boolean elsewhere)
return var;
}
-/*
- * If the variable is an environment variable, free it.
- *
- * Input:
- * v the variable
- * freeValue true if the variable value should be freed as well
- *
- * Results:
- * TRUE if it is an environment variable, FALSE otherwise.
- */
-static Boolean
-VarFreeEnv(Var *v, Boolean freeValue)
+/* TODO: Replace these calls with VarFindSubstring, as far as possible. */
+static Var *
+VarFind(const char *name, GNode *scope, bool elsewhere)
+{
+ return VarFindSubstring(Substring_InitStr(name), scope, elsewhere);
+}
+
+/* If the variable is an environment variable, free it, including its value. */
+static void
+VarFreeEnv(Var *v)
{
- if (!(v->flags & VAR_FROM_ENV))
- return FALSE;
+ if (!v->fromEnv)
+ return;
FStr_Done(&v->name);
- if (freeValue)
- Buf_Done(&v->val);
- else
- Buf_DoneData(&v->val);
+ Buf_Done(&v->val);
free(v);
- return TRUE;
}
-/*
- * Add a new variable of the given name and value to the given scope.
- * The name and val arguments are duplicated so they may safely be freed.
- */
-static void
-VarAdd(const char *name, const char *val, GNode *scope, VarSetFlags flags)
+/* Add a new variable of the given name and value to the given scope. */
+static Var *
+VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
{
HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
- Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), val,
- flags & VAR_SET_READONLY ? VAR_READONLY : VAR_NONE);
+ Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
+ false, (flags & VAR_SET_READONLY) != 0);
HashEntry_Set(he, v);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, value);
+ return v;
}
/*
@@ -516,8 +503,8 @@ Var_Delete(GNode *scope, const char *varname)
}
DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
- v = HashEntry_Get(he);
- if (v->flags & VAR_EXPORTED)
+ v = he->value;
+ if (v->exported)
unsetenv(v->name.str);
if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
var_exportedVars = VAR_EXPORTED_NONE;
@@ -573,7 +560,7 @@ Var_Undef(const char *arg)
return;
}
- varnames = Str_Words(expanded, FALSE);
+ varnames = Str_Words(expanded, false);
if (varnames.len == 1 && varnames.words[0][0] == '\0')
varnames.len = 0;
@@ -586,13 +573,13 @@ Var_Undef(const char *arg)
free(expanded);
}
-static Boolean
+static bool
MayExport(const char *name)
{
if (name[0] == '.')
- return FALSE; /* skip internals */
+ return false; /* skip internals */
if (name[0] == '-')
- return FALSE; /* skip misnamed variables */
+ return false; /* skip misnamed variables */
if (name[1] == '\0') {
/*
* A single char.
@@ -605,34 +592,34 @@ MayExport(const char *name)
case '%':
case '*':
case '!':
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
-static Boolean
+static bool
ExportVarEnv(Var *v)
{
const char *name = v->name.str;
char *val = v->val.data;
char *expr;
- if ((v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
- return FALSE; /* nothing to do */
+ if (v->exported && !v->reexport)
+ return false; /* nothing to do */
if (strchr(val, '$') == NULL) {
- if (!(v->flags & VAR_EXPORTED))
+ if (!v->exported)
setenv(name, val, 1);
- return TRUE;
+ return true;
}
- if (v->flags & VAR_IN_USE) {
+ if (v->inUse) {
/*
* We recursed while exporting in a child.
* This isn't going to end well, just skip it.
*/
- return FALSE;
+ return false;
}
/* XXX: name is injected without escaping it */
@@ -642,59 +629,58 @@ ExportVarEnv(Var *v)
setenv(name, val, 1);
free(val);
free(expr);
- return TRUE;
+ return true;
}
-static Boolean
+static bool
ExportVarPlain(Var *v)
{
if (strchr(v->val.data, '$') == NULL) {
setenv(v->name.str, v->val.data, 1);
- v->flags |= VAR_EXPORTED;
- v->flags &= ~(unsigned)VAR_REEXPORT;
- return TRUE;
+ v->exported = true;
+ v->reexport = false;
+ return true;
}
/*
* Flag the variable as something we need to re-export.
* No point actually exporting it now though,
* the child process can do it at the last minute.
+ * Avoid calling setenv more often than necessary since it can leak.
*/
- v->flags |= VAR_EXPORTED | VAR_REEXPORT;
- return TRUE;
+ v->exported = true;
+ v->reexport = true;
+ return true;
}
-static Boolean
+static bool
ExportVarLiteral(Var *v)
{
- if ((v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
- return FALSE;
+ if (v->exported && !v->reexport)
+ return false;
- if (!(v->flags & VAR_EXPORTED))
+ if (!v->exported)
setenv(v->name.str, v->val.data, 1);
- return TRUE;
+ return true;
}
/*
- * Export a single variable.
+ * Mark a single variable to be exported later for subprocesses.
*
- * We ignore make internal variables (those which start with '.').
- * Also we jump through some hoops to avoid calling setenv
- * more than necessary since it can leak.
- * We only manipulate flags of vars if 'parent' is set.
+ * Internal variables (those starting with '.') are not exported.
*/
-static Boolean
+static bool
ExportVar(const char *name, VarExportMode mode)
{
Var *v;
if (!MayExport(name))
- return FALSE;
+ return false;
- v = VarFind(name, SCOPE_GLOBAL, FALSE);
+ v = VarFind(name, SCOPE_GLOBAL, false);
if (v == NULL)
- return FALSE;
+ return false;
if (mode == VEM_ENV)
return ExportVarEnv(v);
@@ -719,7 +705,7 @@ Var_ReexportVars(void)
* We allow the makefiles to update MAKELEVEL and ensure
* children see a correctly incremented value.
*/
- char tmp[BUFSIZ];
+ char tmp[21];
snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
setenv(MAKE_LEVEL_ENV, tmp, 1);
@@ -729,7 +715,7 @@ Var_ReexportVars(void)
if (var_exportedVars == VAR_EXPORTED_ALL) {
HashIter hi;
- /* Ouch! Exporting all variables at once is crazy... */
+ /* Ouch! Exporting all variables at once is crazy. */
HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
while (HashIter_Next(&hi) != NULL) {
Var *var = hi.entry->value;
@@ -742,7 +728,7 @@ Var_ReexportVars(void)
&xvarnames);
/* TODO: handle errors */
if (xvarnames[0] != '\0') {
- Words varnames = Str_Words(xvarnames, FALSE);
+ Words varnames = Str_Words(xvarnames, false);
size_t i;
for (i = 0; i < varnames.len; i++)
@@ -753,9 +739,10 @@ Var_ReexportVars(void)
}
static void
-ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
+ExportVars(const char *varnames, bool isExport, VarExportMode mode)
+/* TODO: try to combine the parameters 'isExport' and 'mode'. */
{
- Words words = Str_Words(varnames, FALSE);
+ Words words = Str_Words(varnames, false);
size_t i;
if (words.len == 1 && words.words[0][0] == '\0')
@@ -776,7 +763,7 @@ ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
}
static void
-ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode)
+ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode)
{
char *xvarnames;
@@ -795,13 +782,13 @@ Var_Export(VarExportMode mode, const char *varnames)
return;
}
- ExportVarsExpand(varnames, TRUE, mode);
+ ExportVarsExpand(varnames, true, mode);
}
void
Var_ExportVars(const char *varnames)
{
- ExportVarsExpand(varnames, FALSE, VEM_PLAIN);
+ ExportVarsExpand(varnames, false, VEM_PLAIN);
}
@@ -834,7 +821,7 @@ ClearEnv(void)
}
static void
-GetVarnamesToUnexport(Boolean isEnv, const char *arg,
+GetVarnamesToUnexport(bool isEnv, const char *arg,
FStr *out_varnames, UnexportWhat *out_what)
{
UnexportWhat what;
@@ -845,6 +832,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
Parse_Error(PARSE_FATAL,
"The directive .unexport-env does not take "
"arguments");
+ /* continue anyway */
}
what = UNEXPORT_ENV;
@@ -870,17 +858,17 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
static void
UnexportVar(const char *varname, UnexportWhat what)
{
- Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE);
+ Var *v = VarFind(varname, SCOPE_GLOBAL, false);
if (v == NULL) {
DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname);
return;
}
DEBUG1(VAR, "Unexporting \"%s\"\n", varname);
- if (what != UNEXPORT_ENV &&
- (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
+ if (what != UNEXPORT_ENV && v->exported && !v->reexport)
unsetenv(v->name.str);
- v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT);
+ v->exported = false;
+ v->reexport = false;
if (what == UNEXPORT_NAMED) {
/* Remove the variable names from .MAKE.EXPORTED. */
@@ -905,7 +893,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
if (what == UNEXPORT_ENV)
ClearEnv();
- words = Str_Words(varnames->str, FALSE);
+ words = Str_Words(varnames->str, false);
for (i = 0; i < words.len; i++) {
const char *varname = words.words[i];
UnexportVar(varname, what);
@@ -922,7 +910,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
* str must have the form "unexport[-env] varname...".
*/
void
-Var_UnExport(Boolean isEnv, const char *arg)
+Var_UnExport(bool isEnv, const char *arg)
{
UnexportWhat what;
FStr varnames;
@@ -932,6 +920,32 @@ Var_UnExport(Boolean isEnv, const char *arg)
FStr_Done(&varnames);
}
+/*
+ * When there is a variable of the same name in the command line scope, the
+ * global variable would not be visible anywhere. Therefore there is no
+ * point in setting it at all.
+ *
+ * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
+ */
+static bool
+ExistsInCmdline(const char *name, const char *val)
+{
+ Var *v;
+
+ v = VarFind(name, SCOPE_CMDLINE, false);
+ if (v == NULL)
+ return false;
+
+ if (v->fromCmd) {
+ DEBUG3(VAR, "%s: %s = %s ignored!\n",
+ SCOPE_GLOBAL->name, name, val);
+ return true;
+ }
+
+ VarFreeEnv(v);
+ return false;
+}
+
/* Set the variable to the value; the name is not expanded. */
void
Var_SetWithFlags(GNode *scope, const char *name, const char *val,
@@ -945,58 +959,48 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
return;
}
- if (scope == SCOPE_GLOBAL) {
- v = VarFind(name, SCOPE_CMDLINE, FALSE);
- if (v != NULL) {
- if (v->flags & VAR_FROM_CMD) {
- DEBUG3(VAR, "%s:%s = %s ignored!\n",
- scope->name, name, val);
- return;
- }
- VarFreeEnv(v, TRUE);
- }
- }
+ if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
+ return;
/*
* Only look for a variable in the given scope since anything set
* here will override anything in a lower scope, so there's not much
- * point in searching them all just to save a bit of memory...
+ * point in searching them all.
*/
- v = VarFind(name, scope, FALSE);
+ v = VarFind(name, scope, false);
if (v == NULL) {
if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
/*
* This var would normally prevent the same name being
* added to SCOPE_GLOBAL, so delete it from there if
* needed. Otherwise -V name may show the wrong value.
+ *
+ * See ExistsInCmdline.
*/
- /* XXX: name is expanded for the second time */
- Var_DeleteExpand(SCOPE_GLOBAL, name);
+ Var_Delete(SCOPE_GLOBAL, name);
}
- VarAdd(name, val, scope, flags);
+ v = VarAdd(name, val, scope, flags);
} else {
- if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) {
- DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n",
+ if (v->readOnly && !(flags & VAR_SET_READONLY)) {
+ DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
scope->name, name, val);
return;
}
Buf_Empty(&v->val);
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
- if (v->flags & VAR_EXPORTED)
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, val);
+ if (v->exported)
ExportVar(name, VEM_PLAIN);
}
+
/*
* Any variables given on the command line are automatically exported
- * to the environment (as per POSIX standard)
- * Other than internals.
+ * to the environment (as per POSIX standard), except for internals.
*/
if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
name[0] != '.') {
- if (v == NULL)
- v = VarFind(name, scope, FALSE); /* we just added it */
- v->flags |= VAR_FROM_CMD;
+ v->fromCmd = true;
/*
* If requested, don't export these in the environment
@@ -1006,14 +1010,18 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
*/
if (!opts.varNoExportEnv)
setenv(name, val, 1);
+ /* XXX: What about .MAKE.EXPORTED? */
+ /* XXX: Why not just mark the variable for needing export,
+ * as in ExportVarPlain? */
Global_Append(MAKEOVERRIDES, name);
}
+
if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
save_dollars = ParseBoolean(val, save_dollars);
if (v != NULL)
- VarFreeEnv(v, TRUE);
+ VarFreeEnv(v);
}
/* See Var_Set for documentation. */
@@ -1034,8 +1042,9 @@ Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val,
}
if (varname.str[0] == '\0') {
- DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) "
- "name expands to empty string - ignored\n",
+ DEBUG2(VAR,
+ "Var_SetExpand: variable name \"%s\" expands "
+ "to empty string, with value \"%s\" - ignored\n",
unexpanded_name, val);
} else
Var_SetWithFlags(scope, varname.str, val, flags);
@@ -1099,23 +1108,23 @@ Var_Append(GNode *scope, const char *name, const char *val)
if (v == NULL) {
Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
- } else if (v->flags & VAR_READONLY) {
+ } else if (v->readOnly) {
DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
name);
- } else if (scope == SCOPE_CMDLINE || !(v->flags & VAR_FROM_CMD)) {
+ } else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
Buf_AddByte(&v->val, ' ');
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data);
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data);
- if (v->flags & VAR_FROM_ENV) {
+ if (v->fromEnv) {
/*
* If the original variable came from the environment,
* we have to install it in the global scope (we
* could place it in the environment, but then we
* should provide a way to export other variables...)
*/
- v->flags &= ~(unsigned)VAR_FROM_ENV;
+ v->fromEnv = false;
/*
* This is the only place where a variable is
* created whose v->name is not the same as
@@ -1149,28 +1158,28 @@ Var_Append(GNode *scope, const char *name, const char *val)
void
Var_AppendExpand(GNode *scope, const char *name, const char *val)
{
- char *name_freeIt = NULL;
+ FStr xname = FStr_InitRefer(name);
assert(val != NULL);
if (strchr(name, '$') != NULL) {
- const char *unexpanded_name = name;
- (void)Var_Subst(name, scope, VARE_WANTRES, &name_freeIt);
+ char *expanded;
+ (void)Var_Subst(name, scope, VARE_WANTRES, &expanded);
/* TODO: handle errors */
- name = name_freeIt;
- if (name[0] == '\0') {
- /* TODO: update function name in the debug message */
- DEBUG2(VAR, "Var_Append(\"%s\", \"%s\", ...) "
- "name expands to empty string - ignored\n",
- unexpanded_name, val);
- free(name_freeIt);
+ xname = FStr_InitOwn(expanded);
+ if (expanded[0] == '\0') {
+ DEBUG2(VAR,
+ "Var_AppendExpand: variable name \"%s\" expands "
+ "to empty string, with value \"%s\" - ignored\n",
+ name, val);
+ FStr_Done(&xname);
return;
}
}
- Var_Append(scope, name, val);
+ Var_Append(scope, xname.str, val);
- free(name_freeIt);
+ FStr_Done(&xname);
}
void
@@ -1179,15 +1188,15 @@ Global_Append(const char *name, const char *value)
Var_Append(SCOPE_GLOBAL, name, value);
}
-Boolean
+bool
Var_Exists(GNode *scope, const char *name)
{
- Var *v = VarFind(name, scope, TRUE);
+ Var *v = VarFind(name, scope, true);
if (v == NULL)
- return FALSE;
+ return false;
- (void)VarFreeEnv(v, TRUE);
- return TRUE;
+ VarFreeEnv(v);
+ return true;
}
/*
@@ -1198,11 +1207,11 @@ Var_Exists(GNode *scope, const char *name)
* name Variable to find, is expanded once
* scope Scope in which to start search
*/
-Boolean
+bool
Var_ExistsExpand(GNode *scope, const char *name)
{
FStr varname = FStr_InitRefer(name);
- Boolean exists;
+ bool exists;
if (strchr(varname.str, '$') != NULL) {
char *expanded;
@@ -1226,22 +1235,25 @@ Var_ExistsExpand(GNode *scope, const char *name)
*
* Results:
* The value if the variable exists, NULL if it doesn't.
- * If the returned value is not NULL, the caller must free
- * out_freeIt when the returned value is no longer needed.
+ * The value is valid until the next modification to any variable.
*/
FStr
Var_Value(GNode *scope, const char *name)
{
- Var *v = VarFind(name, scope, TRUE);
+ Var *v = VarFind(name, scope, true);
char *value;
if (v == NULL)
return FStr_InitRefer(NULL);
- value = v->val.data;
- return VarFreeEnv(v, FALSE)
- ? FStr_InitOwn(value)
- : FStr_InitRefer(value);
+ if (!v->fromEnv)
+ return FStr_InitRefer(v->val.data);
+
+ /* Since environment variables are short-lived, free it now. */
+ FStr_Done(&v->name);
+ value = Buf_DoneData(&v->val);
+ free(v);
+ return FStr_InitOwn(value);
}
/*
@@ -1251,23 +1263,59 @@ Var_Value(GNode *scope, const char *name)
const char *
GNode_ValueDirect(GNode *gn, const char *name)
{
- Var *v = VarFind(name, gn, FALSE);
+ Var *v = VarFind(name, gn, false);
return v != NULL ? v->val.data : NULL;
}
+static VarEvalMode
+VarEvalMode_WithoutKeepDollar(VarEvalMode emode)
+{
+ if (emode == VARE_KEEP_DOLLAR_UNDEF)
+ return VARE_EVAL_KEEP_UNDEF;
+ if (emode == VARE_EVAL_KEEP_DOLLAR)
+ return VARE_WANTRES;
+ return emode;
+}
+
+static VarEvalMode
+VarEvalMode_UndefOk(VarEvalMode emode)
+{
+ return emode == VARE_UNDEFERR ? VARE_WANTRES : emode;
+}
+
+static bool
+VarEvalMode_ShouldEval(VarEvalMode emode)
+{
+ return emode != VARE_PARSE_ONLY;
+}
+
+static bool
+VarEvalMode_ShouldKeepUndef(VarEvalMode emode)
+{
+ return emode == VARE_EVAL_KEEP_UNDEF ||
+ emode == VARE_KEEP_DOLLAR_UNDEF;
+}
+
+static bool
+VarEvalMode_ShouldKeepDollar(VarEvalMode emode)
+{
+ return emode == VARE_EVAL_KEEP_DOLLAR ||
+ emode == VARE_KEEP_DOLLAR_UNDEF;
+}
+
static void
SepBuf_Init(SepBuf *buf, char sep)
{
Buf_InitSize(&buf->buf, 32);
- buf->needSep = FALSE;
+ buf->needSep = false;
buf->sep = sep;
}
static void
SepBuf_Sep(SepBuf *buf)
{
- buf->needSep = TRUE;
+ buf->needSep = true;
}
static void
@@ -1277,7 +1325,7 @@ SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
return;
if (buf->needSep && buf->sep != '\0') {
Buf_AddByte(&buf->buf, buf->sep);
- buf->needSep = FALSE;
+ buf->needSep = false;
}
Buf_AddBytes(&buf->buf, mem, mem_size);
}
@@ -1294,6 +1342,12 @@ SepBuf_AddStr(SepBuf *buf, const char *str)
SepBuf_AddBytes(buf, str, strlen(str));
}
+static void
+SepBuf_AddSubstring(SepBuf *buf, Substring sub)
+{
+ SepBuf_AddBytesBetween(buf, sub.start, sub.end);
+}
+
static char *
SepBuf_DoneData(SepBuf *buf)
{
@@ -1306,10 +1360,14 @@ SepBuf_DoneData(SepBuf *buf)
* and typically adds a modification of this word to the buffer. It may also
* do nothing or add several words.
*
- * For example, in ${:Ua b c:M*2}, the callback is called 3 times, once for
- * each word of "a b c".
+ * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the
+ * callback is called 3 times, once for "a", "b" and "c".
+ *
+ * Some ModifyWord functions assume that they are always passed a
+ * null-terminated substring, which is currently guaranteed but may change in
+ * the future.
*/
-typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
+typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
/*
@@ -1318,13 +1376,9 @@ typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
*/
/*ARGSUSED*/
static void
-ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *slash = strrchr(word, '/');
- if (slash != NULL)
- SepBuf_AddBytesBetween(buf, word, slash);
- else
- SepBuf_AddStr(buf, ".");
+ SepBuf_AddSubstring(buf, Substring_Dirname(word));
}
/*
@@ -1333,9 +1387,9 @@ ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
*/
/*ARGSUSED*/
static void
-ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- SepBuf_AddStr(buf, str_basename(word));
+ SepBuf_AddSubstring(buf, Substring_Basename(word));
}
/*
@@ -1344,24 +1398,26 @@ ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
*/
/*ARGSUSED*/
static void
-ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = strrchr(word, '.');
+ const char *lastDot = Substring_LastIndex(word, '.');
if (lastDot != NULL)
- SepBuf_AddStr(buf, lastDot + 1);
+ SepBuf_AddBytesBetween(buf, lastDot + 1, word.end);
}
/*
* Callback for ModifyWords to implement the :R modifier.
- * Add the basename of the given word to the buffer.
+ * Add the filename without extension of the given word to the buffer.
*/
/*ARGSUSED*/
static void
-ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = strrchr(word, '.');
- size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word);
- SepBuf_AddBytes(buf, word, len);
+ const char *lastDot, *end;
+
+ lastDot = Substring_LastIndex(word, '.');
+ end = lastDot != NULL ? lastDot : word.end;
+ SepBuf_AddBytesBetween(buf, word.start, end);
}
/*
@@ -1369,12 +1425,13 @@ ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
* Place the word in the buffer if it matches the given pattern.
*/
static void
-ModifyWord_Match(const char *word, SepBuf *buf, void *data)
+ModifyWord_Match(Substring word, SepBuf *buf, void *data)
{
const char *pattern = data;
- DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern);
- if (Str_Match(word, pattern))
- SepBuf_AddStr(buf, word);
+
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ if (Str_Match(word.start, pattern))
+ SepBuf_AddSubstring(buf, word);
}
/*
@@ -1382,194 +1439,146 @@ ModifyWord_Match(const char *word, SepBuf *buf, void *data)
* Place the word in the buffer if it doesn't match the given pattern.
*/
static void
-ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
+ModifyWord_NoMatch(Substring word, SepBuf *buf, void *data)
{
const char *pattern = data;
- if (!Str_Match(word, pattern))
- SepBuf_AddStr(buf, word);
-}
-
-#ifdef SYSVVARSUB
-
-/*
- * Check word against pattern for a match (% is a wildcard).
- *
- * Input:
- * word Word to examine
- * pattern Pattern to examine against
- *
- * Results:
- * Returns the start of the match, or NULL.
- * out_match_len returns the length of the match, if any.
- * out_hasPercent returns whether the pattern contains a percent.
- */
-static const char *
-SysVMatch(const char *word, const char *pattern,
- size_t *out_match_len, Boolean *out_hasPercent)
-{
- const char *p = pattern;
- const char *w = word;
- const char *percent;
- size_t w_len;
- size_t p_len;
- const char *w_tail;
-
- *out_hasPercent = FALSE;
- percent = strchr(p, '%');
- if (percent != NULL) { /* ${VAR:...%...=...} */
- *out_hasPercent = TRUE;
- if (w[0] == '\0')
- return NULL; /* empty word does not match pattern */
-
- /* check that the prefix matches */
- for (; p != percent && *w != '\0' && *w == *p; w++, p++)
- continue;
- if (p != percent)
- return NULL; /* No match */
-
- p++; /* Skip the percent */
- if (*p == '\0') {
- /* No more pattern, return the rest of the string */
- *out_match_len = strlen(w);
- return w;
- }
- }
-
- /* Test whether the tail matches */
- w_len = strlen(w);
- p_len = strlen(p);
- if (w_len < p_len)
- return NULL;
-
- w_tail = w + w_len - p_len;
- if (memcmp(p, w_tail, p_len) != 0)
- return NULL;
- *out_match_len = (size_t)(w_tail - w);
- return w;
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ if (!Str_Match(word.start, pattern))
+ SepBuf_AddSubstring(buf, word);
}
-struct ModifyWord_SYSVSubstArgs {
+#ifdef SYSVVARSUB
+struct ModifyWord_SysVSubstArgs {
GNode *scope;
- const char *lhs;
+ Substring lhsPrefix;
+ bool lhsPercent;
+ Substring lhsSuffix;
const char *rhs;
};
/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
static void
-ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
+ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
{
- const struct ModifyWord_SYSVSubstArgs *args = data;
- char *rhs_expanded;
- const char *rhs;
+ const struct ModifyWord_SysVSubstArgs *args = data;
+ FStr rhs;
+ char *rhsExp;
const char *percent;
- size_t match_len;
- Boolean lhsPercent;
- const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent);
- if (match == NULL) {
- SepBuf_AddStr(buf, word);
+ if (Substring_IsEmpty(word))
return;
- }
- /*
- * Append rhs to the buffer, substituting the first '%' with the
- * match, but only if the lhs had a '%' as well.
- */
+ if (!Substring_HasPrefix(word, args->lhsPrefix))
+ goto no_match;
+ if (!Substring_HasSuffix(word, args->lhsSuffix))
+ goto no_match;
- (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded);
- /* TODO: handle errors */
+ rhs = FStr_InitRefer(args->rhs);
+ if (strchr(rhs.str, '$') != NULL) {
+ (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhsExp);
+ /* TODO: handle errors */
+ rhs = FStr_InitOwn(rhsExp);
+ }
- rhs = rhs_expanded;
- percent = strchr(rhs, '%');
+ percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
- if (percent != NULL && lhsPercent) {
- /* Copy the prefix of the replacement pattern */
- SepBuf_AddBytesBetween(buf, rhs, percent);
- rhs = percent + 1;
- }
- if (percent != NULL || !lhsPercent)
- SepBuf_AddBytes(buf, match, match_len);
+ if (percent != NULL)
+ SepBuf_AddBytesBetween(buf, rhs.str, percent);
+ if (percent != NULL || !args->lhsPercent)
+ SepBuf_AddBytesBetween(buf,
+ word.start + Substring_Length(args->lhsPrefix),
+ word.end - Substring_Length(args->lhsSuffix));
+ SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
- /* Append the suffix of the replacement pattern */
- SepBuf_AddStr(buf, rhs);
+ FStr_Done(&rhs);
+ return;
- free(rhs_expanded);
+no_match:
+ SepBuf_AddSubstring(buf, word);
}
#endif
struct ModifyWord_SubstArgs {
- const char *lhs;
- size_t lhsLen;
- const char *rhs;
- size_t rhsLen;
- VarPatternFlags pflags;
- Boolean matched;
+ Substring lhs;
+ Substring rhs;
+ PatternFlags pflags;
+ bool matched;
};
+static const char *
+Substring_Find(Substring haystack, Substring needle)
+{
+ size_t len, needleLen, i;
+
+ len = Substring_Length(haystack);
+ needleLen = Substring_Length(needle);
+ for (i = 0; i + needleLen <= len; i++)
+ if (memcmp(haystack.start + i, needle.start, needleLen) == 0)
+ return haystack.start + i;
+ return NULL;
+}
+
/*
* Callback for ModifyWords to implement the :S,from,to, modifier.
* Perform a string substitution on the given word.
*/
static void
-ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
+ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
{
- size_t wordLen = strlen(word);
struct ModifyWord_SubstArgs *args = data;
- const char *match;
+ size_t wordLen, lhsLen;
+ const char *wordEnd, *match;
+ wordLen = Substring_Length(word);
+ wordEnd = word.end;
if (args->pflags.subOnce && args->matched)
goto nosub;
+ lhsLen = Substring_Length(args->lhs);
if (args->pflags.anchorStart) {
- if (wordLen < args->lhsLen ||
- memcmp(word, args->lhs, args->lhsLen) != 0)
+ if (wordLen < lhsLen ||
+ memcmp(word.start, args->lhs.start, lhsLen) != 0)
goto nosub;
- if ((args->pflags.anchorEnd) && wordLen != args->lhsLen)
+ if (args->pflags.anchorEnd && wordLen != lhsLen)
goto nosub;
/* :S,^prefix,replacement, or :S,^whole$,replacement, */
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- SepBuf_AddBytes(buf, word + args->lhsLen,
- wordLen - args->lhsLen);
- args->matched = TRUE;
+ SepBuf_AddSubstring(buf, args->rhs);
+ SepBuf_AddBytesBetween(buf, word.start + lhsLen, wordEnd);
+ args->matched = true;
return;
}
if (args->pflags.anchorEnd) {
- const char *start;
-
- if (wordLen < args->lhsLen)
+ if (wordLen < lhsLen)
goto nosub;
-
- start = word + (wordLen - args->lhsLen);
- if (memcmp(start, args->lhs, args->lhsLen) != 0)
+ if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0)
goto nosub;
/* :S,suffix$,replacement, */
- SepBuf_AddBytesBetween(buf, word, start);
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- args->matched = TRUE;
+ SepBuf_AddBytesBetween(buf, word.start, wordEnd - lhsLen);
+ SepBuf_AddSubstring(buf, args->rhs);
+ args->matched = true;
return;
}
- if (args->lhs[0] == '\0')
+ if (Substring_IsEmpty(args->lhs))
goto nosub;
/* unanchored case, may match more than once */
- while ((match = strstr(word, args->lhs)) != NULL) {
- SepBuf_AddBytesBetween(buf, word, match);
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- args->matched = TRUE;
- wordLen -= (size_t)(match - word) + args->lhsLen;
- word += (size_t)(match - word) + args->lhsLen;
- if (wordLen == 0 || !args->pflags.subGlobal)
+ while ((match = Substring_Find(word, args->lhs)) != NULL) {
+ SepBuf_AddBytesBetween(buf, word.start, match);
+ SepBuf_AddSubstring(buf, args->rhs);
+ args->matched = true;
+ word.start = match + lhsLen;
+ if (Substring_IsEmpty(word) || !args->pflags.subGlobal)
break;
}
nosub:
- SepBuf_AddBytes(buf, word, wordLen);
+ SepBuf_AddSubstring(buf, word);
}
#ifndef NO_REGEX
@@ -1587,9 +1596,9 @@ VarREError(int reerr, const regex_t *pat, const char *str)
struct ModifyWord_SubstRegexArgs {
regex_t re;
size_t nsub;
- char *replace;
- VarPatternFlags pflags;
- Boolean matched;
+ const char *replace;
+ PatternFlags pflags;
+ bool matched;
};
/*
@@ -1597,15 +1606,17 @@ struct ModifyWord_SubstRegexArgs {
* Perform a regex substitution on the given word.
*/
static void
-ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
+ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
{
struct ModifyWord_SubstRegexArgs *args = data;
int xrv;
- const char *wp = word;
- char *rp;
+ const char *wp;
+ const char *rp;
int flags = 0;
regmatch_t m[10];
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ wp = word.start;
if (args->pflags.subOnce && args->matched)
goto nosub;
@@ -1614,9 +1625,14 @@ tryagain:
switch (xrv) {
case 0:
- args->matched = TRUE;
+ args->matched = true;
SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
+ /*
+ * Replacement of regular expressions is not specified by
+ * POSIX, therefore implement it here.
+ */
+
for (rp = args->replace; *rp != '\0'; rp++) {
if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
SepBuf_AddBytes(buf, rp + 1, 1);
@@ -1643,9 +1659,11 @@ tryagain:
Error("No subexpression \\%u",
(unsigned)n);
} else if (m[n].rm_so == -1) {
- Error(
- "No match for subexpression \\%u",
- (unsigned)n);
+ if (opts.strict) {
+ Error(
+ "No match for subexpression \\%u",
+ (unsigned)n);
+ }
} else {
SepBuf_AddBytesBetween(buf,
wp + m[n].rm_so, wp + m[n].rm_eo);
@@ -1680,34 +1698,35 @@ tryagain:
struct ModifyWord_LoopArgs {
GNode *scope;
- char *tvar; /* name of temporary variable */
- char *str; /* string to expand */
- VarEvalFlags eflags;
+ const char *var; /* name of the temporary variable */
+ const char *body; /* string to expand */
+ VarEvalMode emode;
};
/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
static void
-ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
+ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
{
const struct ModifyWord_LoopArgs *args;
char *s;
- if (word[0] == '\0')
+ if (Substring_IsEmpty(word))
return;
args = data;
- /* XXX: The variable name should not be expanded here. */
- Var_SetExpandWithFlags(args->scope, args->tvar, word,
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ Var_SetWithFlags(args->scope, args->var, word.start,
VAR_SET_NO_EXPORT);
- (void)Var_Subst(args->str, args->scope, args->eflags, &s);
+ (void)Var_Subst(args->body, args->scope, args->emode, &s);
/* TODO: handle errors */
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
DEBUG4(VAR, "ModifyWord_Loop: "
"in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
- word, args->tvar, args->str, s);
+ word.start, args->var, args->body, s);
if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
- buf->needSep = FALSE;
+ buf->needSep = false;
SepBuf_AddStr(buf, s);
free(s);
}
@@ -1718,8 +1737,8 @@ ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
* It can also reverse the words.
*/
static char *
-VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
- int last)
+VarSelectWords(const char *str, int first, int last,
+ char sep, bool oneBigWord)
{
Words words;
int len, start, end, step;
@@ -1737,7 +1756,7 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
words.words[0] = words.freeIt;
words.words[1] = NULL;
} else {
- words = Str_Words(str, FALSE);
+ words = Str_Words(str, false);
}
/*
@@ -1779,60 +1798,18 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
*/
/*ARGSUSED*/
static void
-ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
+ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
{
struct stat st;
char rbuf[MAXPATHLEN];
+ const char *rp;
- const char *rp = cached_realpath(word, rbuf);
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ rp = cached_realpath(word.start, rbuf);
if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
- word = rp;
-
- SepBuf_AddStr(buf, word);
-}
-
-/*
- * Modify each of the words of the passed string using the given function.
- *
- * Input:
- * str String whose words should be modified
- * modifyWord Function that modifies a single word
- * modifyWord_args Custom arguments for modifyWord
- *
- * Results:
- * A string of all the words modified appropriately.
- */
-static char *
-ModifyWords(const char *str,
- ModifyWordsCallback modifyWord, void *modifyWord_args,
- Boolean oneBigWord, char sep)
-{
- SepBuf result;
- Words words;
- size_t i;
-
- if (oneBigWord) {
- SepBuf_Init(&result, sep);
- modifyWord(str, &result, modifyWord_args);
- return SepBuf_DoneData(&result);
- }
-
- SepBuf_Init(&result, sep);
-
- words = Str_Words(str, FALSE);
-
- DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
- str, (unsigned)words.len);
-
- for (i = 0; i < words.len; i++) {
- modifyWord(words.words[i], &result, modifyWord_args);
- if (result.buf.len > 0)
- SepBuf_Sep(&result);
- }
-
- Words_Free(words);
-
- return SepBuf_DoneData(&result);
+ SepBuf_AddStr(buf, rp);
+ else
+ SepBuf_AddSubstring(buf, word);
}
@@ -1846,7 +1823,7 @@ Words_JoinFree(Words words)
for (i = 0; i < words.len; i++) {
if (i != 0) {
- /* XXX: Use st->sep instead of ' ', for consistency. */
+ /* XXX: Use ch->sep instead of ' ', for consistency. */
Buf_AddByte(&buf, ' ');
}
Buf_AddStr(&buf, words.words[i]);
@@ -1857,51 +1834,31 @@ Words_JoinFree(Words words)
return Buf_DoneData(&buf);
}
-/* Remove adjacent duplicate words. */
-static char *
-VarUniq(const char *str)
-{
- Words words = Str_Words(str, FALSE);
-
- if (words.len > 1) {
- size_t i, j;
- for (j = 0, i = 1; i < words.len; i++)
- if (strcmp(words.words[i], words.words[j]) != 0 &&
- (++j != i))
- words.words[j] = words.words[i];
- words.len = j + 1;
- }
-
- return Words_JoinFree(words);
-}
-
/*
* Quote shell meta-characters and space characters in the string.
* If quoteDollar is set, also quote and double any '$' characters.
*/
-static char *
-VarQuote(const char *str, Boolean quoteDollar)
+static void
+VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
{
- Buffer buf;
- Buf_Init(&buf);
+ const char *p;
- for (; *str != '\0'; str++) {
- if (*str == '\n') {
+ LazyBuf_Init(buf, str);
+ for (p = str; *p != '\0'; p++) {
+ if (*p == '\n') {
const char *newline = Shell_GetNewline();
if (newline == NULL)
newline = "\\\n";
- Buf_AddStr(&buf, newline);
+ LazyBuf_AddStr(buf, newline);
continue;
}
- if (ch_isspace(*str) || is_shell_metachar((unsigned char)*str))
- Buf_AddByte(&buf, '\\');
- Buf_AddByte(&buf, *str);
- if (quoteDollar && *str == '$')
- Buf_AddStr(&buf, "\\$");
+ if (ch_isspace(*p) || is_shell_metachar((unsigned char)*p))
+ LazyBuf_Add(buf, '\\');
+ LazyBuf_Add(buf, *p);
+ if (quoteDollar && *p == '$')
+ LazyBuf_AddStr(buf, "\\$");
}
-
- return Buf_DoneData(&buf);
}
/*
@@ -1969,7 +1926,7 @@ VarHash(const char *str)
}
static char *
-VarStrftime(const char *fmt, Boolean zulu, time_t tim)
+VarStrftime(const char *fmt, bool zulu, time_t tim)
{
char buf[BUFSIZ];
@@ -1985,25 +1942,15 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
/*
* The ApplyModifier functions take an expression that is being evaluated.
- * Their task is to apply a single modifier to the expression.
- * To do this, they parse the modifier and its parameters from pp and apply
- * the parsed modifier to the current value of the expression, generating a
- * new value from it.
- *
- * The modifier typically lasts until the next ':', or a closing '}' or ')'
- * (taken from st->endc), or the end of the string (parse error).
- *
- * The high-level behavior of these functions is:
- *
- * 1. parse the modifier
- * 2. evaluate the modifier
- * 3. housekeeping
+ * Their task is to apply a single modifier to the expression. This involves
+ * parsing the modifier, evaluating it and finally updating the value of the
+ * expression.
*
* Parsing the modifier
*
* If parsing succeeds, the parsing position *pp is updated to point to the
* first character following the modifier, which typically is either ':' or
- * st->endc. The modifier doesn't have to check for this delimiter character,
+ * ch->endc. The modifier doesn't have to check for this delimiter character,
* this is done by ApplyModifiers.
*
* XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
@@ -2025,18 +1972,21 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
* message. Both of these return values will stop processing the variable
* expression. (XXX: As of 2020-08-23, evaluation of the whole string
* continues nevertheless after skipping a few bytes, which essentially is
- * undefined behavior. Not in the sense of C, but still it's impossible to
- * predict what happens in the parser.)
+ * undefined behavior. Not in the sense of C, but still the resulting string
+ * is garbage.)
*
* Evaluating the modifier
*
* After parsing, the modifier is evaluated. The side effects from evaluating
* nested variable expressions in the modifier text often already happen
- * during parsing though.
+ * during parsing though. For most modifiers this doesn't matter since their
+ * only noticeable effect is that the update the value of the expression.
+ * Some modifiers such as ':sh' or '::=' have noticeable side effects though.
*
* Evaluating the modifier usually takes the current value of the variable
- * expression from st->val, or the variable name from st->var->name and stores
- * the result in st->newVal.
+ * expression from ch->expr->value, or the variable name from ch->var->name
+ * and stores the result back in expr->value via Expr_SetValueOwn or
+ * Expr_SetValueRefer.
*
* If evaluating fails (as of 2020-08-23), an error message is printed using
* Error. This function has no side-effects, it really just prints the error
@@ -2047,69 +1997,134 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
* Housekeeping
*
* Some modifiers such as :D and :U turn undefined expressions into defined
- * expressions (see VEF_UNDEF, VEF_DEF).
+ * expressions (see Expr_Define).
*
* Some modifiers need to free some memory.
*/
-typedef enum VarExprStatus {
- /* The variable expression is based in a regular, defined variable. */
- VES_NONE,
+typedef enum ExprDefined {
+ /* The variable expression is based on a regular, defined variable. */
+ DEF_REGULAR,
/* The variable expression is based on an undefined variable. */
- VES_UNDEF,
+ DEF_UNDEF,
/*
* The variable expression started as an undefined expression, but one
- * of the modifiers (such as :D or :U) has turned the expression from
- * undefined to defined.
+ * of the modifiers (such as ':D' or ':U') has turned the expression
+ * from undefined to defined.
*/
- VES_DEF
-} VarExprStatus;
+ DEF_DEFINED
+} ExprDefined;
-static const char * const VarExprStatus_Name[] = {
- "none",
- "VES_UNDEF",
- "VES_DEF"
+static const char *const ExprDefined_Name[] = {
+ "regular",
+ "undefined",
+ "defined"
};
-typedef struct ApplyModifiersState {
+#if __STDC_VERSION__ >= 199901L
+#define const_member const
+#else
+#define const_member /* no const possible */
+#endif
+
+/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */
+typedef struct Expr {
+ const char *name;
+ FStr value;
+ VarEvalMode const_member emode;
+ GNode *const_member scope;
+ ExprDefined defined;
+} Expr;
+
+/*
+ * The status of applying a chain of modifiers to an expression.
+ *
+ * The modifiers of an expression are broken into chains of modifiers,
+ * starting a new nested chain whenever an indirect modifier starts. There
+ * are at most 2 nesting levels: the outer one for the direct modifiers, and
+ * the inner one for the indirect modifiers.
+ *
+ * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of
+ * modifiers:
+ *
+ * Chain 1 starts with the single modifier ':M*'.
+ * Chain 2 starts with all modifiers from ${IND1}.
+ * Chain 2 ends at the ':' between ${IND1} and ${IND2}.
+ * Chain 3 starts with all modifiers from ${IND2}.
+ * Chain 3 ends at the ':' after ${IND2}.
+ * Chain 1 continues with the the 2 modifiers ':O' and ':u'.
+ * Chain 1 ends at the final '}' of the expression.
+ *
+ * After such a chain ends, its properties no longer have any effect.
+ *
+ * It may or may not have been intended that 'defined' has scope Expr while
+ * 'sep' and 'oneBigWord' have smaller scope.
+ *
+ * See varmod-indirect.mk.
+ */
+typedef struct ModChain {
+ Expr *expr;
/* '\0' or '{' or '(' */
- const char startc;
+ char const_member startc;
/* '\0' or '}' or ')' */
- const char endc;
- Var *const var;
- GNode *const scope;
- const VarEvalFlags eflags;
- /*
- * The new value of the expression, after applying the modifier,
- * never NULL.
- */
- FStr newVal;
+ char const_member endc;
/* Word separator in expansions (see the :ts modifier). */
char sep;
/*
- * TRUE if some modifiers that otherwise split the variable value
+ * True if some modifiers that otherwise split the variable value
* into words, like :S and :C, treat the variable value as a single
* big word, possibly containing spaces.
*/
- Boolean oneBigWord;
- VarExprStatus exprStatus;
-} ApplyModifiersState;
+ bool oneBigWord;
+} ModChain;
+
+static void
+Expr_Define(Expr *expr)
+{
+ if (expr->defined == DEF_UNDEF)
+ expr->defined = DEF_DEFINED;
+}
+
+static void
+Expr_SetValue(Expr *expr, FStr value)
+{
+ FStr_Done(&expr->value);
+ expr->value = value;
+}
+
+static void
+Expr_SetValueOwn(Expr *expr, char *value)
+{
+ Expr_SetValue(expr, FStr_InitOwn(value));
+}
static void
-ApplyModifiersState_Define(ApplyModifiersState *st)
+Expr_SetValueRefer(Expr *expr, const char *value)
{
- if (st->exprStatus == VES_UNDEF)
- st->exprStatus = VES_DEF;
+ Expr_SetValue(expr, FStr_InitRefer(value));
}
+static bool
+Expr_ShouldEval(const Expr *expr)
+{
+ return VarEvalMode_ShouldEval(expr->emode);
+}
+
+static bool
+ModChain_ShouldEval(const ModChain *ch)
+{
+ return Expr_ShouldEval(ch->expr);
+}
+
+
typedef enum ApplyModifierResult {
/* Continue parsing */
AMR_OK,
- /* Not a match, try other modifiers as well */
+ /* Not a match, try other modifiers as well. */
AMR_UNKNOWN,
- /* Error out with "Bad modifier" message */
+ /* Error out with "Bad modifier" message. */
AMR_BAD,
- /* Error out without error message */
+ /* Error out without the standard error message. */
AMR_CLEANUP
} ApplyModifierResult;
@@ -2117,83 +2132,78 @@ typedef enum ApplyModifierResult {
* Allow backslashes to escape the delimiter, $, and \, but don't touch other
* backslashes.
*/
-static Boolean
+static bool
IsEscapedModifierPart(const char *p, char delim,
struct ModifyWord_SubstArgs *subst)
{
if (p[0] != '\\')
- return FALSE;
+ return false;
if (p[1] == delim || p[1] == '\\' || p[1] == '$')
- return TRUE;
+ return true;
return p[1] == '&' && subst != NULL;
}
-/* See ParseModifierPart */
+/* See ParseModifierPart for the documentation. */
static VarParseResult
ParseModifierPartSubst(
const char **pp,
char delim,
- VarEvalFlags eflags,
- ApplyModifiersState *st,
- char **out_part,
- /* Optionally stores the length of the returned string, just to save
- * another strlen call. */
- size_t *out_length,
- /* For the first part of the :S modifier, sets the VARP_ANCHOR_END flag
- * if the last character of the pattern is a $. */
- VarPatternFlags *out_pflags,
+ VarEvalMode emode,
+ ModChain *ch,
+ LazyBuf *part,
+ /* For the first part of the modifier ':S', set anchorEnd if the last
+ * character of the pattern is a $. */
+ PatternFlags *out_pflags,
/* For the second part of the :S modifier, allow ampersands to be
* escaped and replace unescaped ampersands with subst->lhs. */
struct ModifyWord_SubstArgs *subst
)
{
- Buffer buf;
const char *p;
- Buf_Init(&buf);
+ p = *pp;
+ LazyBuf_Init(part, p);
/*
* Skim through until the matching delimiter is found; pick up
* variable expressions on the way.
*/
- p = *pp;
while (*p != '\0' && *p != delim) {
const char *varstart;
if (IsEscapedModifierPart(p, delim, subst)) {
- Buf_AddByte(&buf, p[1]);
+ LazyBuf_Add(part, p[1]);
p += 2;
continue;
}
if (*p != '$') { /* Unescaped, simple text */
if (subst != NULL && *p == '&')
- Buf_AddBytes(&buf, subst->lhs, subst->lhsLen);
+ LazyBuf_AddSubstring(part, subst->lhs);
else
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(part, *p);
p++;
continue;
}
if (p[1] == delim) { /* Unescaped $ at end of pattern */
if (out_pflags != NULL)
- out_pflags->anchorEnd = TRUE;
+ out_pflags->anchorEnd = true;
else
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(part, *p);
p++;
continue;
}
- if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */
+ if (VarEvalMode_ShouldEval(emode)) {
+ /* Nested variable, evaluated */
const char *nested_p = p;
FStr nested_val;
- VarEvalFlags nested_eflags =
- eflags & ~(unsigned)VARE_KEEP_DOLLAR;
- (void)Var_Parse(&nested_p, st->scope, nested_eflags,
- &nested_val);
+ (void)Var_Parse(&nested_p, ch->expr->scope,
+ VarEvalMode_WithoutKeepDollar(emode), &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ LazyBuf_AddStr(part, nested_val.str);
FStr_Done(&nested_val);
p += nested_p - p;
continue;
@@ -2201,10 +2211,10 @@ ParseModifierPartSubst(
/*
* XXX: This whole block is very similar to Var_Parse without
- * VARE_WANTRES. There may be subtle edge cases though that
- * are not yet covered in the unit tests and that are parsed
- * differently, depending on whether they are evaluated or
- * not.
+ * VARE_WANTRES. There may be subtle edge cases
+ * though that are not yet covered in the unit tests and that
+ * are parsed differently, depending on whether they are
+ * evaluated or not.
*
* This subtle difference is not documented in the manual
* page, neither is the difference between parsing :D and
@@ -2231,27 +2241,29 @@ ParseModifierPartSubst(
depth--;
}
}
- Buf_AddBytesBetween(&buf, varstart, p);
+ LazyBuf_AddBytesBetween(part, varstart, p);
} else {
- Buf_AddByte(&buf, *varstart);
+ LazyBuf_Add(part, *varstart);
p++;
}
}
if (*p != delim) {
*pp = p;
- Error("Unfinished modifier for %s ('%c' missing)",
- st->var->name.str, delim);
- *out_part = NULL;
+ Error("Unfinished modifier for \"%s\" ('%c' missing)",
+ ch->expr->name, delim);
+ LazyBuf_Done(part);
return VPR_ERR;
}
*pp = p + 1;
- if (out_length != NULL)
- *out_length = buf.len;
- *out_part = Buf_DoneData(&buf);
- DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part);
+ {
+ Substring sub = LazyBuf_Get(part);
+ DEBUG2(VAR, "Modifier part: \"%.*s\"\n",
+ (int)Substring_Length(sub), sub.start);
+ }
+
return VPR_OK;
}
@@ -2261,10 +2273,9 @@ ParseModifierPartSubst(
* including the next unescaped delimiter. The delimiter, as well as the
* backslash or the dollar, can be escaped with a backslash.
*
- * Return the parsed (and possibly expanded) string, or NULL if no delimiter
- * was found. On successful return, the parsing position pp points right
- * after the delimiter. The delimiter is not included in the returned
- * value though.
+ * Return VPR_OK if parsing succeeded, together with the parsed (and possibly
+ * expanded) part. In that case, pp points right after the delimiter. The
+ * delimiter is not included in the part though.
*/
static VarParseResult
ParseModifierPart(
@@ -2272,36 +2283,39 @@ ParseModifierPart(
const char **pp,
/* Parsing stops at this delimiter */
char delim,
- /* Flags for evaluating nested variables; if VARE_WANTRES is not set,
- * the text is only parsed. */
- VarEvalFlags eflags,
- ApplyModifiersState *st,
- char **out_part
+ /* Mode for evaluating nested variables. */
+ VarEvalMode emode,
+ ModChain *ch,
+ LazyBuf *part
)
{
- return ParseModifierPartSubst(pp, delim, eflags, st, out_part,
- NULL, NULL, NULL);
+ return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL);
+}
+
+MAKE_INLINE bool
+IsDelimiter(char c, const ModChain *ch)
+{
+ return c == ':' || c == ch->endc;
}
/* Test whether mod starts with modname, followed by a delimiter. */
-MAKE_INLINE Boolean
-ModMatch(const char *mod, const char *modname, char endc)
+MAKE_INLINE bool
+ModMatch(const char *mod, const char *modname, const ModChain *ch)
{
size_t n = strlen(modname);
- return strncmp(mod, modname, n) == 0 &&
- (mod[n] == endc || mod[n] == ':');
+ return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], ch);
}
/* Test whether mod starts with modname, followed by a delimiter or '='. */
-MAKE_INLINE Boolean
-ModMatchEq(const char *mod, const char *modname, char endc)
+MAKE_INLINE bool
+ModMatchEq(const char *mod, const char *modname, const ModChain *ch)
{
size_t n = strlen(modname);
return strncmp(mod, modname, n) == 0 &&
- (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
+ (IsDelimiter(mod[n], ch) || mod[n] == '=');
}
-static Boolean
+static bool
TryParseIntBase0(const char **pp, int *out_num)
{
char *end;
@@ -2309,127 +2323,180 @@ TryParseIntBase0(const char **pp, int *out_num)
errno = 0;
n = strtol(*pp, &end, 0);
+
+ if (end == *pp)
+ return false;
if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
- return FALSE;
+ return false;
if (n < INT_MIN || n > INT_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_num = (int)n;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
TryParseSize(const char **pp, size_t *out_num)
{
char *end;
unsigned long n;
if (!ch_isdigit(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, 10);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
if (n > SIZE_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_num = (size_t)n;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
TryParseChar(const char **pp, int base, char *out_ch)
{
char *end;
unsigned long n;
if (!ch_isalnum(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, base);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
if (n > UCHAR_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_ch = (char)n;
- return TRUE;
+ return true;
+}
+
+/*
+ * Modify each word of the expression using the given function and place the
+ * result back in the expression.
+ */
+static void
+ModifyWords(ModChain *ch,
+ ModifyWordProc modifyWord, void *modifyWord_args,
+ bool oneBigWord)
+{
+ Expr *expr = ch->expr;
+ const char *val = expr->value.str;
+ SepBuf result;
+ SubstringWords words;
+ size_t i;
+ Substring word;
+
+ if (oneBigWord) {
+ SepBuf_Init(&result, ch->sep);
+ /* XXX: performance: Substring_InitStr calls strlen */
+ word = Substring_InitStr(val);
+ modifyWord(word, &result, modifyWord_args);
+ goto done;
+ }
+
+ words = Substring_Words(val, false);
+
+ DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
+ val, (unsigned)words.len);
+
+ SepBuf_Init(&result, ch->sep);
+ for (i = 0; i < words.len; i++) {
+ modifyWord(words.words[i], &result, modifyWord_args);
+ if (result.buf.len > 0)
+ SepBuf_Sep(&result);
+ }
+
+ SubstringWords_Free(words);
+
+done:
+ Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
}
/* :@var@...${var}...@ */
static ApplyModifierResult
-ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Loop(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
struct ModifyWord_LoopArgs args;
char prev_sep;
VarParseResult res;
+ LazyBuf tvarBuf, strBuf;
+ FStr tvar, str;
- args.scope = st->scope;
+ args.scope = expr->scope;
(*pp)++; /* Skip the first '@' */
- res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
+ res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
- if (opts.strict && strchr(args.tvar, '$') != NULL) {
+ tvar = LazyBuf_DoneGet(&tvarBuf);
+ args.var = tvar.str;
+ if (strchr(args.var, '$') != NULL) {
Parse_Error(PARSE_FATAL,
"In the :@ modifier of \"%s\", the variable name \"%s\" "
"must not contain a dollar.",
- st->var->name.str, args.tvar);
+ expr->name, args.var);
return AMR_CLEANUP;
}
- res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str);
+ res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &strBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
-
- args.eflags = st->eflags & ~(unsigned)VARE_KEEP_DOLLAR;
- prev_sep = st->sep;
- st->sep = ' '; /* XXX: should be st->sep for consistency */
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Loop, &args, st->oneBigWord, st->sep));
- st->sep = prev_sep;
- /* XXX: Consider restoring the previous variable instead of deleting. */
- /*
- * XXX: The variable name should not be expanded here, see
- * ModifyWord_Loop.
- */
- Var_DeleteExpand(st->scope, args.tvar);
- free(args.tvar);
- free(args.str);
+ str = LazyBuf_DoneGet(&strBuf);
+ args.body = str.str;
+
+ if (!Expr_ShouldEval(expr))
+ goto done;
+
+ args.emode = VarEvalMode_WithoutKeepDollar(expr->emode);
+ prev_sep = ch->sep;
+ ch->sep = ' '; /* XXX: should be ch->sep for consistency */
+ ModifyWords(ch, ModifyWord_Loop, &args, ch->oneBigWord);
+ ch->sep = prev_sep;
+ /* XXX: Consider restoring the previous value instead of deleting. */
+ Var_Delete(expr->scope, args.var);
+
+done:
+ FStr_Done(&tvar);
+ FStr_Done(&str);
return AMR_OK;
}
/* :Ddefined or :Uundefined */
static ApplyModifierResult
-ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Defined(const char **pp, ModChain *ch)
{
- Buffer buf;
+ Expr *expr = ch->expr;
+ LazyBuf buf;
const char *p;
- VarEvalFlags eflags = VARE_NONE;
- if (st->eflags & VARE_WANTRES)
- if ((**pp == 'D') == (st->exprStatus == VES_NONE))
- eflags = st->eflags;
+ VarEvalMode emode = VARE_PARSE_ONLY;
+ if (Expr_ShouldEval(expr))
+ if ((**pp == 'D') == (expr->defined == DEF_REGULAR))
+ emode = expr->emode;
- Buf_Init(&buf);
p = *pp + 1;
- while (*p != st->endc && *p != ':' && *p != '\0') {
+ LazyBuf_Init(&buf, p);
+ while (!IsDelimiter(*p, ch) && *p != '\0') {
/* XXX: This code is similar to the one in Var_Parse.
* See if the code can be merged.
- * See also ApplyModifier_Match. */
+ * See also ApplyModifier_Match and ParseModifierPart. */
/* Escaped delimiter or other special character */
+ /* See Buf_AddEscaped in for.c. */
if (*p == '\\') {
char c = p[1];
- if (c == st->endc || c == ':' || c == '$' ||
- c == '\\') {
- Buf_AddByte(&buf, c);
+ if (IsDelimiter(c, ch) || c == '$' || c == '\\') {
+ LazyBuf_Add(&buf, c);
p += 2;
continue;
}
@@ -2439,173 +2506,197 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, st->scope, eflags, &nested_val);
+ (void)Var_Parse(&p, expr->scope, emode, &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ if (Expr_ShouldEval(expr))
+ LazyBuf_AddStr(&buf, nested_val.str);
FStr_Done(&nested_val);
continue;
}
/* Ordinary text */
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(&buf, *p);
p++;
}
*pp = p;
- ApplyModifiersState_Define(st);
+ Expr_Define(expr);
+
+ if (VarEvalMode_ShouldEval(emode))
+ Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf)));
+ else
+ LazyBuf_Done(&buf);
- if (eflags & VARE_WANTRES) {
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
- } else {
- st->newVal = FStr_InitRefer(val);
- Buf_Done(&buf);
- }
return AMR_OK;
}
/* :L */
static ApplyModifierResult
-ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Literal(const char **pp, ModChain *ch)
{
- ApplyModifiersState_Define(st);
- st->newVal = FStr_InitOwn(bmake_strdup(st->var->name.str));
+ Expr *expr = ch->expr;
+
(*pp)++;
+
+ if (Expr_ShouldEval(expr)) {
+ Expr_Define(expr);
+ Expr_SetValueOwn(expr, bmake_strdup(expr->name));
+ }
+
return AMR_OK;
}
-static Boolean
+static bool
TryParseTime(const char **pp, time_t *out_time)
{
char *end;
unsigned long n;
if (!ch_isdigit(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, 10);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
*pp = end;
*out_time = (time_t)n; /* ignore possible truncation for now */
- return TRUE;
+ return true;
}
/* :gmtime */
static ApplyModifierResult
-ApplyModifier_Gmtime(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Gmtime(const char **pp, ModChain *ch)
{
time_t utc;
const char *mod = *pp;
- if (!ModMatchEq(mod, "gmtime", st->endc))
+ if (!ModMatchEq(mod, "gmtime", ch))
return AMR_UNKNOWN;
if (mod[6] == '=') {
- const char *arg = mod + 7;
- if (!TryParseTime(&arg, &utc)) {
+ const char *p = mod + 7;
+ if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
"Invalid time value: %s", mod + 7);
return AMR_CLEANUP;
}
- *pp = arg;
+ *pp = p;
} else {
utc = 0;
*pp = mod + 6;
}
- st->newVal = FStr_InitOwn(VarStrftime(val, TRUE, utc));
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr,
+ VarStrftime(ch->expr->value.str, true, utc));
+
return AMR_OK;
}
/* :localtime */
static ApplyModifierResult
-ApplyModifier_Localtime(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_Localtime(const char **pp, ModChain *ch)
{
time_t utc;
const char *mod = *pp;
- if (!ModMatchEq(mod, "localtime", st->endc))
+ if (!ModMatchEq(mod, "localtime", ch))
return AMR_UNKNOWN;
if (mod[9] == '=') {
- const char *arg = mod + 10;
- if (!TryParseTime(&arg, &utc)) {
+ const char *p = mod + 10;
+ if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
"Invalid time value: %s", mod + 10);
return AMR_CLEANUP;
}
- *pp = arg;
+ *pp = p;
} else {
utc = 0;
*pp = mod + 9;
}
- st->newVal = FStr_InitOwn(VarStrftime(val, FALSE, utc));
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr,
+ VarStrftime(ch->expr->value.str, false, utc));
+
return AMR_OK;
}
/* :hash */
static ApplyModifierResult
-ApplyModifier_Hash(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Hash(const char **pp, ModChain *ch)
{
- if (!ModMatch(*pp, "hash", st->endc))
+ if (!ModMatch(*pp, "hash", ch))
return AMR_UNKNOWN;
-
- st->newVal = FStr_InitOwn(VarHash(val));
*pp += 4;
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr, VarHash(ch->expr->value.str));
+
return AMR_OK;
}
/* :P */
static ApplyModifierResult
-ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Path(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
GNode *gn;
char *path;
- ApplyModifiersState_Define(st);
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
- gn = Targ_FindNode(st->var->name.str);
+ Expr_Define(expr);
+
+ gn = Targ_FindNode(expr->name);
if (gn == NULL || gn->type & OP_NOPATH) {
path = NULL;
} else if (gn->path != NULL) {
path = bmake_strdup(gn->path);
} else {
SearchPath *searchPath = Suff_FindPath(gn);
- path = Dir_FindFile(st->var->name.str, searchPath);
+ path = Dir_FindFile(expr->name, searchPath);
}
if (path == NULL)
- path = bmake_strdup(st->var->name.str);
- st->newVal = FStr_InitOwn(path);
+ path = bmake_strdup(expr->name);
+ Expr_SetValueOwn(expr, path);
- (*pp)++;
return AMR_OK;
}
/* :!cmd! */
static ApplyModifierResult
-ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
+ApplyModifier_ShellCommand(const char **pp, ModChain *ch)
{
- char *cmd;
+ Expr *expr = ch->expr;
const char *errfmt;
VarParseResult res;
+ LazyBuf cmdBuf;
+ FStr cmd;
(*pp)++;
- res = ParseModifierPart(pp, '!', st->eflags, st, &cmd);
+ res = ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ cmd = LazyBuf_DoneGet(&cmdBuf);
+
errfmt = NULL;
- if (st->eflags & VARE_WANTRES)
- st->newVal = FStr_InitOwn(Cmd_Exec(cmd, &errfmt));
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr, Cmd_Exec(cmd.str, &errfmt));
else
- st->newVal = FStr_InitRefer("");
+ Expr_SetValueRefer(expr, "");
if (errfmt != NULL)
- Error(errfmt, cmd); /* XXX: why still return AMR_OK? */
- free(cmd);
+ Error(errfmt, cmd.str); /* XXX: why still return AMR_OK? */
+ FStr_Done(&cmd);
+ Expr_Define(expr);
- ApplyModifiersState_Define(st);
return AMR_OK;
}
@@ -2614,21 +2705,22 @@ ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
* The :range=7 modifier generates an integer sequence from 1 to 7.
*/
static ApplyModifierResult
-ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Range(const char **pp, ModChain *ch)
{
size_t n;
Buffer buf;
size_t i;
const char *mod = *pp;
- if (!ModMatchEq(mod, "range", st->endc))
+ if (!ModMatchEq(mod, "range", ch))
return AMR_UNKNOWN;
if (mod[5] == '=') {
const char *p = mod + 6;
if (!TryParseSize(&p, &n)) {
Parse_Error(PARSE_FATAL,
- "Invalid number: %s", mod + 6);
+ "Invalid number \"%s\" for ':range' modifier",
+ mod + 6);
return AMR_CLEANUP;
}
*pp = p;
@@ -2637,8 +2729,11 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
*pp = mod + 5;
}
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
if (n == 0) {
- Words words = Str_Words(val, FALSE);
+ Words words = Str_Words(ch->expr->value.str, false);
n = words.len;
Words_Free(words);
}
@@ -2647,47 +2742,50 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
for (i = 0; i < n; i++) {
if (i != 0) {
- /* XXX: Use st->sep instead of ' ', for consistency. */
+ /* XXX: Use ch->sep instead of ' ', for consistency. */
Buf_AddByte(&buf, ' ');
}
Buf_AddInt(&buf, 1 + (int)i);
}
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
+ Expr_SetValueOwn(ch->expr, Buf_DoneData(&buf));
return AMR_OK;
}
-/* :Mpattern or :Npattern */
-static ApplyModifierResult
-ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
+/* Parse a ':M' or ':N' modifier. */
+static void
+ParseModifier_Match(const char **pp, const ModChain *ch,
+ char **out_pattern)
{
const char *mod = *pp;
- Boolean copy = FALSE; /* pattern should be, or has been, copied */
- Boolean needSubst = FALSE;
+ Expr *expr = ch->expr;
+ bool copy = false; /* pattern should be, or has been, copied */
+ bool needSubst = false;
const char *endpat;
char *pattern;
- ModifyWordsCallback callback;
/*
* In the loop below, ignore ':' unless we are at (or back to) the
* original brace level.
* XXX: This will likely not work right if $() and ${} are intermixed.
*/
- /* XXX: This code is similar to the one in Var_Parse.
+ /*
+ * XXX: This code is similar to the one in Var_Parse.
* See if the code can be merged.
- * See also ApplyModifier_Defined. */
+ * See also ApplyModifier_Defined.
+ */
int nest = 0;
const char *p;
for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
if (*p == '\\' &&
- (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) {
+ (IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
if (!needSubst)
- copy = TRUE;
+ copy = true;
p++;
continue;
}
if (*p == '$')
- needSubst = TRUE;
+ needSubst = true;
if (*p == '(' || *p == '{')
nest++;
if (*p == ')' || *p == '}') {
@@ -2709,8 +2807,8 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
src = mod + 1;
for (; src < endpat; src++, dst++) {
if (src[0] == '\\' && src + 1 < endpat &&
- /* XXX: st->startc is missing here; see above */
- (src[1] == ':' || src[1] == st->endc))
+ /* XXX: ch->startc is missing here; see above */
+ IsDelimiter(src[1], ch))
src++;
*dst = *src;
}
@@ -2721,84 +2819,104 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
if (needSubst) {
char *old_pattern = pattern;
- (void)Var_Subst(pattern, st->scope, st->eflags, &pattern);
+ (void)Var_Subst(pattern, expr->scope, expr->emode, &pattern);
/* TODO: handle errors */
free(old_pattern);
}
- DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n",
- st->var->name.str, val, pattern);
+ DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern);
+
+ *out_pattern = pattern;
+}
+
+/* :Mpattern or :Npattern */
+static ApplyModifierResult
+ApplyModifier_Match(const char **pp, ModChain *ch)
+{
+ const char mod = **pp;
+ char *pattern;
+
+ ParseModifier_Match(pp, ch, &pattern);
+
+ if (ModChain_ShouldEval(ch)) {
+ ModifyWordProc modifyWord =
+ mod == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
+ ModifyWords(ch, modifyWord, pattern, ch->oneBigWord);
+ }
- callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
- st->newVal = FStr_InitOwn(ModifyWords(val, callback, pattern,
- st->oneBigWord, st->sep));
free(pattern);
return AMR_OK;
}
+static void
+ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
+{
+ for (;; (*pp)++) {
+ if (**pp == 'g')
+ pflags->subGlobal = true;
+ else if (**pp == '1')
+ pflags->subOnce = true;
+ else if (**pp == 'W')
+ *oneBigWord = true;
+ else
+ break;
+ }
+}
+
+MAKE_INLINE PatternFlags
+PatternFlags_None(void)
+{
+ PatternFlags pflags = { false, false, false, false };
+ return pflags;
+}
+
/* :S,from,to, */
static ApplyModifierResult
-ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Subst(const char **pp, ModChain *ch)
{
struct ModifyWord_SubstArgs args;
- char *lhs, *rhs;
- Boolean oneBigWord;
+ bool oneBigWord;
VarParseResult res;
+ LazyBuf lhsBuf, rhsBuf;
char delim = (*pp)[1];
if (delim == '\0') {
- Error("Missing delimiter for :S modifier");
+ Error("Missing delimiter for modifier ':S'");
(*pp)++;
return AMR_CLEANUP;
}
*pp += 2;
- args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
- args.matched = FALSE;
+ args.pflags = PatternFlags_None();
+ args.matched = false;
- /*
- * If pattern begins with '^', it is anchored to the
- * start of the word -- skip over it and flag pattern.
- */
if (**pp == '^') {
- args.pflags.anchorStart = TRUE;
+ args.pflags.anchorStart = true;
(*pp)++;
}
- res = ParseModifierPartSubst(pp, delim, st->eflags, st, &lhs,
- &args.lhsLen, &args.pflags, NULL);
+ res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf,
+ &args.pflags, NULL);
if (res != VPR_OK)
return AMR_CLEANUP;
- args.lhs = lhs;
+ args.lhs = LazyBuf_Get(&lhsBuf);
- res = ParseModifierPartSubst(pp, delim, st->eflags, st, &rhs,
- &args.rhsLen, NULL, &args);
- if (res != VPR_OK)
+ res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf,
+ NULL, &args);
+ if (res != VPR_OK) {
+ LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
- args.rhs = rhs;
-
- oneBigWord = st->oneBigWord;
- for (;; (*pp)++) {
- switch (**pp) {
- case 'g':
- args.pflags.subGlobal = TRUE;
- continue;
- case '1':
- args.pflags.subOnce = TRUE;
- continue;
- case 'W':
- oneBigWord = TRUE;
- continue;
- }
- break;
}
+ args.rhs = LazyBuf_Get(&rhsBuf);
+
+ oneBigWord = ch->oneBigWord;
+ ParsePatternFlags(pp, &args.pflags, &oneBigWord);
- st->newVal = FStr_InitOwn(ModifyWords(val, ModifyWord_Subst, &args,
- oneBigWord, st->sep));
+ ModifyWords(ch, ModifyWord_Subst, &args, oneBigWord);
- free(lhs);
- free(rhs);
+ LazyBuf_Done(&lhsBuf);
+ LazyBuf_Done(&rhsBuf);
return AMR_OK;
}
@@ -2806,13 +2924,14 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
/* :C,from,to, */
static ApplyModifierResult
-ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Regex(const char **pp, ModChain *ch)
{
- char *re;
struct ModifyWord_SubstRegexArgs args;
- Boolean oneBigWord;
+ bool oneBigWord;
int error;
VarParseResult res;
+ LazyBuf reBuf, replaceBuf;
+ FStr re, replace;
char delim = (*pp)[1];
if (delim == '\0') {
@@ -2823,50 +2942,47 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
*pp += 2;
- res = ParseModifierPart(pp, delim, st->eflags, st, &re);
+ res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ re = LazyBuf_DoneGet(&reBuf);
- res = ParseModifierPart(pp, delim, st->eflags, st, &args.replace);
- if (args.replace == NULL) {
- free(re);
+ res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf);
+ if (res != VPR_OK) {
+ FStr_Done(&re);
return AMR_CLEANUP;
}
+ replace = LazyBuf_DoneGet(&replaceBuf);
+ args.replace = replace.str;
- args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
- args.matched = FALSE;
- oneBigWord = st->oneBigWord;
- for (;; (*pp)++) {
- switch (**pp) {
- case 'g':
- args.pflags.subGlobal = TRUE;
- continue;
- case '1':
- args.pflags.subOnce = TRUE;
- continue;
- case 'W':
- oneBigWord = TRUE;
- continue;
- }
- break;
+ args.pflags = PatternFlags_None();
+ args.matched = false;
+ oneBigWord = ch->oneBigWord;
+ ParsePatternFlags(pp, &args.pflags, &oneBigWord);
+
+ if (!ModChain_ShouldEval(ch)) {
+ FStr_Done(&replace);
+ FStr_Done(&re);
+ return AMR_OK;
}
- error = regcomp(&args.re, re, REG_EXTENDED);
- free(re);
+ error = regcomp(&args.re, re.str, REG_EXTENDED);
if (error != 0) {
VarREError(error, &args.re, "Regex compilation error");
- free(args.replace);
+ FStr_Done(&replace);
+ FStr_Done(&re);
return AMR_CLEANUP;
}
args.nsub = args.re.re_nsub + 1;
if (args.nsub > 10)
args.nsub = 10;
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_SubstRegex, &args,
- oneBigWord, st->sep));
+
+ ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord);
+
regfree(&args.re);
- free(args.replace);
+ FStr_Done(&replace);
+ FStr_Done(&re);
return AMR_OK;
}
@@ -2874,40 +2990,59 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
/* :Q, :q */
static ApplyModifierResult
-ApplyModifier_Quote(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Quote(const char **pp, ModChain *ch)
{
- if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
- st->newVal = FStr_InitOwn(VarQuote(val, **pp == 'q'));
- (*pp)++;
- return AMR_OK;
- } else
+ LazyBuf buf;
+ bool quoteDollar;
+
+ quoteDollar = **pp == 'q';
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ VarQuote(ch->expr->value.str, quoteDollar, &buf);
+ if (buf.data != NULL)
+ Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
+ else
+ LazyBuf_Done(&buf);
+
+ return AMR_OK;
}
/*ARGSUSED*/
static void
-ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
+ModifyWord_Copy(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
{
- SepBuf_AddStr(buf, word);
+ SepBuf_AddSubstring(buf, word);
}
/* :ts<separator> */
static ApplyModifierResult
-ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_ToSep(const char **pp, ModChain *ch)
{
const char *sep = *pp + 2;
+ /*
+ * Even in parse-only mode, proceed as normal since there is
+ * neither any observable side effect nor a performance penalty.
+ * Checking for wantRes for every single piece of code in here
+ * would make the code in this function too hard to read.
+ */
+
/* ":ts<any><endc>" or ":ts<any>:" */
- if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) {
- st->sep = sep[0];
+ if (sep[0] != ch->endc && IsDelimiter(sep[1], ch)) {
*pp = sep + 1;
+ ch->sep = sep[0];
goto ok;
}
/* ":ts<endc>" or ":ts:" */
- if (sep[0] == st->endc || sep[0] == ':') {
- st->sep = '\0'; /* no separator */
+ if (IsDelimiter(sep[0], ch)) {
*pp = sep;
+ ch->sep = '\0'; /* no separator */
goto ok;
}
@@ -2919,15 +3054,15 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
/* ":ts\n" */
if (sep[1] == 'n') {
- st->sep = '\n';
*pp = sep + 2;
+ ch->sep = '\n';
goto ok;
}
/* ":ts\t" */
if (sep[1] == 't') {
- st->sep = '\t';
*pp = sep + 2;
+ ch->sep = '\t';
goto ok;
}
@@ -2944,12 +3079,12 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
return AMR_BAD; /* ":ts<backslash><unrecognised>". */
}
- if (!TryParseChar(&p, base, &st->sep)) {
+ if (!TryParseChar(&p, base, &ch->sep)) {
Parse_Error(PARSE_FATAL,
"Invalid character number: %s", p);
return AMR_CLEANUP;
}
- if (*p != ':' && *p != st->endc) {
+ if (!IsDelimiter(*p, ch)) {
(*pp)++; /* just for backwards compatibility */
return AMR_BAD;
}
@@ -2958,8 +3093,7 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
}
ok:
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Copy, NULL, st->oneBigWord, st->sep));
+ ModifyWords(ch, ModifyWord_Copy, NULL, ch->oneBigWord);
return AMR_OK;
}
@@ -2993,107 +3127,109 @@ str_tolower(const char *str)
/* :tA, :tu, :tl, :ts<separator>, etc. */
static ApplyModifierResult
-ApplyModifier_To(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_To(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *mod = *pp;
assert(mod[0] == 't');
- if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') {
+ if (IsDelimiter(mod[1], ch) || mod[1] == '\0') {
*pp = mod + 1;
return AMR_BAD; /* Found ":t<endc>" or ":t:". */
}
if (mod[1] == 's')
- return ApplyModifier_ToSep(pp, val, st);
+ return ApplyModifier_ToSep(pp, ch);
- if (mod[2] != st->endc && mod[2] != ':') {
+ if (!IsDelimiter(mod[2], ch)) { /* :t<unrecognized> */
*pp = mod + 1;
- return AMR_BAD; /* Found ":t<unrecognised><unrecognised>". */
+ return AMR_BAD;
}
- /* Check for two-character options: ":tu", ":tl" */
- if (mod[1] == 'A') { /* absolute path */
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Realpath, NULL,
- st->oneBigWord, st->sep));
+ if (mod[1] == 'A') { /* :tA */
*pp = mod + 2;
+ ModifyWords(ch, ModifyWord_Realpath, NULL, ch->oneBigWord);
return AMR_OK;
}
- if (mod[1] == 'u') { /* :tu */
- st->newVal = FStr_InitOwn(str_toupper(val));
+ if (mod[1] == 'u') { /* :tu */
*pp = mod + 2;
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(expr, str_toupper(expr->value.str));
return AMR_OK;
}
- if (mod[1] == 'l') { /* :tl */
- st->newVal = FStr_InitOwn(str_tolower(val));
+ if (mod[1] == 'l') { /* :tl */
*pp = mod + 2;
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(expr, str_tolower(expr->value.str));
return AMR_OK;
}
- if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
- st->oneBigWord = mod[1] == 'W';
- st->newVal = FStr_InitRefer(val);
+ if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
*pp = mod + 2;
+ ch->oneBigWord = mod[1] == 'W';
return AMR_OK;
}
/* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
- *pp = mod + 1;
+ *pp = mod + 1; /* XXX: unnecessary but observable */
return AMR_BAD;
}
/* :[#], :[1], :[-1..1], etc. */
static ApplyModifierResult
-ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Words(const char **pp, ModChain *ch)
{
- char *estr;
+ Expr *expr = ch->expr;
+ const char *estr;
int first, last;
VarParseResult res;
const char *p;
+ LazyBuf estrBuf;
+ FStr festr;
(*pp)++; /* skip the '[' */
- res = ParseModifierPart(pp, ']', st->eflags, st, &estr);
+ res = ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ festr = LazyBuf_DoneGet(&estrBuf);
+ estr = festr.str;
+
+ if (!IsDelimiter(**pp, ch))
+ goto bad_modifier; /* Found junk after ']' */
- /* now *pp points just after the closing ']' */
- if (**pp != ':' && **pp != st->endc)
- goto bad_modifier; /* Found junk after ']' */
+ if (!ModChain_ShouldEval(ch))
+ goto ok;
if (estr[0] == '\0')
- goto bad_modifier; /* empty square brackets in ":[]". */
+ goto bad_modifier; /* Found ":[]". */
- if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
- if (st->oneBigWord) {
- st->newVal = FStr_InitRefer("1");
+ if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
+ if (ch->oneBigWord) {
+ Expr_SetValueRefer(expr, "1");
} else {
Buffer buf;
- Words words = Str_Words(val, FALSE);
+ Words words = Str_Words(expr->value.str, false);
size_t ac = words.len;
Words_Free(words);
/* 3 digits + '\0' is usually enough */
Buf_InitSize(&buf, 4);
Buf_AddInt(&buf, (int)ac);
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
+ Expr_SetValueOwn(expr, Buf_DoneData(&buf));
}
goto ok;
}
- if (estr[0] == '*' && estr[1] == '\0') {
- /* Found ":[*]" */
- st->oneBigWord = TRUE;
- st->newVal = FStr_InitRefer(val);
+ if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
+ ch->oneBigWord = true;
goto ok;
}
- if (estr[0] == '@' && estr[1] == '\0') {
- /* Found ":[@]" */
- st->oneBigWord = FALSE;
- st->newVal = FStr_InitRefer(val);
+ if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
+ ch->oneBigWord = false;
goto ok;
}
@@ -3121,8 +3257,7 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
*/
if (first == 0 && last == 0) {
/* ":[0]" or perhaps ":[0..0]" */
- st->oneBigWord = TRUE;
- st->newVal = FStr_InitRefer(val);
+ ch->oneBigWord = true;
goto ok;
}
@@ -3131,15 +3266,16 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
goto bad_modifier;
/* Normal case: select the words described by first and last. */
- st->newVal = FStr_InitOwn(
- VarSelectWords(st->sep, st->oneBigWord, val, first, last));
+ Expr_SetValueOwn(expr,
+ VarSelectWords(expr->value.str, first, last,
+ ch->sep, ch->oneBigWord));
ok:
- free(estr);
+ FStr_Done(&festr);
return AMR_OK;
bad_modifier:
- free(estr);
+ FStr_Done(&festr);
return AMR_BAD;
}
@@ -3170,94 +3306,107 @@ ShuffleStrings(char **strs, size_t n)
/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
static ApplyModifierResult
-ApplyModifier_Order(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Order(const char **pp, ModChain *ch)
{
const char *mod = (*pp)++; /* skip past the 'O' in any case */
+ Words words;
+ enum SortMode {
+ ASC, DESC, SHUFFLE
+ } mode;
- Words words = Str_Words(val, FALSE);
-
- if (mod[1] == st->endc || mod[1] == ':') {
- /* :O sorts ascending */
- qsort(words.words, words.len, sizeof words.words[0],
- str_cmp_asc);
-
+ if (IsDelimiter(mod[1], ch)) {
+ mode = ASC;
} else if ((mod[1] == 'r' || mod[1] == 'x') &&
- (mod[2] == st->endc || mod[2] == ':')) {
+ IsDelimiter(mod[2], ch)) {
(*pp)++;
-
- if (mod[1] == 'r') { /* :Or sorts descending */
- qsort(words.words, words.len, sizeof words.words[0],
- str_cmp_desc);
- } else
- ShuffleStrings(words.words, words.len);
- } else {
- Words_Free(words);
+ mode = mod[1] == 'r' ? DESC : SHUFFLE;
+ } else
return AMR_BAD;
- }
- st->newVal = FStr_InitOwn(Words_JoinFree(words));
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ words = Str_Words(ch->expr->value.str, false);
+ if (mode == SHUFFLE)
+ ShuffleStrings(words.words, words.len);
+ else
+ qsort(words.words, words.len, sizeof words.words[0],
+ mode == ASC ? str_cmp_asc : str_cmp_desc);
+ Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+
return AMR_OK;
}
/* :? then : else */
static ApplyModifierResult
-ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
+ApplyModifier_IfElse(const char **pp, ModChain *ch)
{
- char *then_expr, *else_expr;
+ Expr *expr = ch->expr;
VarParseResult res;
+ LazyBuf buf;
+ FStr then_expr, else_expr;
- Boolean value = FALSE;
- VarEvalFlags then_eflags = VARE_NONE;
- VarEvalFlags else_eflags = VARE_NONE;
+ bool value = false;
+ VarEvalMode then_emode = VARE_PARSE_ONLY;
+ VarEvalMode else_emode = VARE_PARSE_ONLY;
int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
- if (st->eflags & VARE_WANTRES) {
- cond_rc = Cond_EvalCondition(st->var->name.str, &value);
+ if (Expr_ShouldEval(expr)) {
+ cond_rc = Cond_EvalCondition(expr->name, &value);
if (cond_rc != COND_INVALID && value)
- then_eflags = st->eflags;
+ then_emode = expr->emode;
if (cond_rc != COND_INVALID && !value)
- else_eflags = st->eflags;
+ else_emode = expr->emode;
}
(*pp)++; /* skip past the '?' */
- res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr);
+ res = ParseModifierPart(pp, ':', then_emode, ch, &buf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ then_expr = LazyBuf_DoneGet(&buf);
- res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr);
- if (res != VPR_OK)
+ res = ParseModifierPart(pp, ch->endc, else_emode, ch, &buf);
+ if (res != VPR_OK) {
+ FStr_Done(&then_expr);
return AMR_CLEANUP;
+ }
+ else_expr = LazyBuf_DoneGet(&buf);
+
+ (*pp)--; /* Go back to the ch->endc. */
- (*pp)--;
if (cond_rc == COND_INVALID) {
- Error("Bad conditional expression `%s' in %s?%s:%s",
- st->var->name.str, st->var->name.str, then_expr, else_expr);
+ Error("Bad conditional expression '%s' in '%s?%s:%s'",
+ expr->name, expr->name, then_expr.str, else_expr.str);
return AMR_CLEANUP;
}
- if (value) {
- st->newVal = FStr_InitOwn(then_expr);
- free(else_expr);
+ if (!ModChain_ShouldEval(ch)) {
+ FStr_Done(&then_expr);
+ FStr_Done(&else_expr);
+ } else if (value) {
+ Expr_SetValue(expr, then_expr);
+ FStr_Done(&else_expr);
} else {
- st->newVal = FStr_InitOwn(else_expr);
- free(then_expr);
+ FStr_Done(&then_expr);
+ Expr_SetValue(expr, else_expr);
}
- ApplyModifiersState_Define(st);
+ Expr_Define(expr);
return AMR_OK;
}
/*
- * The ::= modifiers actually assign a value to the variable.
- * Their main purpose is in supporting modifiers of .for loop
- * iterators and other obscure uses. They always expand to
- * nothing. In a target rule that would otherwise expand to an
- * empty line they can be preceded with @: to keep make happy.
- * Eg.
+ * The ::= modifiers are special in that they do not read the variable value
+ * but instead assign to that variable. They always expand to an empty
+ * string.
*
- * foo: .USE
+ * Their main purpose is in supporting .for loops that generate shell commands
+ * since an ordinary variable assignment at that point would terminate the
+ * dependency group for these targets. For example:
+ *
+ * list-targets: .USE
* .for i in ${.TARGET} ${.TARGET:R}.gz
- * @: ${t::=$i}
- * @echo blah ${t:T}
+ * @${t::=$i}
+ * @echo 'The target is ${t:T}.'
* .endfor
*
* ::=<str> Assigns <str> as the new value of variable.
@@ -3268,12 +3417,13 @@ ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
* variable.
*/
static ApplyModifierResult
-ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Assign(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
GNode *scope;
- char delim;
- char *val;
+ FStr val;
VarParseResult res;
+ LazyBuf buf;
const char *mod = *pp;
const char *op = mod + 1;
@@ -3283,22 +3433,13 @@ ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=')
goto ok;
return AMR_UNKNOWN; /* "::<unrecognised>" */
-ok:
- if (st->var->name.str[0] == '\0') {
+ok:
+ if (expr->name[0] == '\0') {
*pp = mod + 1;
return AMR_BAD;
}
- scope = st->scope; /* scope where v belongs */
- if (st->exprStatus == VES_NONE && st->scope != SCOPE_GLOBAL) {
- Var *gv = VarFind(st->var->name.str, st->scope, FALSE);
- if (gv == NULL)
- scope = SCOPE_GLOBAL;
- else
- VarFreeEnv(gv, TRUE);
- }
-
switch (op[0]) {
case '+':
case '?':
@@ -3310,41 +3451,51 @@ ok:
break;
}
- delim = st->startc == '(' ? ')' : '}';
- res = ParseModifierPart(pp, delim, st->eflags, st, &val);
+ res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ val = LazyBuf_DoneGet(&buf);
- (*pp)--;
+ (*pp)--; /* Go back to the ch->endc. */
- /* XXX: Expanding the variable name at this point sounds wrong. */
- if (st->eflags & VARE_WANTRES) {
- switch (op[0]) {
- case '+':
- Var_AppendExpand(scope, st->var->name.str, val);
- break;
- case '!': {
- const char *errfmt;
- char *cmd_output = Cmd_Exec(val, &errfmt);
- if (errfmt != NULL)
- Error(errfmt, val);
- else
- Var_SetExpand(scope,
- st->var->name.str, cmd_output);
- free(cmd_output);
- break;
- }
- case '?':
- if (st->exprStatus == VES_NONE)
- break;
- /* FALLTHROUGH */
- default:
- Var_SetExpand(scope, st->var->name.str, val);
+ if (!Expr_ShouldEval(expr))
+ goto done;
+
+ scope = expr->scope; /* scope where v belongs */
+ if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
+ Var *gv = VarFind(expr->name, expr->scope, false);
+ if (gv == NULL)
+ scope = SCOPE_GLOBAL;
+ else
+ VarFreeEnv(gv);
+ }
+
+ switch (op[0]) {
+ case '+':
+ Var_Append(scope, expr->name, val.str);
+ break;
+ case '!': {
+ const char *errfmt;
+ char *cmd_output = Cmd_Exec(val.str, &errfmt);
+ if (errfmt != NULL)
+ Error(errfmt, val.str);
+ else
+ Var_Set(scope, expr->name, cmd_output);
+ free(cmd_output);
+ break;
+ }
+ case '?':
+ if (expr->defined == DEF_REGULAR)
break;
- }
+ /* FALLTHROUGH */
+ default:
+ Var_Set(scope, expr->name, val.str);
+ break;
}
- free(val);
- st->newVal = FStr_InitRefer("");
+ Expr_SetValueRefer(expr, "");
+
+done:
+ FStr_Done(&val);
return AMR_OK;
}
@@ -3353,24 +3504,33 @@ ok:
* remember current value
*/
static ApplyModifierResult
-ApplyModifier_Remember(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_Remember(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *mod = *pp;
- if (!ModMatchEq(mod, "_", st->endc))
+ FStr name;
+
+ if (!ModMatchEq(mod, "_", ch))
return AMR_UNKNOWN;
+ name = FStr_InitRefer("_");
if (mod[1] == '=') {
- size_t n = strcspn(mod + 2, ":)}");
- char *name = bmake_strldup(mod + 2, n);
- Var_SetExpand(st->scope, name, val);
- free(name);
- *pp = mod + 2 + n;
- } else {
- Var_Set(st->scope, "_", val);
+ /*
+ * XXX: This ad-hoc call to strcspn deviates from the usual
+ * behavior defined in ParseModifierPart. This creates an
+ * unnecessary, undocumented inconsistency in make.
+ */
+ const char *arg = mod + 2;
+ size_t argLen = strcspn(arg, ":)}");
+ *pp = arg + argLen;
+ name = FStr_InitOwn(bmake_strldup(arg, argLen));
+ } else
*pp = mod + 1;
- }
- st->newVal = FStr_InitRefer(val);
+
+ if (Expr_ShouldEval(expr))
+ Var_Set(expr->scope, name.str, expr->value.str);
+ FStr_Done(&name);
+
return AMR_OK;
}
@@ -3379,40 +3539,68 @@ ApplyModifier_Remember(const char **pp, const char *val,
* for a single-letter modifier such as :H, :T.
*/
static ApplyModifierResult
-ApplyModifier_WordFunc(const char **pp, const char *val,
- ApplyModifiersState *st, ModifyWordsCallback modifyWord)
+ApplyModifier_WordFunc(const char **pp, ModChain *ch,
+ ModifyWordProc modifyWord)
{
- char delim = (*pp)[1];
- if (delim != st->endc && delim != ':')
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
-
- st->newVal = FStr_InitOwn(ModifyWords(val, modifyWord, NULL,
- st->oneBigWord, st->sep));
(*pp)++;
+
+ if (ModChain_ShouldEval(ch))
+ ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
+
return AMR_OK;
}
+/* Remove adjacent duplicate words. */
static ApplyModifierResult
-ApplyModifier_Unique(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Unique(const char **pp, ModChain *ch)
{
- if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
- st->newVal = FStr_InitOwn(VarUniq(val));
- (*pp)++;
- return AMR_OK;
- } else
+ Words words;
+
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ words = Str_Words(ch->expr->value.str, false);
+
+ if (words.len > 1) {
+ size_t si, di;
+
+ di = 0;
+ for (si = 1; si < words.len; si++) {
+ if (strcmp(words.words[si], words.words[di]) != 0) {
+ di++;
+ if (di != si)
+ words.words[di] = words.words[si];
+ }
+ }
+ words.len = di + 1;
+ }
+
+ Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+
+ return AMR_OK;
}
#ifdef SYSVVARSUB
/* :from=to */
static ApplyModifierResult
-ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_SysV(const char **pp, ModChain *ch)
{
- char *lhs, *rhs;
+ Expr *expr = ch->expr;
VarParseResult res;
+ LazyBuf lhsBuf, rhsBuf;
+ FStr rhs;
+ struct ModifyWord_SysVSubstArgs args;
+ Substring lhs;
+ const char *lhsSuffix;
const char *mod = *pp;
- Boolean eqFound = FALSE;
+ bool eqFound = false;
/*
* First we make a pass through the string trying to verify it is a
@@ -3422,38 +3610,50 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
const char *p = mod;
while (*p != '\0' && depth > 0) {
if (*p == '=') { /* XXX: should also test depth == 1 */
- eqFound = TRUE;
- /* continue looking for st->endc */
- } else if (*p == st->endc)
+ eqFound = true;
+ /* continue looking for ch->endc */
+ } else if (*p == ch->endc)
depth--;
- else if (*p == st->startc)
+ else if (*p == ch->startc)
depth++;
if (depth > 0)
p++;
}
- if (*p != st->endc || !eqFound)
+ if (*p != ch->endc || !eqFound)
return AMR_UNKNOWN;
- res = ParseModifierPart(pp, '=', st->eflags, st, &lhs);
+ res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
/* The SysV modifier lasts until the end of the variable expression. */
- res = ParseModifierPart(pp, st->endc, st->eflags, st, &rhs);
- if (res != VPR_OK)
+ res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf);
+ if (res != VPR_OK) {
+ LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
-
- (*pp)--;
- if (lhs[0] == '\0' && val[0] == '\0') {
- st->newVal = FStr_InitRefer(val); /* special case */
- } else {
- struct ModifyWord_SYSVSubstArgs args = { st->scope, lhs, rhs };
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_SYSVSubst, &args,
- st->oneBigWord, st->sep));
}
- free(lhs);
- free(rhs);
+ rhs = LazyBuf_DoneGet(&rhsBuf);
+
+ (*pp)--; /* Go back to the ch->endc. */
+
+ /* Do not turn an empty expression into non-empty. */
+ if (lhsBuf.len == 0 && expr->value.str[0] == '\0')
+ goto done;
+
+ lhs = LazyBuf_Get(&lhsBuf);
+ lhsSuffix = Substring_SkipFirst(lhs, '%');
+
+ args.scope = expr->scope;
+ args.lhsPrefix = Substring_Init(lhs.start,
+ lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start);
+ args.lhsPercent = lhsSuffix != lhs.start;
+ args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end);
+ args.rhs = rhs.str;
+
+ ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord);
+
+done:
+ LazyBuf_Done(&lhsBuf);
return AMR_OK;
}
#endif
@@ -3461,133 +3661,155 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
#ifdef SUNSHCMD
/* :sh */
static ApplyModifierResult
-ApplyModifier_SunShell(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_SunShell(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *p = *pp;
- if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) {
- if (st->eflags & VARE_WANTRES) {
- const char *errfmt;
- st->newVal = FStr_InitOwn(Cmd_Exec(val, &errfmt));
- if (errfmt != NULL)
- Error(errfmt, val);
- } else
- st->newVal = FStr_InitRefer("");
- *pp = p + 2;
- return AMR_OK;
- } else
+ if (!(p[1] == 'h' && IsDelimiter(p[2], ch)))
return AMR_UNKNOWN;
+ *pp = p + 2;
+
+ if (Expr_ShouldEval(expr)) {
+ const char *errfmt;
+ char *output = Cmd_Exec(expr->value.str, &errfmt);
+ if (errfmt != NULL)
+ Error(errfmt, expr->value.str);
+ Expr_SetValueOwn(expr, output);
+ }
+
+ return AMR_OK;
}
#endif
static void
-LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc,
- const char *val)
+LogBeforeApply(const ModChain *ch, const char *mod)
{
- char eflags_str[VarEvalFlags_ToStringSize];
- char vflags_str[VarFlags_ToStringSize];
- Boolean is_single_char = mod[0] != '\0' &&
- (mod[1] == endc || mod[1] == ':');
+ const Expr *expr = ch->expr;
+ bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch);
+
+ /*
+ * At this point, only the first character of the modifier can
+ * be used since the end of the modifier is not yet known.
+ */
- /* At this point, only the first character of the modifier can
- * be used since the end of the modifier is not yet known. */
- debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n",
- st->var->name.str, mod[0], is_single_char ? "" : "...", val,
- VarEvalFlags_ToString(eflags_str, st->eflags),
- VarFlags_ToString(vflags_str, st->var->flags),
- VarExprStatus_Name[st->exprStatus]);
+ if (!Expr_ShouldEval(expr)) {
+ debug_printf("Parsing modifier ${%s:%c%s}\n",
+ expr->name, mod[0], is_single_char ? "" : "...");
+ return;
+ }
+
+ if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
+ expr->defined == DEF_REGULAR) {
+ debug_printf(
+ "Evaluating modifier ${%s:%c%s} on value \"%s\"\n",
+ expr->name, mod[0], is_single_char ? "" : "...",
+ expr->value.str);
+ return;
+ }
+
+ debug_printf(
+ "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n",
+ expr->name, mod[0], is_single_char ? "" : "...", expr->value.str,
+ VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]);
}
static void
-LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
+LogAfterApply(const ModChain *ch, const char *p, const char *mod)
{
- char eflags_str[VarEvalFlags_ToStringSize];
- char vflags_str[VarFlags_ToStringSize];
- const char *quot = st->newVal.str == var_Error ? "" : "\"";
- const char *newVal =
- st->newVal.str == var_Error ? "error" : st->newVal.str;
+ const Expr *expr = ch->expr;
+ const char *value = expr->value.str;
+ const char *quot = value == var_Error ? "" : "\"";
+
+ if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
+ expr->defined == DEF_REGULAR) {
+
+ debug_printf("Result of ${%s:%.*s} is %s%s%s\n",
+ expr->name, (int)(p - mod), mod,
+ quot, value == var_Error ? "error" : value, quot);
+ return;
+ }
- debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n",
- st->var->name.str, (int)(p - mod), mod, quot, newVal, quot,
- VarEvalFlags_ToString(eflags_str, st->eflags),
- VarFlags_ToString(vflags_str, st->var->flags),
- VarExprStatus_Name[st->exprStatus]);
+ debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n",
+ expr->name, (int)(p - mod), mod,
+ quot, value == var_Error ? "error" : value, quot,
+ VarEvalMode_Name[expr->emode],
+ ExprDefined_Name[expr->defined]);
}
static ApplyModifierResult
-ApplyModifier(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier(const char **pp, ModChain *ch)
{
switch (**pp) {
+ case '!':
+ return ApplyModifier_ShellCommand(pp, ch);
case ':':
- return ApplyModifier_Assign(pp, st);
+ return ApplyModifier_Assign(pp, ch);
+ case '?':
+ return ApplyModifier_IfElse(pp, ch);
case '@':
- return ApplyModifier_Loop(pp, val, st);
+ return ApplyModifier_Loop(pp, ch);
+ case '[':
+ return ApplyModifier_Words(pp, ch);
case '_':
- return ApplyModifier_Remember(pp, val, st);
+ return ApplyModifier_Remember(pp, ch);
+#ifndef NO_REGEX
+ case 'C':
+ return ApplyModifier_Regex(pp, ch);
+#endif
case 'D':
- case 'U':
- return ApplyModifier_Defined(pp, val, st);
- case 'L':
- return ApplyModifier_Literal(pp, st);
- case 'P':
- return ApplyModifier_Path(pp, st);
- case '!':
- return ApplyModifier_ShellCommand(pp, st);
- case '[':
- return ApplyModifier_Words(pp, val, st);
+ return ApplyModifier_Defined(pp, ch);
+ case 'E':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
case 'g':
- return ApplyModifier_Gmtime(pp, val, st);
+ return ApplyModifier_Gmtime(pp, ch);
+ case 'H':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head);
case 'h':
- return ApplyModifier_Hash(pp, val, st);
+ return ApplyModifier_Hash(pp, ch);
+ case 'L':
+ return ApplyModifier_Literal(pp, ch);
case 'l':
- return ApplyModifier_Localtime(pp, val, st);
- case 't':
- return ApplyModifier_To(pp, val, st);
- case 'N':
+ return ApplyModifier_Localtime(pp, ch);
case 'M':
- return ApplyModifier_Match(pp, val, st);
- case 'S':
- return ApplyModifier_Subst(pp, val, st);
- case '?':
- return ApplyModifier_IfElse(pp, st);
-#ifndef NO_REGEX
- case 'C':
- return ApplyModifier_Regex(pp, val, st);
-#endif
- case 'q':
+ case 'N':
+ return ApplyModifier_Match(pp, ch);
+ case 'O':
+ return ApplyModifier_Order(pp, ch);
+ case 'P':
+ return ApplyModifier_Path(pp, ch);
case 'Q':
- return ApplyModifier_Quote(pp, val, st);
- case 'T':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Tail);
- case 'H':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Head);
- case 'E':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Suffix);
+ case 'q':
+ return ApplyModifier_Quote(pp, ch);
case 'R':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Root);
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root);
case 'r':
- return ApplyModifier_Range(pp, val, st);
- case 'O':
- return ApplyModifier_Order(pp, val, st);
- case 'u':
- return ApplyModifier_Unique(pp, val, st);
+ return ApplyModifier_Range(pp, ch);
+ case 'S':
+ return ApplyModifier_Subst(pp, ch);
#ifdef SUNSHCMD
case 's':
- return ApplyModifier_SunShell(pp, val, st);
+ return ApplyModifier_SunShell(pp, ch);
#endif
+ case 'T':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail);
+ case 't':
+ return ApplyModifier_To(pp, ch);
+ case 'U':
+ return ApplyModifier_Defined(pp, ch);
+ case 'u':
+ return ApplyModifier_Unique(pp, ch);
default:
return AMR_UNKNOWN;
}
}
-static FStr ApplyModifiers(const char **, FStr, char, char, Var *,
- VarExprStatus *, GNode *, VarEvalFlags);
+static void ApplyModifiers(Expr *, const char **, char, char);
typedef enum ApplyModifiersIndirectResult {
/* The indirect modifiers have been applied successfully. */
AMIR_CONTINUE,
/* Fall back to the SysV modifier. */
- AMIR_APPLY_MODS,
+ AMIR_SYSV,
/* Error out. */
AMIR_OUT
} ApplyModifiersIndirectResult;
@@ -3602,25 +3824,22 @@ typedef enum ApplyModifiersIndirectResult {
* Multiple groups of indirect modifiers can be chained by separating them
* with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
*
- * If the variable expression is not followed by st->endc or ':', fall
+ * If the variable expression is not followed by ch->endc or ':', fall
* back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
- *
- * The expression ${VAR:${M1}${M2}} is not treated as an indirect
- * modifier, and it is neither a SysV modifier but a parse error.
*/
static ApplyModifiersIndirectResult
-ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
- FStr *inout_value)
+ApplyModifiersIndirect(ModChain *ch, const char **pp)
{
+ Expr *expr = ch->expr;
const char *p = *pp;
FStr mods;
- (void)Var_Parse(&p, st->scope, st->eflags, &mods);
+ (void)Var_Parse(&p, expr->scope, expr->emode, &mods);
/* TODO: handle errors */
- if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) {
+ if (mods.str[0] != '\0' && *p != '\0' && !IsDelimiter(*p, ch)) {
FStr_Done(&mods);
- return AMIR_APPLY_MODS;
+ return AMIR_SYSV;
}
DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
@@ -3628,10 +3847,8 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
if (mods.str[0] != '\0') {
const char *modsp = mods.str;
- FStr newVal = ApplyModifiers(&modsp, *inout_value, '\0', '\0',
- st->var, &st->exprStatus, st->scope, st->eflags);
- *inout_value = newVal;
- if (newVal.str == var_Error || *modsp != '\0') {
+ ApplyModifiers(expr, &modsp, '\0', '\0');
+ if (expr->value.str == var_Error || *modsp != '\0') {
FStr_Done(&mods);
*pp = p;
return AMIR_OUT; /* error already reported */
@@ -3641,10 +3858,10 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
if (*p == ':')
p++;
- else if (*p == '\0' && st->endc != '\0') {
- Error("Unclosed variable specification after complex "
- "modifier (expecting '%c') for %s",
- st->endc, st->var->name.str);
+ else if (*p == '\0' && ch->endc != '\0') {
+ Error("Unclosed variable expression after indirect "
+ "modifier, expecting '%c' for variable \"%s\"",
+ ch->endc, expr->name);
*pp = p;
return AMIR_OUT;
}
@@ -3654,36 +3871,36 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
}
static ApplyModifierResult
-ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
- const char **pp, FStr *inout_value)
+ApplySingleModifier(const char **pp, ModChain *ch)
{
ApplyModifierResult res;
+ const char *mod = *pp;
const char *p = *pp;
- const char *const val = inout_value->str;
if (DEBUG(VAR))
- LogBeforeApply(st, mod, endc, val);
+ LogBeforeApply(ch, mod);
- res = ApplyModifier(&p, val, st);
+ res = ApplyModifier(&p, ch);
#ifdef SYSVVARSUB
if (res == AMR_UNKNOWN) {
assert(p == mod);
- res = ApplyModifier_SysV(&p, val, st);
+ res = ApplyModifier_SysV(&p, ch);
}
#endif
if (res == AMR_UNKNOWN) {
- Parse_Error(PARSE_FATAL, "Unknown modifier '%c'", *mod);
/*
* Guess the end of the current modifier.
* XXX: Skipping the rest of the modifier hides
* errors and leads to wrong results.
* Parsing should rather stop here.
*/
- for (p++; *p != ':' && *p != st->endc && *p != '\0'; p++)
+ for (p++; !IsDelimiter(*p, ch) && *p != '\0'; p++)
continue;
- st->newVal = FStr_InitRefer(var_Error);
+ Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"",
+ (int)(p - mod), mod);
+ Expr_SetValueRefer(ch->expr, var_Error);
}
if (res == AMR_CLEANUP || res == AMR_BAD) {
*pp = p;
@@ -3691,20 +3908,18 @@ ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
}
if (DEBUG(VAR))
- LogAfterApply(st, p, mod);
+ LogAfterApply(ch, p, mod);
- if (st->newVal.str != val) {
- FStr_Done(inout_value);
- *inout_value = st->newVal;
- }
- if (*p == '\0' && st->endc != '\0') {
+ if (*p == '\0' && ch->endc != '\0') {
Error(
- "Unclosed variable specification (expecting '%c') "
- "for \"%s\" (value \"%s\") modifier %c",
- st->endc, st->var->name.str, inout_value->str, *mod);
+ "Unclosed variable expression, expecting '%c' for "
+ "modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
+ ch->endc,
+ (int)(p - mod), mod,
+ ch->expr->name, ch->expr->value.str);
} else if (*p == ':') {
p++;
- } else if (opts.strict && *p != '\0' && *p != endc) {
+ } else if (opts.strict && *p != '\0' && *p != ch->endc) {
Parse_Error(PARSE_FATAL,
"Missing delimiter ':' after modifier \"%.*s\"",
(int)(p - mod), mod);
@@ -3717,44 +3932,46 @@ ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
return AMR_OK;
}
+#if __STDC_VERSION__ >= 199901L
+#define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \
+ (ModChain) { expr, startc, endc, sep, oneBigWord }
+#else
+MAKE_INLINE ModChain
+ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
+{
+ ModChain ch;
+ ch.expr = expr;
+ ch.startc = startc;
+ ch.endc = endc;
+ ch.sep = sep;
+ ch.oneBigWord = oneBigWord;
+ return ch;
+}
+#endif
+
/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
-static FStr
+static void
ApplyModifiers(
- const char **pp, /* the parsing position, updated upon return */
- FStr value, /* the current value of the expression */
- char startc, /* '(' or '{', or '\0' for indirect modifiers */
- char endc, /* ')' or '}', or '\0' for indirect modifiers */
- Var *v,
- VarExprStatus *exprStatus,
- GNode *scope, /* for looking up and modifying variables */
- VarEvalFlags eflags
+ Expr *expr,
+ const char **pp, /* the parsing position, updated upon return */
+ char startc, /* '(' or '{'; or '\0' for indirect modifiers */
+ char endc /* ')' or '}'; or '\0' for indirect modifiers */
)
{
- ApplyModifiersState st = {
- startc, endc, v, scope, eflags,
-#if defined(lint)
- /* lint cannot parse C99 struct initializers yet. */
- { var_Error, NULL },
-#else
- FStr_InitRefer(var_Error), /* .newVal */
-#endif
- ' ', /* .sep */
- FALSE, /* .oneBigWord */
- *exprStatus /* .exprStatus */
- };
+ ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false);
const char *p;
const char *mod;
assert(startc == '(' || startc == '{' || startc == '\0');
assert(endc == ')' || endc == '}' || endc == '\0');
- assert(value.str != NULL);
+ assert(expr->value.str != NULL);
p = *pp;
if (*p == '\0' && endc != '\0') {
Error(
"Unclosed variable expression (expecting '%c') for \"%s\"",
- st.endc, st.var->name.str);
+ ch.endc, expr->name);
goto cleanup;
}
@@ -3762,19 +3979,22 @@ ApplyModifiers(
ApplyModifierResult res;
if (*p == '$') {
- ApplyModifiersIndirectResult amir;
- amir = ApplyModifiersIndirect(&st, &p, &value);
+ ApplyModifiersIndirectResult amir =
+ ApplyModifiersIndirect(&ch, &p);
if (amir == AMIR_CONTINUE)
continue;
if (amir == AMIR_OUT)
break;
+ /*
+ * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
+ * it as a SysV modifier, as that is the only modifier
+ * that can start with '$'.
+ */
}
- /* default value, in case of errors */
- st.newVal = FStr_InitRefer(var_Error);
mod = p;
- res = ApplySingleModifier(&st, mod, endc, &p, &value);
+ res = ApplySingleModifier(&p, &ch);
if (res == AMR_CLEANUP)
goto cleanup;
if (res == AMR_BAD)
@@ -3782,48 +4002,60 @@ ApplyModifiers(
}
*pp = p;
- assert(value.str != NULL); /* Use var_Error or varUndefined instead. */
- *exprStatus = st.exprStatus;
- return value;
+ assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */
+ return;
bad_modifier:
/* XXX: The modifier end is only guessed. */
- Error("Bad modifier `:%.*s' for %s",
- (int)strcspn(mod, ":)}"), mod, st.var->name.str);
+ Error("Bad modifier \":%.*s\" for variable \"%s\"",
+ (int)strcspn(mod, ":)}"), mod, expr->name);
cleanup:
+ /*
+ * TODO: Use p + strlen(p) instead, to stop parsing immediately.
+ *
+ * In the unit tests, this generates a few unterminated strings in the
+ * shell commands though. Instead of producing these unfinished
+ * strings, commands with evaluation errors should not be run at all.
+ *
+ * To make that happen, Var_Subst must report the actual errors
+ * instead of returning VPR_OK unconditionally.
+ */
*pp = p;
- FStr_Done(&value);
- *exprStatus = st.exprStatus;
- return FStr_InitRefer(var_Error);
+ Expr_SetValueRefer(expr, var_Error);
}
/*
- * Only four of the local variables are treated specially as they are the
- * only four that will be set when dynamic sources are expanded.
+ * Only 4 of the 7 local variables are treated specially as they are the only
+ * ones that will be set when dynamic sources are expanded.
*/
-static Boolean
-VarnameIsDynamic(const char *name, size_t len)
+static bool
+VarnameIsDynamic(Substring varname)
{
+ const char *name;
+ size_t len;
+
+ name = varname.start;
+ len = Substring_Length(varname);
if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) {
switch (name[0]) {
case '@':
case '%':
case '*':
case '!':
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) {
- return strcmp(name, ".TARGET") == 0 ||
- strcmp(name, ".ARCHIVE") == 0 ||
- strcmp(name, ".PREFIX") == 0 ||
- strcmp(name, ".MEMBER") == 0;
+ return Substring_Equals(varname, ".TARGET") ||
+ Substring_Equals(varname, ".ARCHIVE") ||
+ Substring_Equals(varname, ".PREFIX") ||
+ Substring_Equals(varname, ".MEMBER");
}
- return FALSE;
+ return false;
}
static const char *
@@ -3857,58 +4089,45 @@ UndefinedShortVarValue(char varname, const GNode *scope)
* Parse a variable name, until the end character or a colon, whichever
* comes first.
*/
-static char *
+static void
ParseVarname(const char **pp, char startc, char endc,
- GNode *scope, VarEvalFlags eflags,
- size_t *out_varname_len)
+ GNode *scope, VarEvalMode emode,
+ LazyBuf *buf)
{
- Buffer buf;
const char *p = *pp;
- int depth = 1;
+ int depth = 0; /* Track depth so we can spot parse errors. */
- Buf_Init(&buf);
+ LazyBuf_Init(buf, p);
while (*p != '\0') {
- /* Track depth so we can spot parse errors. */
+ if ((*p == endc || *p == ':') && depth == 0)
+ break;
if (*p == startc)
depth++;
- if (*p == endc) {
- if (--depth == 0)
- break;
- }
- if (*p == ':' && depth == 1)
- break;
+ if (*p == endc)
+ depth--;
/* A variable inside a variable, expand. */
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, scope, eflags, &nested_val);
+ (void)Var_Parse(&p, scope, emode, &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ LazyBuf_AddStr(buf, nested_val.str);
FStr_Done(&nested_val);
} else {
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(buf, *p);
p++;
}
}
*pp = p;
- *out_varname_len = buf.len;
- return Buf_DoneData(&buf);
}
static VarParseResult
ValidShortVarname(char varname, const char *start)
{
- switch (varname) {
- case '\0':
- case ')':
- case '}':
- case ':':
- case '$':
- break; /* and continue below */
- default:
+ if (varname != '$' && varname != ':' && varname != '}' &&
+ varname != ')' && varname != '\0')
return VPR_OK;
- }
if (!opts.strict)
return VPR_ERR; /* XXX: Missing error message */
@@ -3926,50 +4145,45 @@ ValidShortVarname(char varname, const char *start)
}
/*
- * Parse a single-character variable name such as $V or $@.
+ * Parse a single-character variable name such as in $V or $@.
* Return whether to continue parsing.
*/
-static Boolean
-ParseVarnameShort(char startc, const char **pp, GNode *scope,
- VarEvalFlags eflags,
- VarParseResult *out_FALSE_res, const char **out_FALSE_val,
- Var **out_TRUE_var)
+static bool
+ParseVarnameShort(char varname, const char **pp, GNode *scope,
+ VarEvalMode emode,
+ VarParseResult *out_false_res, const char **out_false_val,
+ Var **out_true_var)
{
char name[2];
Var *v;
VarParseResult vpr;
- /*
- * If it's not bounded by braces of some sort, life is much simpler.
- * We just need to check for the first character and return the
- * value if it exists.
- */
-
- vpr = ValidShortVarname(startc, *pp);
+ vpr = ValidShortVarname(varname, *pp);
if (vpr != VPR_OK) {
(*pp)++;
- *out_FALSE_val = var_Error;
- *out_FALSE_res = vpr;
- return FALSE;
+ *out_false_res = vpr;
+ *out_false_val = var_Error;
+ return false;
}
- name[0] = startc;
+ name[0] = varname;
name[1] = '\0';
- v = VarFind(name, scope, TRUE);
+ v = VarFind(name, scope, true);
if (v == NULL) {
const char *val;
*pp += 2;
- val = UndefinedShortVarValue(startc, scope);
+ val = UndefinedShortVarValue(varname, scope);
if (val == NULL)
- val = eflags & VARE_UNDEFERR ? var_Error : varUndefined;
+ val = emode == VARE_UNDEFERR
+ ? var_Error : varUndefined;
if (opts.strict && val == var_Error) {
Parse_Error(PARSE_FATAL,
"Variable \"%s\" is undefined", name);
- *out_FALSE_res = VPR_ERR;
- *out_FALSE_val = val;
- return FALSE;
+ *out_false_res = VPR_ERR;
+ *out_false_val = val;
+ return false;
}
/*
@@ -3982,72 +4196,64 @@ ParseVarnameShort(char startc, const char **pp, GNode *scope,
* If undefined expressions are allowed, this should rather
* be VPR_UNDEF instead of VPR_OK.
*/
- *out_FALSE_res = eflags & VARE_UNDEFERR ? VPR_UNDEF : VPR_OK;
- *out_FALSE_val = val;
- return FALSE;
+ *out_false_res = emode == VARE_UNDEFERR
+ ? VPR_UNDEF : VPR_OK;
+ *out_false_val = val;
+ return false;
}
- *out_TRUE_var = v;
- return TRUE;
+ *out_true_var = v;
+ return true;
}
/* Find variables like @F or <D. */
static Var *
-FindLocalLegacyVar(const char *varname, size_t namelen, GNode *scope,
+FindLocalLegacyVar(Substring varname, GNode *scope,
const char **out_extraModifiers)
{
+ Var *v;
+
/* Only resolve these variables if scope is a "real" target. */
if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL)
return NULL;
- if (namelen != 2)
+ if (Substring_Length(varname) != 2)
return NULL;
- if (varname[1] != 'F' && varname[1] != 'D')
+ if (varname.start[1] != 'F' && varname.start[1] != 'D')
return NULL;
- if (strchr("@%?*!<>", varname[0]) == NULL)
+ if (strchr("@%?*!<>", varname.start[0]) == NULL)
return NULL;
- {
- char name[] = { varname[0], '\0' };
- Var *v = VarFind(name, scope, FALSE);
-
- if (v != NULL) {
- if (varname[1] == 'D') {
- *out_extraModifiers = "H:";
- } else { /* F */
- *out_extraModifiers = "T:";
- }
- }
- return v;
- }
+ v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false);
+ if (v == NULL)
+ return NULL;
+
+ *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:";
+ return v;
}
static VarParseResult
-EvalUndefined(Boolean dynamic, const char *start, const char *p, char *varname,
- VarEvalFlags eflags,
- FStr *out_val)
+EvalUndefined(bool dynamic, const char *start, const char *p,
+ Substring varname, VarEvalMode emode, FStr *out_val)
{
if (dynamic) {
*out_val = FStr_InitOwn(bmake_strsedup(start, p));
- free(varname);
return VPR_OK;
}
- if ((eflags & VARE_UNDEFERR) && opts.strict) {
+ if (emode == VARE_UNDEFERR && opts.strict) {
Parse_Error(PARSE_FATAL,
- "Variable \"%s\" is undefined", varname);
- free(varname);
+ "Variable \"%.*s\" is undefined",
+ (int)Substring_Length(varname), varname.start);
*out_val = FStr_InitRefer(var_Error);
return VPR_ERR;
}
- if (eflags & VARE_UNDEFERR) {
- free(varname);
+ if (emode == VARE_UNDEFERR) {
*out_val = FStr_InitRefer(var_Error);
return VPR_UNDEF; /* XXX: Should be VPR_ERR instead. */
}
- free(varname);
*out_val = FStr_InitRefer(varUndefined);
return VPR_OK;
}
@@ -4058,59 +4264,60 @@ EvalUndefined(Boolean dynamic, const char *start, const char *p, char *varname,
* ${VAR:Modifiers}, up to the ':' that starts the modifiers.
* Return whether to continue parsing.
*/
-static Boolean
+static bool
ParseVarnameLong(
- const char *p,
+ const char **pp,
char startc,
GNode *scope,
- VarEvalFlags eflags,
-
- const char **out_FALSE_pp,
- VarParseResult *out_FALSE_res,
- FStr *out_FALSE_val,
-
- char *out_TRUE_endc,
- const char **out_TRUE_p,
- Var **out_TRUE_v,
- Boolean *out_TRUE_haveModifier,
- const char **out_TRUE_extraModifiers,
- Boolean *out_TRUE_dynamic,
- VarExprStatus *out_TRUE_exprStatus
+ VarEvalMode emode,
+
+ const char **out_false_pp,
+ VarParseResult *out_false_res,
+ FStr *out_false_val,
+
+ char *out_true_endc,
+ Var **out_true_v,
+ bool *out_true_haveModifier,
+ const char **out_true_extraModifiers,
+ bool *out_true_dynamic,
+ ExprDefined *out_true_exprDefined
)
{
- size_t namelen;
- char *varname;
+ LazyBuf varname;
Var *v;
- Boolean haveModifier;
- Boolean dynamic = FALSE;
+ bool haveModifier;
+ bool dynamic = false;
+ const char *p = *pp;
const char *const start = p;
char endc = startc == '(' ? ')' : '}';
p += 2; /* skip "${" or "$(" or "y(" */
- varname = ParseVarname(&p, startc, endc, scope, eflags, &namelen);
+ ParseVarname(&p, startc, endc, scope, emode, &varname);
if (*p == ':') {
- haveModifier = TRUE;
+ haveModifier = true;
} else if (*p == endc) {
- haveModifier = FALSE;
+ haveModifier = false;
} else {
- Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", varname);
- free(varname);
- *out_FALSE_pp = p;
- *out_FALSE_val = FStr_InitRefer(var_Error);
- *out_FALSE_res = VPR_ERR;
- return FALSE;
+ Substring name = LazyBuf_Get(&varname);
+ Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
+ (int)Substring_Length(name), name.start);
+ LazyBuf_Done(&varname);
+ *out_false_pp = p;
+ *out_false_val = FStr_InitRefer(var_Error);
+ *out_false_res = VPR_ERR;
+ return false;
}
- v = VarFind(varname, scope, TRUE);
+ v = VarFindSubstring(LazyBuf_Get(&varname), scope, true);
/* At this point, p points just after the variable name,
* either at ':' or at endc. */
if (v == NULL) {
- v = FindLocalLegacyVar(varname, namelen, scope,
- out_TRUE_extraModifiers);
+ v = FindLocalLegacyVar(LazyBuf_Get(&varname), scope,
+ out_true_extraModifiers);
}
if (v == NULL) {
@@ -4118,15 +4325,15 @@ ParseVarnameLong(
* Defer expansion of dynamic variables if they appear in
* non-local scope since they are not defined there.
*/
- dynamic = VarnameIsDynamic(varname, namelen) &&
+ dynamic = VarnameIsDynamic(LazyBuf_Get(&varname)) &&
(scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
if (!haveModifier) {
p++; /* skip endc */
- *out_FALSE_pp = p;
- *out_FALSE_res = EvalUndefined(dynamic, start, p,
- varname, eflags, out_FALSE_val);
- return FALSE;
+ *out_false_pp = p;
+ *out_false_res = EvalUndefined(dynamic, start, p,
+ LazyBuf_Get(&varname), emode, out_false_val);
+ return false;
}
/*
@@ -4135,34 +4342,34 @@ ParseVarnameLong(
* variable name, such as :L or :?.
*
* Most modifiers leave this expression in the "undefined"
- * state (VEF_UNDEF), only a few modifiers like :D, :U, :L,
+ * state (VES_UNDEF), only a few modifiers like :D, :U, :L,
* :P turn this undefined expression into a defined
- * expression (VEF_DEF).
+ * expression (VES_DEF).
*
- * At the end, after applying all modifiers, if the expression
+ * In the end, after applying all modifiers, if the expression
* is still undefined, Var_Parse will return an empty string
* instead of the actually computed value.
*/
- v = VarNew(FStr_InitOwn(varname), "", VAR_NONE);
- *out_TRUE_exprStatus = VES_UNDEF;
+ v = VarNew(LazyBuf_DoneGet(&varname), "", false, false);
+ *out_true_exprDefined = DEF_UNDEF;
} else
- free(varname);
+ LazyBuf_Done(&varname);
- *out_TRUE_endc = endc;
- *out_TRUE_p = p;
- *out_TRUE_v = v;
- *out_TRUE_haveModifier = haveModifier;
- *out_TRUE_dynamic = dynamic;
- return TRUE;
+ *pp = p;
+ *out_true_endc = endc;
+ *out_true_v = v;
+ *out_true_haveModifier = haveModifier;
+ *out_true_dynamic = dynamic;
+ return true;
}
/* Free the environment variable now since we own it. */
static void
-FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
+FreeEnvVar(Var *v, FStr *inout_val)
{
char *varValue = Buf_DoneData(&v->val);
- if (value == varValue)
- *out_val_freeIt = varValue;
+ if (inout_val->str == varValue)
+ inout_val->freeIt = varValue;
else
free(varValue);
@@ -4170,6 +4377,54 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
free(v);
}
+#if __STDC_VERSION__ >= 199901L
+#define Expr_Literal(name, value, emode, scope, defined) \
+ { name, value, emode, scope, defined }
+#else
+MAKE_INLINE Expr
+Expr_Literal(const char *name, FStr value,
+ VarEvalMode emode, GNode *scope, ExprDefined defined)
+{
+ Expr expr;
+
+ expr.name = name;
+ expr.value = value;
+ expr.emode = emode;
+ expr.scope = scope;
+ expr.defined = defined;
+ return expr;
+}
+#endif
+
+/*
+ * Expressions of the form ${:U...} with a trivial value are often generated
+ * by .for loops and are boring, therefore parse and evaluate them in a fast
+ * lane without debug logging.
+ */
+static bool
+Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
+{
+ const char *p;
+
+ p = *pp;
+ if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U'))
+ return false;
+
+ p += 4;
+ while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' &&
+ *p != '}' && *p != '\0')
+ p++;
+ if (*p != '}')
+ return false;
+
+ if (emode == VARE_PARSE_ONLY)
+ *out_value = FStr_InitRefer("");
+ else
+ *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p));
+ *pp = p + 1;
+ return true;
+}
+
/*
* Given the start of a variable expression (such as $v, $(VAR),
* ${VAR:Mpattern}), extract the variable name and value, and the modifiers,
@@ -4183,7 +4438,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
* point to the "y" of "empty(VARNAME:Modifiers)", which
* is syntactically the same.
* scope The scope for finding variables
- * eflags Control the exact details of parsing
+ * emode Controls the exact details of parsing and evaluation
*
* Output:
* *pp The position where to continue parsing.
@@ -4195,49 +4450,48 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
* *out_val The value of the variable expression, never NULL.
* *out_val var_Error if there was a parse error.
* *out_val var_Error if the base variable of the expression was
- * undefined, eflags contains VARE_UNDEFERR, and none of
+ * undefined, emode is VARE_UNDEFERR, and none of
* the modifiers turned the undefined expression into a
* defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
* *out_val varUndefined if the base variable of the expression
- * was undefined, eflags did not contain VARE_UNDEFERR,
+ * was undefined, emode was not VARE_UNDEFERR,
* and none of the modifiers turned the undefined
* expression into a defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
- * *out_val_freeIt Must be freed by the caller after using *out_val.
*/
-/* coverity[+alloc : arg-*4] */
VarParseResult
-Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
+Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
{
const char *p = *pp;
const char *const start = p;
- /* TRUE if have modifiers for the variable. */
- Boolean haveModifier;
+ /* true if have modifiers for the variable. */
+ bool haveModifier;
/* Starting character if variable in parens or braces. */
char startc;
/* Ending character if variable in parens or braces. */
char endc;
/*
- * TRUE if the variable is local and we're expanding it in a
+ * true if the variable is local and we're expanding it in a
* non-local scope. This is done to support dynamic sources.
* The result is just the expression, unaltered.
*/
- Boolean dynamic;
+ bool dynamic;
const char *extramodifiers;
Var *v;
- FStr value;
- char eflags_str[VarEvalFlags_ToStringSize];
- VarExprStatus exprStatus = VES_NONE;
+ Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode,
+ scope, DEF_REGULAR);
- DEBUG2(VAR, "Var_Parse: %s with %s\n", start,
- VarEvalFlags_ToString(eflags_str, eflags));
+ if (Var_Parse_FastLane(pp, emode, out_val))
+ return VPR_OK;
+
+ DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
*out_val = FStr_InitRefer(NULL);
extramodifiers = NULL; /* extra modifiers to apply first */
- dynamic = FALSE;
+ dynamic = false;
/*
* Appease GCC, which thinks that the variable might not be
@@ -4248,21 +4502,22 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
startc = p[1];
if (startc != '(' && startc != '{') {
VarParseResult res;
- if (!ParseVarnameShort(startc, pp, scope, eflags, &res,
+ if (!ParseVarnameShort(startc, pp, scope, emode, &res,
&out_val->str, &v))
return res;
- haveModifier = FALSE;
+ haveModifier = false;
p++;
} else {
VarParseResult res;
- if (!ParseVarnameLong(p, startc, scope, eflags,
+ if (!ParseVarnameLong(&p, startc, scope, emode,
pp, &res, out_val,
- &endc, &p, &v, &haveModifier, &extramodifiers,
- &dynamic, &exprStatus))
+ &endc, &v, &haveModifier, &extramodifiers,
+ &dynamic, &expr.defined))
return res;
}
- if (v->flags & VAR_IN_USE)
+ expr.name = v->name.str;
+ if (v->inUse)
Fatal("Variable %s is recursive.", v->name.str);
/*
@@ -4274,37 +4529,34 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
* the then-current value of the variable. This might also invoke
* undefined behavior.
*/
- value = FStr_InitRefer(v->val.data);
+ expr.value = FStr_InitRefer(v->val.data);
/*
* Before applying any modifiers, expand any nested expressions from
* the variable value.
*/
- if (strchr(value.str, '$') != NULL && (eflags & VARE_WANTRES)) {
+ if (strchr(expr.value.str, '$') != NULL &&
+ VarEvalMode_ShouldEval(emode)) {
char *expanded;
- VarEvalFlags nested_eflags = eflags;
+ VarEvalMode nested_emode = emode;
if (opts.strict)
- nested_eflags &= ~(unsigned)VARE_UNDEFERR;
- v->flags |= VAR_IN_USE;
- (void)Var_Subst(value.str, scope, nested_eflags, &expanded);
- v->flags &= ~(unsigned)VAR_IN_USE;
+ nested_emode = VarEvalMode_UndefOk(nested_emode);
+ v->inUse = true;
+ (void)Var_Subst(expr.value.str, scope, nested_emode,
+ &expanded);
+ v->inUse = false;
/* TODO: handle errors */
- value = FStr_InitOwn(expanded);
+ Expr_SetValueOwn(&expr, expanded);
}
- if (haveModifier || extramodifiers != NULL) {
- if (extramodifiers != NULL) {
- const char *em = extramodifiers;
- value = ApplyModifiers(&em, value, '\0', '\0',
- v, &exprStatus, scope, eflags);
- }
-
- if (haveModifier) {
- p++; /* Skip initial colon. */
+ if (extramodifiers != NULL) {
+ const char *em = extramodifiers;
+ ApplyModifiers(&expr, &em, '\0', '\0');
+ }
- value = ApplyModifiers(&p, value, startc, endc,
- v, &exprStatus, scope, eflags);
- }
+ if (haveModifier) {
+ p++; /* Skip initial colon. */
+ ApplyModifiers(&expr, &p, startc, endc);
}
if (*p != '\0') /* Skip past endc if possible. */
@@ -4312,41 +4564,40 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
*pp = p;
- if (v->flags & VAR_FROM_ENV) {
- FreeEnvVar(&value.freeIt, v, value.str);
+ if (v->fromEnv) {
+ FreeEnvVar(v, &expr.value);
- } else if (exprStatus != VES_NONE) {
- if (exprStatus != VES_DEF) {
- FStr_Done(&value);
+ } else if (expr.defined != DEF_REGULAR) {
+ if (expr.defined == DEF_UNDEF) {
if (dynamic) {
- value = FStr_InitOwn(bmake_strsedup(start, p));
+ Expr_SetValueOwn(&expr,
+ bmake_strsedup(start, p));
} else {
/*
* The expression is still undefined,
* therefore discard the actual value and
* return an error marker instead.
*/
- value = FStr_InitRefer(eflags & VARE_UNDEFERR
- ? var_Error : varUndefined);
+ Expr_SetValueRefer(&expr,
+ emode == VARE_UNDEFERR
+ ? var_Error : varUndefined);
}
}
- if (value.str != v->val.data)
+ /* XXX: This is not standard memory management. */
+ if (expr.value.str != v->val.data)
Buf_Done(&v->val);
FStr_Done(&v->name);
free(v);
}
- *out_val = (FStr){ value.str, value.freeIt };
+ *out_val = expr.value;
return VPR_OK; /* XXX: Is not correct in all cases */
}
static void
-VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags)
+VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode)
{
- /*
- * A dollar sign may be escaped with another dollar
- * sign.
- */
- if (save_dollars && (eflags & VARE_KEEP_DOLLAR))
+ /* A dollar sign may be escaped with another dollar sign. */
+ if (save_dollars && VarEvalMode_ShouldKeepDollar(emode))
Buf_AddByte(res, '$');
Buf_AddByte(res, '$');
*pp += 2;
@@ -4354,19 +4605,19 @@ VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags)
static void
VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
- VarEvalFlags eflags, Boolean *inout_errorReported)
+ VarEvalMode emode, bool *inout_errorReported)
{
const char *p = *pp;
const char *nested_p = p;
FStr val;
- (void)Var_Parse(&nested_p, scope, eflags, &val);
+ (void)Var_Parse(&nested_p, scope, emode, &val);
/* TODO: handle errors */
if (val.str == var_Error || val.str == varUndefined) {
- if (!(eflags & VARE_KEEP_UNDEF)) {
+ if (!VarEvalMode_ShouldKeepUndef(emode)) {
p = nested_p;
- } else if ((eflags & VARE_UNDEFERR) || val.str == var_Error) {
+ } else if (emode == VARE_UNDEFERR || val.str == var_Error) {
/*
* XXX: This condition is wrong. If val == var_Error,
@@ -4386,7 +4637,7 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
(int)(size_t)(nested_p - p), p);
}
p = nested_p;
- *inout_errorReported = TRUE;
+ *inout_errorReported = true;
} else {
/* Copy the initial '$' of the undefined expression,
* thereby deferring expansion of the expression, but
@@ -4430,10 +4681,10 @@ VarSubstPlain(const char **pp, Buffer *res)
* expanded.
* scope The scope in which to start searching for
* variables. The other scopes are searched as well.
- * eflags Special effects during expansion.
+ * emode The mode for parsing or evaluating subexpressions.
*/
VarParseResult
-Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res)
+Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res)
{
const char *p = str;
Buffer res;
@@ -4441,16 +4692,16 @@ Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res)
/* Set true if an error has already been reported,
* to prevent a plethora of messages when recursing */
/* XXX: Why is the 'static' necessary here? */
- static Boolean errorReported;
+ static bool errorReported;
Buf_Init(&res);
- errorReported = FALSE;
+ errorReported = false;
while (*p != '\0') {
if (p[0] == '$' && p[1] == '$')
- VarSubstDollarDollar(&p, &res, eflags);
+ VarSubstDollarDollar(&p, &res, emode);
else if (p[0] == '$')
- VarSubstExpr(&p, &res, scope, eflags, &errorReported);
+ VarSubstExpr(&p, &res, scope, emode, &errorReported);
else
VarSubstPlain(&p, &res);
}