Commit | Line | Data |
---|---|---|
9320ab9e | 1 | /* |
ad787160 C |
2 | * Copyright (c) 1988, 1989, 1990, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
ab950546 | 4 | * Copyright (c) 1989 by Berkeley Softworks |
9320ab9e KB |
5 | * All rights reserved. |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Adam de Boor. | |
ab950546 | 9 | * |
ad787160 C |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
9320ab9e KB |
37 | */ |
38 | ||
39 | #ifndef lint | |
ad787160 | 40 | static char sccsid[] = "@(#)var.c 8.1 (Berkeley) 6/6/93"; |
9320ab9e KB |
41 | #endif /* not lint */ |
42 | ||
43 | /*- | |
44 | * var.c -- | |
45 | * Variable-handling functions | |
ab950546 KB |
46 | * |
47 | * Interface: | |
48 | * Var_Set Set the value of a variable in the given | |
49 | * context. The variable is created if it doesn't | |
50 | * yet exist. The value and variable name need not | |
51 | * be preserved. | |
52 | * | |
53 | * Var_Append Append more characters to an existing variable | |
54 | * in the given context. The variable needn't | |
55 | * exist already -- it will be created if it doesn't. | |
56 | * A space is placed between the old value and the | |
57 | * new one. | |
58 | * | |
59 | * Var_Exists See if a variable exists. | |
60 | * | |
61 | * Var_Value Return the value of a variable in a context or | |
62 | * NULL if the variable is undefined. | |
63 | * | |
40a80f0c KB |
64 | * Var_Subst Substitute named variable, or all variables if |
65 | * NULL in a string using | |
ab950546 KB |
66 | * the given context as the top-most one. If the |
67 | * third argument is non-zero, Parse_Error is | |
68 | * called if any variables are undefined. | |
69 | * | |
70 | * Var_Parse Parse a variable expansion from a string and | |
71 | * return the result and the number of characters | |
72 | * consumed. | |
73 | * | |
74 | * Var_Delete Delete a variable in a context. | |
75 | * | |
76 | * Var_Init Initialize this module. | |
77 | * | |
78 | * Debugging: | |
79 | * Var_Dump Print out all variables defined in the given | |
80 | * context. | |
81 | * | |
82 | * XXX: There's a lot of duplication in these functions. | |
83 | */ | |
ab950546 KB |
84 | |
85 | #include <ctype.h> | |
86 | #include "make.h" | |
87 | #include "buf.h" | |
ab950546 KB |
88 | |
89 | /* | |
90 | * This is a harmless return value for Var_Parse that can be used by Var_Subst | |
91 | * to determine if there was an error in parsing -- easier than returning | |
92 | * a flag, as things outside this module don't give a hoot. | |
93 | */ | |
94 | char var_Error[] = ""; | |
95 | ||
96 | /* | |
97 | * Similar to var_Error, but returned when the 'err' flag for Var_Parse is | |
98 | * set false. Why not just use a constant? Well, gcc likes to condense | |
99 | * identical string instances... | |
100 | */ | |
40a80f0c | 101 | static char varNoError[] = ""; |
ab950546 KB |
102 | |
103 | /* | |
104 | * Internally, variables are contained in four different contexts. | |
105 | * 1) the environment. They may not be changed. If an environment | |
106 | * variable is appended-to, the result is placed in the global | |
107 | * context. | |
108 | * 2) the global context. Variables set in the Makefile are located in | |
109 | * the global context. It is the penultimate context searched when | |
110 | * substituting. | |
111 | * 3) the command-line context. All variables set on the command line | |
112 | * are placed in this context. They are UNALTERABLE once placed here. | |
113 | * 4) the local context. Each target has associated with it a context | |
114 | * list. On this list are located the structures describing such | |
115 | * local variables as $(@) and $(*) | |
116 | * The four contexts are searched in the reverse order from which they are | |
117 | * listed. | |
118 | */ | |
119 | GNode *VAR_GLOBAL; /* variables from the makefile */ | |
120 | GNode *VAR_CMD; /* variables defined on the command-line */ | |
121 | ||
122 | #define FIND_CMD 0x1 /* look in VAR_CMD when searching */ | |
123 | #define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ | |
124 | #define FIND_ENV 0x4 /* look in the environment also */ | |
125 | ||
126 | typedef struct Var { | |
127 | char *name; /* the variable's name */ | |
128 | Buffer val; /* its value */ | |
129 | int flags; /* miscellaneous status flags */ | |
130 | #define VAR_IN_USE 1 /* Variable's value currently being used. | |
131 | * Used to avoid recursion */ | |
132 | #define VAR_FROM_ENV 2 /* Variable comes from the environment */ | |
133 | #define VAR_JUNK 4 /* Variable is a junk variable that | |
134 | * should be destroyed when done with | |
135 | * it. Used by Var_Parse for undefined, | |
136 | * modified variables */ | |
137 | } Var; | |
182ca07d | 138 | |
40a80f0c KB |
139 | typedef struct { |
140 | char *lhs; /* String to match */ | |
141 | int leftLen; /* Length of string */ | |
142 | char *rhs; /* Replacement string (w/ &'s removed) */ | |
143 | int rightLen; /* Length of replacement */ | |
144 | int flags; | |
145 | #define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ | |
146 | #define VAR_MATCH_START 2 /* Match at start of word */ | |
147 | #define VAR_MATCH_END 4 /* Match at end of word */ | |
148 | #define VAR_NO_SUB 8 /* Substitution is non-global and already done */ | |
149 | } VarPattern; | |
150 | ||
151 | static int VarCmp __P((Var *, char *)); | |
152 | static Var *VarFind __P((char *, GNode *, int)); | |
153 | static void VarAdd __P((char *, char *, GNode *)); | |
154 | static Boolean VarHead __P((char *, Boolean, Buffer)); | |
155 | static Boolean VarTail __P((char *, Boolean, Buffer)); | |
156 | static Boolean VarSuffix __P((char *, Boolean, Buffer)); | |
157 | static Boolean VarRoot __P((char *, Boolean, Buffer)); | |
158 | static Boolean VarMatch __P((char *, Boolean, Buffer, char *)); | |
159 | static Boolean VarNoMatch __P((char *, Boolean, Buffer, char *)); | |
160 | static Boolean VarSubstitute __P((char *, Boolean, Buffer, VarPattern *)); | |
161 | static char *VarModify __P((char *, Boolean (*modProc )(), ClientData)); | |
162 | static int VarPrintVar __P((Var *)); | |
163 | ||
ab950546 KB |
164 | /*- |
165 | *----------------------------------------------------------------------- | |
166 | * VarCmp -- | |
167 | * See if the given variable matches the named one. Called from | |
168 | * Lst_Find when searching for a variable of a given name. | |
169 | * | |
170 | * Results: | |
171 | * 0 if they match. non-zero otherwise. | |
172 | * | |
173 | * Side Effects: | |
174 | * none | |
175 | *----------------------------------------------------------------------- | |
176 | */ | |
177 | static int | |
178 | VarCmp (v, name) | |
179 | Var *v; /* VAR structure to compare */ | |
180 | char *name; /* name to look for */ | |
181 | { | |
182 | return (strcmp (name, v->name)); | |
183 | } | |
182ca07d | 184 | |
ab950546 KB |
185 | /*- |
186 | *----------------------------------------------------------------------- | |
187 | * VarFind -- | |
188 | * Find the given variable in the given context and any other contexts | |
189 | * indicated. | |
190 | * | |
191 | * Results: | |
192 | * A pointer to the structure describing the desired variable or | |
193 | * NIL if the variable does not exist. | |
194 | * | |
195 | * Side Effects: | |
196 | * None | |
197 | *----------------------------------------------------------------------- | |
198 | */ | |
199 | static Var * | |
200 | VarFind (name, ctxt, flags) | |
201 | char *name; /* name to find */ | |
202 | GNode *ctxt; /* context in which to find it */ | |
203 | int flags; /* FIND_GLOBAL set means to look in the | |
204 | * VAR_GLOBAL context as well. | |
205 | * FIND_CMD set means to look in the VAR_CMD | |
206 | * context also. | |
207 | * FIND_ENV set means to look in the | |
208 | * environment */ | |
209 | { | |
210 | LstNode var; | |
211 | Var *v; | |
212 | ||
dc4b9e67 KB |
213 | /* |
214 | * If the variable name begins with a '.', it could very well be one of | |
215 | * the local ones. We check the name against all the local variables | |
216 | * and substitute the short version in for 'name' if it matches one of | |
217 | * them. | |
218 | */ | |
219 | if (*name == '.' && isupper(name[1])) | |
220 | switch (name[1]) { | |
221 | case 'A': | |
222 | if (!strcmp(name, ".ALLSRC")) | |
223 | name = ALLSRC; | |
224 | if (!strcmp(name, ".ARCHIVE")) | |
225 | name = ARCHIVE; | |
226 | break; | |
227 | case 'I': | |
228 | if (!strcmp(name, ".IMPSRC")) | |
229 | name = IMPSRC; | |
230 | break; | |
231 | case 'M': | |
232 | if (!strcmp(name, ".MEMBER")) | |
233 | name = MEMBER; | |
234 | break; | |
235 | case 'O': | |
236 | if (!strcmp(name, ".OODATE")) | |
237 | name = OODATE; | |
238 | break; | |
239 | case 'P': | |
240 | if (!strcmp(name, ".PREFIX")) | |
241 | name = PREFIX; | |
242 | break; | |
243 | case 'T': | |
244 | if (!strcmp(name, ".TARGET")) | |
245 | name = TARGET; | |
246 | break; | |
247 | } | |
ab950546 KB |
248 | /* |
249 | * First look for the variable in the given context. If it's not there, | |
250 | * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, | |
251 | * depending on the FIND_* flags in 'flags' | |
252 | */ | |
253 | var = Lst_Find (ctxt->context, (ClientData)name, VarCmp); | |
254 | ||
255 | if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { | |
256 | var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp); | |
257 | } | |
258 | if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) && | |
259 | (ctxt != VAR_GLOBAL)) | |
260 | { | |
261 | var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); | |
262 | } | |
263 | if ((var == NILLNODE) && (flags & FIND_ENV)) { | |
264 | char *env; | |
265 | ||
266 | if ((env = getenv (name)) != NULL) { | |
267 | /* | |
268 | * If the variable is found in the environment, we only duplicate | |
269 | * its value (since eVarVal was allocated on the stack). The name | |
270 | * doesn't need duplication since it's always in the environment | |
271 | */ | |
272 | int len; | |
273 | ||
54527040 | 274 | v = (Var *) emalloc(sizeof(Var)); |
ab950546 KB |
275 | v->name = name; |
276 | ||
277 | len = strlen(env); | |
278 | ||
279 | v->val = Buf_Init(len); | |
280 | Buf_AddBytes(v->val, len, (Byte *)env); | |
281 | ||
282 | v->flags = VAR_FROM_ENV; | |
283 | return (v); | |
284 | } else if (checkEnvFirst && (flags & FIND_GLOBAL) && | |
285 | (ctxt != VAR_GLOBAL)) | |
286 | { | |
287 | var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); | |
288 | if (var == NILLNODE) { | |
289 | return ((Var *) NIL); | |
290 | } else { | |
291 | return ((Var *)Lst_Datum(var)); | |
292 | } | |
293 | } else { | |
294 | return((Var *)NIL); | |
295 | } | |
296 | } else if (var == NILLNODE) { | |
297 | return ((Var *) NIL); | |
298 | } else { | |
299 | return ((Var *) Lst_Datum (var)); | |
300 | } | |
301 | } | |
182ca07d | 302 | |
ab950546 KB |
303 | /*- |
304 | *----------------------------------------------------------------------- | |
305 | * VarAdd -- | |
306 | * Add a new variable of name name and value val to the given context | |
307 | * | |
308 | * Results: | |
309 | * None | |
310 | * | |
311 | * Side Effects: | |
312 | * The new variable is placed at the front of the given context | |
313 | * The name and val arguments are duplicated so they may | |
314 | * safely be freed. | |
315 | *----------------------------------------------------------------------- | |
316 | */ | |
40a80f0c | 317 | static void |
ab950546 KB |
318 | VarAdd (name, val, ctxt) |
319 | char *name; /* name of variable to add */ | |
320 | char *val; /* value to set it to */ | |
321 | GNode *ctxt; /* context in which to set it */ | |
322 | { | |
323 | register Var *v; | |
324 | int len; | |
325 | ||
54527040 | 326 | v = (Var *) emalloc (sizeof (Var)); |
ab950546 | 327 | |
182ca07d | 328 | v->name = strdup (name); |
ab950546 | 329 | |
40a80f0c | 330 | len = val ? strlen(val) : 0; |
ab950546 KB |
331 | v->val = Buf_Init(len+1); |
332 | Buf_AddBytes(v->val, len, (Byte *)val); | |
333 | ||
334 | v->flags = 0; | |
335 | ||
336 | (void) Lst_AtFront (ctxt->context, (ClientData)v); | |
337 | if (DEBUG(VAR)) { | |
338 | printf("%s:%s = %s\n", ctxt->name, name, val); | |
339 | } | |
340 | } | |
182ca07d | 341 | |
ab950546 KB |
342 | /*- |
343 | *----------------------------------------------------------------------- | |
344 | * Var_Delete -- | |
345 | * Remove a variable from a context. | |
346 | * | |
347 | * Results: | |
348 | * None. | |
349 | * | |
350 | * Side Effects: | |
351 | * The Var structure is removed and freed. | |
352 | * | |
353 | *----------------------------------------------------------------------- | |
354 | */ | |
355 | void | |
356 | Var_Delete(name, ctxt) | |
357 | char *name; | |
358 | GNode *ctxt; | |
359 | { | |
360 | LstNode ln; | |
361 | ||
362 | if (DEBUG(VAR)) { | |
363 | printf("%s:delete %s\n", ctxt->name, name); | |
364 | } | |
365 | ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp); | |
366 | if (ln != NILLNODE) { | |
367 | register Var *v; | |
368 | ||
369 | v = (Var *)Lst_Datum(ln); | |
370 | Lst_Remove(ctxt->context, ln); | |
371 | Buf_Destroy(v->val, TRUE); | |
372 | free(v->name); | |
373 | free((char *)v); | |
374 | } | |
375 | } | |
182ca07d | 376 | |
ab950546 KB |
377 | /*- |
378 | *----------------------------------------------------------------------- | |
379 | * Var_Set -- | |
380 | * Set the variable name to the value val in the given context. | |
381 | * | |
382 | * Results: | |
383 | * None. | |
384 | * | |
385 | * Side Effects: | |
386 | * If the variable doesn't yet exist, a new record is created for it. | |
387 | * Else the old value is freed and the new one stuck in its place | |
388 | * | |
389 | * Notes: | |
390 | * The variable is searched for only in its context before being | |
391 | * created in that context. I.e. if the context is VAR_GLOBAL, | |
392 | * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only | |
393 | * VAR_CMD->context is searched. This is done to avoid the literally | |
394 | * thousands of unnecessary strcmp's that used to be done to | |
395 | * set, say, $(@) or $(<). | |
396 | *----------------------------------------------------------------------- | |
397 | */ | |
398 | void | |
399 | Var_Set (name, val, ctxt) | |
400 | char *name; /* name of variable to set */ | |
401 | char *val; /* value to give to the variable */ | |
402 | GNode *ctxt; /* context in which to set it */ | |
403 | { | |
404 | register Var *v; | |
405 | ||
406 | /* | |
407 | * We only look for a variable in the given context since anything set | |
408 | * here will override anything in a lower context, so there's not much | |
409 | * point in searching them all just to save a bit of memory... | |
410 | */ | |
411 | v = VarFind (name, ctxt, 0); | |
412 | if (v == (Var *) NIL) { | |
413 | VarAdd (name, val, ctxt); | |
414 | } else { | |
415 | Buf_Discard(v->val, Buf_Size(v->val)); | |
416 | Buf_AddBytes(v->val, strlen(val), (Byte *)val); | |
417 | ||
418 | if (DEBUG(VAR)) { | |
419 | printf("%s:%s = %s\n", ctxt->name, name, val); | |
420 | } | |
421 | } | |
422 | /* | |
423 | * Any variables given on the command line are automatically exported | |
424 | * to the environment (as per POSIX standard) | |
425 | */ | |
426 | if (ctxt == VAR_CMD) { | |
f022bac1 | 427 | setenv(name, val, 1); |
ab950546 KB |
428 | } |
429 | } | |
182ca07d | 430 | |
ab950546 KB |
431 | /*- |
432 | *----------------------------------------------------------------------- | |
433 | * Var_Append -- | |
434 | * The variable of the given name has the given value appended to it in | |
435 | * the given context. | |
436 | * | |
437 | * Results: | |
438 | * None | |
439 | * | |
440 | * Side Effects: | |
441 | * If the variable doesn't exist, it is created. Else the strings | |
442 | * are concatenated (with a space in between). | |
443 | * | |
444 | * Notes: | |
445 | * Only if the variable is being sought in the global context is the | |
446 | * environment searched. | |
447 | * XXX: Knows its calling circumstances in that if called with ctxt | |
448 | * an actual target, it will only search that context since only | |
449 | * a local variable could be being appended to. This is actually | |
450 | * a big win and must be tolerated. | |
451 | *----------------------------------------------------------------------- | |
452 | */ | |
453 | void | |
454 | Var_Append (name, val, ctxt) | |
455 | char *name; /* Name of variable to modify */ | |
456 | char *val; /* String to append to it */ | |
457 | GNode *ctxt; /* Context in which this should occur */ | |
458 | { | |
459 | register Var *v; | |
ab950546 KB |
460 | |
461 | v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); | |
462 | ||
463 | if (v == (Var *) NIL) { | |
464 | VarAdd (name, val, ctxt); | |
465 | } else { | |
466 | Buf_AddByte(v->val, (Byte)' '); | |
467 | Buf_AddBytes(v->val, strlen(val), (Byte *)val); | |
468 | ||
469 | if (DEBUG(VAR)) { | |
470 | printf("%s:%s = %s\n", ctxt->name, name, | |
471 | Buf_GetAll(v->val, (int *)NULL)); | |
472 | } | |
473 | ||
474 | if (v->flags & VAR_FROM_ENV) { | |
475 | /* | |
476 | * If the original variable came from the environment, we | |
477 | * have to install it in the global context (we could place | |
478 | * it in the environment, but then we should provide a way to | |
479 | * export other variables...) | |
480 | */ | |
481 | v->flags &= ~VAR_FROM_ENV; | |
482 | Lst_AtFront(ctxt->context, (ClientData)v); | |
483 | } | |
484 | } | |
485 | } | |
182ca07d | 486 | |
ab950546 KB |
487 | /*- |
488 | *----------------------------------------------------------------------- | |
489 | * Var_Exists -- | |
490 | * See if the given variable exists. | |
491 | * | |
492 | * Results: | |
493 | * TRUE if it does, FALSE if it doesn't | |
494 | * | |
495 | * Side Effects: | |
496 | * None. | |
497 | * | |
498 | *----------------------------------------------------------------------- | |
499 | */ | |
500 | Boolean | |
501 | Var_Exists(name, ctxt) | |
502 | char *name; /* Variable to find */ | |
503 | GNode *ctxt; /* Context in which to start search */ | |
504 | { | |
505 | Var *v; | |
506 | ||
507 | v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); | |
508 | ||
509 | if (v == (Var *)NIL) { | |
510 | return(FALSE); | |
511 | } else if (v->flags & VAR_FROM_ENV) { | |
512 | Buf_Destroy(v->val, TRUE); | |
513 | free((char *)v); | |
514 | } | |
515 | return(TRUE); | |
516 | } | |
182ca07d | 517 | |
ab950546 KB |
518 | /*- |
519 | *----------------------------------------------------------------------- | |
520 | * Var_Value -- | |
521 | * Return the value of the named variable in the given context | |
522 | * | |
523 | * Results: | |
524 | * The value if the variable exists, NULL if it doesn't | |
525 | * | |
526 | * Side Effects: | |
527 | * None | |
528 | *----------------------------------------------------------------------- | |
529 | */ | |
530 | char * | |
531 | Var_Value (name, ctxt) | |
532 | char *name; /* name to find */ | |
533 | GNode *ctxt; /* context in which to search for it */ | |
534 | { | |
535 | Var *v; | |
536 | ||
537 | v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); | |
538 | if (v != (Var *) NIL) { | |
539 | return ((char *)Buf_GetAll(v->val, (int *)NULL)); | |
540 | } else { | |
541 | return ((char *) NULL); | |
542 | } | |
543 | } | |
182ca07d | 544 | |
ab950546 KB |
545 | /*- |
546 | *----------------------------------------------------------------------- | |
547 | * VarHead -- | |
548 | * Remove the tail of the given word and place the result in the given | |
549 | * buffer. | |
550 | * | |
551 | * Results: | |
552 | * TRUE if characters were added to the buffer (a space needs to be | |
553 | * added to the buffer before the next word). | |
554 | * | |
555 | * Side Effects: | |
556 | * The trimmed word is added to the buffer. | |
557 | * | |
558 | *----------------------------------------------------------------------- | |
559 | */ | |
560 | static Boolean | |
561 | VarHead (word, addSpace, buf) | |
562 | char *word; /* Word to trim */ | |
563 | Boolean addSpace; /* True if need to add a space to the buffer | |
564 | * before sticking in the head */ | |
565 | Buffer buf; /* Buffer in which to store it */ | |
566 | { | |
567 | register char *slash; | |
568 | ||
40a80f0c | 569 | slash = strrchr (word, '/'); |
ab950546 KB |
570 | if (slash != (char *)NULL) { |
571 | if (addSpace) { | |
572 | Buf_AddByte (buf, (Byte)' '); | |
573 | } | |
574 | *slash = '\0'; | |
575 | Buf_AddBytes (buf, strlen (word), (Byte *)word); | |
576 | *slash = '/'; | |
577 | return (TRUE); | |
578 | } else { | |
579 | /* | |
580 | * If no directory part, give . (q.v. the POSIX standard) | |
581 | */ | |
582 | if (addSpace) { | |
583 | Buf_AddBytes(buf, 2, (Byte *)" ."); | |
584 | } else { | |
585 | Buf_AddByte(buf, (Byte)'.'); | |
586 | } | |
587 | return(TRUE); | |
588 | } | |
589 | } | |
182ca07d | 590 | |
ab950546 KB |
591 | /*- |
592 | *----------------------------------------------------------------------- | |
593 | * VarTail -- | |
594 | * Remove the head of the given word and place the result in the given | |
595 | * buffer. | |
596 | * | |
597 | * Results: | |
598 | * TRUE if characters were added to the buffer (a space needs to be | |
599 | * added to the buffer before the next word). | |
600 | * | |
601 | * Side Effects: | |
602 | * The trimmed word is added to the buffer. | |
603 | * | |
604 | *----------------------------------------------------------------------- | |
605 | */ | |
606 | static Boolean | |
607 | VarTail (word, addSpace, buf) | |
608 | char *word; /* Word to trim */ | |
609 | Boolean addSpace; /* TRUE if need to stick a space in the | |
610 | * buffer before adding the tail */ | |
611 | Buffer buf; /* Buffer in which to store it */ | |
612 | { | |
613 | register char *slash; | |
614 | ||
615 | if (addSpace) { | |
616 | Buf_AddByte (buf, (Byte)' '); | |
617 | } | |
618 | ||
40a80f0c | 619 | slash = strrchr (word, '/'); |
ab950546 KB |
620 | if (slash != (char *)NULL) { |
621 | *slash++ = '\0'; | |
622 | Buf_AddBytes (buf, strlen(slash), (Byte *)slash); | |
623 | slash[-1] = '/'; | |
624 | } else { | |
625 | Buf_AddBytes (buf, strlen(word), (Byte *)word); | |
626 | } | |
627 | return (TRUE); | |
628 | } | |
182ca07d | 629 | |
ab950546 KB |
630 | /*- |
631 | *----------------------------------------------------------------------- | |
632 | * VarSuffix -- | |
633 | * Place the suffix of the given word in the given buffer. | |
634 | * | |
635 | * Results: | |
636 | * TRUE if characters were added to the buffer (a space needs to be | |
637 | * added to the buffer before the next word). | |
638 | * | |
639 | * Side Effects: | |
640 | * The suffix from the word is placed in the buffer. | |
641 | * | |
642 | *----------------------------------------------------------------------- | |
643 | */ | |
644 | static Boolean | |
645 | VarSuffix (word, addSpace, buf) | |
646 | char *word; /* Word to trim */ | |
647 | Boolean addSpace; /* TRUE if need to add a space before placing | |
648 | * the suffix in the buffer */ | |
649 | Buffer buf; /* Buffer in which to store it */ | |
650 | { | |
651 | register char *dot; | |
652 | ||
40a80f0c | 653 | dot = strrchr (word, '.'); |
ab950546 KB |
654 | if (dot != (char *)NULL) { |
655 | if (addSpace) { | |
656 | Buf_AddByte (buf, (Byte)' '); | |
657 | } | |
658 | *dot++ = '\0'; | |
659 | Buf_AddBytes (buf, strlen (dot), (Byte *)dot); | |
660 | dot[-1] = '.'; | |
661 | return (TRUE); | |
662 | } else { | |
663 | return (addSpace); | |
664 | } | |
665 | } | |
182ca07d | 666 | |
ab950546 KB |
667 | /*- |
668 | *----------------------------------------------------------------------- | |
669 | * VarRoot -- | |
670 | * Remove the suffix of the given word and place the result in the | |
671 | * buffer. | |
672 | * | |
673 | * Results: | |
674 | * TRUE if characters were added to the buffer (a space needs to be | |
675 | * added to the buffer before the next word). | |
676 | * | |
677 | * Side Effects: | |
678 | * The trimmed word is added to the buffer. | |
679 | * | |
680 | *----------------------------------------------------------------------- | |
681 | */ | |
682 | static Boolean | |
683 | VarRoot (word, addSpace, buf) | |
684 | char *word; /* Word to trim */ | |
685 | Boolean addSpace; /* TRUE if need to add a space to the buffer | |
686 | * before placing the root in it */ | |
687 | Buffer buf; /* Buffer in which to store it */ | |
688 | { | |
689 | register char *dot; | |
690 | ||
691 | if (addSpace) { | |
692 | Buf_AddByte (buf, (Byte)' '); | |
693 | } | |
694 | ||
40a80f0c | 695 | dot = strrchr (word, '.'); |
ab950546 KB |
696 | if (dot != (char *)NULL) { |
697 | *dot = '\0'; | |
698 | Buf_AddBytes (buf, strlen (word), (Byte *)word); | |
699 | *dot = '.'; | |
700 | } else { | |
701 | Buf_AddBytes (buf, strlen(word), (Byte *)word); | |
702 | } | |
703 | return (TRUE); | |
704 | } | |
182ca07d | 705 | |
ab950546 KB |
706 | /*- |
707 | *----------------------------------------------------------------------- | |
708 | * VarMatch -- | |
709 | * Place the word in the buffer if it matches the given pattern. | |
710 | * Callback function for VarModify to implement the :M modifier. | |
711 | * | |
712 | * Results: | |
713 | * TRUE if a space should be placed in the buffer before the next | |
714 | * word. | |
715 | * | |
716 | * Side Effects: | |
717 | * The word may be copied to the buffer. | |
718 | * | |
719 | *----------------------------------------------------------------------- | |
720 | */ | |
721 | static Boolean | |
722 | VarMatch (word, addSpace, buf, pattern) | |
723 | char *word; /* Word to examine */ | |
724 | Boolean addSpace; /* TRUE if need to add a space to the | |
725 | * buffer before adding the word, if it | |
726 | * matches */ | |
727 | Buffer buf; /* Buffer in which to store it */ | |
728 | char *pattern; /* Pattern the word must match */ | |
729 | { | |
730 | if (Str_Match(word, pattern)) { | |
731 | if (addSpace) { | |
732 | Buf_AddByte(buf, (Byte)' '); | |
733 | } | |
734 | addSpace = TRUE; | |
735 | Buf_AddBytes(buf, strlen(word), (Byte *)word); | |
736 | } | |
737 | return(addSpace); | |
738 | } | |
182ca07d | 739 | |
ab950546 KB |
740 | /*- |
741 | *----------------------------------------------------------------------- | |
742 | * VarNoMatch -- | |
743 | * Place the word in the buffer if it doesn't match the given pattern. | |
744 | * Callback function for VarModify to implement the :N modifier. | |
745 | * | |
746 | * Results: | |
747 | * TRUE if a space should be placed in the buffer before the next | |
748 | * word. | |
749 | * | |
750 | * Side Effects: | |
751 | * The word may be copied to the buffer. | |
752 | * | |
753 | *----------------------------------------------------------------------- | |
754 | */ | |
755 | static Boolean | |
756 | VarNoMatch (word, addSpace, buf, pattern) | |
757 | char *word; /* Word to examine */ | |
758 | Boolean addSpace; /* TRUE if need to add a space to the | |
759 | * buffer before adding the word, if it | |
760 | * matches */ | |
761 | Buffer buf; /* Buffer in which to store it */ | |
762 | char *pattern; /* Pattern the word must match */ | |
763 | { | |
764 | if (!Str_Match(word, pattern)) { | |
765 | if (addSpace) { | |
766 | Buf_AddByte(buf, (Byte)' '); | |
767 | } | |
768 | addSpace = TRUE; | |
769 | Buf_AddBytes(buf, strlen(word), (Byte *)word); | |
770 | } | |
771 | return(addSpace); | |
772 | } | |
182ca07d | 773 | |
ab950546 KB |
774 | |
775 | /*- | |
776 | *----------------------------------------------------------------------- | |
777 | * VarSubstitute -- | |
778 | * Perform a string-substitution on the given word, placing the | |
779 | * result in the passed buffer. | |
780 | * | |
781 | * Results: | |
782 | * TRUE if a space is needed before more characters are added. | |
783 | * | |
784 | * Side Effects: | |
785 | * None. | |
786 | * | |
787 | *----------------------------------------------------------------------- | |
788 | */ | |
789 | static Boolean | |
790 | VarSubstitute (word, addSpace, buf, pattern) | |
791 | char *word; /* Word to modify */ | |
792 | Boolean addSpace; /* True if space should be added before | |
793 | * other characters */ | |
794 | Buffer buf; /* Buffer for result */ | |
795 | register VarPattern *pattern; /* Pattern for substitution */ | |
796 | { | |
797 | register int wordLen; /* Length of word */ | |
798 | register char *cp; /* General pointer */ | |
799 | ||
800 | wordLen = strlen(word); | |
801 | if ((pattern->flags & VAR_NO_SUB) == 0) { | |
802 | /* | |
803 | * Still substituting -- break it down into simple anchored cases | |
804 | * and if none of them fits, perform the general substitution case. | |
805 | */ | |
806 | if ((pattern->flags & VAR_MATCH_START) && | |
807 | (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { | |
808 | /* | |
809 | * Anchored at start and beginning of word matches pattern | |
810 | */ | |
811 | if ((pattern->flags & VAR_MATCH_END) && | |
812 | (wordLen == pattern->leftLen)) { | |
813 | /* | |
814 | * Also anchored at end and matches to the end (word | |
815 | * is same length as pattern) add space and rhs only | |
816 | * if rhs is non-null. | |
817 | */ | |
818 | if (pattern->rightLen != 0) { | |
819 | if (addSpace) { | |
820 | Buf_AddByte(buf, (Byte)' '); | |
821 | } | |
822 | addSpace = TRUE; | |
823 | Buf_AddBytes(buf, pattern->rightLen, | |
824 | (Byte *)pattern->rhs); | |
825 | } | |
826 | } else if (pattern->flags & VAR_MATCH_END) { | |
827 | /* | |
828 | * Doesn't match to end -- copy word wholesale | |
829 | */ | |
830 | goto nosub; | |
831 | } else { | |
832 | /* | |
833 | * Matches at start but need to copy in trailing characters | |
834 | */ | |
835 | if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ | |
836 | if (addSpace) { | |
837 | Buf_AddByte(buf, (Byte)' '); | |
838 | } | |
839 | addSpace = TRUE; | |
840 | } | |
841 | Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); | |
842 | Buf_AddBytes(buf, wordLen - pattern->leftLen, | |
843 | (Byte *)(word + pattern->leftLen)); | |
844 | } | |
845 | } else if (pattern->flags & VAR_MATCH_START) { | |
846 | /* | |
847 | * Had to match at start of word and didn't -- copy whole word. | |
848 | */ | |
849 | goto nosub; | |
850 | } else if (pattern->flags & VAR_MATCH_END) { | |
851 | /* | |
852 | * Anchored at end, Find only place match could occur (leftLen | |
853 | * characters from the end of the word) and see if it does. Note | |
854 | * that because the $ will be left at the end of the lhs, we have | |
855 | * to use strncmp. | |
856 | */ | |
857 | cp = word + (wordLen - pattern->leftLen); | |
858 | if ((cp >= word) && | |
859 | (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { | |
860 | /* | |
861 | * Match found. If we will place characters in the buffer, | |
862 | * add a space before hand as indicated by addSpace, then | |
863 | * stuff in the initial, unmatched part of the word followed | |
864 | * by the right-hand-side. | |
865 | */ | |
866 | if (((cp - word) + pattern->rightLen) != 0) { | |
867 | if (addSpace) { | |
868 | Buf_AddByte(buf, (Byte)' '); | |
869 | } | |
870 | addSpace = TRUE; | |
871 | } | |
872 | Buf_AddBytes(buf, cp - word, (Byte *)word); | |
873 | Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); | |
874 | } else { | |
875 | /* | |
876 | * Had to match at end and didn't. Copy entire word. | |
877 | */ | |
878 | goto nosub; | |
879 | } | |
880 | } else { | |
881 | /* | |
882 | * Pattern is unanchored: search for the pattern in the word using | |
883 | * String_FindSubstring, copying unmatched portions and the | |
884 | * right-hand-side for each match found, handling non-global | |
885 | * subsititutions correctly, etc. When the loop is done, any | |
886 | * remaining part of the word (word and wordLen are adjusted | |
887 | * accordingly through the loop) is copied straight into the | |
888 | * buffer. | |
889 | * addSpace is set FALSE as soon as a space is added to the | |
890 | * buffer. | |
891 | */ | |
892 | register Boolean done; | |
893 | int origSize; | |
894 | ||
895 | done = FALSE; | |
896 | origSize = Buf_Size(buf); | |
897 | while (!done) { | |
898 | cp = Str_FindSubstring(word, pattern->lhs); | |
899 | if (cp != (char *)NULL) { | |
900 | if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ | |
901 | Buf_AddByte(buf, (Byte)' '); | |
902 | addSpace = FALSE; | |
903 | } | |
904 | Buf_AddBytes(buf, cp-word, (Byte *)word); | |
905 | Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); | |
906 | wordLen -= (cp - word) + pattern->leftLen; | |
907 | word = cp + pattern->leftLen; | |
908 | if (wordLen == 0) { | |
909 | done = TRUE; | |
910 | } | |
911 | if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { | |
912 | done = TRUE; | |
913 | pattern->flags |= VAR_NO_SUB; | |
914 | } | |
915 | } else { | |
916 | done = TRUE; | |
917 | } | |
918 | } | |
919 | if (wordLen != 0) { | |
920 | if (addSpace) { | |
921 | Buf_AddByte(buf, (Byte)' '); | |
922 | } | |
923 | Buf_AddBytes(buf, wordLen, (Byte *)word); | |
924 | } | |
925 | /* | |
926 | * If added characters to the buffer, need to add a space | |
927 | * before we add any more. If we didn't add any, just return | |
928 | * the previous value of addSpace. | |
929 | */ | |
930 | return ((Buf_Size(buf) != origSize) || addSpace); | |
931 | } | |
932 | /* | |
933 | * Common code for anchored substitutions: if performed a substitution | |
934 | * and it's not supposed to be global, mark the pattern as requiring | |
935 | * no more substitutions. addSpace was set TRUE if characters were | |
936 | * added to the buffer. | |
937 | */ | |
938 | if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { | |
939 | pattern->flags |= VAR_NO_SUB; | |
940 | } | |
941 | return (addSpace); | |
942 | } | |
943 | nosub: | |
944 | if (addSpace) { | |
945 | Buf_AddByte(buf, (Byte)' '); | |
946 | } | |
947 | Buf_AddBytes(buf, wordLen, (Byte *)word); | |
948 | return(TRUE); | |
949 | } | |
182ca07d | 950 | |
ab950546 KB |
951 | /*- |
952 | *----------------------------------------------------------------------- | |
953 | * VarModify -- | |
954 | * Modify each of the words of the passed string using the given | |
955 | * function. Used to implement all modifiers. | |
956 | * | |
957 | * Results: | |
958 | * A string of all the words modified appropriately. | |
959 | * | |
960 | * Side Effects: | |
961 | * None. | |
962 | * | |
963 | *----------------------------------------------------------------------- | |
964 | */ | |
965 | static char * | |
966 | VarModify (str, modProc, datum) | |
967 | char *str; /* String whose words should be trimmed */ | |
968 | Boolean (*modProc)(); /* Function to use to modify them */ | |
969 | ClientData datum; /* Datum to pass it */ | |
970 | { | |
971 | Buffer buf; /* Buffer for the new string */ | |
972 | register char *cp; /* Pointer to end of current word */ | |
973 | char endc; /* Character that ended the word */ | |
974 | Boolean addSpace; /* TRUE if need to add a space to the | |
975 | * buffer before adding the trimmed | |
976 | * word */ | |
977 | ||
978 | buf = Buf_Init (0); | |
979 | cp = str; | |
980 | addSpace = FALSE; | |
981 | ||
40a80f0c | 982 | for (;;) { |
ab950546 KB |
983 | /* |
984 | * Skip to next word and place cp at its end. | |
985 | */ | |
986 | while (isspace (*str)) { | |
987 | str++; | |
988 | } | |
40a80f0c KB |
989 | for (cp = str; *cp != '\0' && !isspace (*cp); cp++) |
990 | continue; | |
ab950546 KB |
991 | if (cp == str) { |
992 | /* | |
993 | * If we didn't go anywhere, we must be done! | |
994 | */ | |
995 | Buf_AddByte (buf, '\0'); | |
996 | str = (char *)Buf_GetAll (buf, (int *)NULL); | |
997 | Buf_Destroy (buf, FALSE); | |
998 | return (str); | |
999 | } | |
1000 | /* | |
1001 | * Nuke terminating character, but save it in endc b/c if str was | |
1002 | * some variable's value, it would not be good to screw it | |
1003 | * over... | |
1004 | */ | |
1005 | endc = *cp; | |
1006 | *cp = '\0'; | |
1007 | ||
1008 | addSpace = (* modProc) (str, addSpace, buf, datum); | |
1009 | ||
1010 | if (endc) { | |
1011 | *cp++ = endc; | |
1012 | } | |
1013 | str = cp; | |
1014 | } | |
1015 | } | |
182ca07d | 1016 | |
ab950546 KB |
1017 | /*- |
1018 | *----------------------------------------------------------------------- | |
1019 | * Var_Parse -- | |
1020 | * Given the start of a variable invocation, extract the variable | |
1021 | * name and find its value, then modify it according to the | |
1022 | * specification. | |
1023 | * | |
1024 | * Results: | |
1025 | * The (possibly-modified) value of the variable or var_Error if the | |
1026 | * specification is invalid. The length of the specification is | |
1027 | * placed in *lengthPtr (for invalid specifications, this is just | |
1028 | * 2...?). | |
1029 | * A Boolean in *freePtr telling whether the returned string should | |
1030 | * be freed by the caller. | |
1031 | * | |
1032 | * Side Effects: | |
1033 | * None. | |
1034 | * | |
1035 | *----------------------------------------------------------------------- | |
1036 | */ | |
1037 | char * | |
1038 | Var_Parse (str, ctxt, err, lengthPtr, freePtr) | |
1039 | char *str; /* The string to parse */ | |
1040 | GNode *ctxt; /* The context for the variable */ | |
1041 | Boolean err; /* TRUE if undefined variables are an error */ | |
1042 | int *lengthPtr; /* OUT: The length of the specification */ | |
1043 | Boolean *freePtr; /* OUT: TRUE if caller should free result */ | |
1044 | { | |
1045 | register char *tstr; /* Pointer into str */ | |
1046 | Var *v; /* Variable in invocation */ | |
1047 | register char *cp; /* Secondary pointer into str (place marker | |
1048 | * for tstr) */ | |
1049 | Boolean haveModifier;/* TRUE if have modifiers for the variable */ | |
1050 | register char endc; /* Ending character when variable in parens | |
1051 | * or braces */ | |
1052 | char *start; | |
1053 | Boolean dynamic; /* TRUE if the variable is local and we're | |
1054 | * expanding it in a non-local context. This | |
1055 | * is done to support dynamic sources. The | |
1056 | * result is just the invocation, unaltered */ | |
1057 | ||
1058 | *freePtr = FALSE; | |
1059 | dynamic = FALSE; | |
1060 | start = str; | |
1061 | ||
1062 | if (str[1] != '(' && str[1] != '{') { | |
1063 | /* | |
1064 | * If it's not bounded by braces of some sort, life is much simpler. | |
1065 | * We just need to check for the first character and return the | |
1066 | * value if it exists. | |
1067 | */ | |
1068 | char name[2]; | |
1069 | ||
1070 | name[0] = str[1]; | |
1071 | name[1] = '\0'; | |
1072 | ||
1073 | v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); | |
1074 | if (v == (Var *)NIL) { | |
1075 | *lengthPtr = 2; | |
1076 | ||
1077 | if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { | |
1078 | /* | |
1079 | * If substituting a local variable in a non-local context, | |
1080 | * assume it's for dynamic source stuff. We have to handle | |
1081 | * this specially and return the longhand for the variable | |
1082 | * with the dollar sign escaped so it makes it back to the | |
1083 | * caller. Only four of the local variables are treated | |
1084 | * specially as they are the only four that will be set | |
1085 | * when dynamic sources are expanded. | |
1086 | */ | |
1087 | switch (str[1]) { | |
1088 | case '@': | |
1089 | return("$(.TARGET)"); | |
1090 | case '%': | |
1091 | return("$(.ARCHIVE)"); | |
1092 | case '*': | |
1093 | return("$(.PREFIX)"); | |
1094 | case '!': | |
1095 | return("$(.MEMBER)"); | |
1096 | } | |
1097 | } | |
1098 | /* | |
1099 | * Error | |
1100 | */ | |
1101 | return (err ? var_Error : varNoError); | |
1102 | } else { | |
1103 | haveModifier = FALSE; | |
1104 | tstr = &str[1]; | |
1105 | endc = str[1]; | |
1106 | } | |
1107 | } else { | |
1108 | endc = str[1] == '(' ? ')' : '}'; | |
1109 | ||
1110 | /* | |
1111 | * Skip to the end character or a colon, whichever comes first. | |
1112 | */ | |
1113 | for (tstr = str + 2; | |
1114 | *tstr != '\0' && *tstr != endc && *tstr != ':'; | |
1115 | tstr++) | |
1116 | { | |
1117 | continue; | |
1118 | } | |
1119 | if (*tstr == ':') { | |
1120 | haveModifier = TRUE; | |
1121 | } else if (*tstr != '\0') { | |
1122 | haveModifier = FALSE; | |
1123 | } else { | |
1124 | /* | |
1125 | * If we never did find the end character, return NULL | |
1126 | * right now, setting the length to be the distance to | |
1127 | * the end of the string, since that's what make does. | |
1128 | */ | |
1129 | *lengthPtr = tstr - str; | |
1130 | return (var_Error); | |
1131 | } | |
1132 | *tstr = '\0'; | |
1133 | ||
1134 | v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); | |
1135 | if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && | |
1136 | ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) | |
1137 | { | |
1138 | /* | |
1139 | * Check for bogus D and F forms of local variables since we're | |
1140 | * in a local context and the name is the right length. | |
1141 | */ | |
1142 | switch(str[2]) { | |
1143 | case '@': | |
1144 | case '%': | |
1145 | case '*': | |
1146 | case '!': | |
1147 | case '>': | |
1148 | case '<': | |
1149 | { | |
1150 | char vname[2]; | |
1151 | char *val; | |
1152 | ||
1153 | /* | |
1154 | * Well, it's local -- go look for it. | |
1155 | */ | |
1156 | vname[0] = str[2]; | |
1157 | vname[1] = '\0'; | |
1158 | v = VarFind(vname, ctxt, 0); | |
1159 | ||
1160 | if (v != (Var *)NIL) { | |
1161 | /* | |
1162 | * No need for nested expansion or anything, as we're | |
1163 | * the only one who sets these things and we sure don't | |
1164 | * but nested invocations in them... | |
1165 | */ | |
1166 | val = (char *)Buf_GetAll(v->val, (int *)NULL); | |
1167 | ||
1168 | if (str[3] == 'D') { | |
1169 | val = VarModify(val, VarHead, (ClientData)0); | |
1170 | } else { | |
1171 | val = VarModify(val, VarTail, (ClientData)0); | |
1172 | } | |
1173 | /* | |
1174 | * Resulting string is dynamically allocated, so | |
1175 | * tell caller to free it. | |
1176 | */ | |
1177 | *freePtr = TRUE; | |
1178 | *lengthPtr = tstr-start+1; | |
1179 | *tstr = endc; | |
1180 | return(val); | |
1181 | } | |
1182 | break; | |
1183 | } | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | if (v == (Var *)NIL) { | |
1188 | if ((((tstr-str) == 3) || | |
1189 | ((((tstr-str) == 4) && (str[3] == 'F' || | |
1190 | str[3] == 'D')))) && | |
1191 | ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) | |
1192 | { | |
1193 | /* | |
1194 | * If substituting a local variable in a non-local context, | |
1195 | * assume it's for dynamic source stuff. We have to handle | |
1196 | * this specially and return the longhand for the variable | |
1197 | * with the dollar sign escaped so it makes it back to the | |
1198 | * caller. Only four of the local variables are treated | |
1199 | * specially as they are the only four that will be set | |
1200 | * when dynamic sources are expanded. | |
1201 | */ | |
1202 | switch (str[2]) { | |
1203 | case '@': | |
1204 | case '%': | |
1205 | case '*': | |
1206 | case '!': | |
1207 | dynamic = TRUE; | |
1208 | break; | |
1209 | } | |
1210 | } else if (((tstr-str) > 4) && (str[2] == '.') && | |
1211 | isupper(str[3]) && | |
1212 | ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) | |
1213 | { | |
1214 | int len; | |
1215 | ||
1216 | len = (tstr-str) - 3; | |
1217 | if ((strncmp(str+2, ".TARGET", len) == 0) || | |
1218 | (strncmp(str+2, ".ARCHIVE", len) == 0) || | |
1219 | (strncmp(str+2, ".PREFIX", len) == 0) || | |
1220 | (strncmp(str+2, ".MEMBER", len) == 0)) | |
1221 | { | |
1222 | dynamic = TRUE; | |
1223 | } | |
1224 | } | |
1225 | ||
1226 | if (!haveModifier) { | |
1227 | /* | |
1228 | * No modifiers -- have specification length so we can return | |
1229 | * now. | |
1230 | */ | |
1231 | *lengthPtr = tstr - start + 1; | |
1232 | *tstr = endc; | |
1233 | if (dynamic) { | |
54527040 | 1234 | str = emalloc(*lengthPtr + 1); |
ab950546 KB |
1235 | strncpy(str, start, *lengthPtr); |
1236 | str[*lengthPtr] = '\0'; | |
1237 | *freePtr = TRUE; | |
1238 | return(str); | |
1239 | } else { | |
1240 | return (err ? var_Error : varNoError); | |
1241 | } | |
1242 | } else { | |
1243 | /* | |
1244 | * Still need to get to the end of the variable specification, | |
1245 | * so kludge up a Var structure for the modifications | |
1246 | */ | |
54527040 | 1247 | v = (Var *) emalloc(sizeof(Var)); |
ab950546 KB |
1248 | v->name = &str[1]; |
1249 | v->val = Buf_Init(1); | |
1250 | v->flags = VAR_JUNK; | |
1251 | } | |
1252 | } | |
1253 | } | |
1254 | ||
1255 | if (v->flags & VAR_IN_USE) { | |
1256 | Fatal("Variable %s is recursive.", v->name); | |
1257 | /*NOTREACHED*/ | |
1258 | } else { | |
1259 | v->flags |= VAR_IN_USE; | |
1260 | } | |
1261 | /* | |
1262 | * Before doing any modification, we have to make sure the value | |
1263 | * has been fully expanded. If it looks like recursion might be | |
1264 | * necessary (there's a dollar sign somewhere in the variable's value) | |
1265 | * we just call Var_Subst to do any other substitutions that are | |
1266 | * necessary. Note that the value returned by Var_Subst will have | |
1267 | * been dynamically-allocated, so it will need freeing when we | |
1268 | * return. | |
1269 | */ | |
1270 | str = (char *)Buf_GetAll(v->val, (int *)NULL); | |
40a80f0c KB |
1271 | if (strchr (str, '$') != (char *)NULL) { |
1272 | str = Var_Subst(NULL, str, ctxt, err); | |
ab950546 KB |
1273 | *freePtr = TRUE; |
1274 | } | |
1275 | ||
1276 | v->flags &= ~VAR_IN_USE; | |
1277 | ||
1278 | /* | |
1279 | * Now we need to apply any modifiers the user wants applied. | |
1280 | * These are: | |
1281 | * :M<pattern> words which match the given <pattern>. | |
1282 | * <pattern> is of the standard file | |
1283 | * wildcarding form. | |
1284 | * :S<d><pat1><d><pat2><d>[g] | |
1285 | * Substitute <pat2> for <pat1> in the value | |
1286 | * :H Substitute the head of each word | |
1287 | * :T Substitute the tail of each word | |
1288 | * :E Substitute the extension (minus '.') of | |
1289 | * each word | |
1290 | * :R Substitute the root of each word | |
1291 | * (pathname minus the suffix). | |
1292 | * :lhs=rhs Like :S, but the rhs goes to the end of | |
1293 | * the invocation. | |
1294 | */ | |
1295 | if ((str != (char *)NULL) && haveModifier) { | |
1296 | /* | |
1297 | * Skip initial colon while putting it back. | |
1298 | */ | |
1299 | *tstr++ = ':'; | |
1300 | while (*tstr != endc) { | |
1301 | char *newStr; /* New value to return */ | |
1302 | char termc; /* Character which terminated scan */ | |
1303 | ||
1304 | if (DEBUG(VAR)) { | |
1305 | printf("Applying :%c to \"%s\"\n", *tstr, str); | |
1306 | } | |
1307 | switch (*tstr) { | |
1308 | case 'N': | |
1309 | case 'M': | |
1310 | { | |
1311 | char *pattern; | |
1312 | char *cp2; | |
1313 | Boolean copy; | |
1314 | ||
1315 | copy = FALSE; | |
1316 | for (cp = tstr + 1; | |
1317 | *cp != '\0' && *cp != ':' && *cp != endc; | |
1318 | cp++) | |
1319 | { | |
1320 | if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ | |
1321 | copy = TRUE; | |
1322 | cp++; | |
1323 | } | |
1324 | } | |
1325 | termc = *cp; | |
1326 | *cp = '\0'; | |
1327 | if (copy) { | |
1328 | /* | |
1329 | * Need to compress the \:'s out of the pattern, so | |
1330 | * allocate enough room to hold the uncompressed | |
1331 | * pattern (note that cp started at tstr+1, so | |
1332 | * cp - tstr takes the null byte into account) and | |
1333 | * compress the pattern into the space. | |
1334 | */ | |
54527040 | 1335 | pattern = emalloc(cp - tstr); |
ab950546 KB |
1336 | for (cp2 = pattern, cp = tstr + 1; |
1337 | *cp != '\0'; | |
1338 | cp++, cp2++) | |
1339 | { | |
1340 | if ((*cp == '\\') && | |
1341 | (cp[1] == ':' || cp[1] == endc)) { | |
1342 | cp++; | |
1343 | } | |
1344 | *cp2 = *cp; | |
1345 | } | |
1346 | *cp2 = '\0'; | |
1347 | } else { | |
1348 | pattern = &tstr[1]; | |
1349 | } | |
1350 | if (*tstr == 'M' || *tstr == 'm') { | |
1351 | newStr = VarModify(str, VarMatch, (ClientData)pattern); | |
1352 | } else { | |
1353 | newStr = VarModify(str, VarNoMatch, | |
1354 | (ClientData)pattern); | |
1355 | } | |
1356 | if (copy) { | |
1357 | free(pattern); | |
1358 | } | |
1359 | break; | |
1360 | } | |
1361 | case 'S': | |
1362 | { | |
1363 | VarPattern pattern; | |
1364 | register char delim; | |
1365 | Buffer buf; /* Buffer for patterns */ | |
ab950546 KB |
1366 | |
1367 | pattern.flags = 0; | |
1368 | delim = tstr[1]; | |
1369 | tstr += 2; | |
1370 | /* | |
1371 | * If pattern begins with '^', it is anchored to the | |
1372 | * start of the word -- skip over it and flag pattern. | |
1373 | */ | |
1374 | if (*tstr == '^') { | |
1375 | pattern.flags |= VAR_MATCH_START; | |
1376 | tstr += 1; | |
1377 | } | |
1378 | ||
1379 | buf = Buf_Init(0); | |
1380 | ||
1381 | /* | |
1382 | * Pass through the lhs looking for 1) escaped delimiters, | |
1383 | * '$'s and backslashes (place the escaped character in | |
1384 | * uninterpreted) and 2) unescaped $'s that aren't before | |
1385 | * the delimiter (expand the variable substitution). | |
1386 | * The result is left in the Buffer buf. | |
1387 | */ | |
1388 | for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { | |
1389 | if ((*cp == '\\') && | |
1390 | ((cp[1] == delim) || | |
1391 | (cp[1] == '$') || | |
1392 | (cp[1] == '\\'))) | |
1393 | { | |
1394 | Buf_AddByte(buf, (Byte)cp[1]); | |
1395 | cp++; | |
1396 | } else if (*cp == '$') { | |
1397 | if (cp[1] != delim) { | |
1398 | /* | |
1399 | * If unescaped dollar sign not before the | |
1400 | * delimiter, assume it's a variable | |
1401 | * substitution and recurse. | |
1402 | */ | |
1403 | char *cp2; | |
1404 | int len; | |
1405 | Boolean freeIt; | |
1406 | ||
1407 | cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); | |
1408 | Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); | |
1409 | if (freeIt) { | |
1410 | free(cp2); | |
1411 | } | |
1412 | cp += len - 1; | |
1413 | } else { | |
1414 | /* | |
1415 | * Unescaped $ at end of pattern => anchor | |
1416 | * pattern at end. | |
1417 | */ | |
1418 | pattern.flags |= VAR_MATCH_END; | |
1419 | } | |
1420 | } else { | |
1421 | Buf_AddByte(buf, (Byte)*cp); | |
1422 | } | |
1423 | } | |
1424 | ||
1425 | Buf_AddByte(buf, (Byte)'\0'); | |
1426 | ||
1427 | /* | |
1428 | * If lhs didn't end with the delimiter, complain and | |
1429 | * return NULL | |
1430 | */ | |
1431 | if (*cp != delim) { | |
1432 | *lengthPtr = cp - start + 1; | |
1433 | if (*freePtr) { | |
1434 | free(str); | |
1435 | } | |
1436 | Buf_Destroy(buf, TRUE); | |
1437 | Error("Unclosed substitution for %s (%c missing)", | |
1438 | v->name, delim); | |
1439 | return (var_Error); | |
1440 | } | |
1441 | ||
1442 | /* | |
1443 | * Fetch pattern and destroy buffer, but preserve the data | |
1444 | * in it, since that's our lhs. Note that Buf_GetAll | |
1445 | * will return the actual number of bytes, which includes | |
1446 | * the null byte, so we have to decrement the length by | |
1447 | * one. | |
1448 | */ | |
1449 | pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen); | |
1450 | pattern.leftLen--; | |
1451 | Buf_Destroy(buf, FALSE); | |
1452 | ||
1453 | /* | |
1454 | * Now comes the replacement string. Three things need to | |
1455 | * be done here: 1) need to compress escaped delimiters and | |
1456 | * ampersands and 2) need to replace unescaped ampersands | |
1457 | * with the l.h.s. (since this isn't regexp, we can do | |
1458 | * it right here) and 3) expand any variable substitutions. | |
1459 | */ | |
1460 | buf = Buf_Init(0); | |
1461 | ||
1462 | tstr = cp + 1; | |
1463 | for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { | |
1464 | if ((*cp == '\\') && | |
1465 | ((cp[1] == delim) || | |
1466 | (cp[1] == '&') || | |
1467 | (cp[1] == '\\') || | |
1468 | (cp[1] == '$'))) | |
1469 | { | |
1470 | Buf_AddByte(buf, (Byte)cp[1]); | |
1471 | cp++; | |
1472 | } else if ((*cp == '$') && (cp[1] != delim)) { | |
1473 | char *cp2; | |
1474 | int len; | |
1475 | Boolean freeIt; | |
1476 | ||
1477 | cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); | |
1478 | Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); | |
1479 | cp += len - 1; | |
1480 | if (freeIt) { | |
1481 | free(cp2); | |
1482 | } | |
1483 | } else if (*cp == '&') { | |
1484 | Buf_AddBytes(buf, pattern.leftLen, | |
1485 | (Byte *)pattern.lhs); | |
1486 | } else { | |
1487 | Buf_AddByte(buf, (Byte)*cp); | |
1488 | } | |
1489 | } | |
1490 | ||
1491 | Buf_AddByte(buf, (Byte)'\0'); | |
1492 | ||
1493 | /* | |
1494 | * If didn't end in delimiter character, complain | |
1495 | */ | |
1496 | if (*cp != delim) { | |
1497 | *lengthPtr = cp - start + 1; | |
1498 | if (*freePtr) { | |
1499 | free(str); | |
1500 | } | |
1501 | Buf_Destroy(buf, TRUE); | |
1502 | Error("Unclosed substitution for %s (%c missing)", | |
1503 | v->name, delim); | |
1504 | return (var_Error); | |
1505 | } | |
1506 | ||
1507 | pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); | |
1508 | pattern.rightLen--; | |
1509 | Buf_Destroy(buf, FALSE); | |
1510 | ||
1511 | /* | |
1512 | * Check for global substitution. If 'g' after the final | |
1513 | * delimiter, substitution is global and is marked that | |
1514 | * way. | |
1515 | */ | |
1516 | cp++; | |
1517 | if (*cp == 'g') { | |
1518 | pattern.flags |= VAR_SUB_GLOBAL; | |
1519 | cp++; | |
1520 | } | |
1521 | ||
1522 | termc = *cp; | |
1523 | newStr = VarModify(str, VarSubstitute, | |
1524 | (ClientData)&pattern); | |
1525 | /* | |
1526 | * Free the two strings. | |
1527 | */ | |
1528 | free(pattern.lhs); | |
1529 | free(pattern.rhs); | |
1530 | break; | |
1531 | } | |
1532 | case 'T': | |
1533 | if (tstr[1] == endc || tstr[1] == ':') { | |
1534 | newStr = VarModify (str, VarTail, (ClientData)0); | |
1535 | cp = tstr + 1; | |
1536 | termc = *cp; | |
1537 | break; | |
1538 | } | |
1539 | /*FALLTHRU*/ | |
1540 | case 'H': | |
1541 | if (tstr[1] == endc || tstr[1] == ':') { | |
1542 | newStr = VarModify (str, VarHead, (ClientData)0); | |
1543 | cp = tstr + 1; | |
1544 | termc = *cp; | |
1545 | break; | |
1546 | } | |
1547 | /*FALLTHRU*/ | |
1548 | case 'E': | |
1549 | if (tstr[1] == endc || tstr[1] == ':') { | |
1550 | newStr = VarModify (str, VarSuffix, (ClientData)0); | |
1551 | cp = tstr + 1; | |
1552 | termc = *cp; | |
1553 | break; | |
1554 | } | |
1555 | /*FALLTHRU*/ | |
1556 | case 'R': | |
1557 | if (tstr[1] == endc || tstr[1] == ':') { | |
1558 | newStr = VarModify (str, VarRoot, (ClientData)0); | |
1559 | cp = tstr + 1; | |
1560 | termc = *cp; | |
1561 | break; | |
1562 | } | |
1563 | /*FALLTHRU*/ | |
1564 | default: { | |
1565 | /* | |
1566 | * This can either be a bogus modifier or a System-V | |
1567 | * substitution command. | |
1568 | */ | |
1569 | VarPattern pattern; | |
1570 | Boolean eqFound; | |
1571 | ||
1572 | pattern.flags = 0; | |
1573 | eqFound = FALSE; | |
1574 | /* | |
1575 | * First we make a pass through the string trying | |
1576 | * to verify it is a SYSV-make-style translation: | |
1577 | * it must be: <string1>=<string2>) | |
1578 | */ | |
1579 | for (cp = tstr; *cp != '\0' && *cp != endc; cp++) { | |
1580 | if (*cp == '=') { | |
1581 | eqFound = TRUE; | |
1582 | /* continue looking for endc */ | |
1583 | } | |
1584 | } | |
1585 | if (*cp == endc && eqFound) { | |
1586 | ||
1587 | /* | |
1588 | * Now we break this sucker into the lhs and | |
1589 | * rhs. We must null terminate them of course. | |
1590 | */ | |
40a80f0c KB |
1591 | for (cp = tstr; *cp != '='; cp++) |
1592 | continue; | |
ab950546 KB |
1593 | pattern.lhs = tstr; |
1594 | pattern.leftLen = cp - tstr; | |
1595 | *cp++ = '\0'; | |
1596 | ||
1597 | pattern.rhs = cp; | |
1598 | while (*cp != endc) { | |
1599 | cp++; | |
1600 | } | |
1601 | pattern.rightLen = cp - pattern.rhs; | |
1602 | *cp = '\0'; | |
1603 | ||
1604 | /* | |
1605 | * SYSV modifications happen through the whole | |
1606 | * string. Note the pattern is anchored at the end. | |
1607 | */ | |
1608 | pattern.flags |= VAR_SUB_GLOBAL|VAR_MATCH_END; | |
1609 | ||
1610 | newStr = VarModify(str, VarSubstitute, | |
1611 | (ClientData)&pattern); | |
1612 | ||
1613 | /* | |
1614 | * Restore the nulled characters | |
1615 | */ | |
1616 | pattern.lhs[pattern.leftLen] = '='; | |
1617 | pattern.rhs[pattern.rightLen] = endc; | |
1618 | termc = endc; | |
1619 | } else { | |
1620 | Error ("Unknown modifier '%c'\n", *tstr); | |
1621 | for (cp = tstr+1; | |
1622 | *cp != ':' && *cp != endc && *cp != '\0'; | |
40a80f0c KB |
1623 | cp++) |
1624 | continue; | |
ab950546 KB |
1625 | termc = *cp; |
1626 | newStr = var_Error; | |
1627 | } | |
1628 | } | |
1629 | } | |
1630 | if (DEBUG(VAR)) { | |
1631 | printf("Result is \"%s\"\n", newStr); | |
1632 | } | |
1633 | ||
1634 | if (*freePtr) { | |
1635 | free (str); | |
1636 | } | |
1637 | str = newStr; | |
1638 | if (str != var_Error) { | |
1639 | *freePtr = TRUE; | |
1640 | } else { | |
1641 | *freePtr = FALSE; | |
1642 | } | |
1643 | if (termc == '\0') { | |
1644 | Error("Unclosed variable specification for %s", v->name); | |
1645 | } else if (termc == ':') { | |
1646 | *cp++ = termc; | |
1647 | } else { | |
1648 | *cp = termc; | |
1649 | } | |
1650 | tstr = cp; | |
1651 | } | |
1652 | *lengthPtr = tstr - start + 1; | |
1653 | } else { | |
1654 | *lengthPtr = tstr - start + 1; | |
1655 | *tstr = endc; | |
1656 | } | |
1657 | ||
1658 | if (v->flags & VAR_FROM_ENV) { | |
1659 | Boolean destroy = FALSE; | |
1660 | ||
1661 | if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { | |
1662 | destroy = TRUE; | |
1663 | } else { | |
1664 | /* | |
1665 | * Returning the value unmodified, so tell the caller to free | |
1666 | * the thing. | |
1667 | */ | |
1668 | *freePtr = TRUE; | |
1669 | } | |
1670 | Buf_Destroy(v->val, destroy); | |
1671 | free((Address)v); | |
1672 | } else if (v->flags & VAR_JUNK) { | |
1673 | /* | |
1674 | * Perform any free'ing needed and set *freePtr to FALSE so the caller | |
1675 | * doesn't try to free a static pointer. | |
1676 | */ | |
1677 | if (*freePtr) { | |
1678 | free(str); | |
1679 | } | |
1680 | *freePtr = FALSE; | |
1681 | free((Address)v); | |
1682 | if (dynamic) { | |
54527040 | 1683 | str = emalloc(*lengthPtr + 1); |
ab950546 KB |
1684 | strncpy(str, start, *lengthPtr); |
1685 | str[*lengthPtr] = '\0'; | |
1686 | *freePtr = TRUE; | |
1687 | } else { | |
1688 | str = var_Error; | |
1689 | } | |
1690 | } | |
1691 | return (str); | |
1692 | } | |
182ca07d | 1693 | |
ab950546 KB |
1694 | /*- |
1695 | *----------------------------------------------------------------------- | |
1696 | * Var_Subst -- | |
1697 | * Substitute for all variables in the given string in the given context | |
1698 | * If undefErr is TRUE, Parse_Error will be called when an undefined | |
1699 | * variable is encountered. | |
1700 | * | |
1701 | * Results: | |
1702 | * The resulting string. | |
1703 | * | |
1704 | * Side Effects: | |
1705 | * None. The old string must be freed by the caller | |
1706 | *----------------------------------------------------------------------- | |
1707 | */ | |
1708 | char * | |
40a80f0c KB |
1709 | Var_Subst (var, str, ctxt, undefErr) |
1710 | char *var; /* Named variable || NULL for all */ | |
1711 | char *str; /* the string in which to substitute */ | |
ab950546 KB |
1712 | GNode *ctxt; /* the context wherein to find variables */ |
1713 | Boolean undefErr; /* TRUE if undefineds are an error */ | |
1714 | { | |
1715 | Buffer buf; /* Buffer for forming things */ | |
1716 | char *val; /* Value to substitute for a variable */ | |
1717 | int length; /* Length of the variable invocation */ | |
1718 | Boolean doFree; /* Set true if val should be freed */ | |
1719 | static Boolean errorReported; /* Set true if an error has already | |
1720 | * been reported to prevent a plethora | |
1721 | * of messages when recursing */ | |
1722 | ||
40a80f0c | 1723 | buf = Buf_Init (MAKE_BSIZE); |
ab950546 KB |
1724 | errorReported = FALSE; |
1725 | ||
1726 | while (*str) { | |
40a80f0c | 1727 | if (var == NULL && (*str == '$') && (str[1] == '$')) { |
ab950546 KB |
1728 | /* |
1729 | * A dollar sign may be escaped either with another dollar sign. | |
1730 | * In such a case, we skip over the escape character and store the | |
1731 | * dollar sign into the buffer directly. | |
1732 | */ | |
1733 | str++; | |
1734 | Buf_AddByte(buf, (Byte)*str); | |
1735 | str++; | |
1736 | } else if (*str != '$') { | |
1737 | /* | |
1738 | * Skip as many characters as possible -- either to the end of | |
1739 | * the string or to the next dollar sign (variable invocation). | |
1740 | */ | |
1741 | char *cp; | |
1742 | ||
40a80f0c KB |
1743 | for (cp = str++; *str != '$' && *str != '\0'; str++) |
1744 | continue; | |
ab950546 KB |
1745 | Buf_AddBytes(buf, str - cp, (Byte *)cp); |
1746 | } else { | |
40a80f0c KB |
1747 | if (var != NULL) { |
1748 | int expand; | |
1749 | for (;;) { | |
1750 | if (str[1] != '(' && str[1] != '{') { | |
1751 | if (str[1] != *var) { | |
1752 | Buf_AddBytes(buf, 2, (Byte *) str); | |
1753 | str += 2; | |
1754 | expand = FALSE; | |
1755 | } | |
1756 | else | |
1757 | expand = TRUE; | |
1758 | break; | |
1759 | } | |
1760 | else { | |
1761 | char *p; | |
1762 | ||
1763 | /* | |
1764 | * Scan up to the end of the variable name. | |
1765 | */ | |
1766 | for (p = &str[2]; *p && | |
1767 | *p != ':' && *p != ')' && *p != '}'; p++) | |
1768 | if (*p == '$') | |
1769 | break; | |
1770 | /* | |
1771 | * A variable inside the variable. We cannot expand | |
1772 | * the external variable yet, so we try again with | |
1773 | * the nested one | |
1774 | */ | |
1775 | if (*p == '$') { | |
1776 | Buf_AddBytes(buf, p - str, (Byte *) str); | |
1777 | str = p; | |
1778 | continue; | |
1779 | } | |
1780 | ||
1781 | if (strncmp(var, str + 2, p - str - 2) != 0 || | |
1782 | var[p - str - 2] != '\0') { | |
1783 | /* | |
1784 | * Not the variable we want to expand, scan | |
1785 | * until the next variable | |
1786 | */ | |
1787 | for (;*p != '$' && *p != '\0'; p++) | |
1788 | continue; | |
1789 | Buf_AddBytes(buf, p - str, (Byte *) str); | |
1790 | str = p; | |
1791 | expand = FALSE; | |
1792 | } | |
1793 | else | |
1794 | expand = TRUE; | |
1795 | break; | |
1796 | } | |
1797 | } | |
1798 | if (!expand) | |
1799 | continue; | |
1800 | } | |
1801 | ||
ab950546 KB |
1802 | val = Var_Parse (str, ctxt, undefErr, &length, &doFree); |
1803 | ||
1804 | /* | |
1805 | * When we come down here, val should either point to the | |
1806 | * value of this variable, suitably modified, or be NULL. | |
1807 | * Length should be the total length of the potential | |
1808 | * variable invocation (from $ to end character...) | |
1809 | */ | |
1810 | if (val == var_Error || val == varNoError) { | |
1811 | /* | |
1812 | * If performing old-time variable substitution, skip over | |
1813 | * the variable and continue with the substitution. Otherwise, | |
1814 | * store the dollar sign and advance str so we continue with | |
1815 | * the string... | |
1816 | */ | |
1817 | if (oldVars) { | |
ab950546 KB |
1818 | str += length; |
1819 | } else if (undefErr) { | |
1820 | /* | |
1821 | * If variable is undefined, complain and skip the | |
1822 | * variable. The complaint will stop us from doing anything | |
1823 | * when the file is parsed. | |
1824 | */ | |
1825 | if (!errorReported) { | |
1826 | Parse_Error (PARSE_FATAL, | |
1827 | "Undefined variable \"%.*s\"",length,str); | |
1828 | } | |
1829 | str += length; | |
1830 | errorReported = TRUE; | |
1831 | } else { | |
1832 | Buf_AddByte (buf, (Byte)*str); | |
1833 | str += 1; | |
1834 | } | |
1835 | } else { | |
1836 | /* | |
1837 | * We've now got a variable structure to store in. But first, | |
1838 | * advance the string pointer. | |
1839 | */ | |
1840 | str += length; | |
1841 | ||
1842 | /* | |
1843 | * Copy all the characters from the variable value straight | |
1844 | * into the new string. | |
1845 | */ | |
1846 | Buf_AddBytes (buf, strlen (val), (Byte *)val); | |
1847 | if (doFree) { | |
1848 | free ((Address)val); | |
1849 | } | |
1850 | } | |
1851 | } | |
1852 | } | |
1853 | ||
1854 | Buf_AddByte (buf, '\0'); | |
1855 | str = (char *)Buf_GetAll (buf, (int *)NULL); | |
1856 | Buf_Destroy (buf, FALSE); | |
1857 | return (str); | |
1858 | } | |
182ca07d | 1859 | |
ab950546 KB |
1860 | /*- |
1861 | *----------------------------------------------------------------------- | |
1862 | * Var_GetTail -- | |
1863 | * Return the tail from each of a list of words. Used to set the | |
1864 | * System V local variables. | |
1865 | * | |
1866 | * Results: | |
1867 | * The resulting string. | |
1868 | * | |
1869 | * Side Effects: | |
1870 | * None. | |
1871 | * | |
1872 | *----------------------------------------------------------------------- | |
1873 | */ | |
1874 | char * | |
1875 | Var_GetTail(file) | |
1876 | char *file; /* Filename to modify */ | |
1877 | { | |
1878 | return(VarModify(file, VarTail, (ClientData)0)); | |
1879 | } | |
182ca07d | 1880 | |
ab950546 KB |
1881 | /*- |
1882 | *----------------------------------------------------------------------- | |
1883 | * Var_GetHead -- | |
1884 | * Find the leading components of a (list of) filename(s). | |
1885 | * XXX: VarHead does not replace foo by ., as (sun) System V make | |
1886 | * does. | |
1887 | * | |
1888 | * Results: | |
1889 | * The leading components. | |
1890 | * | |
1891 | * Side Effects: | |
1892 | * None. | |
1893 | * | |
1894 | *----------------------------------------------------------------------- | |
1895 | */ | |
1896 | char * | |
1897 | Var_GetHead(file) | |
1898 | char *file; /* Filename to manipulate */ | |
1899 | { | |
1900 | return(VarModify(file, VarHead, (ClientData)0)); | |
1901 | } | |
182ca07d | 1902 | |
ab950546 KB |
1903 | /*- |
1904 | *----------------------------------------------------------------------- | |
1905 | * Var_Init -- | |
1906 | * Initialize the module | |
1907 | * | |
1908 | * Results: | |
1909 | * None | |
1910 | * | |
1911 | * Side Effects: | |
1912 | * The VAR_CMD and VAR_GLOBAL contexts are created | |
1913 | *----------------------------------------------------------------------- | |
1914 | */ | |
1915 | void | |
1916 | Var_Init () | |
1917 | { | |
1918 | VAR_GLOBAL = Targ_NewGN ("Global"); | |
1919 | VAR_CMD = Targ_NewGN ("Command"); | |
1920 | ||
1921 | } | |
182ca07d | 1922 | |
ab950546 | 1923 | /****************** PRINT DEBUGGING INFO *****************/ |
40a80f0c | 1924 | static int |
ab950546 KB |
1925 | VarPrintVar (v) |
1926 | Var *v; | |
1927 | { | |
1928 | printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, (int *)NULL)); | |
1929 | return (0); | |
1930 | } | |
182ca07d | 1931 | |
ab950546 KB |
1932 | /*- |
1933 | *----------------------------------------------------------------------- | |
1934 | * Var_Dump -- | |
1935 | * print all variables in a context | |
1936 | *----------------------------------------------------------------------- | |
1937 | */ | |
40a80f0c | 1938 | void |
ab950546 KB |
1939 | Var_Dump (ctxt) |
1940 | GNode *ctxt; | |
1941 | { | |
1942 | Lst_ForEach (ctxt->context, VarPrintVar); | |
1943 | } |