Mon Jul 27 21:54:25 2020 UTC ()
make(1): replace macros with functions

Having the hidden parameter st->endc in the macro made it unnecessarily
difficult to understand the code.


(rillig)
diff -r1.339 -r1.340 src/usr.bin/make/var.c
diff -r1.29 -r1.30 src/usr.bin/make/unit-tests/modmisc.exp
diff -r1.26 -r1.27 src/usr.bin/make/unit-tests/modmisc.mk

cvs diff -r1.339 -r1.340 src/usr.bin/make/var.c (expand / switch to unified diff)

--- src/usr.bin/make/var.c 2020/07/27 21:08:41 1.339
+++ src/usr.bin/make/var.c 2020/07/27 21:54:25 1.340
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: var.c,v 1.339 2020/07/27 21:08:41 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.340 2020/07/27 21:54:25 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990, 1993 4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor. 8 * Adam de Boor.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -59,34 +59,34 @@ @@ -59,34 +59,34 @@
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * SUCH DAMAGE. 68 * SUCH DAMAGE.
69 */ 69 */
70 70
71#ifndef MAKE_NATIVE 71#ifndef MAKE_NATIVE
72static char rcsid[] = "$NetBSD: var.c,v 1.339 2020/07/27 21:08:41 rillig Exp $"; 72static char rcsid[] = "$NetBSD: var.c,v 1.340 2020/07/27 21:54:25 rillig Exp $";
73#else 73#else
74#include <sys/cdefs.h> 74#include <sys/cdefs.h>
75#ifndef lint 75#ifndef lint
76#if 0 76#if 0
77static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; 77static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
78#else 78#else
79__RCSID("$NetBSD: var.c,v 1.339 2020/07/27 21:08:41 rillig Exp $"); 79__RCSID("$NetBSD: var.c,v 1.340 2020/07/27 21:54:25 rillig Exp $");
80#endif 80#endif
81#endif /* not lint */ 81#endif /* not lint */
82#endif 82#endif
83 83
84/*- 84/*-
85 * var.c -- 85 * var.c --
86 * Variable-handling functions 86 * Variable-handling functions
87 * 87 *
88 * Interface: 88 * Interface:
89 * Var_Set Set the value of a variable in the given 89 * Var_Set Set the value of a variable in the given
90 * context. The variable is created if it doesn't 90 * context. The variable is created if it doesn't
91 * yet exist. 91 * yet exist.
92 * 92 *
@@ -2057,32 +2057,41 @@ typedef struct { @@ -2057,32 +2057,41 @@ typedef struct {
2057 2057
2058 Byte sep; /* Word separator in expansions */ 2058 Byte sep; /* Word separator in expansions */
2059 Boolean oneBigWord; /* TRUE if we will treat the variable as a 2059 Boolean oneBigWord; /* TRUE if we will treat the variable as a
2060 * single big word, even if it contains 2060 * single big word, even if it contains
2061 * embedded spaces (as opposed to the 2061 * embedded spaces (as opposed to the
2062 * usual behaviour of treating it as 2062 * usual behaviour of treating it as
2063 * several space-separated words). */ 2063 * several space-separated words). */
2064 2064
2065 /* result */ 2065 /* result */
2066 char *newStr; /* New value to return */ 2066 char *newStr; /* New value to return */
2067} ApplyModifiersState; 2067} ApplyModifiersState;
2068 2068
2069/* we now have some modifiers with long names */ 2069/* we now have some modifiers with long names */
2070#define STRMOD_MATCH(s, want, n) \ 2070static Boolean
2071 (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':')) 2071ModMatch(const char *mod, const char *modname, char endc)
2072#define STRMOD_MATCHX(s, want, n) \ 2072{
2073 (strncmp(s, want, n) == 0 && \ 2073 size_t n = strlen(modname);
2074 (s[n] == st->endc || s[n] == ':' || s[n] == '=')) 2074 return strncmp(mod, modname, n) == 0 &&
2075#define CHARMOD_MATCH(c) (c == st->endc || c == ':') 2075 (mod[n] == endc || mod[n] == ':');
 2076}
 2077
 2078static inline Boolean
 2079ModMatchEq(const char *mod, const char *modname, char endc)
 2080{
 2081 size_t n = strlen(modname);
 2082 return strncmp(mod, modname, n) == 0 &&
 2083 (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
 2084}
2076 2085
2077/* :@var@...${var}...@ */ 2086/* :@var@...${var}...@ */
2078static Boolean 2087static Boolean
2079ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) { 2088ApplyModifier_Loop(const char *mod, ApplyModifiersState *st) {
2080 ModifyWord_LoopArgs args; 2089 ModifyWord_LoopArgs args;
2081 2090
2082 args.ctx = st->ctxt; 2091 args.ctx = st->ctxt;
2083 st->cp = mod + 1; 2092 st->cp = mod + 1;
2084 char delim = '@'; 2093 char delim = '@';
2085 args.tvar = ParseModifierPart(&st->cp, delim, st->eflags & ~VARE_WANTRES, 2094 args.tvar = ParseModifierPart(&st->cp, delim, st->eflags & ~VARE_WANTRES,
2086 st->ctxt, NULL, NULL, NULL); 2095 st->ctxt, NULL, NULL, NULL);
2087 if (args.tvar == NULL) { 2096 if (args.tvar == NULL) {
2088 st->missing_delim = delim; 2097 st->missing_delim = delim;
@@ -2169,74 +2178,77 @@ ApplyModifier_Defined(const char *mod, A @@ -2169,74 +2178,77 @@ ApplyModifier_Defined(const char *mod, A
2169 st->v->flags |= VAR_KEEP; 2178 st->v->flags |= VAR_KEEP;
2170 if (neflags & VARE_WANTRES) { 2179 if (neflags & VARE_WANTRES) {
2171 st->newStr = Buf_Destroy(&buf, FALSE); 2180 st->newStr = Buf_Destroy(&buf, FALSE);
2172 } else { 2181 } else {
2173 st->newStr = st->nstr; 2182 st->newStr = st->nstr;
2174 Buf_Destroy(&buf, TRUE); 2183 Buf_Destroy(&buf, TRUE);
2175 } 2184 }
2176} 2185}
2177 2186
2178/* :gmtime */ 2187/* :gmtime */
2179static Boolean 2188static Boolean
2180ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st) 2189ApplyModifier_Gmtime(const char *mod, ApplyModifiersState *st)
2181{ 2190{
2182 time_t utc; 2191 if (!ModMatchEq(mod, "gmtime", st->endc)) {
2183 char *ep; 2192 st->cp = mod + 1;
2184 
2185 st->cp = mod + 1; /* make sure it is set */ 
2186 if (!STRMOD_MATCHX(mod, "gmtime", 6)) 
2187 return FALSE; 2193 return FALSE;
 2194 }
 2195
 2196 time_t utc;
2188 if (mod[6] == '=') { 2197 if (mod[6] == '=') {
 2198 char *ep;
2189 utc = strtoul(mod + 7, &ep, 10); 2199 utc = strtoul(mod + 7, &ep, 10);
2190 st->cp = ep; 2200 st->cp = ep;
2191 } else { 2201 } else {
2192 utc = 0; 2202 utc = 0;
2193 st->cp = mod + 6; 2203 st->cp = mod + 6;
2194 } 2204 }
2195 st->newStr = VarStrftime(st->nstr, 1, utc); 2205 st->newStr = VarStrftime(st->nstr, 1, utc);
2196 st->termc = *st->cp; 2206 st->termc = *st->cp;
2197 return TRUE; 2207 return TRUE;
2198} 2208}
2199 2209
2200/* :localtime */ 2210/* :localtime */
2201static Boolean 2211static Boolean
2202ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st) 2212ApplyModifier_Localtime(const char *mod, ApplyModifiersState *st)
2203{ 2213{
2204 time_t utc; 2214 if (!ModMatchEq(mod, "localtime", st->endc)) {
2205 char *ep; 2215 st->cp = mod + 1;
2206 
2207 st->cp = mod + 1; /* make sure it is set */ 
2208 if (!STRMOD_MATCHX(mod, "localtime", 9)) 
2209 return FALSE; 2216 return FALSE;
 2217 }
2210 2218
 2219 time_t utc;
2211 if (mod[9] == '=') { 2220 if (mod[9] == '=') {
 2221 char *ep;
2212 utc = strtoul(mod + 10, &ep, 10); 2222 utc = strtoul(mod + 10, &ep, 10);
2213 st->cp = ep; 2223 st->cp = ep;
2214 } else { 2224 } else {
2215 utc = 0; 2225 utc = 0;
2216 st->cp = mod + 9; 2226 st->cp = mod + 9;
2217 } 2227 }
2218 st->newStr = VarStrftime(st->nstr, 0, utc); 2228 st->newStr = VarStrftime(st->nstr, 0, utc);
2219 st->termc = *st->cp; 2229 st->termc = *st->cp;
2220 return TRUE; 2230 return TRUE;
2221} 2231}
2222 2232
2223/* :hash */ 2233/* :hash */
2224static Boolean 2234static Boolean
2225ApplyModifier_Hash(const char *mod, ApplyModifiersState *st) 2235ApplyModifier_Hash(const char *mod, ApplyModifiersState *st)
2226{ 2236{
2227 st->cp = mod + 1; /* make sure it is set */ 2237 if (!ModMatch(mod, "hash", st->endc)) {
2228 if (!STRMOD_MATCH(mod, "hash", 4)) 2238 st->cp = mod + 1;
2229 return FALSE; 2239 return FALSE;
 2240 }
 2241
2230 st->newStr = VarHash(st->nstr); 2242 st->newStr = VarHash(st->nstr);
2231 st->cp = mod + 4; 2243 st->cp = mod + 4;
2232 st->termc = *st->cp; 2244 st->termc = *st->cp;
2233 return TRUE; 2245 return TRUE;
2234} 2246}
2235 2247
2236/* :P */ 2248/* :P */
2237static void 2249static void
2238ApplyModifier_Path(const char *mod, ApplyModifiersState *st) 2250ApplyModifier_Path(const char *mod, ApplyModifiersState *st)
2239{ 2251{
2240 if (st->v->flags & VAR_JUNK) 2252 if (st->v->flags & VAR_JUNK)
2241 st->v->flags |= VAR_KEEP; 2253 st->v->flags |= VAR_KEEP;
2242 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE); 2254 GNode *gn = Targ_FindNode(st->v->name, TARG_NOCREATE);
@@ -2276,34 +2288,34 @@ ApplyModifier_Exclam(const char *mod, Ap @@ -2276,34 +2288,34 @@ ApplyModifier_Exclam(const char *mod, Ap
2276 if (emsg) 2288 if (emsg)
2277 Error(emsg, st->nstr); 2289 Error(emsg, st->nstr);
2278 2290
2279 st->termc = *st->cp; 2291 st->termc = *st->cp;
2280 if (st->v->flags & VAR_JUNK) 2292 if (st->v->flags & VAR_JUNK)
2281 st->v->flags |= VAR_KEEP; 2293 st->v->flags |= VAR_KEEP;
2282 return TRUE; 2294 return TRUE;
2283} 2295}
2284 2296
2285/* :range */ 2297/* :range */
2286static Boolean 2298static Boolean
2287ApplyModifier_Range(const char *mod, ApplyModifiersState *st) 2299ApplyModifier_Range(const char *mod, ApplyModifiersState *st)
2288{ 2300{
2289 int n; 2301 if (!ModMatchEq(mod, "range", st->endc)) {
2290 char *ep; 2302 st->cp = mod + 1;
2291 
2292 st->cp = mod + 1; /* make sure it is set */ 
2293 if (!STRMOD_MATCHX(mod, "range", 5)) 
2294 return FALSE; 2303 return FALSE;
 2304 }
2295 2305
 2306 int n;
2296 if (mod[5] == '=') { 2307 if (mod[5] == '=') {
 2308 char *ep;
2297 n = strtoul(mod + 6, &ep, 10); 2309 n = strtoul(mod + 6, &ep, 10);
2298 st->cp = ep; 2310 st->cp = ep;
2299 } else { 2311 } else {
2300 n = 0; 2312 n = 0;
2301 st->cp = mod + 5; 2313 st->cp = mod + 5;
2302 } 2314 }
2303 st->newStr = VarRange(st->nstr, n); 2315 st->newStr = VarRange(st->nstr, n);
2304 st->termc = *st->cp; 2316 st->termc = *st->cp;
2305 return TRUE; 2317 return TRUE;
2306} 2318}
2307 2319
2308/* :Mpattern or :Npattern */ 2320/* :Mpattern or :Npattern */
2309static void 2321static void
@@ -2900,43 +2912,40 @@ ApplyModifier_Assign(const char *mod, Ap @@ -2900,43 +2912,40 @@ ApplyModifier_Assign(const char *mod, Ap
2900 Var_Set(st->v->name, val, v_ctxt); 2912 Var_Set(st->v->name, val, v_ctxt);
2901 break; 2913 break;
2902 } 2914 }
2903 } 2915 }
2904 free(val); 2916 free(val);
2905 st->newStr = varNoError; 2917 st->newStr = varNoError;
2906 return 0; 2918 return 0;
2907} 2919}
2908 2920
2909/* remember current value */ 2921/* remember current value */
2910static Boolean 2922static Boolean
2911ApplyModifier_Remember(const char *mod, ApplyModifiersState *st) 2923ApplyModifier_Remember(const char *mod, ApplyModifiersState *st)
2912{ 2924{
2913 st->cp = mod + 1; /* make sure it is set */ 2925 if (!ModMatchEq(mod, "_", st->endc)) {
2914 if (!STRMOD_MATCHX(mod, "_", 1)) 2926 st->cp = mod + 1;
2915 return FALSE; 2927 return FALSE;
 2928 }
2916 2929
2917 if (mod[1] == '=') { 2930 if (mod[1] == '=') {
2918 char *np; 2931 size_t n = strcspn(mod + 2, ":)}");
2919 int n; 2932 char *name = bmake_strndup(mod + 2, n);
2920 2933 Var_Set(name, st->nstr, st->ctxt);
2921 st->cp++; 2934 free(name);
2922 n = strcspn(st->cp, ":)}"); 
2923 np = bmake_strndup(st->cp, n + 1); 
2924 np[n] = '\0'; 
2925 st->cp = mod + 2 + n; 2935 st->cp = mod + 2 + n;
2926 Var_Set(np, st->nstr, st->ctxt); 
2927 free(np); 
2928 } else { 2936 } else {
2929 Var_Set("_", st->nstr, st->ctxt); 2937 Var_Set("_", st->nstr, st->ctxt);
 2938 st->cp = mod + 1;
2930 } 2939 }
2931 st->newStr = st->nstr; 2940 st->newStr = st->nstr;
2932 st->termc = *st->cp; 2941 st->termc = *st->cp;
2933 return TRUE; 2942 return TRUE;
2934} 2943}
2935 2944
2936#ifdef SYSVVARSUB 2945#ifdef SYSVVARSUB
2937/* :from=to */ 2946/* :from=to */
2938static int 2947static int
2939ApplyModifier_SysV(const char *mod, ApplyModifiersState *st) 2948ApplyModifier_SysV(const char *mod, ApplyModifiersState *st)
2940{ 2949{
2941 Boolean eqFound = FALSE; 2950 Boolean eqFound = FALSE;
2942 2951

cvs diff -r1.29 -r1.30 src/usr.bin/make/unit-tests/modmisc.exp (expand / switch to unified diff)

--- src/usr.bin/make/unit-tests/modmisc.exp 2020/07/26 11:39:55 1.29
+++ src/usr.bin/make/unit-tests/modmisc.exp 2020/07/27 21:54:25 1.30
@@ -81,14 +81,16 @@ mod-assign: appended=1 2 3. @@ -81,14 +81,16 @@ mod-assign: appended=1 2 3.
812 812
823 823
83mod-assign: ran:3. 83mod-assign: ran:3.
84mod-assign: global: 1, 3, 1 2 3, 3. 84mod-assign: global: 1, 3, 1 2 3, 3.
85mod-assign-nested: then1t1 85mod-assign-nested: then1t1
86mod-assign-nested: else2e2 86mod-assign-nested: else2e2
87mod-assign-nested: then3t3 87mod-assign-nested: then3t3
88mod-assign-nested: else4e4 88mod-assign-nested: else4e4
89mod-tu-space: A B 89mod-tu-space: A B
90mod-quote: new 90mod-quote: new
91 91
92line 92line
93mod-break-many-words: 500 93mod-break-many-words: 500
 94mod-remember: 1 2 3 1 2 3 1 2 3
 95mod-remember: 1 2 3, SAVED=3
94exit status 0 96exit status 0

cvs diff -r1.26 -r1.27 src/usr.bin/make/unit-tests/modmisc.mk (expand / switch to unified diff)

--- src/usr.bin/make/unit-tests/modmisc.mk 2020/07/26 13:09:53 1.26
+++ src/usr.bin/make/unit-tests/modmisc.mk 2020/07/27 21:54:25 1.27
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1# $Id: modmisc.mk,v 1.26 2020/07/26 13:09:53 rillig Exp $ 1# $Id: modmisc.mk,v 1.27 2020/07/27 21:54:25 rillig Exp $
2# 2#
3# miscellaneous modifier tests 3# miscellaneous modifier tests
4 4
5# do not put any dirs in this list which exist on some 5# do not put any dirs in this list which exist on some
6# but not all target systems - an exists() check is below. 6# but not all target systems - an exists() check is below.
7path=:/bin:/tmp::/:.:/no/such/dir:. 7path=:/bin:/tmp::/:.:/no/such/dir:.
8# strip cwd from path. 8# strip cwd from path.
9MOD_NODOT=S/:/ /g:N.:ts: 9MOD_NODOT=S/:/ /g:N.:ts:
10# and decorate, note that $'s need to be doubled. Also note that  10# and decorate, note that $'s need to be doubled. Also note that
11# the modifier_variable can be used with other modifiers. 11# the modifier_variable can be used with other modifiers.
12MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@ 12MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@
13# another mod - pretend it is more interesting 13# another mod - pretend it is more interesting
14MOD_HOMES=S,/home/,/homes/, 14MOD_HOMES=S,/home/,/homes/,
@@ -17,26 +17,27 @@ MOD_SEP=S,:, ,g @@ -17,26 +17,27 @@ MOD_SEP=S,:, ,g
17 17
18all: modvar modvarloop modsysv mod-HTE emptyvar undefvar 18all: modvar modvarloop modsysv mod-HTE emptyvar undefvar
19all: mod-subst 19all: mod-subst
20all: mod-regex 20all: mod-regex
21all: mod-loop-varname mod-loop-resolve mod-loop-varname-dollar 21all: mod-loop-varname mod-loop-resolve mod-loop-varname-dollar
22all: mod-subst-dollar mod-loop-dollar 22all: mod-subst-dollar mod-loop-dollar
23all: mod-regex-limits 23all: mod-regex-limits
24all: mod-regex-errors 24all: mod-regex-errors
25all: mod-assign 25all: mod-assign
26all: mod-assign-nested 26all: mod-assign-nested
27all: mod-tu-space 27all: mod-tu-space
28all: mod-quote 28all: mod-quote
29all: mod-break-many-words 29all: mod-break-many-words
 30all: mod-remember
30 31
31# See also sysv.mk. 32# See also sysv.mk.
32modsysv: 33modsysv:
33 @echo "The answer is ${libfoo.a:L:libfoo.a=42}" 34 @echo "The answer is ${libfoo.a:L:libfoo.a=42}"
34 35
35# Demonstrates modifiers that are given indirectly from a variable. 36# Demonstrates modifiers that are given indirectly from a variable.
36modvar: 37modvar:
37 @echo "path='${path}'" 38 @echo "path='${path}'"
38 @echo "path='${path:${MOD_NODOT}}'" 39 @echo "path='${path:${MOD_NODOT}}'"
39 @echo "path='${path:S,home,homes,:${MOD_NODOT}}'" 40 @echo "path='${path:S,home,homes,:${MOD_NODOT}}'"
40 @echo "path=${path:${MOD_NODOTX}:ts:}" 41 @echo "path=${path:${MOD_NODOTX}:ts:}"
41 @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}" 42 @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}"
42 43
@@ -232,13 +233,21 @@ SINK4:= ${0:?${THEN4::=then4${IT4::=t4}} @@ -232,13 +233,21 @@ SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}
232 233
233mod-tu-space: 234mod-tu-space:
234 # The :tu and :tl modifiers operate on the variable value 235 # The :tu and :tl modifiers operate on the variable value
235 # as a single string, not as a list of words. Therefore, 236 # as a single string, not as a list of words. Therefore,
236 # the adjacent spaces are preserved. 237 # the adjacent spaces are preserved.
237 @echo $@: ${a b:L:tu:Q} 238 @echo $@: ${a b:L:tu:Q}
238 239
239mod-quote: 240mod-quote:
240 @echo $@: new${.newline:Q}${.newline:Q}line 241 @echo $@: new${.newline:Q}${.newline:Q}line
241 242
242# Cover the bmake_realloc in brk_string. 243# Cover the bmake_realloc in brk_string.
243mod-break-many-words: 244mod-break-many-words:
244 @echo $@: ${UNDEF:U:range=500:[#]} 245 @echo $@: ${UNDEF:U:range=500:[#]}
 246
 247# Demonstrate the :_ modifier.
 248# In the parameterized form, having the variable name on the right side
 249# of the = assignment operator is confusing. Luckily this modifier is
 250# only rarely needed.
 251mod-remember:
 252 @echo $@: ${1 2 3:L:_:@var@${_}@}
 253 @echo $@: ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED}