GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: cond.c,v 1.50 2013/11/22 15:47:35 espie Exp $ */ |
||
2 |
/* $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $ */ |
||
3 |
|||
4 |
/* |
||
5 |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
||
6 |
* Copyright (c) 1988, 1989 by Adam de Boor |
||
7 |
* Copyright (c) 1989 by Berkeley Softworks |
||
8 |
* All rights reserved. |
||
9 |
* |
||
10 |
* This code is derived from software contributed to Berkeley by |
||
11 |
* Adam de Boor. |
||
12 |
* |
||
13 |
* Redistribution and use in source and binary forms, with or without |
||
14 |
* modification, are permitted provided that the following conditions |
||
15 |
* are met: |
||
16 |
* 1. Redistributions of source code must retain the above copyright |
||
17 |
* notice, this list of conditions and the following disclaimer. |
||
18 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
19 |
* notice, this list of conditions and the following disclaimer in the |
||
20 |
* documentation and/or other materials provided with the distribution. |
||
21 |
* 3. Neither the name of the University nor the names of its contributors |
||
22 |
* may be used to endorse or promote products derived from this software |
||
23 |
* without specific prior written permission. |
||
24 |
* |
||
25 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
26 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
27 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
28 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
29 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
30 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
31 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
32 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
33 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
34 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
35 |
* SUCH DAMAGE. |
||
36 |
*/ |
||
37 |
|||
38 |
#include <ctype.h> |
||
39 |
#include <stddef.h> |
||
40 |
#include <stdint.h> |
||
41 |
#include <stdio.h> |
||
42 |
#include <stdlib.h> |
||
43 |
#include <string.h> |
||
44 |
#include <ohash.h> |
||
45 |
#include "config.h" |
||
46 |
#include "defines.h" |
||
47 |
#include "dir.h" |
||
48 |
#include "buf.h" |
||
49 |
#include "cond.h" |
||
50 |
#include "cond_int.h" |
||
51 |
#include "condhashconsts.h" |
||
52 |
#include "error.h" |
||
53 |
#include "var.h" |
||
54 |
#include "varname.h" |
||
55 |
#include "targ.h" |
||
56 |
#include "lowparse.h" |
||
57 |
#include "str.h" |
||
58 |
#include "main.h" |
||
59 |
#include "gnode.h" |
||
60 |
#include "lst.h" |
||
61 |
|||
62 |
|||
63 |
/* The parsing of conditional expressions is based on this grammar: |
||
64 |
* E -> F || E |
||
65 |
* E -> F |
||
66 |
* F -> T && F |
||
67 |
* F -> T |
||
68 |
* T -> defined(variable) |
||
69 |
* T -> make(target) |
||
70 |
* T -> exists(file) |
||
71 |
* T -> empty(varspec) |
||
72 |
* T -> target(name) |
||
73 |
* T -> commands(name) |
||
74 |
* T -> symbol |
||
75 |
* T -> $(varspec) op value |
||
76 |
* T -> $(varspec) == "string" |
||
77 |
* T -> $(varspec) != "string" |
||
78 |
* T -> "string" == "string" |
||
79 |
* T -> "string" != "string" |
||
80 |
* T -> number op number |
||
81 |
* T -> ( E ) |
||
82 |
* T -> ! T |
||
83 |
* op -> == | != | > | < | >= | <= |
||
84 |
* |
||
85 |
* 'symbol' is some other symbol to which the default function (condDefProc) |
||
86 |
* is applied. |
||
87 |
* |
||
88 |
* Tokens are scanned from the 'condExpr' string. The scanner (CondToken) |
||
89 |
* will return And for '&' and '&&', Or for '|' and '||', Not for '!', |
||
90 |
* LParen for '(', RParen for ')' and will evaluate the other terminal |
||
91 |
* symbols, using either the default function or the function given in the |
||
92 |
* terminal, and return the result as either true or False. |
||
93 |
* |
||
94 |
* All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */ |
||
95 |
typedef enum { |
||
96 |
False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err |
||
97 |
} Token; |
||
98 |
|||
99 |
/*- |
||
100 |
* Structures to handle elegantly the different forms of #if's. The |
||
101 |
* last two fields are stored in condInvert and condDefProc, respectively. |
||
102 |
*/ |
||
103 |
static bool CondGetArg(const char **, struct Name *, |
||
104 |
const char *, bool); |
||
105 |
static bool CondDoDefined(struct Name *); |
||
106 |
static bool CondDoMake(struct Name *); |
||
107 |
static bool CondDoExists(struct Name *); |
||
108 |
static bool CondDoTarget(struct Name *); |
||
109 |
static bool CondDoTargetWithCommands(struct Name *); |
||
110 |
static bool CondCvtArg(const char *, double *); |
||
111 |
static Token CondToken(bool); |
||
112 |
static Token CondT(bool); |
||
113 |
static Token CondF(bool); |
||
114 |
static Token CondE(bool); |
||
115 |
static Token CondHandleVarSpec(bool); |
||
116 |
static Token CondHandleDefault(bool); |
||
117 |
static Token CondHandleComparison(char *, bool, bool); |
||
118 |
static Token CondHandleString(bool); |
||
119 |
static Token CondHandleNumber(bool); |
||
120 |
static const char *find_cond(const char *); |
||
121 |
|||
122 |
|||
123 |
struct If { |
||
124 |
bool isElse; /* true for else forms */ |
||
125 |
bool doNot; /* true for embedded negation */ |
||
126 |
bool (*defProc)(struct Name *); /* function to apply */ |
||
127 |
}; |
||
128 |
|||
129 |
static struct If ifs[] = { |
||
130 |
{ false,false, CondDoDefined }, /* if, ifdef */ |
||
131 |
{ false,true, CondDoDefined }, /* ifndef */ |
||
132 |
{ false,false, CondDoMake }, /* ifmake */ |
||
133 |
{ false,true, CondDoMake }, /* ifnmake */ |
||
134 |
{ true, false, CondDoDefined }, /* elif, elifdef */ |
||
135 |
{ true, true, CondDoDefined }, /* elifndef */ |
||
136 |
{ true, false, CondDoMake }, /* elifmake */ |
||
137 |
{ true, true, CondDoMake }, /* elifnmake */ |
||
138 |
{ true, false, NULL } |
||
139 |
}; |
||
140 |
|||
141 |
#define COND_IF_INDEX 0 |
||
142 |
#define COND_IFDEF_INDEX 0 |
||
143 |
#define COND_IFNDEF_INDEX 1 |
||
144 |
#define COND_IFMAKE_INDEX 2 |
||
145 |
#define COND_IFNMAKE_INDEX 3 |
||
146 |
#define COND_ELIF_INDEX 4 |
||
147 |
#define COND_ELIFDEF_INDEX 4 |
||
148 |
#define COND_ELIFNDEF_INDEX 5 |
||
149 |
#define COND_ELIFMAKE_INDEX 6 |
||
150 |
#define COND_ELIFNMAKE_INDEX 7 |
||
151 |
#define COND_ELSE_INDEX 8 |
||
152 |
|||
153 |
static bool condInvert; /* Invert the default function */ |
||
154 |
static bool (*condDefProc)(struct Name *); |
||
155 |
/* Default function to apply */ |
||
156 |
static const char *condExpr; /* The expression to parse */ |
||
157 |
static Token condPushBack=None; /* Single push-back token used in parsing */ |
||
158 |
|||
159 |
#define MAXIF 30 /* greatest depth of #if'ing */ |
||
160 |
|||
161 |
static struct { |
||
162 |
bool value; |
||
163 |
Location origin; |
||
164 |
} condStack[MAXIF]; /* Stack of conditionals */ |
||
165 |
|||
166 |
static int condTop = MAXIF; /* Top-most conditional */ |
||
167 |
static int skipIfLevel=0; /* Depth of skipped conditionals */ |
||
168 |
static bool skipLine = false; /* Whether the parse module is skipping lines */ |
||
169 |
|||
170 |
static const char * |
||
171 |
find_cond(const char *p) |
||
172 |
332393 |
{ |
|
173 |
289932 |
for (;;p++) { |
|
174 |
/* XXX: when *p == '\0', strchr() returns !NULL */ |
||
175 |
✓✓ | 332393 |
if (strchr(" \t)&|$", *p) != NULL) |
176 |
42461 |
return p; |
|
177 |
289932 |
} |
|
178 |
} |
||
179 |
|||
180 |
|||
181 |
/*- |
||
182 |
*----------------------------------------------------------------------- |
||
183 |
* CondGetArg -- |
||
184 |
* Find the argument of a built-in function. |
||
185 |
* |
||
186 |
* Results: |
||
187 |
* true if evaluation went okay |
||
188 |
* |
||
189 |
* Side Effects: |
||
190 |
* The line pointer is set to point to the closing parenthesis of the |
||
191 |
* function call. The argument is filled. |
||
192 |
*----------------------------------------------------------------------- |
||
193 |
*/ |
||
194 |
static bool |
||
195 |
CondGetArg(const char **linePtr, struct Name *arg, const char *func, |
||
196 |
bool parens) /* true if arg should be bounded by parens */ |
||
197 |
39154 |
{ |
|
198 |
const char *cp; |
||
199 |
|||
200 |
39154 |
cp = *linePtr; |
|
201 |
/* Set things up to return faster in case of problem */ |
||
202 |
39154 |
arg->s = cp; |
|
203 |
39154 |
arg->e = cp; |
|
204 |
39154 |
arg->tofree = false; |
|
205 |
|||
206 |
/* make and defined are not really keywords, so if CondGetArg doesn't |
||
207 |
* work... |
||
208 |
*/ |
||
209 |
✓✓ | 39154 |
if (parens) { |
210 |
✗✓ | 76842 |
while (ISSPACE(*cp)) |
211 |
cp++; |
||
212 |
✓✓ | 38421 |
if (*cp == '(') |
213 |
37688 |
cp++; |
|
214 |
else |
||
215 |
733 |
return false; |
|
216 |
} |
||
217 |
|||
218 |
✗✓ | 38421 |
if (*cp == '\0') |
219 |
return false; |
||
220 |
|||
221 |
✗✓ | 76842 |
while (ISSPACE(*cp)) |
222 |
cp++; |
||
223 |
|||
224 |
38421 |
cp = VarName_Get(cp, arg, NULL, true, find_cond); |
|
225 |
|||
226 |
✗✓ | 115263 |
while (ISSPACE(*cp)) |
227 |
cp++; |
||
228 |
✓✓ | 38421 |
if (parens) { |
229 |
✓✗ | 37688 |
if (*cp == ')') |
230 |
37688 |
cp++; |
|
231 |
else { |
||
232 |
Parse_Error(PARSE_WARNING, |
||
233 |
"Missing closing parenthesis for %s()", func); |
||
234 |
return false; |
||
235 |
} |
||
236 |
} |
||
237 |
|||
238 |
38421 |
*linePtr = cp; |
|
239 |
38421 |
return true; |
|
240 |
} |
||
241 |
|||
242 |
/*- |
||
243 |
*----------------------------------------------------------------------- |
||
244 |
* CondDoDefined -- |
||
245 |
* Handle the 'defined' function for conditionals. |
||
246 |
* |
||
247 |
* Results: |
||
248 |
* true if the given variable is defined. |
||
249 |
*----------------------------------------------------------------------- |
||
250 |
*/ |
||
251 |
static bool |
||
252 |
CondDoDefined(struct Name *arg) |
||
253 |
22691 |
{ |
|
254 |
22691 |
return Var_Definedi(arg->s, arg->e); |
|
255 |
} |
||
256 |
|||
257 |
/*- |
||
258 |
*----------------------------------------------------------------------- |
||
259 |
* CondDoMake -- |
||
260 |
* Handle the 'make' function for conditionals. |
||
261 |
* |
||
262 |
* Results: |
||
263 |
* true if the given target is being made. |
||
264 |
*----------------------------------------------------------------------- |
||
265 |
*/ |
||
266 |
static bool |
||
267 |
CondDoMake(struct Name *arg) |
||
268 |
15 |
{ |
|
269 |
LstNode ln; |
||
270 |
|||
271 |
✓✓ | 21 |
for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { |
272 |
6 |
char *s = (char *)Lst_Datum(ln); |
|
273 |
✗✓ | 6 |
if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e)) |
274 |
return true; |
||
275 |
} |
||
276 |
|||
277 |
15 |
return false; |
|
278 |
} |
||
279 |
|||
280 |
/*- |
||
281 |
*----------------------------------------------------------------------- |
||
282 |
* CondDoExists -- |
||
283 |
* See if the given file exists. |
||
284 |
* |
||
285 |
* Results: |
||
286 |
* true if the file exists and false if it does not. |
||
287 |
*----------------------------------------------------------------------- |
||
288 |
*/ |
||
289 |
static bool |
||
290 |
CondDoExists(struct Name *arg) |
||
291 |
1319 |
{ |
|
292 |
bool result; |
||
293 |
char *path; |
||
294 |
|||
295 |
1319 |
path = Dir_FindFilei(arg->s, arg->e, defaultPath); |
|
296 |
✓✓ | 1319 |
if (path != NULL) { |
297 |
900 |
result = true; |
|
298 |
900 |
free(path); |
|
299 |
} else { |
||
300 |
419 |
result = false; |
|
301 |
} |
||
302 |
1319 |
return result; |
|
303 |
} |
||
304 |
|||
305 |
/*- |
||
306 |
*----------------------------------------------------------------------- |
||
307 |
* CondDoTarget -- |
||
308 |
* See if the given node exists and is an actual target. |
||
309 |
* |
||
310 |
* Results: |
||
311 |
* true if the node exists as a target and false if it does not. |
||
312 |
*----------------------------------------------------------------------- |
||
313 |
*/ |
||
314 |
static bool |
||
315 |
CondDoTarget(struct Name *arg) |
||
316 |
12788 |
{ |
|
317 |
GNode *gn; |
||
318 |
|||
319 |
12788 |
gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); |
|
320 |
✓✓✓✓ |
12788 |
if (gn != NULL && !OP_NOP(gn->type)) |
321 |
6018 |
return true; |
|
322 |
else |
||
323 |
6770 |
return false; |
|
324 |
} |
||
325 |
|||
326 |
/*- |
||
327 |
*----------------------------------------------------------------------- |
||
328 |
* CondDoTargetWithCommands -- |
||
329 |
* See if the given node exists and has commands. |
||
330 |
* |
||
331 |
* Results: |
||
332 |
* true if the node is complete and false if it does not. |
||
333 |
*----------------------------------------------------------------------- |
||
334 |
*/ |
||
335 |
static bool |
||
336 |
CondDoTargetWithCommands(struct Name *arg) |
||
337 |
{ |
||
338 |
GNode *gn; |
||
339 |
|||
340 |
gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); |
||
341 |
if (gn != NULL && !OP_NOP(gn->type) && (gn->type & OP_HAS_COMMANDS)) |
||
342 |
return true; |
||
343 |
else |
||
344 |
return false; |
||
345 |
} |
||
346 |
|||
347 |
|||
348 |
/*- |
||
349 |
*----------------------------------------------------------------------- |
||
350 |
* CondCvtArg -- |
||
351 |
* Convert the given number into a double. If the number begins |
||
352 |
* with 0x, it is interpreted as a hexadecimal integer |
||
353 |
* and converted to a double from there. All other strings just have |
||
354 |
* strtod called on them. |
||
355 |
* |
||
356 |
* Results: |
||
357 |
* Sets 'value' to double value of string. |
||
358 |
* Returns true if the string was a valid number, false o.w. |
||
359 |
* |
||
360 |
* Side Effects: |
||
361 |
* Can change 'value' even if string is not a valid number. |
||
362 |
*----------------------------------------------------------------------- |
||
363 |
*/ |
||
364 |
static bool |
||
365 |
CondCvtArg(const char *str, double *value) |
||
366 |
9168 |
{ |
|
367 |
✓✓✗✓ |
9168 |
if (*str == '0' && str[1] == 'x') { |
368 |
long i; |
||
369 |
|||
370 |
for (str += 2, i = 0; *str; str++) { |
||
371 |
int x; |
||
372 |
if (ISDIGIT(*str)) |
||
373 |
x = *str - '0'; |
||
374 |
else if (ISXDIGIT(*str)) |
||
375 |
x = 10 + *str - (ISUPPER(*str) ? 'A' : 'a'); |
||
376 |
else |
||
377 |
return false; |
||
378 |
i = (i << 4) + x; |
||
379 |
} |
||
380 |
*value = (double) i; |
||
381 |
return true; |
||
382 |
} |
||
383 |
else { |
||
384 |
char *eptr; |
||
385 |
9168 |
*value = strtod(str, &eptr); |
|
386 |
9168 |
return *eptr == '\0'; |
|
387 |
} |
||
388 |
} |
||
389 |
|||
390 |
|||
391 |
static Token |
||
392 |
CondHandleNumber(bool doEval) |
||
393 |
679 |
{ |
|
394 |
const char *end; |
||
395 |
char *lhs; |
||
396 |
|||
397 |
679 |
end = condExpr; |
|
398 |
✓✗✓✓ |
3395 |
while (!ISSPACE(*end) && strchr("!=><", *end) == NULL) |
399 |
679 |
end++; |
|
400 |
679 |
lhs = Str_dupi(condExpr, end); |
|
401 |
679 |
condExpr = end; |
|
402 |
679 |
return CondHandleComparison(lhs, true, doEval); |
|
403 |
} |
||
404 |
|||
405 |
static Token |
||
406 |
CondHandleVarSpec(bool doEval) |
||
407 |
13767 |
{ |
|
408 |
char *lhs; |
||
409 |
size_t varSpecLen; |
||
410 |
bool doFree; |
||
411 |
|||
412 |
/* Parse the variable spec and skip over it, saving its |
||
413 |
* value in lhs. */ |
||
414 |
13767 |
lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree); |
|
415 |
✗✓ | 13767 |
if (lhs == var_Error) |
416 |
/* Even if !doEval, we still report syntax errors, which |
||
417 |
* is what getting var_Error back with !doEval means. */ |
||
418 |
return Err; |
||
419 |
13767 |
condExpr += varSpecLen; |
|
420 |
|||
421 |
✓✓✗✓ |
27534 |
if (!ISSPACE(*condExpr) && |
422 |
strchr("!=><", *condExpr) == NULL) { |
||
423 |
BUFFER buf; |
||
424 |
|||
425 |
Buf_Init(&buf, 0); |
||
426 |
|||
427 |
Buf_AddString(&buf, lhs); |
||
428 |
|||
429 |
if (doFree) |
||
430 |
free(lhs); |
||
431 |
|||
432 |
for (;*condExpr && !ISSPACE(*condExpr); condExpr++) |
||
433 |
Buf_AddChar(&buf, *condExpr); |
||
434 |
|||
435 |
lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); |
||
436 |
Buf_Destroy(&buf); |
||
437 |
doFree = true; |
||
438 |
} |
||
439 |
|||
440 |
13767 |
return CondHandleComparison(lhs, doFree, doEval); |
|
441 |
} |
||
442 |
|||
443 |
static Token |
||
444 |
CondHandleString(bool doEval) |
||
445 |
{ |
||
446 |
char *lhs; |
||
447 |
const char *begin; |
||
448 |
BUFFER buf; |
||
449 |
|||
450 |
/* find the extent of the string */ |
||
451 |
begin = ++condExpr; |
||
452 |
while (*condExpr && *condExpr != '"') { |
||
453 |
condExpr++; |
||
454 |
} |
||
455 |
|||
456 |
Buf_Init(&buf, 0); |
||
457 |
Buf_Addi(&buf, begin, condExpr); |
||
458 |
if (*condExpr == '"') |
||
459 |
condExpr++; |
||
460 |
lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); |
||
461 |
Buf_Destroy(&buf); |
||
462 |
return CondHandleComparison(lhs, true, doEval); |
||
463 |
} |
||
464 |
|||
465 |
static Token |
||
466 |
CondHandleComparison(char *lhs, bool doFree, bool doEval) |
||
467 |
14446 |
{ |
|
468 |
Token t; |
||
469 |
const char *rhs; |
||
470 |
const char *op; |
||
471 |
|||
472 |
14446 |
t = Err; |
|
473 |
/* Skip whitespace to get to the operator. */ |
||
474 |
✓✓ | 63454 |
while (ISSPACE(*condExpr)) |
475 |
10058 |
condExpr++; |
|
476 |
|||
477 |
/* Make sure the operator is a valid one. If it isn't a |
||
478 |
* known relational operator, pretend we got a |
||
479 |
* != 0 comparison. */ |
||
480 |
14446 |
op = condExpr; |
|
481 |
✓✓ | 14446 |
switch (*condExpr) { |
482 |
case '!': |
||
483 |
case '=': |
||
484 |
case '<': |
||
485 |
case '>': |
||
486 |
✓✗ | 10056 |
if (condExpr[1] == '=') |
487 |
10056 |
condExpr += 2; |
|
488 |
else |
||
489 |
condExpr += 1; |
||
490 |
break; |
||
491 |
default: |
||
492 |
4390 |
op = "!="; |
|
493 |
4390 |
rhs = "0"; |
|
494 |
|||
495 |
4390 |
goto do_compare; |
|
496 |
} |
||
497 |
✓✓ | 40224 |
while (ISSPACE(*condExpr)) |
498 |
10056 |
condExpr++; |
|
499 |
✗✓ | 10056 |
if (*condExpr == '\0') { |
500 |
Parse_Error(PARSE_WARNING, |
||
501 |
"Missing right-hand-side of operator"); |
||
502 |
goto error; |
||
503 |
} |
||
504 |
10056 |
rhs = condExpr; |
|
505 |
14446 |
do_compare: |
|
506 |
✓✓ | 14446 |
if (*rhs == '"') { |
507 |
/* Doing a string comparison. Only allow == and != for |
||
508 |
* operators. */ |
||
509 |
char *string; |
||
510 |
const char *cp; |
||
511 |
int qt; |
||
512 |
BUFFER buf; |
||
513 |
|||
514 |
10442 |
do_string_compare: |
|
515 |
✓✗✗✓ |
10442 |
if ((*op != '!' && *op != '=') || op[1] != '=') { |
516 |
Parse_Error(PARSE_WARNING, |
||
517 |
"String comparison operator should be either == or !="); |
||
518 |
goto error; |
||
519 |
} |
||
520 |
|||
521 |
10442 |
Buf_Init(&buf, 0); |
|
522 |
10442 |
qt = *rhs == '"' ? 1 : 0; |
|
523 |
|||
524 |
✓✓✓✓ ✓✓✓✓ ✓✗ |
70850 |
for (cp = &rhs[qt]; ((qt && *cp != '"') || |
525 |
(!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) { |
||
526 |
✓✓ | 49966 |
if (*cp == '$') { |
527 |
size_t len; |
||
528 |
|||
529 |
✓✗ | 2 |
if (Var_ParseBuffer(&buf, cp, NULL, doEval, |
530 |
&len)) { |
||
531 |
2 |
cp += len; |
|
532 |
2 |
continue; |
|
533 |
} |
||
534 |
✗✓✗✗ |
49964 |
} else if (*cp == '\\' && cp[1] != '\0') |
535 |
/* Backslash escapes things -- skip over next |
||
536 |
* character, if it exists. */ |
||
537 |
cp++; |
||
538 |
✗✓ | 49964 |
Buf_AddChar(&buf, *cp++); |
539 |
} |
||
540 |
|||
541 |
10442 |
string = Buf_Retrieve(&buf); |
|
542 |
|||
543 |
✗✓ | 10442 |
if (DEBUG(COND)) |
544 |
printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", |
||
545 |
lhs, string, op); |
||
546 |
/* Null-terminate rhs and perform the comparison. |
||
547 |
* t is set to the result. */ |
||
548 |
✓✓ | 10442 |
if (*op == '=') |
549 |
9279 |
t = strcmp(lhs, string) ? False : True; |
|
550 |
else |
||
551 |
1163 |
t = strcmp(lhs, string) ? True : False; |
|
552 |
10442 |
free(string); |
|
553 |
✓✓ | 10442 |
if (rhs == condExpr) { |
554 |
✓✓✗✓ |
10056 |
if (!qt && *cp == ')') |
555 |
condExpr = cp; |
||
556 |
✓✓ | 10056 |
else if (*cp == '\0') |
557 |
774 |
condExpr = cp; |
|
558 |
else |
||
559 |
9282 |
condExpr = cp + 1; |
|
560 |
} |
||
561 |
} else { |
||
562 |
/* rhs is either a float or an integer. Convert both the |
||
563 |
* lhs and the rhs to a double and compare the two. */ |
||
564 |
double left, right; |
||
565 |
char *string; |
||
566 |
|||
567 |
✓✓ | 5164 |
if (!CondCvtArg(lhs, &left)) |
568 |
1160 |
goto do_string_compare; |
|
569 |
✗✓ | 4004 |
if (*rhs == '$') { |
570 |
size_t len; |
||
571 |
bool freeIt; |
||
572 |
|||
573 |
string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); |
||
574 |
if (string == var_Error) |
||
575 |
right = 0.0; |
||
576 |
else { |
||
577 |
if (!CondCvtArg(string, &right)) { |
||
578 |
if (freeIt) |
||
579 |
free(string); |
||
580 |
goto do_string_compare; |
||
581 |
} |
||
582 |
if (freeIt) |
||
583 |
free(string); |
||
584 |
if (rhs == condExpr) |
||
585 |
condExpr += len; |
||
586 |
} |
||
587 |
} else { |
||
588 |
✗✓ | 4004 |
if (!CondCvtArg(rhs, &right)) |
589 |
goto do_string_compare; |
||
590 |
✗✓ | 4004 |
if (rhs == condExpr) { |
591 |
/* Skip over the right-hand side. */ |
||
592 |
while (!ISSPACE(*condExpr) && *condExpr != '\0') |
||
593 |
condExpr++; |
||
594 |
} |
||
595 |
} |
||
596 |
|||
597 |
✗✓ | 4004 |
if (DEBUG(COND)) |
598 |
printf("left = %f, right = %f, op = %.2s\n", left, |
||
599 |
right, op); |
||
600 |
✓✗✗✗ ✗ |
4004 |
switch (op[0]) { |
601 |
case '!': |
||
602 |
✗✓ | 4004 |
if (op[1] != '=') { |
603 |
Parse_Error(PARSE_WARNING, "Unknown operator"); |
||
604 |
goto error; |
||
605 |
} |
||
606 |
4004 |
t = left != right ? True : False; |
|
607 |
4004 |
break; |
|
608 |
case '=': |
||
609 |
if (op[1] != '=') { |
||
610 |
Parse_Error(PARSE_WARNING, "Unknown operator"); |
||
611 |
goto error; |
||
612 |
} |
||
613 |
t = left == right ? True : False; |
||
614 |
break; |
||
615 |
case '<': |
||
616 |
if (op[1] == '=') |
||
617 |
t = left <= right ? True : False; |
||
618 |
else |
||
619 |
t = left < right ? True : False; |
||
620 |
break; |
||
621 |
case '>': |
||
622 |
if (op[1] == '=') |
||
623 |
t = left >= right ? True : False; |
||
624 |
else |
||
625 |
t = left > right ? True : False; |
||
626 |
break; |
||
627 |
} |
||
628 |
} |
||
629 |
14446 |
error: |
|
630 |
✓✓ | 14446 |
if (doFree) |
631 |
5053 |
free(lhs); |
|
632 |
14446 |
return t; |
|
633 |
} |
||
634 |
|||
635 |
#define S(s) s, sizeof(s)-1 |
||
636 |
static struct operator { |
||
637 |
const char *s; |
||
638 |
size_t len; |
||
639 |
bool (*proc)(struct Name *); |
||
640 |
} ops[] = { |
||
641 |
{S("defined"), CondDoDefined}, |
||
642 |
{S("make"), CondDoMake}, |
||
643 |
{S("exists"), CondDoExists}, |
||
644 |
{S("target"), CondDoTarget}, |
||
645 |
{S("commands"), CondDoTargetWithCommands}, |
||
646 |
{NULL, 0, NULL} |
||
647 |
}; |
||
648 |
|||
649 |
static Token |
||
650 |
CondHandleDefault(bool doEval) |
||
651 |
45137 |
{ |
|
652 |
bool t; |
||
653 |
bool (*evalProc)(struct Name *); |
||
654 |
45137 |
bool invert = false; |
|
655 |
struct Name arg; |
||
656 |
size_t arglen; |
||
657 |
|||
658 |
45137 |
evalProc = NULL; |
|
659 |
✓✓ | 45137 |
if (strncmp(condExpr, "empty", 5) == 0) { |
660 |
/* Use Var_Parse to parse the spec in parens and return |
||
661 |
* True if the resulting string is empty. */ |
||
662 |
size_t length; |
||
663 |
bool doFree; |
||
664 |
char *val; |
||
665 |
|||
666 |
6716 |
condExpr += 5; |
|
667 |
|||
668 |
✗✓ | 13432 |
for (arglen = 0; condExpr[arglen] != '(' && |
669 |
condExpr[arglen] != '\0';) |
||
670 |
arglen++; |
||
671 |
|||
672 |
✓✗ | 6716 |
if (condExpr[arglen] != '\0') { |
673 |
6716 |
val = Var_Parse(&condExpr[arglen - 1], NULL, |
|
674 |
doEval, &length, &doFree); |
||
675 |
✗✓ | 6716 |
if (val == var_Error) |
676 |
t = Err; |
||
677 |
else { |
||
678 |
/* A variable is empty when it just contains |
||
679 |
* spaces... 4/15/92, christos */ |
||
680 |
char *p; |
||
681 |
✓✓ | 13434 |
for (p = val; ISSPACE(*p); p++) |
682 |
continue; |
||
683 |
6716 |
t = *p == '\0' ? True : False; |
|
684 |
} |
||
685 |
✓✓ | 6716 |
if (doFree) |
686 |
5186 |
free(val); |
|
687 |
/* Advance condExpr to beyond the closing ). Note that |
||
688 |
* we subtract one from arglen + length b/c length |
||
689 |
* is calculated from condExpr[arglen - 1]. */ |
||
690 |
6716 |
condExpr += arglen + length - 1; |
|
691 |
6716 |
return t; |
|
692 |
} else |
||
693 |
condExpr -= 5; |
||
694 |
} else { |
||
695 |
struct operator *op; |
||
696 |
|||
697 |
✓✗ | 84461 |
for (op = ops; op != NULL; op++) |
698 |
✓✓ | 84461 |
if (strncmp(condExpr, op->s, op->len) == 0) { |
699 |
38421 |
condExpr += op->len; |
|
700 |
✓✓ | 38421 |
if (CondGetArg(&condExpr, &arg, op->s, true)) |
701 |
37688 |
evalProc = op->proc; |
|
702 |
else |
||
703 |
733 |
condExpr -= op->len; |
|
704 |
break; |
||
705 |
} |
||
706 |
} |
||
707 |
✓✓ | 38421 |
if (evalProc == NULL) { |
708 |
/* The symbol is itself the argument to the default |
||
709 |
* function. We advance condExpr to the end of the symbol |
||
710 |
* by hand (the next whitespace, closing paren or |
||
711 |
* binary operator) and set to invert the evaluation |
||
712 |
* function if condInvert is true. */ |
||
713 |
733 |
invert = condInvert; |
|
714 |
733 |
evalProc = condDefProc; |
|
715 |
/* XXX should we ignore problems now ? */ |
||
716 |
733 |
CondGetArg(&condExpr, &arg, "", false); |
|
717 |
} |
||
718 |
|||
719 |
/* Evaluate the argument using the set function. If invert |
||
720 |
* is true, we invert the sense of the function. */ |
||
721 |
✓✓✓✓ ✓✓ |
38421 |
t = (!doEval || (*evalProc)(&arg) ? |
722 |
(invert ? False : True) : |
||
723 |
(invert ? True : False)); |
||
724 |
38421 |
VarName_Free(&arg); |
|
725 |
38421 |
return t; |
|
726 |
} |
||
727 |
|||
728 |
/*- |
||
729 |
*----------------------------------------------------------------------- |
||
730 |
* CondToken -- |
||
731 |
* Return the next token from the input. |
||
732 |
* |
||
733 |
* Results: |
||
734 |
* A Token for the next lexical token in the stream. |
||
735 |
* |
||
736 |
* Side Effects: |
||
737 |
* condPushback will be set back to None if it is used. |
||
738 |
*----------------------------------------------------------------------- |
||
739 |
*/ |
||
740 |
static Token |
||
741 |
CondToken(bool doEval) |
||
742 |
256192 |
{ |
|
743 |
|||
744 |
✓✓ | 256192 |
if (condPushBack != None) { |
745 |
Token t; |
||
746 |
|||
747 |
106260 |
t = condPushBack; |
|
748 |
106260 |
condPushBack = None; |
|
749 |
106260 |
return t; |
|
750 |
} |
||
751 |
|||
752 |
✓✓ | 352710 |
while (ISSPACE(*condExpr)) |
753 |
26423 |
condExpr++; |
|
754 |
✓✓✓✓ ✓✓✗✓ ✓✓ |
149932 |
switch (*condExpr) { |
755 |
case '(': |
||
756 |
2036 |
condExpr++; |
|
757 |
2036 |
return LParen; |
|
758 |
case ')': |
||
759 |
2036 |
condExpr++; |
|
760 |
2036 |
return RParen; |
|
761 |
case '|': |
||
762 |
✓✗ | 7288 |
if (condExpr[1] == '|') |
763 |
7288 |
condExpr++; |
|
764 |
7288 |
condExpr++; |
|
765 |
7288 |
return Or; |
|
766 |
case '&': |
||
767 |
✓✗ | 4845 |
if (condExpr[1] == '&') |
768 |
4845 |
condExpr++; |
|
769 |
4845 |
condExpr++; |
|
770 |
4845 |
return And; |
|
771 |
case '!': |
||
772 |
26694 |
condExpr++; |
|
773 |
26694 |
return Not; |
|
774 |
case '\n': |
||
775 |
case '\0': |
||
776 |
47450 |
return EndOfFile; |
|
777 |
case '"': |
||
778 |
return CondHandleString(doEval); |
||
779 |
case '$': |
||
780 |
13767 |
return CondHandleVarSpec(doEval); |
|
781 |
case '0': case '1': case '2': case '3': case '4': |
||
782 |
case '5': case '6': case '7': case '8': case '9': |
||
783 |
679 |
return CondHandleNumber(doEval); |
|
784 |
default: |
||
785 |
45137 |
return CondHandleDefault(doEval); |
|
786 |
} |
||
787 |
} |
||
788 |
|||
789 |
/*- |
||
790 |
*----------------------------------------------------------------------- |
||
791 |
* CondT -- |
||
792 |
* Parse a single term in the expression. This consists of a terminal |
||
793 |
* symbol or Not and a terminal symbol (not including the binary |
||
794 |
* operators): |
||
795 |
* T -> defined(variable) | make(target) | exists(file) | symbol |
||
796 |
* T -> ! T | ( E ) |
||
797 |
* |
||
798 |
* Results: |
||
799 |
* True, False or Err. |
||
800 |
* |
||
801 |
* Side Effects: |
||
802 |
* Tokens are consumed. |
||
803 |
*----------------------------------------------------------------------- |
||
804 |
*/ |
||
805 |
static Token |
||
806 |
CondT(bool doEval) |
||
807 |
88313 |
{ |
|
808 |
Token t; |
||
809 |
|||
810 |
88313 |
t = CondToken(doEval); |
|
811 |
|||
812 |
✗✓ | 88313 |
if (t == EndOfFile) |
813 |
/* If we reached the end of the expression, the expression |
||
814 |
* is malformed... */ |
||
815 |
t = Err; |
||
816 |
✓✓ | 88313 |
else if (t == LParen) { |
817 |
/* T -> ( E ). */ |
||
818 |
2036 |
t = CondE(doEval); |
|
819 |
✓✗ | 2036 |
if (t != Err) |
820 |
✗✓ | 2036 |
if (CondToken(doEval) != RParen) |
821 |
t = Err; |
||
822 |
✓✓ | 86277 |
} else if (t == Not) { |
823 |
26694 |
t = CondT(doEval); |
|
824 |
✓✓ | 26694 |
if (t == True) |
825 |
12102 |
t = False; |
|
826 |
✓✗ | 14592 |
else if (t == False) |
827 |
14592 |
t = True; |
|
828 |
} |
||
829 |
88313 |
return t; |
|
830 |
} |
||
831 |
|||
832 |
/*- |
||
833 |
*----------------------------------------------------------------------- |
||
834 |
* CondF -- |
||
835 |
* Parse a conjunctive factor (nice name, wot?) |
||
836 |
* F -> T && F | T |
||
837 |
* |
||
838 |
* Results: |
||
839 |
* True, False or Err |
||
840 |
* |
||
841 |
* Side Effects: |
||
842 |
* Tokens are consumed. |
||
843 |
*----------------------------------------------------------------------- |
||
844 |
*/ |
||
845 |
static Token |
||
846 |
CondF(bool doEval) |
||
847 |
61619 |
{ |
|
848 |
Token l, o; |
||
849 |
|||
850 |
61619 |
l = CondT(doEval); |
|
851 |
✓✗ | 61619 |
if (l != Err) { |
852 |
61619 |
o = CondToken(doEval); |
|
853 |
|||
854 |
✓✓ | 61619 |
if (o == And) { |
855 |
/* F -> T && F |
||
856 |
* |
||
857 |
* If T is False, the whole thing will be False, but we |
||
858 |
* have to parse the r.h.s. anyway (to throw it away). If |
||
859 |
* T is True, the result is the r.h.s., be it an Err or no. |
||
860 |
* */ |
||
861 |
✓✓ | 4845 |
if (l == True) |
862 |
1895 |
l = CondF(doEval); |
|
863 |
else |
||
864 |
2950 |
(void)CondF(false); |
|
865 |
} else |
||
866 |
/* F -> T. */ |
||
867 |
56774 |
condPushBack = o; |
|
868 |
} |
||
869 |
61619 |
return l; |
|
870 |
} |
||
871 |
|||
872 |
/*- |
||
873 |
*----------------------------------------------------------------------- |
||
874 |
* CondE -- |
||
875 |
* Main expression production. |
||
876 |
* E -> F || E | F |
||
877 |
* |
||
878 |
* Results: |
||
879 |
* True, False or Err. |
||
880 |
* |
||
881 |
* Side Effects: |
||
882 |
* Tokens are, of course, consumed. |
||
883 |
*----------------------------------------------------------------------- |
||
884 |
*/ |
||
885 |
static Token |
||
886 |
CondE(bool doEval) |
||
887 |
56774 |
{ |
|
888 |
Token l, o; |
||
889 |
|||
890 |
56774 |
l = CondF(doEval); |
|
891 |
✓✗ | 56774 |
if (l != Err) { |
892 |
56774 |
o = CondToken(doEval); |
|
893 |
|||
894 |
✓✓ | 56774 |
if (o == Or) { |
895 |
/* E -> F || E |
||
896 |
* |
||
897 |
* A similar thing occurs for ||, except that here we |
||
898 |
* make sure the l.h.s. is False before we bother to |
||
899 |
* evaluate the r.h.s. Once again, if l is False, the |
||
900 |
* result is the r.h.s. and once again if l is True, we |
||
901 |
* parse the r.h.s. to throw it away. */ |
||
902 |
✓✓ | 7288 |
if (l == False) |
903 |
6663 |
l = CondE(doEval); |
|
904 |
else |
||
905 |
625 |
(void)CondE(false); |
|
906 |
} else |
||
907 |
/* E -> F. */ |
||
908 |
49486 |
condPushBack = o; |
|
909 |
} |
||
910 |
56774 |
return l; |
|
911 |
} |
||
912 |
|||
913 |
/* Evaluate conditional in line. |
||
914 |
* returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE, |
||
915 |
* COND_ISUNDEF. |
||
916 |
* A conditional line looks like this: |
||
917 |
* <cond-type> <expr> |
||
918 |
* where <cond-type> is any of if, ifmake, ifnmake, ifdef, |
||
919 |
* ifndef, elif, elifmake, elifnmake, elifdef, elifndef |
||
920 |
* and <expr> consists of &&, ||, !, make(target), defined(variable) |
||
921 |
* and parenthetical groupings thereof. |
||
922 |
*/ |
||
923 |
int |
||
924 |
Cond_Eval(const char *line) |
||
925 |
181733 |
{ |
|
926 |
/* find end of keyword */ |
||
927 |
const char *end; |
||
928 |
uint32_t k; |
||
929 |
size_t len; |
||
930 |
struct If *ifp; |
||
931 |
181733 |
bool value = false; |
|
932 |
int level; /* Level at which to report errors. */ |
||
933 |
|||
934 |
181733 |
level = PARSE_FATAL; |
|
935 |
|||
936 |
✓✓ | 181733 |
for (end = line; ISLOWER(*end); end++) |
937 |
; |
||
938 |
/* quick path: recognize special targets early on */ |
||
939 |
✓✓ | 181733 |
if (*end == '.' || *end == ':') |
940 |
28410 |
return COND_INVALID; |
|
941 |
153323 |
len = end - line; |
|
942 |
153323 |
k = ohash_interval(line, &end); |
|
943 |
✓✓✓✗ ✗✓✗✗ ✗✗✓✓ ✓✗✗✓ ✓ |
153323 |
switch(k % MAGICSLOTS2) { |
944 |
case K_COND_IF % MAGICSLOTS2: |
||
945 |
✓✗✓✗ |
107804 |
if (k == K_COND_IF && len == strlen(COND_IF) && |
946 |
strncmp(line, COND_IF, len) == 0) { |
||
947 |
53902 |
ifp = ifs + COND_IF_INDEX; |
|
948 |
} else |
||
949 |
return COND_INVALID; |
||
950 |
break; |
||
951 |
case K_COND_IFDEF % MAGICSLOTS2: |
||
952 |
✓✗✓✗ |
12 |
if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) && |
953 |
strncmp(line, COND_IFDEF, len) == 0) { |
||
954 |
6 |
ifp = ifs + COND_IFDEF_INDEX; |
|
955 |
} else |
||
956 |
return COND_INVALID; |
||
957 |
break; |
||
958 |
case K_COND_IFNDEF % MAGICSLOTS2: |
||
959 |
✓✗✓✗ |
1454 |
if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) && |
960 |
strncmp(line, COND_IFNDEF, len) == 0) { |
||
961 |
727 |
ifp = ifs + COND_IFNDEF_INDEX; |
|
962 |
} else |
||
963 |
return COND_INVALID; |
||
964 |
break; |
||
965 |
case K_COND_IFMAKE % MAGICSLOTS2: |
||
966 |
if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) && |
||
967 |
strncmp(line, COND_IFMAKE, len) == 0) { |
||
968 |
ifp = ifs + COND_IFMAKE_INDEX; |
||
969 |
} else |
||
970 |
return COND_INVALID; |
||
971 |
break; |
||
972 |
case K_COND_IFNMAKE % MAGICSLOTS2: |
||
973 |
if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) && |
||
974 |
strncmp(line, COND_IFNMAKE, len) == 0) { |
||
975 |
ifp = ifs + COND_IFNMAKE_INDEX; |
||
976 |
} else |
||
977 |
return COND_INVALID; |
||
978 |
break; |
||
979 |
case K_COND_ELIF % MAGICSLOTS2: |
||
980 |
✓✗✓✗ |
1368 |
if (k == K_COND_ELIF && len == strlen(COND_ELIF) && |
981 |
strncmp(line, COND_ELIF, len) == 0) { |
||
982 |
684 |
ifp = ifs + COND_ELIF_INDEX; |
|
983 |
} else |
||
984 |
return COND_INVALID; |
||
985 |
break; |
||
986 |
case K_COND_ELIFDEF % MAGICSLOTS2: |
||
987 |
if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) && |
||
988 |
strncmp(line, COND_ELIFDEF, len) == 0) { |
||
989 |
ifp = ifs + COND_ELIFDEF_INDEX; |
||
990 |
} else |
||
991 |
return COND_INVALID; |
||
992 |
break; |
||
993 |
case K_COND_ELIFNDEF % MAGICSLOTS2: |
||
994 |
if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) && |
||
995 |
strncmp(line, COND_ELIFNDEF, len) == 0) { |
||
996 |
ifp = ifs + COND_ELIFNDEF_INDEX; |
||
997 |
} else |
||
998 |
return COND_INVALID; |
||
999 |
break; |
||
1000 |
case K_COND_ELIFMAKE % MAGICSLOTS2: |
||
1001 |
if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) && |
||
1002 |
strncmp(line, COND_ELIFMAKE, len) == 0) { |
||
1003 |
ifp = ifs + COND_ELIFMAKE_INDEX; |
||
1004 |
} else |
||
1005 |
return COND_INVALID; |
||
1006 |
break; |
||
1007 |
case K_COND_ELIFNMAKE % MAGICSLOTS2: |
||
1008 |
if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) && |
||
1009 |
strncmp(line, COND_ELIFNMAKE, len) == 0) { |
||
1010 |
ifp = ifs + COND_ELIFNMAKE_INDEX; |
||
1011 |
} else |
||
1012 |
return COND_INVALID; |
||
1013 |
break; |
||
1014 |
case K_COND_ELSE % MAGICSLOTS2: |
||
1015 |
/* valid conditional whose value is the inverse |
||
1016 |
* of the previous if we parsed. */ |
||
1017 |
✓✗✓✗ |
13827 |
if (k == K_COND_ELSE && len == strlen(COND_ELSE) && |
1018 |
strncmp(line, COND_ELSE, len) == 0) { |
||
1019 |
✗✓ | 13827 |
if (condTop == MAXIF) { |
1020 |
Parse_Error(level, "if-less else"); |
||
1021 |
return COND_INVALID; |
||
1022 |
✓✓ | 13827 |
} else if (skipIfLevel == 0) { |
1023 |
13626 |
value = !condStack[condTop].value; |
|
1024 |
13626 |
ifp = ifs + COND_ELSE_INDEX; |
|
1025 |
} else |
||
1026 |
201 |
return COND_SKIP; |
|
1027 |
} else |
||
1028 |
return COND_INVALID; |
||
1029 |
break; |
||
1030 |
case K_COND_ENDIF % MAGICSLOTS2: |
||
1031 |
✓✗✓✗ |
54635 |
if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) && |
1032 |
strncmp(line, COND_ENDIF, len) == 0) { |
||
1033 |
/* End of a conditional section. If skipIfLevel is |
||
1034 |
* non-zero, that conditional was skipped, so lines |
||
1035 |
* following it should also be skipped. Hence, we |
||
1036 |
* return COND_SKIP. Otherwise, the conditional was |
||
1037 |
* read so succeeding lines should be parsed (think |
||
1038 |
* about it...) so we return COND_PARSE, unless this |
||
1039 |
* endif isn't paired with a decent if. */ |
||
1040 |
✓✓ | 54635 |
if (skipIfLevel != 0) { |
1041 |
7864 |
skipIfLevel--; |
|
1042 |
7864 |
return COND_SKIP; |
|
1043 |
} else { |
||
1044 |
✗✓ | 46771 |
if (condTop == MAXIF) { |
1045 |
Parse_Error(level, "if-less endif"); |
||
1046 |
return COND_INVALID; |
||
1047 |
} else { |
||
1048 |
46771 |
skipLine = false; |
|
1049 |
46771 |
condTop++; |
|
1050 |
46771 |
return COND_PARSE; |
|
1051 |
} |
||
1052 |
} |
||
1053 |
} else |
||
1054 |
return COND_INVALID; |
||
1055 |
break; |
||
1056 |
|||
1057 |
/* Recognize other keywords there, to simplify parser's task */ |
||
1058 |
case K_COND_FOR % MAGICSLOTS2: |
||
1059 |
✓✗✓✗ |
5117 |
if (k == K_COND_FOR && len == strlen(COND_FOR) && |
1060 |
strncmp(line, COND_FOR, len) == 0) |
||
1061 |
5117 |
return COND_ISFOR; |
|
1062 |
else |
||
1063 |
return COND_INVALID; |
||
1064 |
case K_COND_UNDEF % MAGICSLOTS2: |
||
1065 |
if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) && |
||
1066 |
strncmp(line, COND_UNDEF, len) == 0) |
||
1067 |
return COND_ISUNDEF; |
||
1068 |
else |
||
1069 |
return COND_INVALID; |
||
1070 |
case K_COND_POISON % MAGICSLOTS2: |
||
1071 |
if (k == K_COND_POISON && len == strlen(COND_POISON) && |
||
1072 |
strncmp(line, COND_POISON, len) == 0) |
||
1073 |
return COND_ISPOISON; |
||
1074 |
else |
||
1075 |
return COND_INVALID; |
||
1076 |
case K_COND_INCLUDE % MAGICSLOTS2: |
||
1077 |
✓✗✓✗ |
8417 |
if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) && |
1078 |
strncmp(line, COND_INCLUDE, len) == 0) |
||
1079 |
8417 |
return COND_ISINCLUDE; |
|
1080 |
else |
||
1081 |
return COND_INVALID; |
||
1082 |
default: |
||
1083 |
/* Not a valid conditional type. No error... */ |
||
1084 |
16008 |
return COND_INVALID; |
|
1085 |
} |
||
1086 |
|||
1087 |
✓✓ | 68945 |
if (ifp->isElse) { |
1088 |
✗✓ | 14310 |
if (condTop == MAXIF) { |
1089 |
Parse_Error(level, "if-less elif"); |
||
1090 |
return COND_INVALID; |
||
1091 |
✓✗✓✓ |
14310 |
} else if (skipIfLevel != 0 || condStack[condTop].value) { |
1092 |
/* |
||
1093 |
* Skip if we're meant to or is an else-type |
||
1094 |
* conditional and previous corresponding one was |
||
1095 |
* evaluated to true. |
||
1096 |
*/ |
||
1097 |
5623 |
skipLine = true; |
|
1098 |
5623 |
return COND_SKIP; |
|
1099 |
} |
||
1100 |
✓✓ | 54635 |
} else if (skipLine) { |
1101 |
/* Don't even try to evaluate a conditional that's not an else |
||
1102 |
* if we're skipping things... */ |
||
1103 |
7864 |
skipIfLevel++; |
|
1104 |
7864 |
return COND_SKIP; |
|
1105 |
} else |
||
1106 |
46771 |
condTop--; |
|
1107 |
|||
1108 |
✗✓ | 55458 |
if (condTop < 0) { |
1109 |
/* This is the one case where we can definitely proclaim a fatal |
||
1110 |
* error. If we don't, we're hosed. */ |
||
1111 |
Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", |
||
1112 |
MAXIF); |
||
1113 |
condTop = 0; |
||
1114 |
return COND_INVALID; |
||
1115 |
} |
||
1116 |
|||
1117 |
✓✓ | 55458 |
if (ifp->defProc) { |
1118 |
/* Initialize file-global variables for parsing. */ |
||
1119 |
47450 |
condDefProc = ifp->defProc; |
|
1120 |
47450 |
condInvert = ifp->doNot; |
|
1121 |
|||
1122 |
47450 |
line += len; |
|
1123 |
|||
1124 |
✓✓ | 142350 |
while (*line == ' ' || *line == '\t') |
1125 |
47450 |
line++; |
|
1126 |
|||
1127 |
47450 |
condExpr = line; |
|
1128 |
47450 |
condPushBack = None; |
|
1129 |
|||
1130 |
✓✓✗✗ |
47450 |
switch (CondE(true)) { |
1131 |
case True: |
||
1132 |
✓✗ | 19420 |
if (CondToken(true) == EndOfFile) { |
1133 |
19420 |
value = true; |
|
1134 |
19420 |
break; |
|
1135 |
} |
||
1136 |
goto err; |
||
1137 |
/* FALLTHROUGH */ |
||
1138 |
case False: |
||
1139 |
✓✗ | 28030 |
if (CondToken(true) == EndOfFile) { |
1140 |
28030 |
value = false; |
|
1141 |
28030 |
break; |
|
1142 |
} |
||
1143 |
/* FALLTHROUGH */ |
||
1144 |
case Err: |
||
1145 |
err: |
||
1146 |
Parse_Error(level, "Malformed conditional (%s)", line); |
||
1147 |
return COND_INVALID; |
||
1148 |
default: |
||
1149 |
break; |
||
1150 |
} |
||
1151 |
} |
||
1152 |
|||
1153 |
55458 |
condStack[condTop].value = value; |
|
1154 |
55458 |
Parse_FillLocation(&condStack[condTop].origin); |
|
1155 |
55458 |
skipLine = !value; |
|
1156 |
✓✓ | 55458 |
return value ? COND_PARSE : COND_SKIP; |
1157 |
} |
||
1158 |
|||
1159 |
void |
||
1160 |
Cond_End(void) |
||
1161 |
1458 |
{ |
|
1162 |
int i; |
||
1163 |
|||
1164 |
✗✓ | 1458 |
if (condTop != MAXIF) { |
1165 |
Parse_Error(PARSE_FATAL, "%s%d open conditional%s", |
||
1166 |
condTop == 0 ? "at least ": "", MAXIF-condTop, |
||
1167 |
MAXIF-condTop == 1 ? "" : "s"); |
||
1168 |
for (i = MAXIF-1; i >= condTop; i--) { |
||
1169 |
fprintf(stderr, "\t(%s:%lu)\n", |
||
1170 |
condStack[i].origin.fname, |
||
1171 |
condStack[i].origin.lineno); |
||
1172 |
} |
||
1173 |
} |
||
1174 |
1458 |
condTop = MAXIF; |
|
1175 |
1458 |
} |
Generated by: GCOVR (Version 3.3) |