GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: var.c,v 1.101 2016/10/23 14:54:14 espie Exp $ */ |
||
2 |
/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ |
||
3 |
|||
4 |
/* |
||
5 |
* Copyright (c) 1999,2000,2007 Marc Espie. |
||
6 |
* |
||
7 |
* Extensive code modifications for the OpenBSD project. |
||
8 |
* |
||
9 |
* Redistribution and use in source and binary forms, with or without |
||
10 |
* modification, are permitted provided that the following conditions |
||
11 |
* are met: |
||
12 |
* 1. Redistributions of source code must retain the above copyright |
||
13 |
* notice, this list of conditions and the following disclaimer. |
||
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
15 |
* notice, this list of conditions and the following disclaimer in the |
||
16 |
* documentation and/or other materials provided with the distribution. |
||
17 |
* |
||
18 |
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
||
19 |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
20 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
21 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
||
22 |
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
23 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
24 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
25 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
26 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
27 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
28 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
29 |
*/ |
||
30 |
/* |
||
31 |
* Copyright (c) 1988, 1989, 1990, 1993 |
||
32 |
* The Regents of the University of California. All rights reserved. |
||
33 |
* Copyright (c) 1989 by Berkeley Softworks |
||
34 |
* All rights reserved. |
||
35 |
* |
||
36 |
* This code is derived from software contributed to Berkeley by |
||
37 |
* Adam de Boor. |
||
38 |
* |
||
39 |
* Redistribution and use in source and binary forms, with or without |
||
40 |
* modification, are permitted provided that the following conditions |
||
41 |
* are met: |
||
42 |
* 1. Redistributions of source code must retain the above copyright |
||
43 |
* notice, this list of conditions and the following disclaimer. |
||
44 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
45 |
* notice, this list of conditions and the following disclaimer in the |
||
46 |
* documentation and/or other materials provided with the distribution. |
||
47 |
* 3. Neither the name of the University nor the names of its contributors |
||
48 |
* may be used to endorse or promote products derived from this software |
||
49 |
* without specific prior written permission. |
||
50 |
* |
||
51 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
52 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
53 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
54 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
55 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
56 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
57 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
58 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
59 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
60 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
61 |
* SUCH DAMAGE. |
||
62 |
*/ |
||
63 |
|||
64 |
#include <assert.h> |
||
65 |
#include <stddef.h> |
||
66 |
#include <stdint.h> |
||
67 |
#include <stdio.h> |
||
68 |
#include <stdlib.h> |
||
69 |
#include <string.h> |
||
70 |
#include <ohash.h> |
||
71 |
|||
72 |
#include "config.h" |
||
73 |
#include "defines.h" |
||
74 |
#include "buf.h" |
||
75 |
#include "cmd_exec.h" |
||
76 |
#include "stats.h" |
||
77 |
#include "pathnames.h" |
||
78 |
#include "varmodifiers.h" |
||
79 |
#include "var.h" |
||
80 |
#include "varname.h" |
||
81 |
#include "error.h" |
||
82 |
#include "str.h" |
||
83 |
#include "var_int.h" |
||
84 |
#include "memory.h" |
||
85 |
#include "symtable.h" |
||
86 |
#include "gnode.h" |
||
87 |
#include "dump.h" |
||
88 |
#include "lowparse.h" |
||
89 |
|||
90 |
/* |
||
91 |
* This is a harmless return value for Var_Parse that can be used by Var_Subst |
||
92 |
* to determine if there was an error in parsing -- easier than returning |
||
93 |
* a flag, as things outside this module don't give a hoot. |
||
94 |
*/ |
||
95 |
char var_Error[] = ""; |
||
96 |
|||
97 |
GNode *current_node = NULL; |
||
98 |
/* |
||
99 |
* Similar to var_Error, but returned when the 'err' flag for Var_Parse is |
||
100 |
* set false. Why not just use a constant? Well, gcc likes to condense |
||
101 |
* identical string instances... |
||
102 |
*/ |
||
103 |
static char varNoError[] = ""; |
||
104 |
bool errorIsOkay; |
||
105 |
static bool checkEnvFirst; /* true if environment should be searched for |
||
106 |
* variables before the global context */ |
||
107 |
|||
108 |
void |
||
109 |
Var_setCheckEnvFirst(bool yes) |
||
110 |
{ |
||
111 |
16853 |
checkEnvFirst = yes; |
|
112 |
16853 |
} |
|
113 |
|||
114 |
/* |
||
115 |
* The rules for variable look-up are complicated. |
||
116 |
* |
||
117 |
* - Dynamic variables like $@ and $* are special. They always pertain to |
||
118 |
* a given variable. In this implementation of make, it is an error to |
||
119 |
* try to affect them manually. They are stored in a local symtable directly |
||
120 |
* inside the gnode. |
||
121 |
* |
||
122 |
* Global variables can be obtained: |
||
123 |
* - from the command line |
||
124 |
* - from the environment |
||
125 |
* - from the Makefile proper. |
||
126 |
* All of these are stored in a hash global_variables. |
||
127 |
* |
||
128 |
* Variables set on the command line override Makefile contents, are |
||
129 |
* passed to submakes (see Var_AddCmdLine), and are also exported to the |
||
130 |
* environment. |
||
131 |
* |
||
132 |
* Without -e (!checkEnvFirst), make will see variables set in the |
||
133 |
* Makefile, and default to the environment otherwise. |
||
134 |
* |
||
135 |
* With -e (checkEnvFirst), make will see the environment first, and that |
||
136 |
* will override anything that's set in the Makefile (but not set on |
||
137 |
* the command line). |
||
138 |
* |
||
139 |
* The SHELL variable is very special: it is never obtained from the |
||
140 |
* environment, and never passed to the environment. |
||
141 |
*/ |
||
142 |
|||
143 |
/* definitions pertaining to dynamic variables */ |
||
144 |
|||
145 |
/* full names of dynamic variables */ |
||
146 |
static char *varnames[] = { |
||
147 |
TARGET, |
||
148 |
PREFIX, |
||
149 |
ARCHIVE, |
||
150 |
MEMBER, |
||
151 |
IMPSRC, |
||
152 |
OODATE, |
||
153 |
ALLSRC, |
||
154 |
FTARGET, |
||
155 |
DTARGET, |
||
156 |
FPREFIX, |
||
157 |
DPREFIX, |
||
158 |
FARCHIVE, |
||
159 |
DARCHIVE, |
||
160 |
FMEMBER, |
||
161 |
DMEMBER, |
||
162 |
FIMPSRC, |
||
163 |
DIMPSRC |
||
164 |
}; |
||
165 |
|||
166 |
static bool xtlist[] = { |
||
167 |
false, /* GLOBAL_INDEX */ |
||
168 |
true, /* $@ */ |
||
169 |
false, /* $* */ |
||
170 |
false, /* $! */ |
||
171 |
true, /* $% */ |
||
172 |
true, /* $< */ |
||
173 |
false, /* $? */ |
||
174 |
false, /* $> */ |
||
175 |
true, /* ${@F} */ |
||
176 |
true, /* ${@D} */ |
||
177 |
false, /* ${*F} */ |
||
178 |
false, /* ${*D} */ |
||
179 |
false, /* ${!F} */ |
||
180 |
false, /* ${!D} */ |
||
181 |
true, /* ${%F} */ |
||
182 |
true, /* ${%D} */ |
||
183 |
true, /* ${<F} */ |
||
184 |
true, /* ${<D} */ |
||
185 |
}; |
||
186 |
|||
187 |
/* so that we can access tlist[-1] */ |
||
188 |
static bool *tlist = xtlist+1; |
||
189 |
|||
190 |
/* hashed names of dynamic variables */ |
||
191 |
#include "varhashconsts.h" |
||
192 |
|||
193 |
/* extended indices for System V stuff */ |
||
194 |
#define FTARGET_INDEX 7 |
||
195 |
#define DTARGET_INDEX 8 |
||
196 |
#define FPREFIX_INDEX 9 |
||
197 |
#define DPREFIX_INDEX 10 |
||
198 |
#define FARCHIVE_INDEX 11 |
||
199 |
#define DARCHIVE_INDEX 12 |
||
200 |
#define FMEMBER_INDEX 13 |
||
201 |
#define DMEMBER_INDEX 14 |
||
202 |
#define FIMPSRC_INDEX 15 |
||
203 |
#define DIMPSRC_INDEX 16 |
||
204 |
|||
205 |
#define GLOBAL_INDEX -1 |
||
206 |
|||
207 |
#define EXTENDED2SIMPLE(i) (((i)-LOCAL_SIZE)/2) |
||
208 |
#define IS_EXTENDED_F(i) ((i)%2 == 1) |
||
209 |
|||
210 |
|||
211 |
static struct ohash global_variables; |
||
212 |
|||
213 |
|||
214 |
typedef struct Var_ { |
||
215 |
BUFFER val; /* the variable value */ |
||
216 |
unsigned int flags; /* miscellaneous status flags */ |
||
217 |
#define VAR_IN_USE 1 /* Variable's value currently being used. */ |
||
218 |
/* (Used to avoid recursion) */ |
||
219 |
#define VAR_DUMMY 2 /* Variable is currently just a name */ |
||
220 |
/* In particular: BUFFER is invalid */ |
||
221 |
#define VAR_FROM_CMD 4 /* Special source: command line */ |
||
222 |
#define VAR_FROM_ENV 8 /* Special source: environment */ |
||
223 |
#define VAR_SEEN_ENV 16 /* No need to go look up environment again */ |
||
224 |
#define VAR_SHELL 32 /* Magic behavior */ |
||
225 |
|||
226 |
#define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED) |
||
227 |
/* Defined in var.h */ |
||
228 |
char name[1]; /* the variable's name */ |
||
229 |
} Var; |
||
230 |
|||
231 |
|||
232 |
static struct ohash_info var_info = { |
||
233 |
offsetof(Var, name), |
||
234 |
NULL, |
||
235 |
hash_calloc, hash_free, element_alloc |
||
236 |
}; |
||
237 |
|||
238 |
static int classify_var(const char *, const char **, uint32_t *); |
||
239 |
static Var *find_global_var(const char *, const char *, uint32_t); |
||
240 |
static Var *find_global_var_without_env(const char *, const char *, uint32_t); |
||
241 |
static void fill_from_env(Var *); |
||
242 |
static Var *create_var(const char *, const char *); |
||
243 |
static void var_set_initial_value(Var *, const char *); |
||
244 |
static void var_set_value(Var *, const char *); |
||
245 |
#define var_get_value(v) ((v)->flags & VAR_EXEC_LATER ? \ |
||
246 |
var_exec_cmd(v) : \ |
||
247 |
Buf_Retrieve(&((v)->val))) |
||
248 |
static char *var_exec_cmd(Var *); |
||
249 |
static void var_append_value(Var *, const char *); |
||
250 |
static void poison_check(Var *); |
||
251 |
static void var_set_append(const char *, const char *, const char *, int, bool); |
||
252 |
static void set_magic_shell_variable(void); |
||
253 |
|||
254 |
static void delete_var(Var *); |
||
255 |
static void print_var(Var *); |
||
256 |
|||
257 |
|||
258 |
static const char *find_rparen(const char *); |
||
259 |
static const char *find_ket(const char *); |
||
260 |
typedef const char * (*find_t)(const char *); |
||
261 |
static find_t find_pos(int); |
||
262 |
static void push_used(Var *); |
||
263 |
static void pop_used(Var *); |
||
264 |
static char *get_expanded_value(const char *, const char *, int, uint32_t, |
||
265 |
SymTable *, bool, bool *); |
||
266 |
static bool parse_base_variable_name(const char **, struct Name *, SymTable *); |
||
267 |
|||
268 |
|||
269 |
|||
270 |
/* Variable lookup function: return idx for dynamic variable, or |
||
271 |
* GLOBAL_INDEX if name is not dynamic. Set up *pk for further use. |
||
272 |
*/ |
||
273 |
static int |
||
274 |
classify_var(const char *name, const char **enamePtr, uint32_t *pk) |
||
275 |
{ |
||
276 |
size_t len; |
||
277 |
|||
278 |
45232613 |
*pk = ohash_interval(name, enamePtr); |
|
279 |
24350994 |
len = *enamePtr - name; |
|
280 |
/* substitute short version for long local name */ |
||
281 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓ |
24350994 |
switch (*pk % MAGICSLOTS1) { /* MAGICSLOTS should be the */ |
282 |
case K_LONGALLSRC % MAGICSLOTS1:/* smallest constant yielding */ |
||
283 |
/* distinct case values */ |
||
284 |
✓✓✓✗ |
137139 |
if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) && |
285 |
5036 |
strncmp(name, LONGALLSRC, len) == 0) |
|
286 |
5036 |
return ALLSRC_INDEX; |
|
287 |
break; |
||
288 |
case K_LONGARCHIVE % MAGICSLOTS1: |
||
289 |
✗✓✗✗ |
79077 |
if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) && |
290 |
strncmp(name, LONGARCHIVE, len) == 0) |
||
291 |
return ARCHIVE_INDEX; |
||
292 |
break; |
||
293 |
case K_LONGIMPSRC % MAGICSLOTS1: |
||
294 |
✓✓✓✗ |
179495 |
if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) && |
295 |
40723 |
strncmp(name, LONGIMPSRC, len) == 0) |
|
296 |
40723 |
return IMPSRC_INDEX; |
|
297 |
break; |
||
298 |
case K_LONGMEMBER % MAGICSLOTS1: |
||
299 |
✗✓✗✗ |
225794 |
if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) && |
300 |
strncmp(name, LONGMEMBER, len) == 0) |
||
301 |
return MEMBER_INDEX; |
||
302 |
break; |
||
303 |
case K_LONGOODATE % MAGICSLOTS1: |
||
304 |
✗✓✗✗ |
118616 |
if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) && |
305 |
strncmp(name, LONGOODATE, len) == 0) |
||
306 |
return OODATE_INDEX; |
||
307 |
break; |
||
308 |
case K_LONGPREFIX % MAGICSLOTS1: |
||
309 |
✓✓✓✗ |
511501 |
if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) && |
310 |
1290 |
strncmp(name, LONGPREFIX, len) == 0) |
|
311 |
1290 |
return PREFIX_INDEX; |
|
312 |
break; |
||
313 |
case K_LONGTARGET % MAGICSLOTS1: |
||
314 |
✓✓✓✗ |
302671 |
if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) && |
315 |
82641 |
strncmp(name, LONGTARGET, len) == 0) |
|
316 |
82641 |
return TARGET_INDEX; |
|
317 |
break; |
||
318 |
case K_TARGET % MAGICSLOTS1: |
||
319 |
✓✓ | 35724 |
if (name[0] == TARGET[0] && len == 1) |
320 |
27242 |
return TARGET_INDEX; |
|
321 |
break; |
||
322 |
case K_OODATE % MAGICSLOTS1: |
||
323 |
✓✓ | 20874 |
if (name[0] == OODATE[0] && len == 1) |
324 |
36 |
return OODATE_INDEX; |
|
325 |
break; |
||
326 |
case K_ALLSRC % MAGICSLOTS1: |
||
327 |
✓✓ | 81518 |
if (name[0] == ALLSRC[0] && len == 1) |
328 |
23 |
return ALLSRC_INDEX; |
|
329 |
break; |
||
330 |
case K_IMPSRC % MAGICSLOTS1: |
||
331 |
✓✓ | 50788 |
if (name[0] == IMPSRC[0] && len == 1) |
332 |
8 |
return IMPSRC_INDEX; |
|
333 |
break; |
||
334 |
case K_PREFIX % MAGICSLOTS1: |
||
335 |
✓✓ | 495202 |
if (name[0] == PREFIX[0] && len == 1) |
336 |
97124 |
return PREFIX_INDEX; |
|
337 |
break; |
||
338 |
case K_ARCHIVE % MAGICSLOTS1: |
||
339 |
✗✓ | 26324 |
if (name[0] == ARCHIVE[0] && len == 1) |
340 |
return ARCHIVE_INDEX; |
||
341 |
break; |
||
342 |
case K_MEMBER % MAGICSLOTS1: |
||
343 |
✗✓ | 58212 |
if (name[0] == MEMBER[0] && len == 1) |
344 |
return MEMBER_INDEX; |
||
345 |
break; |
||
346 |
case K_FTARGET % MAGICSLOTS1: |
||
347 |
✗✓✗✗ |
208063 |
if (name[0] == FTARGET[0] && name[1] == FTARGET[1] && len == 2) |
348 |
return FTARGET_INDEX; |
||
349 |
break; |
||
350 |
case K_DTARGET % MAGICSLOTS1: |
||
351 |
✗✓✗✗ |
111929 |
if (name[0] == DTARGET[0] && name[1] == DTARGET[1] && len == 2) |
352 |
return DTARGET_INDEX; |
||
353 |
break; |
||
354 |
case K_FPREFIX % MAGICSLOTS1: |
||
355 |
✗✓✗✗ |
36859 |
if (name[0] == FPREFIX[0] && name[1] == FPREFIX[1] && len == 2) |
356 |
return FPREFIX_INDEX; |
||
357 |
break; |
||
358 |
case K_DPREFIX % MAGICSLOTS1: |
||
359 |
✗✓✗✗ |
185922 |
if (name[0] == DPREFIX[0] && name[1] == DPREFIX[1] && len == 2) |
360 |
return DPREFIX_INDEX; |
||
361 |
break; |
||
362 |
case K_FARCHIVE % MAGICSLOTS1: |
||
363 |
✗✓✗✗ |
308126 |
if (name[0] == FARCHIVE[0] && name[1] == FARCHIVE[1] && |
364 |
len == 2) |
||
365 |
return FARCHIVE_INDEX; |
||
366 |
break; |
||
367 |
case K_DARCHIVE % MAGICSLOTS1: |
||
368 |
✗✓✗✗ |
27235 |
if (name[0] == DARCHIVE[0] && name[1] == DARCHIVE[1] && |
369 |
len == 2) |
||
370 |
return DARCHIVE_INDEX; |
||
371 |
break; |
||
372 |
case K_FMEMBER % MAGICSLOTS1: |
||
373 |
✗✓✗✗ |
118217 |
if (name[0] == FMEMBER[0] && name[1] == FMEMBER[1] && len == 2) |
374 |
return FMEMBER_INDEX; |
||
375 |
break; |
||
376 |
case K_DMEMBER % MAGICSLOTS1: |
||
377 |
✗✓✗✗ |
126252 |
if (name[0] == DMEMBER[0] && name[1] == DMEMBER[1] && len == 2) |
378 |
return DMEMBER_INDEX; |
||
379 |
break; |
||
380 |
case K_FIMPSRC % MAGICSLOTS1: |
||
381 |
✗✓✗✗ |
371170 |
if (name[0] == FIMPSRC[0] && name[1] == FIMPSRC[1] && len == 2) |
382 |
return FIMPSRC_INDEX; |
||
383 |
break; |
||
384 |
case K_DIMPSRC % MAGICSLOTS1: |
||
385 |
✗✓✗✗ |
36480 |
if (name[0] == DIMPSRC[0] && name[1] == DIMPSRC[1] && len == 2) |
386 |
return DIMPSRC_INDEX; |
||
387 |
break; |
||
388 |
default: |
||
389 |
break; |
||
390 |
} |
||
391 |
20627496 |
return GLOBAL_INDEX; |
|
392 |
20881619 |
} |
|
393 |
|||
394 |
|||
395 |
/*** |
||
396 |
*** Internal handling of variables. |
||
397 |
***/ |
||
398 |
|||
399 |
|||
400 |
/* Create a new variable, does not initialize anything except the name. |
||
401 |
* in particular, buffer is invalid, and flag value is invalid. Accordingly, |
||
402 |
* must either: |
||
403 |
* - set flags to VAR_DUMMY |
||
404 |
* - set flags to !VAR_DUMMY, and initialize buffer, for instance with |
||
405 |
* var_set_initial_value(). |
||
406 |
*/ |
||
407 |
static Var * |
||
408 |
create_var(const char *name, const char *ename) |
||
409 |
{ |
||
410 |
3653053 |
return ohash_create_entry(&var_info, name, &ename); |
|
411 |
} |
||
412 |
|||
413 |
/* Initial version of var_set_value(), to be called after create_var(). |
||
414 |
*/ |
||
415 |
static void |
||
416 |
var_set_initial_value(Var *v, const char *val) |
||
417 |
{ |
||
418 |
size_t len; |
||
419 |
|||
420 |
6486588 |
len = strlen(val); |
|
421 |
3243294 |
Buf_Init(&(v->val), len+1); |
|
422 |
3243294 |
Buf_AddChars(&(v->val), len, val); |
|
423 |
3243294 |
} |
|
424 |
|||
425 |
/* Normal version of var_set_value(), to be called after variable is fully |
||
426 |
* initialized. |
||
427 |
*/ |
||
428 |
static void |
||
429 |
var_set_value(Var *v, const char *val) |
||
430 |
{ |
||
431 |
✓✓ | 25178998 |
if ((v->flags & VAR_DUMMY) == 0) { |
432 |
9492901 |
Buf_Reset(&(v->val)); |
|
433 |
9492901 |
Buf_AddString(&(v->val), val); |
|
434 |
9492901 |
} else { |
|
435 |
3096598 |
var_set_initial_value(v, val); |
|
436 |
3096598 |
v->flags &= ~VAR_DUMMY; |
|
437 |
} |
||
438 |
12589499 |
} |
|
439 |
|||
440 |
/* Add to a variable, insert a separating space if the variable was already |
||
441 |
* defined. |
||
442 |
*/ |
||
443 |
static void |
||
444 |
var_append_value(Var *v, const char *val) |
||
445 |
{ |
||
446 |
✓✓ | 7041400 |
if ((v->flags & VAR_DUMMY) == 0) { |
447 |
✓✓ | 6948527 |
Buf_AddSpace(&(v->val)); |
448 |
3374004 |
Buf_AddString(&(v->val), val); |
|
449 |
3374004 |
} else { |
|
450 |
146696 |
var_set_initial_value(v, val); |
|
451 |
146696 |
v->flags &= ~VAR_DUMMY; |
|
452 |
} |
||
453 |
3520700 |
} |
|
454 |
|||
455 |
|||
456 |
/* Delete a variable and all the space associated with it. |
||
457 |
*/ |
||
458 |
static void |
||
459 |
delete_var(Var *v) |
||
460 |
{ |
||
461 |
if ((v->flags & VAR_DUMMY) == 0) |
||
462 |
Buf_Destroy(&(v->val)); |
||
463 |
free(v); |
||
464 |
} |
||
465 |
|||
466 |
|||
467 |
|||
468 |
|||
469 |
/*** |
||
470 |
*** Dynamic variable handling. |
||
471 |
***/ |
||
472 |
|||
473 |
|||
474 |
|||
475 |
/* create empty symtable. |
||
476 |
* XXX: to save space, dynamic variables may be NULL pointers. |
||
477 |
*/ |
||
478 |
void |
||
479 |
SymTable_Init(SymTable *ctxt) |
||
480 |
{ |
||
481 |
static SymTable sym_template; |
||
482 |
11232200 |
memcpy(ctxt, &sym_template, sizeof(*ctxt)); |
|
483 |
5616100 |
} |
|
484 |
|||
485 |
/*** |
||
486 |
*** Global variable handling. |
||
487 |
***/ |
||
488 |
|||
489 |
/* Create a new global var if necessary, and set it up correctly. |
||
490 |
* Do not take environment into account. |
||
491 |
*/ |
||
492 |
static Var * |
||
493 |
find_global_var_without_env(const char *name, const char *ename, uint32_t k) |
||
494 |
{ |
||
495 |
unsigned int slot; |
||
496 |
Var *v; |
||
497 |
|||
498 |
41692254 |
slot = ohash_lookup_interval(&global_variables, name, ename, k); |
|
499 |
20846127 |
v = ohash_find(&global_variables, slot); |
|
500 |
✓✓ | 20846127 |
if (v == NULL) { |
501 |
3653053 |
v = create_var(name, ename); |
|
502 |
3653053 |
v->flags = VAR_DUMMY; |
|
503 |
3653053 |
ohash_insert(&global_variables, slot, v); |
|
504 |
3653053 |
} |
|
505 |
20846127 |
return v; |
|
506 |
} |
||
507 |
|||
508 |
/* Helper for find_global_var(): grab environment value if needed. |
||
509 |
*/ |
||
510 |
static void |
||
511 |
fill_from_env(Var *v) |
||
512 |
{ |
||
513 |
char *env; |
||
514 |
|||
515 |
7058706 |
env = getenv(v->name); |
|
516 |
✓✓ | 3529353 |
if (env == NULL) |
517 |
v->flags |= VAR_SEEN_ENV; |
||
518 |
else { |
||
519 |
63370 |
var_set_value(v, env); |
|
520 |
v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV; |
||
521 |
} |
||
522 |
|||
523 |
#ifdef STATS_VAR_LOOKUP |
||
524 |
STAT_VAR_FROM_ENV++; |
||
525 |
#endif |
||
526 |
3529353 |
} |
|
527 |
|||
528 |
/* Find global var, and obtain its value from the environment if needed. |
||
529 |
*/ |
||
530 |
static Var * |
||
531 |
find_global_var(const char *name, const char *ename, uint32_t k) |
||
532 |
{ |
||
533 |
Var *v; |
||
534 |
|||
535 |
41234790 |
v = find_global_var_without_env(name, ename, k); |
|
536 |
|||
537 |
✓✓ | 20617395 |
if ((v->flags & VAR_SEEN_ENV) == 0) |
538 |
✗✓✗✗ ✓✗ |
7058706 |
if ((checkEnvFirst && (v->flags & VAR_FROM_CMD) == 0) || |
539 |
3529353 |
(v->flags & VAR_DUMMY) != 0) |
|
540 |
3529353 |
fill_from_env(v); |
|
541 |
|||
542 |
20617395 |
return v; |
|
543 |
} |
||
544 |
|||
545 |
/* mark variable with special flags, in a given setup. |
||
546 |
*/ |
||
547 |
void |
||
548 |
Var_Mark(const char *name, const char *ename, unsigned int type) |
||
549 |
{ |
||
550 |
Var *v; |
||
551 |
128 |
uint32_t k; |
|
552 |
int idx; |
||
553 |
128 |
idx = classify_var(name, &ename, &k); |
|
554 |
|||
555 |
✗✓ | 128 |
if (idx != GLOBAL_INDEX) { |
556 |
Parse_Error(PARSE_FATAL, |
||
557 |
"Trying to poison dynamic variable $%s", |
||
558 |
varnames[idx]); |
||
559 |
return; |
||
560 |
} |
||
561 |
|||
562 |
128 |
v = find_global_var(name, ename, k); |
|
563 |
128 |
v->flags |= type; |
|
564 |
/* POISON_NORMAL is not lazy: if the variable already exists in |
||
565 |
* the Makefile, then it's a mistake. |
||
566 |
*/ |
||
567 |
✗✓ | 128 |
if (v->flags & POISON_NORMAL) { |
568 |
if (v->flags & VAR_DUMMY) |
||
569 |
return; |
||
570 |
if (v->flags & VAR_FROM_ENV) |
||
571 |
return; |
||
572 |
Parse_Error(PARSE_FATAL, |
||
573 |
"Poisoned variable %s is already set\n", v->name); |
||
574 |
} |
||
575 |
256 |
} |
|
576 |
|||
577 |
/* Check if there's any reason not to use the variable in this context. |
||
578 |
*/ |
||
579 |
static void |
||
580 |
poison_check(Var *v) |
||
581 |
{ |
||
582 |
if (v->flags & POISON_NORMAL) { |
||
583 |
Parse_Error(PARSE_FATAL, |
||
584 |
"Poisoned variable %s has been referenced\n", v->name); |
||
585 |
return; |
||
586 |
} |
||
587 |
if (v->flags & VAR_DUMMY) { |
||
588 |
Parse_Error(PARSE_FATAL, |
||
589 |
"Poisoned variable %s is not defined\n", v->name); |
||
590 |
return; |
||
591 |
} |
||
592 |
if (v->flags & POISON_EMPTY) |
||
593 |
if (strcmp(var_get_value(v), "") == 0) |
||
594 |
Parse_Error(PARSE_FATAL, |
||
595 |
"Poisoned variable %s is empty\n", v->name); |
||
596 |
} |
||
597 |
|||
598 |
/* Delete global variable. |
||
599 |
*/ |
||
600 |
void |
||
601 |
Var_Deletei(const char *name, const char *ename) |
||
602 |
{ |
||
603 |
Var *v; |
||
604 |
uint32_t k; |
||
605 |
unsigned int slot; |
||
606 |
int idx; |
||
607 |
|||
608 |
idx = classify_var(name, &ename, &k); |
||
609 |
if (idx != GLOBAL_INDEX) { |
||
610 |
Parse_Error(PARSE_FATAL, |
||
611 |
"Trying to delete dynamic variable $%s", varnames[idx]); |
||
612 |
return; |
||
613 |
} |
||
614 |
slot = ohash_lookup_interval(&global_variables, name, ename, k); |
||
615 |
v = ohash_find(&global_variables, slot); |
||
616 |
|||
617 |
if (v == NULL) |
||
618 |
return; |
||
619 |
|||
620 |
if (checkEnvFirst && (v->flags & VAR_FROM_ENV)) |
||
621 |
return; |
||
622 |
|||
623 |
if (v->flags & VAR_FROM_CMD) |
||
624 |
return; |
||
625 |
|||
626 |
ohash_remove(&global_variables, slot); |
||
627 |
delete_var(v); |
||
628 |
} |
||
629 |
|||
630 |
/* Set or add a global variable, in VAR_CMD or VAR_GLOBAL context. |
||
631 |
*/ |
||
632 |
static void |
||
633 |
var_set_append(const char *name, const char *ename, const char *val, int ctxt, |
||
634 |
bool append) |
||
635 |
{ |
||
636 |
Var *v; |
||
637 |
7177399 |
uint32_t k; |
|
638 |
int idx; |
||
639 |
|||
640 |
7177399 |
idx = classify_var(name, &ename, &k); |
|
641 |
✗✓ | 7177399 |
if (idx != GLOBAL_INDEX) { |
642 |
Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s", |
||
643 |
append ? "append to" : "set", varnames[idx]); |
||
644 |
return; |
||
645 |
} |
||
646 |
|||
647 |
7177399 |
v = find_global_var(name, ename, k); |
|
648 |
✗✓ | 7177399 |
if (v->flags & POISON_NORMAL) |
649 |
Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n", |
||
650 |
append ? "append to" : "set", v->name); |
||
651 |
/* so can we write to it ? */ |
||
652 |
✓✓ | 7177399 |
if (ctxt == VAR_CMD) { /* always for command line */ |
653 |
57825 |
(append ? var_append_value : var_set_value)(v, val); |
|
654 |
57825 |
v->flags |= VAR_FROM_CMD; |
|
655 |
✓✗ | 57825 |
if ((v->flags & VAR_SHELL) == 0) { |
656 |
/* Any variables given on the command line are |
||
657 |
* automatically exported to the environment, |
||
658 |
* except for SHELL (as per POSIX standard). |
||
659 |
*/ |
||
660 |
57825 |
esetenv(v->name, val); |
|
661 |
57825 |
} |
|
662 |
✗✓ | 57825 |
if (DEBUG(VAR)) |
663 |
printf("command:%s = %s\n", v->name, var_get_value(v)); |
||
664 |
✓✓✗✗ |
7119574 |
} else if ((v->flags & VAR_FROM_CMD) == 0 && |
665 |
✗✓ | 6701685 |
(!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) { |
666 |
6701685 |
(append ? var_append_value : var_set_value)(v, val); |
|
667 |
✗✓ | 6701685 |
if (DEBUG(VAR)) |
668 |
printf("global:%s = %s\n", v->name, var_get_value(v)); |
||
669 |
✗✓ | 417889 |
} else if (DEBUG(VAR)) |
670 |
printf("overridden:%s = %s\n", v->name, var_get_value(v)); |
||
671 |
14354798 |
} |
|
672 |
|||
673 |
void |
||
674 |
Var_Seti_with_ctxt(const char *name, const char *ename, const char *val, |
||
675 |
int ctxt) |
||
676 |
{ |
||
677 |
6477620 |
var_set_append(name, ename, val, ctxt, false); |
|
678 |
3238810 |
} |
|
679 |
|||
680 |
void |
||
681 |
Var_Appendi_with_ctxt(const char *name, const char *ename, const char *val, |
||
682 |
int ctxt) |
||
683 |
{ |
||
684 |
7877178 |
var_set_append(name, ename, val, ctxt, true); |
|
685 |
3938589 |
} |
|
686 |
|||
687 |
static char * |
||
688 |
var_exec_cmd(Var *v) |
||
689 |
{ |
||
690 |
30 |
char *arg = Buf_Retrieve(&(v->val)); |
|
691 |
15 |
char *err; |
|
692 |
char *res1; |
||
693 |
15 |
res1 = Cmd_Exec(arg, &err); |
|
694 |
✗✓ | 15 |
if (err) |
695 |
Parse_Error(PARSE_WARNING, err, arg); |
||
696 |
15 |
var_set_value(v, res1); |
|
697 |
15 |
free(res1); |
|
698 |
15 |
v->flags &= ~VAR_EXEC_LATER; |
|
699 |
30 |
return Buf_Retrieve(&(v->val)); |
|
700 |
15 |
} |
|
701 |
|||
702 |
/* XXX different semantics for Var_Valuei() and Var_Definedi(): |
||
703 |
* references to poisoned value variables will error out in Var_Valuei(), |
||
704 |
* but not in Var_Definedi(), so the following construct works: |
||
705 |
* .poison BINDIR |
||
706 |
* BINDIR ?= /usr/bin |
||
707 |
*/ |
||
708 |
char * |
||
709 |
Var_Valuei(const char *name, const char *ename) |
||
710 |
{ |
||
711 |
Var *v; |
||
712 |
66982 |
uint32_t k; |
|
713 |
int idx; |
||
714 |
|||
715 |
66982 |
idx = classify_var(name, &ename, &k); |
|
716 |
✗✓ | 66982 |
if (idx != GLOBAL_INDEX) { |
717 |
Parse_Error(PARSE_FATAL, |
||
718 |
"Trying to get value of dynamic variable $%s", |
||
719 |
varnames[idx]); |
||
720 |
return NULL; |
||
721 |
} |
||
722 |
66982 |
v = find_global_var(name, ename, k); |
|
723 |
✗✓ | 66982 |
if (v->flags & POISONS) |
724 |
poison_check(v); |
||
725 |
✓✓ | 66982 |
if ((v->flags & VAR_DUMMY) == 0) |
726 |
✗✓ | 150429 |
return var_get_value(v); |
727 |
else |
||
728 |
16839 |
return NULL; |
|
729 |
66982 |
} |
|
730 |
|||
731 |
bool |
||
732 |
Var_Definedi(const char *name, const char *ename) |
||
733 |
{ |
||
734 |
Var *v; |
||
735 |
6746702 |
uint32_t k; |
|
736 |
int idx; |
||
737 |
|||
738 |
6746702 |
idx = classify_var(name, &ename, &k); |
|
739 |
/* We don't bother writing an error message for dynamic variables, |
||
740 |
* these will be caught when getting set later, usually. |
||
741 |
*/ |
||
742 |
✓✗ | 6746702 |
if (idx == GLOBAL_INDEX) { |
743 |
6746702 |
v = find_global_var(name, ename, k); |
|
744 |
✗✓ | 6746702 |
if (v->flags & POISON_NORMAL) |
745 |
poison_check(v); |
||
746 |
✓✓ | 6746702 |
if ((v->flags & VAR_DUMMY) == 0) |
747 |
1043053 |
return true; |
|
748 |
} |
||
749 |
5703649 |
return false; |
|
750 |
6746702 |
} |
|
751 |
|||
752 |
|||
753 |
/*** |
||
754 |
*** Substitution functions, handling both global and dynamic variables. |
||
755 |
***/ |
||
756 |
|||
757 |
|||
758 |
/* All the scanning functions needed to account for all the forms of |
||
759 |
* variable names that exist: |
||
760 |
* $A, ${AB}, $(ABC), ${A:mod}, $(A:mod) |
||
761 |
*/ |
||
762 |
|||
763 |
static const char * |
||
764 |
find_rparen(const char *p) |
||
765 |
{ |
||
766 |
✓✗✓✗ ✓✓✓✓ |
67733038 |
while (*p != '$' && *p != '\0' && *p != ')' && *p != ':') |
767 |
10162217 |
p++; |
|
768 |
982980 |
return p; |
|
769 |
} |
||
770 |
|||
771 |
static const char * |
||
772 |
find_ket(const char *p) |
||
773 |
{ |
||
774 |
✓✓✓✗ ✓✓✓✓ |
4553928402 |
while (*p != '$' && *p != '\0' && *p != '}' && *p != ':') |
775 |
668702954 |
p++; |
|
776 |
87398041 |
return p; |
|
777 |
} |
||
778 |
|||
779 |
/* Figure out what kind of name we're looking for from a start character. |
||
780 |
*/ |
||
781 |
static find_t |
||
782 |
find_pos(int c) |
||
783 |
{ |
||
784 |
✓✓✗ | 176761812 |
switch(c) { |
785 |
case '(': |
||
786 |
982980 |
return find_rparen; |
|
787 |
case '{': |
||
788 |
87397926 |
return find_ket; |
|
789 |
default: |
||
790 |
Parse_Error(PARSE_FATAL, |
||
791 |
"Wrong character in variable spec %c (can't happen)", c); |
||
792 |
return find_rparen; |
||
793 |
} |
||
794 |
88380906 |
} |
|
795 |
|||
796 |
static bool |
||
797 |
parse_base_variable_name(const char **pstr, struct Name *name, SymTable *ctxt) |
||
798 |
{ |
||
799 |
13780816 |
const char *str = *pstr; |
|
800 |
const char *tstr; |
||
801 |
bool has_modifier = false; |
||
802 |
|||
803 |
✗✓✓ | 6890408 |
switch(str[1]) { |
804 |
case '(': |
||
805 |
case '{': |
||
806 |
/* Find eventual modifiers in the variable */ |
||
807 |
6784200 |
tstr = VarName_Get(str+2, name, ctxt, false, find_pos(str[1])); |
|
808 |
✗✓ | 6784200 |
if (*tstr == '\0') |
809 |
Parse_Error(PARSE_FATAL, "Unterminated variable spec in %s", *pstr); |
||
810 |
✓✓ | 6784200 |
else if (*tstr == ':') |
811 |
4080296 |
has_modifier = true; |
|
812 |
else |
||
813 |
2703904 |
tstr++; |
|
814 |
break; |
||
815 |
default: |
||
816 |
106208 |
name->s = str+1; |
|
817 |
106208 |
name->e = str+2; |
|
818 |
106208 |
name->tofree = false; |
|
819 |
tstr = str + 2; |
||
820 |
106208 |
break; |
|
821 |
} |
||
822 |
6890408 |
*pstr = tstr; |
|
823 |
6890408 |
return has_modifier; |
|
824 |
} |
||
825 |
|||
826 |
bool |
||
827 |
Var_ParseSkip(const char **pstr, SymTable *ctxt) |
||
828 |
{ |
||
829 |
const char *str = *pstr; |
||
830 |
struct Name name; |
||
831 |
bool result; |
||
832 |
bool has_modifier; |
||
833 |
const char *tstr = str; |
||
834 |
|||
835 |
if (str[1] == 0) { |
||
836 |
*pstr = str+1; |
||
837 |
return false; |
||
838 |
} |
||
839 |
has_modifier = parse_base_variable_name(&tstr, &name, ctxt); |
||
840 |
VarName_Free(&name); |
||
841 |
result = true; |
||
842 |
if (has_modifier) { |
||
843 |
bool freePtr = false; |
||
844 |
char *s = VarModifiers_Apply(NULL, NULL, ctxt, true, &freePtr, |
||
845 |
&tstr, str[1]); |
||
846 |
if (s == var_Error) |
||
847 |
result = false; |
||
848 |
if (freePtr) |
||
849 |
free(s); |
||
850 |
} |
||
851 |
*pstr = tstr; |
||
852 |
return result; |
||
853 |
} |
||
854 |
|||
855 |
/* As of now, Var_ParseBuffer is just a wrapper around Var_Parse. For |
||
856 |
* speed, it may be better to revisit the implementation to do things |
||
857 |
* directly. */ |
||
858 |
bool |
||
859 |
Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err, |
||
860 |
size_t *lengthPtr) |
||
861 |
{ |
||
862 |
char *result; |
||
863 |
284077 |
bool freeIt; |
|
864 |
|||
865 |
284077 |
result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt); |
|
866 |
✓✓ | 284077 |
if (result == var_Error) |
867 |
33841 |
return false; |
|
868 |
|||
869 |
250236 |
Buf_AddString(buf, result); |
|
870 |
✓✓ | 250236 |
if (freeIt) |
871 |
18038 |
free(result); |
|
872 |
250236 |
return true; |
|
873 |
284077 |
} |
|
874 |
|||
875 |
/* Helper function for Var_Parse: still recursive, but we tag what variables |
||
876 |
* we expand for better error messages. |
||
877 |
*/ |
||
878 |
#define MAX_DEPTH 350 |
||
879 |
static Var *call_trace[MAX_DEPTH]; |
||
880 |
static int current_depth = 0; |
||
881 |
|||
882 |
static void |
||
883 |
push_used(Var *v) |
||
884 |
{ |
||
885 |
✗✓ | 1184924 |
if (v->flags & VAR_IN_USE) { |
886 |
int i; |
||
887 |
fprintf(stderr, "Problem with variable expansion chain: "); |
||
888 |
for (i = 0; |
||
889 |
i < (current_depth > MAX_DEPTH ? MAX_DEPTH : current_depth); |
||
890 |
i++) |
||
891 |
fprintf(stderr, "%s -> ", call_trace[i]->name); |
||
892 |
fprintf(stderr, "%s\n", v->name); |
||
893 |
Fatal("\tVariable %s is recursive.", v->name); |
||
894 |
/*NOTREACHED*/ |
||
895 |
} |
||
896 |
|||
897 |
592462 |
v->flags |= VAR_IN_USE; |
|
898 |
✓✗ | 592462 |
if (current_depth < MAX_DEPTH) |
899 |
592462 |
call_trace[current_depth] = v; |
|
900 |
592462 |
current_depth++; |
|
901 |
592462 |
} |
|
902 |
|||
903 |
static void |
||
904 |
pop_used(Var *v) |
||
905 |
{ |
||
906 |
1184924 |
v->flags &= ~VAR_IN_USE; |
|
907 |
592462 |
current_depth--; |
|
908 |
592462 |
} |
|
909 |
|||
910 |
static char * |
||
911 |
get_expanded_value(const char *name, const char *ename, int idx, uint32_t k, |
||
912 |
SymTable *ctxt, bool err, bool *freePtr) |
||
913 |
{ |
||
914 |
char *val; |
||
915 |
|||
916 |
/* Before doing any modification, we have to make sure the |
||
917 |
* value has been fully expanded. If it looks like recursion |
||
918 |
* might be necessary (there's a dollar sign somewhere in |
||
919 |
* the variable's value) we just call Var_Subst to do any |
||
920 |
* other substitutions that are necessary. Note that the |
||
921 |
* value returned by Var_Subst will have been dynamically |
||
922 |
* allocated, so it will need freeing when we return. |
||
923 |
*/ |
||
924 |
✓✓ | 6872415 |
if (idx == GLOBAL_INDEX) { |
925 |
6626184 |
Var *v = find_global_var(name, ename, k); |
|
926 |
|||
927 |
✗✓ | 6626184 |
if (v == NULL) |
928 |
return NULL; |
||
929 |
|||
930 |
✗✓ | 6626184 |
if ((v->flags & POISONS) != 0) |
931 |
poison_check(v); |
||
932 |
✓✓ | 6626184 |
if ((v->flags & VAR_DUMMY) != 0) |
933 |
354021 |
return NULL; |
|
934 |
|||
935 |
✓✓ | 18816489 |
val = var_get_value(v); |
936 |
✓✓ | 6272163 |
if (strchr(val, '$') != NULL) { |
937 |
592462 |
push_used(v); |
|
938 |
592462 |
val = Var_Subst(val, ctxt, err); |
|
939 |
592462 |
pop_used(v); |
|
940 |
592462 |
*freePtr = true; |
|
941 |
592462 |
} |
|
942 |
✓✓ | 6272163 |
} else { |
943 |
✓✓ | 246231 |
if (ctxt != NULL) { |
944 |
✓✗ | 240822 |
if (idx < LOCAL_SIZE) |
945 |
240822 |
val = ctxt->locals[idx]; |
|
946 |
else |
||
947 |
val = ctxt->locals[EXTENDED2SIMPLE(idx)]; |
||
948 |
} else |
||
949 |
val = NULL; |
||
950 |
✓✓ | 246231 |
if (val == NULL) |
951 |
5409 |
return NULL; |
|
952 |
|||
953 |
✗✓ | 240822 |
if (idx >= LOCAL_SIZE) { |
954 |
if (IS_EXTENDED_F(idx)) |
||
955 |
val = Var_GetTail(val); |
||
956 |
else |
||
957 |
val = Var_GetHead(val); |
||
958 |
*freePtr = true; |
||
959 |
} |
||
960 |
} |
||
961 |
6512985 |
return val; |
|
962 |
6872415 |
} |
|
963 |
|||
964 |
#define ERRMSG1 "Using $< in a non-suffix rule context is a GNUmake idiom " |
||
965 |
#define ERRMSG2 "Using undefined dynamic variable $%s " |
||
966 |
static void |
||
967 |
bad_dynamic_variable(int idx) |
||
968 |
{ |
||
969 |
Location origin; |
||
970 |
|||
971 |
Parse_FillLocation(&origin); |
||
972 |
if (idx >= LOCAL_SIZE) |
||
973 |
idx = EXTENDED2SIMPLE(idx); |
||
974 |
switch(idx) { |
||
975 |
case IMPSRC_INDEX: |
||
976 |
if (origin.fname) |
||
977 |
Fatal(ERRMSG1 "(%s:%lu)", |
||
978 |
origin.fname, origin.lineno); |
||
979 |
else if (current_node) |
||
980 |
Fatal(ERRMSG1 "(prereq of %s)", current_node->name); |
||
981 |
else |
||
982 |
Fatal(ERRMSG1 "(?)"); |
||
983 |
break; |
||
984 |
default: |
||
985 |
if (origin.fname) |
||
986 |
Error(ERRMSG2 "(%s:%lu)", varnames[idx], |
||
987 |
origin.fname, origin.lineno); |
||
988 |
else if (current_node) |
||
989 |
Error(ERRMSG2 "(prereq of %s)", varnames[idx], |
||
990 |
current_node->name); |
||
991 |
else |
||
992 |
Error(ERRMSG2 "(?)", varnames[idx]); |
||
993 |
break; |
||
994 |
} |
||
995 |
} |
||
996 |
|||
997 |
char * |
||
998 |
Var_Parse(const char *str, /* The string to parse */ |
||
999 |
SymTable *ctxt, /* The context for the variable */ |
||
1000 |
bool err, /* true if undefined variables are an error */ |
||
1001 |
size_t *lengthPtr, /* OUT: The length of the specification */ |
||
1002 |
bool *freePtr) /* OUT: true if caller should free result */ |
||
1003 |
{ |
||
1004 |
6872415 |
const char *tstr; |
|
1005 |
6872415 |
struct Name name; |
|
1006 |
char *val; |
||
1007 |
6872415 |
uint32_t k; |
|
1008 |
int idx; |
||
1009 |
bool has_modifier; |
||
1010 |
|||
1011 |
6872415 |
*freePtr = false; |
|
1012 |
|||
1013 |
6872415 |
tstr = str; |
|
1014 |
|||
1015 |
✗✓ | 6872415 |
if (str[1] == 0) { |
1016 |
*lengthPtr = 1; |
||
1017 |
*freePtr = false; |
||
1018 |
return err ? var_Error : varNoError; |
||
1019 |
} |
||
1020 |
|||
1021 |
6872415 |
has_modifier = parse_base_variable_name(&tstr, &name, ctxt); |
|
1022 |
|||
1023 |
6872415 |
idx = classify_var(name.s, &name.e, &k); |
|
1024 |
6872415 |
val = get_expanded_value(name.s, name.e, idx, k, ctxt, err, freePtr); |
|
1025 |
✓✓ | 6872415 |
if (has_modifier) { |
1026 |
4077367 |
val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, |
|
1027 |
4077367 |
&tstr, str[1]); |
|
1028 |
4077367 |
} |
|
1029 |
✓✓ | 6872415 |
if (val == NULL) { |
1030 |
359430 |
val = err ? var_Error : varNoError; |
|
1031 |
/* If it comes from a dynamic source, and it doesn't have |
||
1032 |
* a context, copy the spec instead. |
||
1033 |
* Specifically, this make allows constructs like: |
||
1034 |
* target.o: $*.c |
||
1035 |
* Absence of a context means "parsing". But these can't |
||
1036 |
* be expanded during parsing, to be consistent with the |
||
1037 |
* way .SUFFIXES work. |
||
1038 |
* .SUFFIXES may be added/reset/removed during parsing, |
||
1039 |
* but in the end, the final list is what's considered for |
||
1040 |
* handling targets. So those dynamic variables must be |
||
1041 |
* handled lazily too. |
||
1042 |
*/ |
||
1043 |
✓✓ | 359430 |
if (idx != GLOBAL_INDEX) { |
1044 |
✓✗ | 5409 |
if (ctxt == NULL) { |
1045 |
5409 |
*freePtr = true; |
|
1046 |
5409 |
val = Str_dupi(str, tstr); |
|
1047 |
5409 |
} else { |
|
1048 |
bad_dynamic_variable(idx); |
||
1049 |
} |
||
1050 |
} |
||
1051 |
} |
||
1052 |
6872415 |
VarName_Free(&name); |
|
1053 |
6872415 |
*lengthPtr = tstr - str; |
|
1054 |
6872415 |
return val; |
|
1055 |
6872415 |
} |
|
1056 |
|||
1057 |
|||
1058 |
char * |
||
1059 |
Var_Subst(const char *str, /* the string in which to substitute */ |
||
1060 |
SymTable *ctxt, /* the context wherein to find variables */ |
||
1061 |
bool undefErr) /* true if undefineds are an error */ |
||
1062 |
{ |
||
1063 |
6412504 |
BUFFER buf; /* Buffer for forming things */ |
|
1064 |
static bool errorReported; |
||
1065 |
|||
1066 |
6412504 |
Buf_Init(&buf, MAKE_BSIZE); |
|
1067 |
6412504 |
errorReported = false; |
|
1068 |
|||
1069 |
8635600 |
for (;;) { |
|
1070 |
char *val; /* Value to substitute for a variable */ |
||
1071 |
8652525 |
size_t length; /* Length of the variable invocation */ |
|
1072 |
8652525 |
bool doFree; /* Set true if val should be freed */ |
|
1073 |
const char *cp; |
||
1074 |
|||
1075 |
/* copy uninteresting stuff */ |
||
1076 |
✓✓✓✓ |
418875110 |
for (cp = str; *str != '\0' && *str != '$'; str++) |
1077 |
; |
||
1078 |
8652525 |
Buf_Addi(&buf, cp, str); |
|
1079 |
✓✓ | 8652525 |
if (*str == '\0') |
1080 |
6412504 |
break; |
|
1081 |
✓✓ | 2240021 |
if (str[1] == '$') { |
1082 |
/* A $ may be escaped with another $. */ |
||
1083 |
✓✓ | 33904 |
Buf_AddChar(&buf, '$'); |
1084 |
16925 |
str += 2; |
|
1085 |
16925 |
continue; |
|
1086 |
} |
||
1087 |
2223096 |
val = Var_Parse(str, ctxt, undefErr, &length, &doFree); |
|
1088 |
/* When we come down here, val should either point to the |
||
1089 |
* value of this variable, suitably modified, or be NULL. |
||
1090 |
* Length should be the total length of the potential |
||
1091 |
* variable invocation (from $ to end character...) */ |
||
1092 |
✓✓ | 2223096 |
if (val == var_Error || val == varNoError) { |
1093 |
/* If errors are not an issue, skip over the variable |
||
1094 |
* and continue with the substitution. Otherwise, store |
||
1095 |
* the dollar sign and advance str so we continue with |
||
1096 |
* the string... */ |
||
1097 |
✓✗ | 289877 |
if (errorIsOkay) |
1098 |
289877 |
str += length; |
|
1099 |
else if (undefErr) { |
||
1100 |
/* If variable is undefined, complain and |
||
1101 |
* skip the variable name. The complaint |
||
1102 |
* will stop us from doing anything when |
||
1103 |
* the file is parsed. */ |
||
1104 |
if (!errorReported) |
||
1105 |
Parse_Error(PARSE_FATAL, |
||
1106 |
"Undefined variable \"%.*s\"", |
||
1107 |
(int)length, str); |
||
1108 |
str += length; |
||
1109 |
errorReported = true; |
||
1110 |
} else { |
||
1111 |
Buf_AddChar(&buf, *str); |
||
1112 |
str++; |
||
1113 |
} |
||
1114 |
} else { |
||
1115 |
/* We've now got a variable structure to store in. |
||
1116 |
* But first, advance the string pointer. */ |
||
1117 |
1933219 |
str += length; |
|
1118 |
|||
1119 |
/* Copy all the characters from the variable value |
||
1120 |
* straight into the new string. */ |
||
1121 |
1933219 |
Buf_AddString(&buf, val); |
|
1122 |
✓✓ | 1933219 |
if (doFree) |
1123 |
548087 |
free(val); |
|
1124 |
} |
||
1125 |
✓✓✓✗ |
19494368 |
} |
1126 |
12825008 |
return Buf_Retrieve(&buf); |
|
1127 |
6412504 |
} |
|
1128 |
|||
1129 |
/* Very quick version of the variable scanner that just looks for target |
||
1130 |
* variables, and never ever errors out |
||
1131 |
*/ |
||
1132 |
bool |
||
1133 |
Var_Check_for_target(const char *str) |
||
1134 |
{ |
||
1135 |
bool seen_target = false; |
||
1136 |
|||
1137 |
130013 |
for (;;) { |
|
1138 |
77008 |
const char *tstr; |
|
1139 |
77008 |
uint32_t k; |
|
1140 |
int idx; |
||
1141 |
bool has_modifier; |
||
1142 |
77008 |
struct Name name; |
|
1143 |
|||
1144 |
/* skip over uninteresting stuff */ |
||
1145 |
✓✓✓✓ |
7168398 |
for (; *str != '\0' && *str != '$'; str++) |
1146 |
; |
||
1147 |
✓✓ | 77008 |
if (*str == '\0') |
1148 |
56010 |
break; |
|
1149 |
✓✓ | 20998 |
if (str[1] == '$') { |
1150 |
/* A $ may be escaped with another $. */ |
||
1151 |
3005 |
str += 2; |
|
1152 |
3005 |
continue; |
|
1153 |
} |
||
1154 |
|||
1155 |
17993 |
tstr = str; |
|
1156 |
|||
1157 |
17993 |
has_modifier = parse_base_variable_name(&tstr, &name, NULL); |
|
1158 |
17993 |
idx = classify_var(name.s, &name.e, &k); |
|
1159 |
✓✓ | 17993 |
if (has_modifier) { |
1160 |
2929 |
bool doFree = false; |
|
1161 |
2929 |
char *val = VarModifiers_Apply(NULL, NULL, NULL, false, |
|
1162 |
2929 |
&doFree, &tstr, str[1]); |
|
1163 |
✗✓ | 2929 |
if (doFree) |
1164 |
free(val); |
||
1165 |
2929 |
} |
|
1166 |
✓✓ | 17993 |
if (tlist[idx]) |
1167 |
7018 |
seen_target = true; |
|
1168 |
17993 |
VarName_Free(&name); |
|
1169 |
17993 |
str = tstr; |
|
1170 |
✓✓✓✗ |
167217 |
} |
1171 |
56010 |
return seen_target; |
|
1172 |
} |
||
1173 |
|||
1174 |
static BUFFER subst_buffer; |
||
1175 |
|||
1176 |
/* we would like to subst on intervals, but it's complicated, so we cheat |
||
1177 |
* by storing the interval in a static buffer. |
||
1178 |
*/ |
||
1179 |
char * |
||
1180 |
Var_Substi(const char *str, const char *estr, SymTable *ctxt, bool undefErr) |
||
1181 |
{ |
||
1182 |
/* delimited string: no need to copy */ |
||
1183 |
✓✗✗✓ |
361858 |
if (estr == NULL || *estr == '\0') |
1184 |
return Var_Subst(str, ctxt, undefErr); |
||
1185 |
|||
1186 |
180929 |
Buf_Reset(&subst_buffer); |
|
1187 |
180929 |
Buf_Addi(&subst_buffer, str, estr); |
|
1188 |
180929 |
return Var_Subst(Buf_Retrieve(&subst_buffer), ctxt, undefErr); |
|
1189 |
180929 |
} |
|
1190 |
|||
1191 |
/*** |
||
1192 |
*** Supplementary support for .for loops. |
||
1193 |
***/ |
||
1194 |
|||
1195 |
|||
1196 |
|||
1197 |
struct LoopVar |
||
1198 |
{ |
||
1199 |
Var old; /* keep old variable value (before the loop) */ |
||
1200 |
Var *me; /* the variable we're dealing with */ |
||
1201 |
}; |
||
1202 |
|||
1203 |
|||
1204 |
struct LoopVar * |
||
1205 |
Var_NewLoopVar(const char *name, const char *ename) |
||
1206 |
{ |
||
1207 |
struct LoopVar *l; |
||
1208 |
uint32_t k; |
||
1209 |
|||
1210 |
211879 |
l = emalloc(sizeof(struct LoopVar)); |
|
1211 |
|||
1212 |
/* we obtain a new variable quickly, make a snapshot of its old |
||
1213 |
* value, and make sure the environment cannot touch us. |
||
1214 |
*/ |
||
1215 |
/* XXX: should we avoid dynamic variables ? */ |
||
1216 |
211879 |
k = ohash_interval(name, &ename); |
|
1217 |
|||
1218 |
211879 |
l->me = find_global_var_without_env(name, ename, k); |
|
1219 |
211879 |
l->old = *(l->me); |
|
1220 |
211879 |
l->me->flags = VAR_SEEN_ENV | VAR_DUMMY; |
|
1221 |
211879 |
return l; |
|
1222 |
} |
||
1223 |
|||
1224 |
char * |
||
1225 |
Var_LoopVarName(struct LoopVar *v) |
||
1226 |
{ |
||
1227 |
return v->me->name; |
||
1228 |
} |
||
1229 |
|||
1230 |
void |
||
1231 |
Var_DeleteLoopVar(struct LoopVar *l) |
||
1232 |
{ |
||
1233 |
✓✓ | 423758 |
if ((l->me->flags & VAR_DUMMY) == 0) |
1234 |
157343 |
Buf_Destroy(&(l->me->val)); |
|
1235 |
211879 |
*(l->me) = l->old; |
|
1236 |
211879 |
free(l); |
|
1237 |
211879 |
} |
|
1238 |
|||
1239 |
void |
||
1240 |
Var_SubstVar(Buffer buf, /* To store result */ |
||
1241 |
const char *str, /* The string in which to substitute */ |
||
1242 |
struct LoopVar *l, /* Handle */ |
||
1243 |
const char *val) /* Its value */ |
||
1244 |
{ |
||
1245 |
18540902 |
const char *var = l->me->name; |
|
1246 |
|||
1247 |
9270451 |
var_set_value(l->me, val); |
|
1248 |
|||
1249 |
21301284 |
for (;;) { |
|
1250 |
const char *start; |
||
1251 |
/* Copy uninteresting stuff */ |
||
1252 |
✓✓✓✓ |
5060429750 |
for (start = str; *str != '\0' && *str != '$'; str++) |
1253 |
; |
||
1254 |
92098913 |
Buf_Addi(buf, start, str); |
|
1255 |
|||
1256 |
start = str; |
||
1257 |
✓✓ | 92098913 |
if (*str++ == '\0') |
1258 |
9270451 |
break; |
|
1259 |
82828462 |
str++; |
|
1260 |
/* and escaped dollars */ |
||
1261 |
✓✓ | 82828462 |
if (start[1] == '$') { |
1262 |
56896 |
Buf_Addi(buf, start, start+2); |
|
1263 |
56896 |
continue; |
|
1264 |
} |
||
1265 |
/* Simple variable, if it's not us, copy. */ |
||
1266 |
✓✗✓✓ |
165543132 |
if (start[1] != '(' && start[1] != '{') { |
1267 |
✓✓✗✓ |
2191298 |
if (start[1] != *var || var[1] != '\0') { |
1268 |
158422 |
Buf_AddChars(buf, 2, start); |
|
1269 |
158422 |
continue; |
|
1270 |
} |
||
1271 |
} else { |
||
1272 |
81596706 |
const char *p; |
|
1273 |
81596706 |
char paren = start[1]; |
|
1274 |
|||
1275 |
|||
1276 |
/* Find the end of the variable specification. */ |
||
1277 |
81596706 |
p = find_pos(paren)(str); |
|
1278 |
/* A variable inside the variable. We don't know how to |
||
1279 |
* expand the external variable at this point, so we |
||
1280 |
* try again with the nested variable. */ |
||
1281 |
✓✓ | 81596706 |
if (*p == '$') { |
1282 |
107964 |
Buf_Addi(buf, start, p); |
|
1283 |
107964 |
str = p; |
|
1284 |
107964 |
continue; |
|
1285 |
} |
||
1286 |
|||
1287 |
✓✓✗✓ |
103850429 |
if (strncmp(var, str, p - str) != 0 || |
1288 |
22361687 |
var[p - str] != '\0') { |
|
1289 |
/* Not the variable we want to expand. */ |
||
1290 |
59127055 |
Buf_Addi(buf, start, p); |
|
1291 |
59127055 |
str = p; |
|
1292 |
59127055 |
continue; |
|
1293 |
} |
||
1294 |
✓✓ | 22361687 |
if (*p == ':') { |
1295 |
11347292 |
bool doFree; /* should val be freed ? */ |
|
1296 |
char *newval; |
||
1297 |
11347292 |
struct Name name; |
|
1298 |
|||
1299 |
11347292 |
doFree = false; |
|
1300 |
11347292 |
name.s = var; |
|
1301 |
11347292 |
name.e = var + (p-str); |
|
1302 |
|||
1303 |
/* val won't be freed since !doFree, but |
||
1304 |
* VarModifiers_Apply doesn't know that, |
||
1305 |
* hence the cast. */ |
||
1306 |
11347292 |
newval = VarModifiers_Apply((char *)val, |
|
1307 |
&name, NULL, false, &doFree, &p, paren); |
||
1308 |
11347292 |
Buf_AddString(buf, newval); |
|
1309 |
✓✗ | 11347292 |
if (doFree) |
1310 |
11347292 |
free(newval); |
|
1311 |
11347292 |
str = p; |
|
1312 |
continue; |
||
1313 |
11347292 |
} else |
|
1314 |
11014395 |
str = p+1; |
|
1315 |
✓✓ | 92611101 |
} |
1316 |
12030833 |
Buf_AddString(buf, val); |
|
1317 |
✓✓✓✗ |
12030833 |
} |
1318 |
9270451 |
} |
|
1319 |
|||
1320 |
/*** |
||
1321 |
*** Odds and ends |
||
1322 |
***/ |
||
1323 |
|||
1324 |
static void |
||
1325 |
set_magic_shell_variable() |
||
1326 |
{ |
||
1327 |
const char *name = "SHELL"; |
||
1328 |
33706 |
const char *ename = NULL; |
|
1329 |
uint32_t k; |
||
1330 |
Var *v; |
||
1331 |
|||
1332 |
16853 |
k = ohash_interval(name, &ename); |
|
1333 |
16853 |
v = find_global_var_without_env(name, ename, k); |
|
1334 |
16853 |
var_set_value(v, _PATH_BSHELL); |
|
1335 |
/* XXX the environment shall never affect it */ |
||
1336 |
16853 |
v->flags = VAR_SHELL | VAR_SEEN_ENV; |
|
1337 |
16853 |
} |
|
1338 |
|||
1339 |
/* |
||
1340 |
* Var_Init |
||
1341 |
* Initialize the module |
||
1342 |
*/ |
||
1343 |
void |
||
1344 |
Var_Init(void) |
||
1345 |
{ |
||
1346 |
33706 |
ohash_init(&global_variables, 10, &var_info); |
|
1347 |
16853 |
set_magic_shell_variable(); |
|
1348 |
|||
1349 |
|||
1350 |
16853 |
errorIsOkay = true; |
|
1351 |
16853 |
Var_setCheckEnvFirst(false); |
|
1352 |
|||
1353 |
16853 |
VarModifiers_Init(); |
|
1354 |
16853 |
Buf_Init(&subst_buffer, MAKE_BSIZE); |
|
1355 |
16853 |
} |
|
1356 |
|||
1357 |
|||
1358 |
static const char *interpret(int); |
||
1359 |
|||
1360 |
static const char * |
||
1361 |
interpret(int f) |
||
1362 |
{ |
||
1363 |
if (f & VAR_DUMMY) |
||
1364 |
return "(D)"; |
||
1365 |
return ""; |
||
1366 |
} |
||
1367 |
|||
1368 |
|||
1369 |
static void |
||
1370 |
print_var(Var *v) |
||
1371 |
{ |
||
1372 |
printf("%-16s%s = %s\n", v->name, interpret(v->flags), |
||
1373 |
(v->flags & VAR_DUMMY) == 0 ? var_get_value(v) : "(none)"); |
||
1374 |
} |
||
1375 |
|||
1376 |
|||
1377 |
void |
||
1378 |
Var_Dump(void) |
||
1379 |
{ |
||
1380 |
Var **t; |
||
1381 |
|||
1382 |
unsigned int i; |
||
1383 |
const char *banner; |
||
1384 |
bool first = true; |
||
1385 |
|||
1386 |
t = sort_ohash_by_name(&global_variables); |
||
1387 |
/* somewhat dirty, but does the trick */ |
||
1388 |
|||
1389 |
#define LOOP(mask, value, do_stuff) \ |
||
1390 |
for (i = 0; t[i] != NULL; i++) \ |
||
1391 |
if ((t[i]->flags & (mask)) == (value)) { \ |
||
1392 |
if (banner) { \ |
||
1393 |
if (first) \ |
||
1394 |
first = false; \ |
||
1395 |
else \ |
||
1396 |
putchar('\n'); \ |
||
1397 |
fputs(banner, stdout); \ |
||
1398 |
banner = NULL; \ |
||
1399 |
} \ |
||
1400 |
do_stuff; \ |
||
1401 |
} |
||
1402 |
|||
1403 |
banner = "#variables from command line:\n"; |
||
1404 |
LOOP(VAR_FROM_CMD | VAR_DUMMY, VAR_FROM_CMD, print_var(t[i])); |
||
1405 |
|||
1406 |
banner = "#global variables:\n"; |
||
1407 |
LOOP(VAR_FROM_ENV| VAR_FROM_CMD | VAR_DUMMY, 0, print_var(t[i])); |
||
1408 |
|||
1409 |
banner = "#variables from env:\n"; |
||
1410 |
LOOP(VAR_FROM_ENV|VAR_DUMMY, VAR_FROM_ENV, print_var(t[i])); |
||
1411 |
|||
1412 |
banner = "#variable name seen, but not defined:"; |
||
1413 |
LOOP(VAR_DUMMY|POISONS, VAR_DUMMY, printf(" %s", t[i]->name)); |
||
1414 |
|||
1415 |
#undef LOOP |
||
1416 |
|||
1417 |
printf("\n\n"); |
||
1418 |
|||
1419 |
for (i = 0; t[i] != NULL; i++) |
||
1420 |
switch(t[i]->flags & POISONS) { |
||
1421 |
case POISON_NORMAL: |
||
1422 |
printf(".poison %s\n", t[i]->name); |
||
1423 |
break; |
||
1424 |
case POISON_EMPTY: |
||
1425 |
printf(".poison empty(%s)\n", t[i]->name); |
||
1426 |
break; |
||
1427 |
case POISON_NOT_DEFINED: |
||
1428 |
printf(".poison !defined(%s)\n", t[i]->name); |
||
1429 |
break; |
||
1430 |
default: |
||
1431 |
break; |
||
1432 |
} |
||
1433 |
free(t); |
||
1434 |
printf("\n"); |
||
1435 |
} |
||
1436 |
|||
1437 |
static const char *quotable = " \t\n\\'\""; |
||
1438 |
|||
1439 |
/* POSIX says that variable assignments passed on the command line should be |
||
1440 |
* propagated to sub makes through MAKEFLAGS. |
||
1441 |
*/ |
||
1442 |
void |
||
1443 |
Var_AddCmdline(const char *name) |
||
1444 |
{ |
||
1445 |
Var *v; |
||
1446 |
33706 |
unsigned int i; |
|
1447 |
16853 |
BUFFER buf; |
|
1448 |
char *s; |
||
1449 |
|||
1450 |
16853 |
Buf_Init(&buf, MAKE_BSIZE); |
|
1451 |
|||
1452 |
✓✓ | 464828 |
for (v = ohash_first(&global_variables, &i); v != NULL; |
1453 |
215561 |
v = ohash_next(&global_variables, &i)) { |
|
1454 |
/* This is not as expensive as it looks: this function is |
||
1455 |
* called before parsing Makefiles, so there are just a |
||
1456 |
* few non cmdling variables in there. |
||
1457 |
*/ |
||
1458 |
✓✓ | 215561 |
if (!(v->flags & VAR_FROM_CMD)) { |
1459 |
continue; |
||
1460 |
} |
||
1461 |
/* We assume variable names don't need quoting */ |
||
1462 |
47031 |
Buf_AddString(&buf, v->name); |
|
1463 |
✗✓ | 94062 |
Buf_AddChar(&buf, '='); |
1464 |
✗✓✓✓ |
753334 |
for (s = var_get_value(v); *s != '\0'; s++) { |
1465 |
✓✓ | 282605 |
if (strchr(quotable, *s)) |
1466 |
✗✓ | 48 |
Buf_AddChar(&buf, '\\'); |
1467 |
✗✓ | 565210 |
Buf_AddChar(&buf, *s); |
1468 |
} |
||
1469 |
✗✓ | 94062 |
Buf_AddSpace(&buf); |
1470 |
47031 |
} |
|
1471 |
16853 |
Var_Append(name, Buf_Retrieve(&buf)); |
|
1472 |
16853 |
Buf_Destroy(&buf); |
|
1473 |
16853 |
} |
Generated by: GCOVR (Version 3.3) |