Fri Jan 7 20:15:10 2022 UTC ()
make: clean up handling of .for loops

Sort ForLoop members in natural reading order.

Remove redundant condition in ForLoop_ParseItems; at that point, the
number of variables is non-zero.

Rename Buf_AddEscaped since that function is not part of the Buffer API,
it is specific to .for loops.

No functional change.


(rillig)
diff -r1.156 -r1.157 src/usr.bin/make/for.c

cvs diff -r1.156 -r1.157 src/usr.bin/make/for.c (expand / switch to unified diff)

--- src/usr.bin/make/for.c 2022/01/07 20:09:58 1.156
+++ src/usr.bin/make/for.c 2022/01/07 20:15:10 1.157
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: for.c,v 1.156 2022/01/07 20:09:58 rillig Exp $ */ 1/* $NetBSD: for.c,v 1.157 2022/01/07 20:15:10 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1992, The Regents of the University of California. 4 * Copyright (c) 1992, The Regents of the University of California.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -48,63 +48,62 @@ @@ -48,63 +48,62 @@
48 * body is scanned for variable expressions, and those that match the 48 * body is scanned for variable expressions, and those that match the
49 * variable names are replaced with expressions of the form ${:U...}. After 49 * variable names are replaced with expressions of the form ${:U...}. After
50 * that, the body is treated like a file from an .include directive. 50 * that, the body is treated like a file from an .include directive.
51 * 51 *
52 * Interface: 52 * Interface:
53 * For_Eval Evaluate the loop in the passed line. 53 * For_Eval Evaluate the loop in the passed line.
54 * 54 *
55 * For_Run Run accumulated loop 55 * For_Run Run accumulated loop
56 */ 56 */
57 57
58#include "make.h" 58#include "make.h"
59 59
60/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ 60/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
61MAKE_RCSID("$NetBSD: for.c,v 1.156 2022/01/07 20:09:58 rillig Exp $"); 61MAKE_RCSID("$NetBSD: for.c,v 1.157 2022/01/07 20:15:10 rillig Exp $");
62 62
63 63
64typedef struct ForLoop { 64typedef struct ForLoop {
65 Buffer body; /* Unexpanded body of the loop */ 
66 Vector /* of 'char *' */ vars; /* Iteration variables */ 65 Vector /* of 'char *' */ vars; /* Iteration variables */
67 SubstringWords items; /* Substitution items */ 66 SubstringWords items; /* Substitution items */
 67 Buffer body; /* Unexpanded body of the loop */
68 unsigned int nextItem; /* Where to continue iterating */ 68 unsigned int nextItem; /* Where to continue iterating */
69} ForLoop; 69} ForLoop;
70 70
71 71
72static ForLoop *accumFor; /* Loop being accumulated */ 72static ForLoop *accumFor; /* Loop being accumulated */
73 73
74 74
75static ForLoop * 75static ForLoop *
76ForLoop_New(void) 76ForLoop_New(void)
77{ 77{
78 ForLoop *f = bmake_malloc(sizeof *f); 78 ForLoop *f = bmake_malloc(sizeof *f);
79 79
80 Buf_Init(&f->body); 
81 Vector_Init(&f->vars, sizeof(char *)); 80 Vector_Init(&f->vars, sizeof(char *));
82 SubstringWords_Init(&f->items); 81 SubstringWords_Init(&f->items);
 82 Buf_Init(&f->body);
83 f->nextItem = 0; 83 f->nextItem = 0;
84 84
85 return f; 85 return f;
86} 86}
87 87
88static void 88static void
89ForLoop_Free(ForLoop *f) 89ForLoop_Free(ForLoop *f)
90{ 90{
91 Buf_Done(&f->body); 
92 
93 while (f->vars.len > 0) 91 while (f->vars.len > 0)
94 free(*(char **)Vector_Pop(&f->vars)); 92 free(*(char **)Vector_Pop(&f->vars));
95 Vector_Done(&f->vars); 93 Vector_Done(&f->vars);
96 94
97 SubstringWords_Free(f->items); 95 SubstringWords_Free(f->items);
 96 Buf_Done(&f->body);
98 97
99 free(f); 98 free(f);
100} 99}
101 100
102static bool 101static bool
103ForLoop_ParseVarnames(ForLoop *f, const char **pp) 102ForLoop_ParseVarnames(ForLoop *f, const char **pp)
104{ 103{
105 const char *p = *pp; 104 const char *p = *pp;
106 105
107 for (;;) { 106 for (;;) {
108 size_t len; 107 size_t len;
109 108
110 cpp_skip_whitespace(&p); 109 cpp_skip_whitespace(&p);
@@ -146,27 +145,27 @@ ForLoop_ParseItems(ForLoop *f, const cha @@ -146,27 +145,27 @@ ForLoop_ParseItems(ForLoop *f, const cha
146 cpp_skip_whitespace(&p); 145 cpp_skip_whitespace(&p);
147 146
148 if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { 147 if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
149 Parse_Error(PARSE_FATAL, "Error in .for loop items"); 148 Parse_Error(PARSE_FATAL, "Error in .for loop items");
150 return false; 149 return false;
151 } 150 }
152 151
153 f->items = Substring_Words(items, false); 152 f->items = Substring_Words(items, false);
154 free(items); 153 free(items);
155 154
156 if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0])) 155 if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0]))
157 f->items.len = 0; /* .for var in ${:U} */ 156 f->items.len = 0; /* .for var in ${:U} */
158 157
159 if (f->items.len != 0 && f->items.len % f->vars.len != 0) { 158 if (f->items.len % f->vars.len != 0) {
160 Parse_Error(PARSE_FATAL, 159 Parse_Error(PARSE_FATAL,
161 "Wrong number of words (%u) in .for " 160 "Wrong number of words (%u) in .for "
162 "substitution list with %u variables", 161 "substitution list with %u variables",
163 (unsigned)f->items.len, (unsigned)f->vars.len); 162 (unsigned)f->items.len, (unsigned)f->vars.len);
164 return false; 163 return false;
165 } 164 }
166 165
167 return true; 166 return true;
168} 167}
169 168
170static bool 169static bool
171IsFor(const char *p) 170IsFor(const char *p)
172{ 171{
@@ -174,33 +173,30 @@ IsFor(const char *p) @@ -174,33 +173,30 @@ IsFor(const char *p)
174} 173}
175 174
176static bool 175static bool
177IsEndfor(const char *p) 176IsEndfor(const char *p)
178{ 177{
179 return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && 178 return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
180 (p[6] == '\0' || ch_isspace(p[6])); 179 (p[6] == '\0' || ch_isspace(p[6]));
181} 180}
182 181
183/* 182/*
184 * Evaluate the for loop in the passed line. The line looks like this: 183 * Evaluate the for loop in the passed line. The line looks like this:
185 * .for <varname...> in <value...> 184 * .for <varname...> in <value...>
186 * 185 *
187 * Input: 
188 * line Line to parse 
189 * 
190 * Results: 186 * Results:
191 * 0: Not a .for statement, parse the line 187 * 0 not a .for directive
192 * 1: We found a for loop 188 * 1 found a .for directive
193 * -1: A .for statement with a bad syntax error, discard. 189 * -1 erroneous .for directive
194 */ 190 */
195int 191int
196For_Eval(const char *line) 192For_Eval(const char *line)
197{ 193{
198 ForLoop *f; 194 ForLoop *f;
199 const char *p; 195 const char *p;
200 196
201 p = line + 1; /* skip the '.' */ 197 p = line + 1; /* skip the '.' */
202 cpp_skip_whitespace(&p); 198 cpp_skip_whitespace(&p);
203 199
204 if (!IsFor(p)) { 200 if (!IsFor(p)) {
205 if (IsEndfor(p)) { 201 if (IsEndfor(p)) {
206 Parse_Error(PARSE_FATAL, "for-less endfor"); 202 Parse_Error(PARSE_FATAL, "for-less endfor");
@@ -231,27 +227,27 @@ For_Eval(const char *line) @@ -231,27 +227,27 @@ For_Eval(const char *line)
231 * Returns false when the matching .endfor is reached. 227 * Returns false when the matching .endfor is reached.
232 */ 228 */
233bool 229bool
234For_Accum(const char *line, int *forLevel) 230For_Accum(const char *line, int *forLevel)
235{ 231{
236 const char *p = line; 232 const char *p = line;
237 233
238 if (*p == '.') { 234 if (*p == '.') {
239 p++; 235 p++;
240 cpp_skip_whitespace(&p); 236 cpp_skip_whitespace(&p);
241 237
242 if (IsEndfor(p)) { 238 if (IsEndfor(p)) {
243 DEBUG1(FOR, "For: end for %d\n", *forLevel); 239 DEBUG1(FOR, "For: end for %d\n", *forLevel);
244 if (--*forLevel <= 0) 240 if (--*forLevel == 0)
245 return false; 241 return false;
246 } else if (IsFor(p)) { 242 } else if (IsFor(p)) {
247 (*forLevel)++; 243 (*forLevel)++;
248 DEBUG1(FOR, "For: new loop %d\n", *forLevel); 244 DEBUG1(FOR, "For: new loop %d\n", *forLevel);
249 } 245 }
250 } 246 }
251 247
252 Buf_AddStr(&accumFor->body, line); 248 Buf_AddStr(&accumFor->body, line);
253 Buf_AddByte(&accumFor->body, '\n'); 249 Buf_AddByte(&accumFor->body, '\n');
254 return true; 250 return true;
255} 251}
256 252
257 253
@@ -299,27 +295,27 @@ NeedsEscapes(Substring value, char endc) @@ -299,27 +295,27 @@ NeedsEscapes(Substring value, char endc)
299 *p == '\n') 295 *p == '\n')
300 return true; 296 return true;
301 } 297 }
302 return false; 298 return false;
303} 299}
304 300
305/* 301/*
306 * While expanding the body of a .for loop, write the item in the ${:U...} 302 * While expanding the body of a .for loop, write the item in the ${:U...}
307 * expression, escaping characters as needed. 303 * expression, escaping characters as needed.
308 * 304 *
309 * The result is later unescaped by ApplyModifier_Defined. 305 * The result is later unescaped by ApplyModifier_Defined.
310 */ 306 */
311static void 307static void
312Buf_AddEscaped(Buffer *cmds, Substring item, char endc) 308AddEscaped(Buffer *cmds, Substring item, char endc)
313{ 309{
314 const char *p; 310 const char *p;
315 char ch; 311 char ch;
316 312
317 if (!NeedsEscapes(item, endc)) { 313 if (!NeedsEscapes(item, endc)) {
318 Buf_AddBytesBetween(cmds, item.start, item.end); 314 Buf_AddBytesBetween(cmds, item.start, item.end);
319 return; 315 return;
320 } 316 }
321 317
322 /* 318 /*
323 * Escape ':', '$', '\\' and 'endc' - these will be removed later by 319 * Escape ':', '$', '\\' and 'endc' - these will be removed later by
324 * :U processing, see ApplyModifier_Defined. 320 * :U processing, see ApplyModifier_Defined.
325 */ 321 */
@@ -367,27 +363,27 @@ ForLoop_SubstVarLong(ForLoop *f, Buffer  @@ -367,27 +363,27 @@ ForLoop_SubstVarLong(ForLoop *f, Buffer
367 p++, varname++; 363 p++, varname++;
368 if (*varname != '\0') 364 if (*varname != '\0')
369 continue; 365 continue;
370 /* XXX: why test for backslash here? */ 366 /* XXX: why test for backslash here? */
371 if (*p != ':' && *p != endc && *p != '\\') 367 if (*p != ':' && *p != endc && *p != '\\')
372 continue; 368 continue;
373 369
374 /* 370 /*
375 * Found a variable match. Skip over the variable name and 371 * Found a variable match. Skip over the variable name and
376 * instead add ':U<value>' to the current body. 372 * instead add ':U<value>' to the current body.
377 */ 373 */
378 Buf_AddBytesBetween(body, *inout_mark, start); 374 Buf_AddBytesBetween(body, *inout_mark, start);
379 Buf_AddStr(body, ":U"); 375 Buf_AddStr(body, ":U");
380 Buf_AddEscaped(body, f->items.words[f->nextItem + i], endc); 376 AddEscaped(body, f->items.words[f->nextItem + i], endc);
381 377
382 *inout_mark = p; 378 *inout_mark = p;
383 *pp = p; 379 *pp = p;
384 return; 380 return;
385 } 381 }
386} 382}
387 383
388/* 384/*
389 * When expanding the body of a .for loop, replace single-character 385 * When expanding the body of a .for loop, replace single-character
390 * variable expressions like $i with their ${:U...} expansion. 386 * variable expressions like $i with their ${:U...} expansion.
391 */ 387 */
392static void 388static void
393ForLoop_SubstVarShort(ForLoop *f, Buffer *body, 389ForLoop_SubstVarShort(ForLoop *f, Buffer *body,
@@ -405,27 +401,27 @@ ForLoop_SubstVarShort(ForLoop *f, Buffer @@ -405,27 +401,27 @@ ForLoop_SubstVarShort(ForLoop *f, Buffer
405 for (i = 0; i < f->vars.len; i++) { 401 for (i = 0; i < f->vars.len; i++) {
406 const char *varname = vars[i]; 402 const char *varname = vars[i];
407 if (varname[0] == ch && varname[1] == '\0') 403 if (varname[0] == ch && varname[1] == '\0')
408 goto found; 404 goto found;
409 } 405 }
410 return; 406 return;
411 407
412found: 408found:
413 Buf_AddBytesBetween(body, *inout_mark, p); 409 Buf_AddBytesBetween(body, *inout_mark, p);
414 *inout_mark = p + 1; 410 *inout_mark = p + 1;
415 411
416 /* Replace $<ch> with ${:U<value>} */ 412 /* Replace $<ch> with ${:U<value>} */
417 Buf_AddStr(body, "{:U"); 413 Buf_AddStr(body, "{:U");
418 Buf_AddEscaped(body, f->items.words[f->nextItem + i], '}'); 414 AddEscaped(body, f->items.words[f->nextItem + i], '}');
419 Buf_AddByte(body, '}'); 415 Buf_AddByte(body, '}');
420} 416}
421 417
422/* 418/*
423 * Compute the body for the current iteration by copying the unexpanded body, 419 * Compute the body for the current iteration by copying the unexpanded body,
424 * replacing the expressions for the iteration variables on the way. 420 * replacing the expressions for the iteration variables on the way.
425 * 421 *
426 * Using variable expressions ensures that the .for loop can't generate 422 * Using variable expressions ensures that the .for loop can't generate
427 * syntax, and that the later parsing will still see a variable. 423 * syntax, and that the later parsing will still see a variable.
428 * This code assumes that the variable with the empty name will never be 424 * This code assumes that the variable with the empty name will never be
429 * defined, see unit-tests/varname-empty.mk for more details. 425 * defined, see unit-tests/varname-empty.mk for more details.
430 * 426 *
431 * The detection of substitutions of the loop control variables is naive. 427 * The detection of substitutions of the loop control variables is naive.