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