Module Name:    src
Committed By:   rillig
Date:           Sun Feb 14 17:24:47 UTC 2021

Modified Files:
        src/usr.bin/make: var.c
        src/usr.bin/make/unit-tests: varmod-indirect.exp

Log Message:
make: update line numbers in expected test output

The documentation from the previous commit added a few lines.


To generate a diff of this commit:
cvs rdiff -u -r1.813 -r1.814 src/usr.bin/make/var.c
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/make/unit-tests/varmod-indirect.exp

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.813 src/usr.bin/make/var.c:1.814
--- src/usr.bin/make/var.c:1.813	Sun Feb 14 13:53:28 2021
+++ src/usr.bin/make/var.c	Sun Feb 14 17:24:47 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.813 2021/02/14 13:53:28 rillig Exp $	*/
+/*	$NetBSD: var.c,v 1.814 2021/02/14 17:24:47 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -139,7 +139,7 @@
 #include "metachar.h"
 
 /*	"@(#)var.c	8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.813 2021/02/14 13:53:28 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.814 2021/02/14 17:24:47 rillig Exp $");
 
 typedef enum VarFlags {
 	VAR_NONE	= 0,
@@ -2087,25 +2087,30 @@ typedef struct ApplyModifiersState {
 	VarExprStatus exprStatus;
 } ApplyModifiersState;
 
-typedef ApplyModifiersState Expr;
+/*
+ * A variable expression such as $@ or ${VAR:Mpattern:Q}.
+ */
+typedef struct Expr {
+	ApplyModifiersState *st;	/* only during ApplyModifiers */
+} Expr;
 
 static void
 Expr_Define(Expr *expr)
 {
-	if (expr->exprStatus == VES_UNDEF)
-		expr->exprStatus = VES_DEF;
+	if (expr->st->exprStatus == VES_UNDEF)
+		expr->st->exprStatus = VES_DEF;
 }
 
 static void
 Expr_SetValueOwn(Expr *expr, char *value)
 {
-	expr->newValue = FStr_InitOwn(value);
+	expr->st->newValue = FStr_InitOwn(value);
 }
 
 static void
 Expr_SetValueRefer(Expr *expr, const char *value)
 {
-	expr->newValue = FStr_InitRefer(value);
+	expr->st->newValue = FStr_InitRefer(value);
 }
 
 typedef enum ApplyModifierResult {
@@ -2140,7 +2145,7 @@ ParseModifierPartSubst(
     const char **pp,
     char delim,
     VarEvalFlags eflags,
-    ApplyModifiersState *st,
+    Expr *expr,
     char **out_part,
     /* Optionally stores the length of the returned string, just to save
      * another strlen call. */
@@ -2153,6 +2158,7 @@ ParseModifierPartSubst(
     struct ModifyWord_SubstArgs *subst
 )
 {
+	ApplyModifiersState *st = expr->st;
 	Buffer buf;
 	const char *p;
 
@@ -2281,11 +2287,11 @@ ParseModifierPart(
     /* Flags for evaluating nested variables; if VARE_WANTRES is not set,
      * the text is only parsed. */
     VarEvalFlags eflags,
-    ApplyModifiersState *st,
+    Expr *expr,
     char **out_part
 )
 {
-	return ParseModifierPartSubst(pp, delim, eflags, st, out_part,
+	return ParseModifierPartSubst(pp, delim, eflags, expr, out_part,
 	    NULL, NULL, NULL);
 }
 
@@ -2369,8 +2375,9 @@ TryParseChar(const char **pp, int base, 
 
 /* :@var@...${var}...@ */
 static ApplyModifierResult
-ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Loop(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	struct ModifyWord_LoopArgs args;
 	char prev_sep;
 	VarParseResult res;
@@ -2378,7 +2385,7 @@ ApplyModifier_Loop(const char **pp, cons
 	args.scope = st->scope;
 
 	(*pp)++;		/* Skip the first '@' */
-	res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
+	res = ParseModifierPart(pp, '@', VARE_NONE, expr, &args.tvar);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 	if (opts.strict && strchr(args.tvar, '$') != NULL) {
@@ -2389,14 +2396,14 @@ ApplyModifier_Loop(const char **pp, cons
 		return AMR_CLEANUP;
 	}
 
-	res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str);
+	res = ParseModifierPart(pp, '@', VARE_NONE, expr, &args.str);
 	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 */
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, ModifyWord_Loop, &args, st->oneBigWord, st->sep));
 	st->sep = prev_sep;
 	/* XXX: Consider restoring the previous variable instead of deleting. */
@@ -2412,8 +2419,9 @@ ApplyModifier_Loop(const char **pp, cons
 
 /* :Ddefined or :Uundefined */
 static ApplyModifierResult
-ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Defined(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	Buffer buf;
 	const char *p;
 
@@ -2458,12 +2466,12 @@ ApplyModifier_Defined(const char **pp, c
 	}
 	*pp = p;
 
-	Expr_Define(st);
+	Expr_Define(expr);
 
 	if (eflags & VARE_WANTRES) {
-		Expr_SetValueOwn(st, Buf_DoneData(&buf));
+		Expr_SetValueOwn(expr, Buf_DoneData(&buf));
 	} else {
-		Expr_SetValueRefer(st, val);
+		Expr_SetValueRefer(expr, val);
 		Buf_Done(&buf);
 	}
 	return AMR_OK;
@@ -2471,10 +2479,10 @@ ApplyModifier_Defined(const char **pp, c
 
 /* :L */
 static ApplyModifierResult
-ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Literal(const char **pp, Expr *expr)
 {
-	Expr_Define(st);
-	Expr_SetValueOwn(st, bmake_strdup(st->var->name.str));
+	Expr_Define(expr);
+	Expr_SetValueOwn(expr, bmake_strdup(expr->st->var->name.str));
 	(*pp)++;
 	return AMR_OK;
 }
@@ -2500,12 +2508,12 @@ TryParseTime(const char **pp, time_t *ou
 
 /* :gmtime */
 static ApplyModifierResult
-ApplyModifier_Gmtime(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Gmtime(const char **pp, const char *val, Expr *expr)
 {
 	time_t utc;
 
 	const char *mod = *pp;
-	if (!ModMatchEq(mod, "gmtime", st->endc))
+	if (!ModMatchEq(mod, "gmtime", expr->st->endc))
 		return AMR_UNKNOWN;
 
 	if (mod[6] == '=') {
@@ -2520,19 +2528,19 @@ ApplyModifier_Gmtime(const char **pp, co
 		utc = 0;
 		*pp = mod + 6;
 	}
-	Expr_SetValueOwn(st, VarStrftime(val, TRUE, utc));
+	Expr_SetValueOwn(expr, VarStrftime(val, TRUE, utc));
 	return AMR_OK;
 }
 
 /* :localtime */
 static ApplyModifierResult
 ApplyModifier_Localtime(const char **pp, const char *val,
-			ApplyModifiersState *st)
+			Expr *expr)
 {
 	time_t utc;
 
 	const char *mod = *pp;
-	if (!ModMatchEq(mod, "localtime", st->endc))
+	if (!ModMatchEq(mod, "localtime", expr->st->endc))
 		return AMR_UNKNOWN;
 
 	if (mod[9] == '=') {
@@ -2547,30 +2555,31 @@ ApplyModifier_Localtime(const char **pp,
 		utc = 0;
 		*pp = mod + 9;
 	}
-	Expr_SetValueOwn(st, VarStrftime(val, FALSE, utc));
+	Expr_SetValueOwn(expr, VarStrftime(val, FALSE, utc));
 	return AMR_OK;
 }
 
 /* :hash */
 static ApplyModifierResult
-ApplyModifier_Hash(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Hash(const char **pp, const char *val, Expr *expr)
 {
-	if (!ModMatch(*pp, "hash", st->endc))
+	if (!ModMatch(*pp, "hash", expr->st->endc))
 		return AMR_UNKNOWN;
 
-	Expr_SetValueOwn(st, VarHash(val));
+	Expr_SetValueOwn(expr, VarHash(val));
 	*pp += 4;
 	return AMR_OK;
 }
 
 /* :P */
 static ApplyModifierResult
-ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Path(const char **pp, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	GNode *gn;
 	char *path;
 
-	Expr_Define(st);
+	Expr_Define(expr);
 
 	gn = Targ_FindNode(st->var->name.str);
 	if (gn == NULL || gn->type & OP_NOPATH) {
@@ -2583,7 +2592,7 @@ ApplyModifier_Path(const char **pp, Appl
 	}
 	if (path == NULL)
 		path = bmake_strdup(st->var->name.str);
-	Expr_SetValueOwn(st, path);
+	Expr_SetValueOwn(expr, path);
 
 	(*pp)++;
 	return AMR_OK;
@@ -2591,27 +2600,28 @@ ApplyModifier_Path(const char **pp, Appl
 
 /* :!cmd! */
 static ApplyModifierResult
-ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
+ApplyModifier_ShellCommand(const char **pp, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	char *cmd;
 	const char *errfmt;
 	VarParseResult res;
 
 	(*pp)++;
-	res = ParseModifierPart(pp, '!', st->eflags, st, &cmd);
+	res = ParseModifierPart(pp, '!', st->eflags, expr, &cmd);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
 	errfmt = NULL;
 	if (st->eflags & VARE_WANTRES)
-		Expr_SetValueOwn(st, Cmd_Exec(cmd, &errfmt));
+		Expr_SetValueOwn(expr, Cmd_Exec(cmd, &errfmt));
 	else
-		Expr_SetValueRefer(st, "");
+		Expr_SetValueRefer(expr, "");
 	if (errfmt != NULL)
 		Error(errfmt, cmd);	/* XXX: why still return AMR_OK? */
 	free(cmd);
 
-	Expr_Define(st);
+	Expr_Define(expr);
 	return AMR_OK;
 }
 
@@ -2620,8 +2630,9 @@ ApplyModifier_ShellCommand(const char **
  * 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, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	size_t n;
 	Buffer buf;
 	size_t i;
@@ -2659,14 +2670,15 @@ ApplyModifier_Range(const char **pp, con
 		Buf_AddInt(&buf, 1 + (int)i);
 	}
 
-	Expr_SetValueOwn(st, Buf_DoneData(&buf));
+	Expr_SetValueOwn(expr, Buf_DoneData(&buf));
 	return AMR_OK;
 }
 
 /* :Mpattern or :Npattern */
 static ApplyModifierResult
-ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Match(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *mod = *pp;
 	Boolean copy = FALSE;	/* pattern should be, or has been, copied */
 	Boolean needSubst = FALSE;
@@ -2736,7 +2748,7 @@ ApplyModifier_Match(const char **pp, con
 	    st->var->name.str, val, pattern);
 
 	callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, callback, pattern, st->oneBigWord, st->sep));
 	free(pattern);
 	return AMR_OK;
@@ -2744,8 +2756,9 @@ ApplyModifier_Match(const char **pp, con
 
 /* :S,from,to, */
 static ApplyModifierResult
-ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Subst(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	struct ModifyWord_SubstArgs args;
 	char *lhs, *rhs;
 	Boolean oneBigWord;
@@ -2772,13 +2785,13 @@ ApplyModifier_Subst(const char **pp, con
 		(*pp)++;
 	}
 
-	res = ParseModifierPartSubst(pp, delim, st->eflags, st, &lhs,
+	res = ParseModifierPartSubst(pp, delim, st->eflags, expr, &lhs,
 	    &args.lhsLen, &args.pflags, NULL);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 	args.lhs = lhs;
 
-	res = ParseModifierPartSubst(pp, delim, st->eflags, st, &rhs,
+	res = ParseModifierPartSubst(pp, delim, st->eflags, expr, &rhs,
 	    &args.rhsLen, NULL, &args);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
@@ -2796,7 +2809,7 @@ ApplyModifier_Subst(const char **pp, con
 			break;
 	}
 
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, ModifyWord_Subst, &args, oneBigWord, st->sep));
 
 	free(lhs);
@@ -2808,8 +2821,9 @@ ApplyModifier_Subst(const char **pp, con
 
 /* :C,from,to, */
 static ApplyModifierResult
-ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Regex(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	char *re;
 	struct ModifyWord_SubstRegexArgs args;
 	Boolean oneBigWord;
@@ -2825,11 +2839,11 @@ ApplyModifier_Regex(const char **pp, con
 
 	*pp += 2;
 
-	res = ParseModifierPart(pp, delim, st->eflags, st, &re);
+	res = ParseModifierPart(pp, delim, st->eflags, expr, &re);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
-	res = ParseModifierPart(pp, delim, st->eflags, st, &args.replace);
+	res = ParseModifierPart(pp, delim, st->eflags, expr, &args.replace);
 	if (args.replace == NULL) {
 		free(re);
 		return AMR_CLEANUP;
@@ -2860,7 +2874,7 @@ ApplyModifier_Regex(const char **pp, con
 	args.nsub = args.re.re_nsub + 1;
 	if (args.nsub > 10)
 		args.nsub = 10;
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, ModifyWord_SubstRegex, &args,
 		oneBigWord, st->sep));
 	regfree(&args.re);
@@ -2872,10 +2886,10 @@ ApplyModifier_Regex(const char **pp, con
 
 /* :Q, :q */
 static ApplyModifierResult
-ApplyModifier_Quote(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Quote(const char **pp, const char *val, Expr *expr)
 {
-	if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
-		Expr_SetValueOwn(st, VarQuote(val, **pp == 'q'));
+	if ((*pp)[1] == expr->st->endc || (*pp)[1] == ':') {
+		Expr_SetValueOwn(expr, VarQuote(val, **pp == 'q'));
 		(*pp)++;
 		return AMR_OK;
 	} else
@@ -2891,8 +2905,9 @@ ModifyWord_Copy(const char *word, SepBuf
 
 /* :ts<separator> */
 static ApplyModifierResult
-ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_ToSep(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *sep = *pp + 2;
 
 	/* ":ts<any><endc>" or ":ts<any>:" */
@@ -2956,7 +2971,7 @@ ApplyModifier_ToSep(const char **pp, con
 	}
 
 ok:
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, ModifyWord_Copy, NULL, st->oneBigWord, st->sep));
 	return AMR_OK;
 }
@@ -2991,8 +3006,9 @@ 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, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *mod = *pp;
 	assert(mod[0] == 't');
 
@@ -3002,7 +3018,7 @@ ApplyModifier_To(const char **pp, const 
 	}
 
 	if (mod[1] == 's')
-		return ApplyModifier_ToSep(pp, val, st);
+		return ApplyModifier_ToSep(pp, val, expr);
 
 	if (mod[2] != st->endc && mod[2] != ':') {	/* :t<unrecognized> */
 		*pp = mod + 1;
@@ -3010,7 +3026,7 @@ ApplyModifier_To(const char **pp, const 
 	}
 
 	if (mod[1] == 'A') {				/* :tA */
-		Expr_SetValueOwn(st,
+		Expr_SetValueOwn(expr,
 		    ModifyWords(val, ModifyWord_Realpath, NULL,
 		        st->oneBigWord, st->sep));
 		*pp = mod + 2;
@@ -3018,20 +3034,20 @@ ApplyModifier_To(const char **pp, const 
 	}
 
 	if (mod[1] == 'u') {				/* :tu */
-		Expr_SetValueOwn(st, str_toupper(val));
+		Expr_SetValueOwn(expr, str_toupper(val));
 		*pp = mod + 2;
 		return AMR_OK;
 	}
 
 	if (mod[1] == 'l') {				/* :tl */
-		Expr_SetValueOwn(st, str_tolower(val));
+		Expr_SetValueOwn(expr, str_tolower(val));
 		*pp = mod + 2;
 		return AMR_OK;
 	}
 
 	if (mod[1] == 'W' || mod[1] == 'w') {		/* :tW, :tw */
 		st->oneBigWord = mod[1] == 'W';
-		Expr_SetValueRefer(st, val);
+		Expr_SetValueRefer(expr, val);
 		*pp = mod + 2;
 		return AMR_OK;
 	}
@@ -3043,15 +3059,16 @@ ApplyModifier_To(const char **pp, const 
 
 /* :[#], :[1], :[-1..1], etc. */
 static ApplyModifierResult
-ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Words(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	char *estr;
 	int first, last;
 	VarParseResult res;
 	const char *p;
 
 	(*pp)++;		/* skip the '[' */
-	res = ParseModifierPart(pp, ']', st->eflags, st, &estr);
+	res = ParseModifierPart(pp, ']', st->eflags, expr, &estr);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
@@ -3063,7 +3080,7 @@ ApplyModifier_Words(const char **pp, con
 
 	if (estr[0] == '#' && estr[1] == '\0') {	/* Found ":[#]" */
 		if (st->oneBigWord) {
-			Expr_SetValueRefer(st, "1");
+			Expr_SetValueRefer(expr, "1");
 		} else {
 			Buffer buf;
 
@@ -3074,20 +3091,20 @@ ApplyModifier_Words(const char **pp, con
 			/* 3 digits + '\0' is usually enough */
 			Buf_InitSize(&buf, 4);
 			Buf_AddInt(&buf, (int)ac);
-			Expr_SetValueOwn(st, Buf_DoneData(&buf));
+			Expr_SetValueOwn(expr, Buf_DoneData(&buf));
 		}
 		goto ok;
 	}
 
 	if (estr[0] == '*' && estr[1] == '\0') {	/* Found ":[*]" */
 		st->oneBigWord = TRUE;
-		Expr_SetValueRefer(st, val);
+		Expr_SetValueRefer(expr, val);
 		goto ok;
 	}
 
 	if (estr[0] == '@' && estr[1] == '\0') {	/* Found ":[@]" */
 		st->oneBigWord = FALSE;
-		Expr_SetValueRefer(st, val);
+		Expr_SetValueRefer(expr, val);
 		goto ok;
 	}
 
@@ -3116,7 +3133,7 @@ ApplyModifier_Words(const char **pp, con
 	if (first == 0 && last == 0) {
 		/* ":[0]" or perhaps ":[0..0]" */
 		st->oneBigWord = TRUE;
-		Expr_SetValueRefer(st, val);
+		Expr_SetValueRefer(expr, val);
 		goto ok;
 	}
 
@@ -3125,7 +3142,7 @@ ApplyModifier_Words(const char **pp, con
 		goto bad_modifier;
 
 	/* Normal case: select the words described by first and last. */
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    VarSelectWords(st->sep, st->oneBigWord, val, first, last));
 
 ok:
@@ -3164,8 +3181,9 @@ 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, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *mod = (*pp)++;	/* skip past the 'O' in any case */
 
 	Words words = Str_Words(val, FALSE);
@@ -3189,14 +3207,15 @@ ApplyModifier_Order(const char **pp, con
 		return AMR_BAD;
 	}
 
-	Expr_SetValueOwn(st, Words_JoinFree(words));
+	Expr_SetValueOwn(expr, Words_JoinFree(words));
 	return AMR_OK;
 }
 
 /* :? then : else */
 static ApplyModifierResult
-ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
+ApplyModifier_IfElse(const char **pp, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	char *then_expr, *else_expr;
 	VarParseResult res;
 
@@ -3214,11 +3233,11 @@ ApplyModifier_IfElse(const char **pp, Ap
 	}
 
 	(*pp)++;			/* skip past the '?' */
-	res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr);
+	res = ParseModifierPart(pp, ':', then_eflags, expr, &then_expr);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
-	res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr);
+	res = ParseModifierPart(pp, st->endc, else_eflags, expr, &else_expr);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
@@ -3231,13 +3250,13 @@ ApplyModifier_IfElse(const char **pp, Ap
 	}
 
 	if (value) {
-		Expr_SetValueOwn(st, then_expr);
+		Expr_SetValueOwn(expr, then_expr);
 		free(else_expr);
 	} else {
-		Expr_SetValueOwn(st, else_expr);
+		Expr_SetValueOwn(expr, else_expr);
 		free(then_expr);
 	}
-	Expr_Define(st);
+	Expr_Define(expr);
 	return AMR_OK;
 }
 
@@ -3264,8 +3283,9 @@ ApplyModifier_IfElse(const char **pp, Ap
  *			variable.
  */
 static ApplyModifierResult
-ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Assign(const char **pp, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	GNode *scope;
 	char *val;
 	VarParseResult res;
@@ -3305,7 +3325,7 @@ ok:
 		break;
 	}
 
-	res = ParseModifierPart(pp, st->endc, st->eflags, st, &val);
+	res = ParseModifierPart(pp, st->endc, st->eflags, expr, &val);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
@@ -3338,7 +3358,7 @@ ok:
 		}
 	}
 	free(val);
-	Expr_SetValueRefer(st, "");
+	Expr_SetValueRefer(expr, "");
 	return AMR_OK;
 }
 
@@ -3348,8 +3368,9 @@ ok:
  */
 static ApplyModifierResult
 ApplyModifier_Remember(const char **pp, const char *val,
-		       ApplyModifiersState *st)
+		       Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *mod = *pp;
 	if (!ModMatchEq(mod, "_", st->endc))
 		return AMR_UNKNOWN;
@@ -3364,7 +3385,7 @@ ApplyModifier_Remember(const char **pp, 
 		Var_Set(st->scope, "_", val);
 		*pp = mod + 1;
 	}
-	Expr_SetValueRefer(st, val);
+	Expr_SetValueRefer(expr, val);
 	return AMR_OK;
 }
 
@@ -3374,23 +3395,25 @@ ApplyModifier_Remember(const char **pp, 
  */
 static ApplyModifierResult
 ApplyModifier_WordFunc(const char **pp, const char *val,
-		       ApplyModifiersState *st, ModifyWordsCallback modifyWord)
+		       Expr *expr, ModifyWordsCallback modifyWord)
 {
+	ApplyModifiersState *st = expr->st;
 	char delim = (*pp)[1];
 	if (delim != st->endc && delim != ':')
 		return AMR_UNKNOWN;
 
-	Expr_SetValueOwn(st,
+	Expr_SetValueOwn(expr,
 	    ModifyWords(val, modifyWord, NULL, st->oneBigWord, st->sep));
 	(*pp)++;
 	return AMR_OK;
 }
 
 static ApplyModifierResult
-ApplyModifier_Unique(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Unique(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
-		Expr_SetValueOwn(st, VarUniq(val));
+		Expr_SetValueOwn(expr, VarUniq(val));
 		(*pp)++;
 		return AMR_OK;
 	} else
@@ -3400,8 +3423,9 @@ ApplyModifier_Unique(const char **pp, co
 #ifdef SYSVVARSUB
 /* :from=to */
 static ApplyModifierResult
-ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_SysV(const char **pp, const char *val, Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	char *lhs, *rhs;
 	VarParseResult res;
 
@@ -3428,22 +3452,22 @@ ApplyModifier_SysV(const char **pp, cons
 	if (*p != st->endc || !eqFound)
 		return AMR_UNKNOWN;
 
-	res = ParseModifierPart(pp, '=', st->eflags, st, &lhs);
+	res = ParseModifierPart(pp, '=', st->eflags, expr, &lhs);
 	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);
+	res = ParseModifierPart(pp, st->endc, st->eflags, expr, &rhs);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
 
 	(*pp)--;		/* Go back to the st->endc. */
 
 	if (lhs[0] == '\0' && val[0] == '\0') {
-		Expr_SetValueRefer(st, val); /* special case */
+		Expr_SetValueRefer(expr, val); /* special case */
 	} else {
 		struct ModifyWord_SYSVSubstArgs args = { st->scope, lhs, rhs };
-		Expr_SetValueOwn(st,
+		Expr_SetValueOwn(expr,
 		    ModifyWords(val, ModifyWord_SYSVSubst, &args,
 			st->oneBigWord, st->sep));
 	}
@@ -3457,17 +3481,18 @@ ApplyModifier_SysV(const char **pp, cons
 /* :sh */
 static ApplyModifierResult
 ApplyModifier_SunShell(const char **pp, const char *val,
-		       ApplyModifiersState *st)
+		       Expr *expr)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *p = *pp;
 	if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) {
 		if (st->eflags & VARE_WANTRES) {
 			const char *errfmt;
-			Expr_SetValueOwn(st, Cmd_Exec(val, &errfmt));
+			Expr_SetValueOwn(expr, Cmd_Exec(val, &errfmt));
 			if (errfmt != NULL)
 				Error(errfmt, val);
 		} else
-			Expr_SetValueRefer(st, "");
+			Expr_SetValueRefer(expr, "");
 		*pp = p + 2;
 		return AMR_OK;
 	} else
@@ -3476,9 +3501,10 @@ ApplyModifier_SunShell(const char **pp, 
 #endif
 
 static void
-LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc,
+LogBeforeApply(const Expr *expr, const char *mod, char endc,
 	       const char *val)
 {
+	ApplyModifiersState *st = expr->st;
 	char eflags_str[VarEvalFlags_ToStringSize];
 	char vflags_str[VarFlags_ToStringSize];
 	Boolean is_single_char = mod[0] != '\0' &&
@@ -3494,8 +3520,9 @@ LogBeforeApply(const ApplyModifiersState
 }
 
 static void
-LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
+LogAfterApply(Expr *expr, const char *p, const char *mod)
 {
+	ApplyModifiersState *st = expr->st;
 	char eflags_str[VarEvalFlags_ToStringSize];
 	char vflags_str[VarFlags_ToStringSize];
 	const char *quot = st->newValue.str == var_Error ? "" : "\"";
@@ -3510,72 +3537,72 @@ LogAfterApply(ApplyModifiersState *st, c
 }
 
 static ApplyModifierResult
-ApplyModifier(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier(const char **pp, const char *val, Expr *expr)
 {
 	switch (**pp) {
 	case ':':
-		return ApplyModifier_Assign(pp, st);
+		return ApplyModifier_Assign(pp, expr);
 	case '@':
-		return ApplyModifier_Loop(pp, val, st);
+		return ApplyModifier_Loop(pp, val, expr);
 	case '_':
-		return ApplyModifier_Remember(pp, val, st);
+		return ApplyModifier_Remember(pp, val, expr);
 	case 'D':
 	case 'U':
-		return ApplyModifier_Defined(pp, val, st);
+		return ApplyModifier_Defined(pp, val, expr);
 	case 'L':
-		return ApplyModifier_Literal(pp, st);
+		return ApplyModifier_Literal(pp, expr);
 	case 'P':
-		return ApplyModifier_Path(pp, st);
+		return ApplyModifier_Path(pp, expr);
 	case '!':
-		return ApplyModifier_ShellCommand(pp, st);
+		return ApplyModifier_ShellCommand(pp, expr);
 	case '[':
-		return ApplyModifier_Words(pp, val, st);
+		return ApplyModifier_Words(pp, val, expr);
 	case 'g':
-		return ApplyModifier_Gmtime(pp, val, st);
+		return ApplyModifier_Gmtime(pp, val, expr);
 	case 'h':
-		return ApplyModifier_Hash(pp, val, st);
+		return ApplyModifier_Hash(pp, val, expr);
 	case 'l':
-		return ApplyModifier_Localtime(pp, val, st);
+		return ApplyModifier_Localtime(pp, val, expr);
 	case 't':
-		return ApplyModifier_To(pp, val, st);
+		return ApplyModifier_To(pp, val, expr);
 	case 'N':
 	case 'M':
-		return ApplyModifier_Match(pp, val, st);
+		return ApplyModifier_Match(pp, val, expr);
 	case 'S':
-		return ApplyModifier_Subst(pp, val, st);
+		return ApplyModifier_Subst(pp, val, expr);
 	case '?':
-		return ApplyModifier_IfElse(pp, st);
+		return ApplyModifier_IfElse(pp, expr);
 #ifndef NO_REGEX
 	case 'C':
-		return ApplyModifier_Regex(pp, val, st);
+		return ApplyModifier_Regex(pp, val, expr);
 #endif
 	case 'q':
 	case 'Q':
-		return ApplyModifier_Quote(pp, val, st);
+		return ApplyModifier_Quote(pp, val, expr);
 	case 'T':
-		return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Tail);
+		return ApplyModifier_WordFunc(pp, val, expr, ModifyWord_Tail);
 	case 'H':
-		return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Head);
+		return ApplyModifier_WordFunc(pp, val, expr, ModifyWord_Head);
 	case 'E':
-		return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Suffix);
+		return ApplyModifier_WordFunc(pp, val, expr, ModifyWord_Suffix);
 	case 'R':
-		return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Root);
+		return ApplyModifier_WordFunc(pp, val, expr, ModifyWord_Root);
 	case 'r':
-		return ApplyModifier_Range(pp, val, st);
+		return ApplyModifier_Range(pp, val, expr);
 	case 'O':
-		return ApplyModifier_Order(pp, val, st);
+		return ApplyModifier_Order(pp, val, expr);
 	case 'u':
-		return ApplyModifier_Unique(pp, val, st);
+		return ApplyModifier_Unique(pp, val, expr);
 #ifdef SUNSHCMD
 	case 's':
-		return ApplyModifier_SunShell(pp, val, st);
+		return ApplyModifier_SunShell(pp, val, expr);
 #endif
 	default:
 		return AMR_UNKNOWN;
 	}
 }
 
-static FStr ApplyModifiers(const char **, FStr, char, char, Var *,
+static FStr ApplyModifiers(Expr *, const char **, FStr, char, char, Var *,
 			    VarExprStatus *, GNode *, VarEvalFlags);
 
 typedef enum ApplyModifiersIndirectResult {
@@ -3604,9 +3631,10 @@ typedef enum ApplyModifiersIndirectResul
  * modifier, and it is neither a SysV modifier but a parse error.
  */
 static ApplyModifiersIndirectResult
-ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
+ApplyModifiersIndirect(Expr *expr, const char **pp,
 		       FStr *inout_value)
 {
+	ApplyModifiersState *st = expr->st;
 	const char *p = *pp;
 	FStr mods;
 
@@ -3623,7 +3651,7 @@ ApplyModifiersIndirect(ApplyModifiersSta
 
 	if (mods.str[0] != '\0') {
 		const char *modsp = mods.str;
-		FStr newValue = ApplyModifiers(&modsp, *inout_value, '\0', '\0',
+		FStr newValue = ApplyModifiers(expr, &modsp, *inout_value, '\0', '\0',
 		    st->var, &st->exprStatus, st->scope, st->eflags);
 		*inout_value = newValue;
 		if (newValue.str == var_Error || *modsp != '\0') {
@@ -3649,22 +3677,23 @@ ApplyModifiersIndirect(ApplyModifiersSta
 }
 
 static ApplyModifierResult
-ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
+ApplySingleModifier(Expr *expr, const char *mod, char endc,
 		    const char **pp, FStr *inout_value)
 {
+	ApplyModifiersState *st = expr->st;
 	ApplyModifierResult res;
 	const char *p = *pp;
 	const char *const val = inout_value->str;
 
 	if (DEBUG(VAR))
-		LogBeforeApply(st, mod, endc, val);
+		LogBeforeApply(expr, mod, endc, val);
 
-	res = ApplyModifier(&p, val, st);
+	res = ApplyModifier(&p, val, expr);
 
 #ifdef SYSVVARSUB
 	if (res == AMR_UNKNOWN) {
 		assert(p == mod);
-		res = ApplyModifier_SysV(&p, val, st);
+		res = ApplyModifier_SysV(&p, val, expr);
 	}
 #endif
 
@@ -3678,7 +3707,7 @@ ApplySingleModifier(ApplyModifiersState 
 		 */
 		for (p++; *p != ':' && *p != st->endc && *p != '\0'; p++)
 			continue;
-		Expr_SetValueRefer(st, var_Error);
+		Expr_SetValueRefer(expr, var_Error);
 	}
 	if (res == AMR_CLEANUP || res == AMR_BAD) {
 		*pp = p;
@@ -3686,7 +3715,7 @@ ApplySingleModifier(ApplyModifiersState 
 	}
 
 	if (DEBUG(VAR))
-		LogAfterApply(st, p, mod);
+		LogAfterApply(expr, p, mod);
 
 	if (st->newValue.str != val) {
 		FStr_Done(inout_value);
@@ -3715,6 +3744,7 @@ ApplySingleModifier(ApplyModifiersState 
 /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
 static FStr
 ApplyModifiers(
+    Expr *expr,
     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 */
@@ -3739,12 +3769,15 @@ ApplyModifiers(
 	};
 	const char *p;
 	const char *mod;
+	ApplyModifiersState *prevst;
 
 	assert(startc == '(' || startc == '{' || startc == '\0');
 	assert(endc == ')' || endc == '}' || endc == '\0');
 	assert(value.str != NULL);
 
 	p = *pp;
+	prevst = expr->st;
+	expr->st = &st;
 
 	if (*p == '\0' && endc != '\0') {
 		Error(
@@ -3758,7 +3791,7 @@ ApplyModifiers(
 
 		if (*p == '$') {
 			ApplyModifiersIndirectResult amir;
-			amir = ApplyModifiersIndirect(&st, &p, &value);
+			amir = ApplyModifiersIndirect(expr, &p, &value);
 			if (amir == AMIR_CONTINUE)
 				continue;
 			if (amir == AMIR_OUT)
@@ -3769,7 +3802,7 @@ ApplyModifiers(
 		st.newValue = FStr_InitRefer(var_Error);
 		mod = p;
 
-		res = ApplySingleModifier(&st, mod, endc, &p, &value);
+		res = ApplySingleModifier(expr, mod, endc, &p, &value);
 		if (res == AMR_CLEANUP)
 			goto cleanup;
 		if (res == AMR_BAD)
@@ -3779,6 +3812,7 @@ ApplyModifiers(
 	*pp = p;
 	assert(value.str != NULL); /* Use var_Error or varUndefined instead. */
 	*exprStatus = st.exprStatus;
+	expr->st = prevst;
 	return value;
 
 bad_modifier:
@@ -3790,6 +3824,7 @@ cleanup:
 	*pp = p;
 	FStr_Done(&value);
 	*exprStatus = st.exprStatus;
+	expr->st = prevst;
 	return FStr_InitRefer(var_Error);
 }
 
@@ -4226,6 +4261,7 @@ Var_Parse(const char **pp, GNode *scope,
 	FStr value;
 	char eflags_str[VarEvalFlags_ToStringSize];
 	VarExprStatus exprStatus = VES_NONE;
+	Expr expr = { NULL };
 
 	DEBUG2(VAR, "Var_Parse: %s with %s\n", start,
 	    VarEvalFlags_ToString(eflags_str, eflags));
@@ -4290,14 +4326,14 @@ Var_Parse(const char **pp, GNode *scope,
 	if (haveModifier || extramodifiers != NULL) {
 		if (extramodifiers != NULL) {
 			const char *em = extramodifiers;
-			value = ApplyModifiers(&em, value, '\0', '\0',
+			value = ApplyModifiers(&expr, &em, value, '\0', '\0',
 			    v, &exprStatus, scope, eflags);
 		}
 
 		if (haveModifier) {
 			p++;	/* Skip initial colon. */
 
-			value = ApplyModifiers(&p, value, startc, endc,
+			value = ApplyModifiers(&expr, &p, value, startc, endc,
 			    v, &exprStatus, scope, eflags);
 		}
 	}

Index: src/usr.bin/make/unit-tests/varmod-indirect.exp
diff -u src/usr.bin/make/unit-tests/varmod-indirect.exp:1.9 src/usr.bin/make/unit-tests/varmod-indirect.exp:1.10
--- src/usr.bin/make/unit-tests/varmod-indirect.exp:1.9	Sun Feb 14 16:12:46 2021
+++ src/usr.bin/make/unit-tests/varmod-indirect.exp	Sun Feb 14 17:24:47 2021
@@ -1,20 +1,20 @@
-make: "varmod-indirect.mk" line 17: Unknown modifier '$'
-make: "varmod-indirect.mk" line 50: Unknown modifier '$'
-make: "varmod-indirect.mk" line 53: 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 138: before
-make: "varmod-indirect.mk" line 138: after
-make: "varmod-indirect.mk" line 144: before
-make: "varmod-indirect.mk" line 144: after
-make: "varmod-indirect.mk" line 150: before
-make: "varmod-indirect.mk" line 150: after
-make: "varmod-indirect.mk" line 154: Unknown modifier 'Z'
-make: "varmod-indirect.mk" line 155: before
-make: "varmod-indirect.mk" line 155: after
-ParseReadLine (164): '_:=	before ${UNDEF} after'
+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 with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
 Global:_ = before ${UNDEF} after
-ParseReadLine (167): '_:=	before ${UNDEF:${:US,a,a,}} after'
+ParseReadLine (169): '_:=	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)
@@ -29,7 +29,7 @@ Var_Parse: ${:US,a,a,}} after with VARE_
 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 (177): '_:=	before ${UNDEF:${:U}} after'
+ParseReadLine (179): '_:=	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)
@@ -39,20 +39,20 @@ Var_Parse: ${:U}} after with VARE_WANTRE
 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 (182): '_:=	before ${UNDEF:${:UZ}} after'
+ParseReadLine (184): '_:=	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)
 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 182: Unknown modifier 'Z'
+make: "varmod-indirect.mk" line 184: 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 (184): '.MAKEFLAGS: -d0'
+ParseReadLine (186): '.MAKEFLAGS: -d0'
 ParseDoDependency(.MAKEFLAGS: -d0)
 Global:.MAKEFLAGS =  -r -k -d 0 -d pv -d
 Global:.MAKEFLAGS =  -r -k -d 0 -d pv -d 0

Reply via email to