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