Annotation of src/usr.bin/make/var.c, Revision 1.20
1.20 ! espie 1: /* $OpenBSD: var.c,v 1.19 1999/12/09 18:18:24 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.20 ! espie 73: static char rcsid[] = "$OpenBSD: var.c,v 1.19 1999/12/09 18:18:24 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.16 espie 547: Buf_GetAll(v->val, NULL));
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:
600: v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
601: if (v != (Var *) NIL) {
1.16 espie 602: char *p = Buf_GetAll(v->val, NULL);
1.1 deraadt 603: return p;
1.17 espie 604: } else
605: return NULL;
1.1 deraadt 606: }
607:
608: /*-
609: *-----------------------------------------------------------------------
1.11 espie 610: * VarUppercase --
611: * Place the Upper cased word in the given buffer.
612: *
613: * Results:
614: * TRUE if characters were added to the buffer (a space needs to be
615: * added to the buffer before the next word).
616: *
617: * Side Effects:
618: * The word is added to the buffer.
619: *
620: *-----------------------------------------------------------------------
621: */
622: static Boolean
623: VarUppercase (word, addSpace, buf, dummy)
624: char *word; /* Word to Upper Case */
625: Boolean addSpace; /* True if need to add a space to the buffer
626: * before sticking in the head */
627: Buffer buf; /* Buffer in which to store it */
628: ClientData dummy;
629: {
630: size_t len = strlen(word);
631:
1.19 espie 632: if (addSpace)
633: Buf_AddSpace(buf);
634: while (len--)
1.16 espie 635: Buf_AddChar(buf, toupper(*word++));
1.11 espie 636: return (TRUE);
637: }
638:
639: /*-
640: *-----------------------------------------------------------------------
641: * VarLowercase --
642: * Place the Lower cased word in the given buffer.
643: *
644: * Results:
645: * TRUE if characters were added to the buffer (a space needs to be
646: * added to the buffer before the next word).
647: *
648: * Side Effects:
649: * The word is added to the buffer.
650: *
651: *-----------------------------------------------------------------------
652: */
653: static Boolean
654: VarLowercase (word, addSpace, buf, dummy)
655: char *word; /* Word to Lower Case */
656: Boolean addSpace; /* True if need to add a space to the buffer
657: * before sticking in the head */
658: Buffer buf; /* Buffer in which to store it */
659: ClientData dummy;
660: {
661: size_t len = strlen(word);
662:
1.19 espie 663: if (addSpace)
664: Buf_AddSpace(buf);
665: while (len--)
1.16 espie 666: Buf_AddChar(buf, tolower(*word++));
1.11 espie 667: return (TRUE);
668: }
669:
670: /*-
671: *-----------------------------------------------------------------------
1.1 deraadt 672: * VarHead --
673: * Remove the tail of the given word and place the result in the given
674: * buffer.
675: *
676: * Results:
677: * TRUE if characters were added to the buffer (a space needs to be
678: * added to the buffer before the next word).
679: *
680: * Side Effects:
681: * The trimmed word is added to the buffer.
682: *
683: *-----------------------------------------------------------------------
684: */
685: static Boolean
686: VarHead (word, addSpace, buf, dummy)
687: char *word; /* Word to trim */
688: Boolean addSpace; /* True if need to add a space to the buffer
689: * before sticking in the head */
690: Buffer buf; /* Buffer in which to store it */
691: ClientData dummy;
692: {
693: register char *slash;
694:
1.19 espie 695: slash = strrchr(word, '/');
696: if (slash != NULL) {
697: if (addSpace)
698: Buf_AddSpace(buf);
699: Buf_AddInterval(buf, word, slash);
1.1 deraadt 700: return (TRUE);
701: } else {
1.19 espie 702: /* If no directory part, give . (q.v. the POSIX standard) */
703: if (addSpace)
704: Buf_AddString(buf, " .");
705: else
1.16 espie 706: Buf_AddChar(buf, '.');
1.1 deraadt 707: }
708: return(dummy ? TRUE : TRUE);
709: }
710:
711: /*-
712: *-----------------------------------------------------------------------
713: * VarTail --
714: * Remove the head of the given word and place the result in the given
715: * buffer.
716: *
717: * Results:
718: * TRUE if characters were added to the buffer (a space needs to be
719: * added to the buffer before the next word).
720: *
721: * Side Effects:
722: * The trimmed word is added to the buffer.
723: *
724: *-----------------------------------------------------------------------
725: */
726: static Boolean
727: VarTail (word, addSpace, buf, dummy)
728: char *word; /* Word to trim */
729: Boolean addSpace; /* TRUE if need to stick a space in the
730: * buffer before adding the tail */
731: Buffer buf; /* Buffer in which to store it */
732: ClientData dummy;
733: {
734: register char *slash;
735:
1.19 espie 736: if (addSpace)
737: Buf_AddSpace(buf);
738: slash = strrchr(word, '/');
739: if (slash != NULL)
740: Buf_AddString(buf, slash+1);
741: else
742: Buf_AddString(buf, word);
1.1 deraadt 743: return (dummy ? TRUE : TRUE);
744: }
745:
746: /*-
747: *-----------------------------------------------------------------------
748: * VarSuffix --
749: * Place the suffix of the given word in the given buffer.
750: *
751: * Results:
752: * TRUE if characters were added to the buffer (a space needs to be
753: * added to the buffer before the next word).
754: *
755: * Side Effects:
756: * The suffix from the word is placed in the buffer.
757: *
758: *-----------------------------------------------------------------------
759: */
760: static Boolean
761: VarSuffix (word, addSpace, buf, dummy)
762: char *word; /* Word to trim */
763: Boolean addSpace; /* TRUE if need to add a space before placing
764: * the suffix in the buffer */
765: Buffer buf; /* Buffer in which to store it */
766: ClientData dummy;
767: {
1.19 espie 768: char *dot;
1.1 deraadt 769:
1.19 espie 770: dot = strrchr(word, '.');
771: if (dot != NULL) {
772: if (addSpace)
773: Buf_AddSpace(buf);
774: Buf_AddString(buf, dot+1);
1.1 deraadt 775: addSpace = TRUE;
776: }
777: return (dummy ? addSpace : addSpace);
778: }
779:
780: /*-
781: *-----------------------------------------------------------------------
782: * VarRoot --
783: * Remove the suffix of the given word and place the result in the
784: * buffer.
785: *
786: * Results:
787: * TRUE if characters were added to the buffer (a space needs to be
788: * added to the buffer before the next word).
789: *
790: * Side Effects:
791: * The trimmed word is added to the buffer.
792: *
793: *-----------------------------------------------------------------------
794: */
795: static Boolean
796: VarRoot (word, addSpace, buf, dummy)
797: char *word; /* Word to trim */
798: Boolean addSpace; /* TRUE if need to add a space to the buffer
799: * before placing the root in it */
800: Buffer buf; /* Buffer in which to store it */
801: ClientData dummy;
802: {
1.19 espie 803: char *dot;
1.1 deraadt 804:
1.19 espie 805: if (addSpace)
806: Buf_AddSpace(buf);
1.1 deraadt 807:
1.19 espie 808: dot = strrchr(word, '.');
809: if (dot != NULL)
810: Buf_AddInterval(buf, word, dot);
811: else
812: Buf_AddString(buf, word);
1.1 deraadt 813: return (dummy ? TRUE : TRUE);
814: }
815:
816: /*-
817: *-----------------------------------------------------------------------
818: * VarMatch --
819: * Place the word in the buffer if it matches the given pattern.
820: * Callback function for VarModify to implement the :M modifier.
1.5 millert 821: *
1.1 deraadt 822: * Results:
823: * TRUE if a space should be placed in the buffer before the next
824: * word.
825: *
826: * Side Effects:
827: * The word may be copied to the buffer.
828: *
829: *-----------------------------------------------------------------------
830: */
831: static Boolean
832: VarMatch (word, addSpace, buf, pattern)
833: char *word; /* Word to examine */
834: Boolean addSpace; /* TRUE if need to add a space to the
835: * buffer before adding the word, if it
836: * matches */
837: Buffer buf; /* Buffer in which to store it */
838: ClientData pattern; /* Pattern the word must match */
839: {
840: if (Str_Match(word, (char *) pattern)) {
1.19 espie 841: if (addSpace)
842: Buf_AddSpace(buf);
1.1 deraadt 843: addSpace = TRUE;
1.19 espie 844: Buf_AddString(buf, word);
1.1 deraadt 845: }
846: return(addSpace);
847: }
848:
1.4 briggs 849: #ifdef SYSVVARSUB
1.1 deraadt 850: /*-
851: *-----------------------------------------------------------------------
852: * VarSYSVMatch --
853: * Place the word in the buffer if it matches the given pattern.
854: * Callback function for VarModify to implement the System V %
855: * modifiers.
1.5 millert 856: *
1.1 deraadt 857: * Results:
858: * TRUE if a space should be placed in the buffer before the next
859: * word.
860: *
861: * Side Effects:
862: * The word may be copied to the buffer.
863: *
864: *-----------------------------------------------------------------------
865: */
866: static Boolean
867: VarSYSVMatch (word, addSpace, buf, patp)
868: char *word; /* Word to examine */
869: Boolean addSpace; /* TRUE if need to add a space to the
870: * buffer before adding the word, if it
871: * matches */
872: Buffer buf; /* Buffer in which to store it */
873: ClientData patp; /* Pattern the word must match */
874: {
875: int len;
876: char *ptr;
877: VarPattern *pat = (VarPattern *) patp;
878:
1.7 deraadt 879: if (*word) {
880: if (addSpace)
1.19 espie 881: Buf_AddSpace(buf);
1.7 deraadt 882:
883: addSpace = TRUE;
884:
885: if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
886: Str_SYSVSubst(buf, pat->rhs, ptr, len);
887: else
1.19 espie 888: Buf_AddString(buf, word);
1.7 deraadt 889: }
1.1 deraadt 890: return(addSpace);
891: }
1.4 briggs 892: #endif
1.1 deraadt 893:
894:
895: /*-
896: *-----------------------------------------------------------------------
897: * VarNoMatch --
898: * Place the word in the buffer if it doesn't match the given pattern.
899: * Callback function for VarModify to implement the :N modifier.
1.5 millert 900: *
1.1 deraadt 901: * Results:
902: * TRUE if a space should be placed in the buffer before the next
903: * word.
904: *
905: * Side Effects:
906: * The word may be copied to the buffer.
907: *
908: *-----------------------------------------------------------------------
909: */
910: static Boolean
911: VarNoMatch (word, addSpace, buf, pattern)
912: char *word; /* Word to examine */
913: Boolean addSpace; /* TRUE if need to add a space to the
914: * buffer before adding the word, if it
915: * matches */
916: Buffer buf; /* Buffer in which to store it */
917: ClientData pattern; /* Pattern the word must match */
918: {
919: if (!Str_Match(word, (char *) pattern)) {
1.19 espie 920: if (addSpace)
921: Buf_AddSpace(buf);
1.1 deraadt 922: addSpace = TRUE;
1.19 espie 923: Buf_AddString(buf, word);
1.1 deraadt 924: }
925: return(addSpace);
926: }
927:
928:
929: /*-
930: *-----------------------------------------------------------------------
931: * VarSubstitute --
932: * Perform a string-substitution on the given word, placing the
933: * result in the passed buffer.
934: *
935: * Results:
936: * TRUE if a space is needed before more characters are added.
937: *
938: * Side Effects:
939: * None.
940: *
941: *-----------------------------------------------------------------------
942: */
943: static Boolean
944: VarSubstitute (word, addSpace, buf, patternp)
945: char *word; /* Word to modify */
946: Boolean addSpace; /* True if space should be added before
947: * other characters */
948: Buffer buf; /* Buffer for result */
949: ClientData patternp; /* Pattern for substitution */
950: {
1.16 espie 951: register size_t wordLen; /* Length of word */
1.1 deraadt 952: register char *cp; /* General pointer */
953: VarPattern *pattern = (VarPattern *) patternp;
954:
955: wordLen = strlen(word);
1.6 millert 956: if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
957: (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
1.1 deraadt 958: /*
1.6 millert 959: * Still substituting -- break it down into simple anchored cases
1.1 deraadt 960: * and if none of them fits, perform the general substitution case.
961: */
962: if ((pattern->flags & VAR_MATCH_START) &&
963: (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
964: /*
965: * Anchored at start and beginning of word matches pattern
966: */
967: if ((pattern->flags & VAR_MATCH_END) &&
968: (wordLen == pattern->leftLen)) {
969: /*
970: * Also anchored at end and matches to the end (word
971: * is same length as pattern) add space and rhs only
972: * if rhs is non-null.
973: */
974: if (pattern->rightLen != 0) {
1.19 espie 975: if (addSpace)
976: Buf_AddSpace(buf);
1.1 deraadt 977: addSpace = TRUE;
1.16 espie 978: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.1 deraadt 979: }
1.6 millert 980: pattern->flags |= VAR_SUB_MATCHED;
1.1 deraadt 981: } else if (pattern->flags & VAR_MATCH_END) {
982: /*
983: * Doesn't match to end -- copy word wholesale
984: */
985: goto nosub;
986: } else {
987: /*
988: * Matches at start but need to copy in trailing characters
989: */
990: if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
1.19 espie 991: if (addSpace)
992: Buf_AddSpace(buf);
1.1 deraadt 993: addSpace = TRUE;
994: }
1.16 espie 995: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
996: Buf_AddChars(buf, wordLen - pattern->leftLen,
997: word + pattern->leftLen);
1.6 millert 998: pattern->flags |= VAR_SUB_MATCHED;
1.1 deraadt 999: }
1000: } else if (pattern->flags & VAR_MATCH_START) {
1001: /*
1002: * Had to match at start of word and didn't -- copy whole word.
1003: */
1004: goto nosub;
1005: } else if (pattern->flags & VAR_MATCH_END) {
1006: /*
1007: * Anchored at end, Find only place match could occur (leftLen
1008: * characters from the end of the word) and see if it does. Note
1009: * that because the $ will be left at the end of the lhs, we have
1010: * to use strncmp.
1011: */
1012: cp = word + (wordLen - pattern->leftLen);
1013: if ((cp >= word) &&
1014: (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
1015: /*
1016: * Match found. If we will place characters in the buffer,
1017: * add a space before hand as indicated by addSpace, then
1018: * stuff in the initial, unmatched part of the word followed
1019: * by the right-hand-side.
1020: */
1021: if (((cp - word) + pattern->rightLen) != 0) {
1.19 espie 1022: if (addSpace)
1023: Buf_AddSpace(buf);
1.1 deraadt 1024: addSpace = TRUE;
1025: }
1.19 espie 1026: Buf_AddInterval(buf, word, cp);
1.16 espie 1027: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.6 millert 1028: pattern->flags |= VAR_SUB_MATCHED;
1.1 deraadt 1029: } else {
1030: /*
1031: * Had to match at end and didn't. Copy entire word.
1032: */
1033: goto nosub;
1034: }
1035: } else {
1036: /*
1037: * Pattern is unanchored: search for the pattern in the word using
1038: * String_FindSubstring, copying unmatched portions and the
1039: * right-hand-side for each match found, handling non-global
1.5 millert 1040: * substitutions correctly, etc. When the loop is done, any
1.1 deraadt 1041: * remaining part of the word (word and wordLen are adjusted
1042: * accordingly through the loop) is copied straight into the
1043: * buffer.
1044: * addSpace is set FALSE as soon as a space is added to the
1045: * buffer.
1046: */
1047: register Boolean done;
1.16 espie 1048: size_t origSize;
1.1 deraadt 1049:
1050: done = FALSE;
1051: origSize = Buf_Size(buf);
1052: while (!done) {
1.15 espie 1053: cp = strstr(word, pattern->lhs);
1.1 deraadt 1054: if (cp != (char *)NULL) {
1055: if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
1.19 espie 1056: Buf_AddSpace(buf);
1.1 deraadt 1057: addSpace = FALSE;
1058: }
1.19 espie 1059: Buf_AddInterval(buf, word, cp);
1.16 espie 1060: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.1 deraadt 1061: wordLen -= (cp - word) + pattern->leftLen;
1062: word = cp + pattern->leftLen;
1.5 millert 1063: if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
1.1 deraadt 1064: done = TRUE;
1065: }
1.6 millert 1066: pattern->flags |= VAR_SUB_MATCHED;
1.1 deraadt 1067: } else {
1068: done = TRUE;
1069: }
1070: }
1071: if (wordLen != 0) {
1.19 espie 1072: if (addSpace)
1073: Buf_AddSpace(buf);
1.16 espie 1074: Buf_AddChars(buf, wordLen, word);
1.1 deraadt 1075: }
1076: /*
1077: * If added characters to the buffer, need to add a space
1078: * before we add any more. If we didn't add any, just return
1079: * the previous value of addSpace.
1080: */
1.16 espie 1081: return (Buf_Size(buf) != origSize || addSpace);
1.1 deraadt 1082: }
1083: return (addSpace);
1084: }
1085: nosub:
1.19 espie 1086: if (addSpace)
1087: Buf_AddSpace(buf);
1.16 espie 1088: Buf_AddChars(buf, wordLen, word);
1.1 deraadt 1089: return(TRUE);
1090: }
1091:
1.6 millert 1092: #ifndef MAKE_BOOTSTRAP
1093: /*-
1094: *-----------------------------------------------------------------------
1095: * VarREError --
1096: * Print the error caused by a regcomp or regexec call.
1097: *
1098: * Results:
1099: * None.
1100: *
1101: * Side Effects:
1102: * An error gets printed.
1103: *
1104: *-----------------------------------------------------------------------
1105: */
1106: static void
1107: VarREError(err, pat, str)
1108: int err;
1109: regex_t *pat;
1110: const char *str;
1111: {
1112: char *errbuf;
1113: int errlen;
1114:
1115: errlen = regerror(err, pat, 0, 0);
1116: errbuf = emalloc(errlen);
1117: regerror(err, pat, errbuf, errlen);
1118: Error("%s: %s", str, errbuf);
1119: free(errbuf);
1120: }
1121:
1122: /*-
1123: *-----------------------------------------------------------------------
1124: * VarRESubstitute --
1125: * Perform a regex substitution on the given word, placing the
1126: * result in the passed buffer.
1127: *
1128: * Results:
1129: * TRUE if a space is needed before more characters are added.
1130: *
1131: * Side Effects:
1132: * None.
1133: *
1134: *-----------------------------------------------------------------------
1135: */
1136: static Boolean
1137: VarRESubstitute(word, addSpace, buf, patternp)
1138: char *word;
1139: Boolean addSpace;
1140: Buffer buf;
1141: ClientData patternp;
1142: {
1143: VarREPattern *pat;
1144: int xrv;
1145: char *wp;
1146: char *rp;
1147: int added;
1148:
1149: #define MAYBE_ADD_SPACE() \
1150: if (addSpace && !added) \
1.19 espie 1151: Buf_AddSpace(buf); \
1.6 millert 1152: added = 1
1153:
1154: added = 0;
1155: wp = word;
1156: pat = patternp;
1157:
1158: if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
1159: (VAR_SUB_ONE|VAR_SUB_MATCHED))
1160: xrv = REG_NOMATCH;
1161: else {
1162: tryagain:
1163: xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
1164: }
1165:
1166: switch (xrv) {
1167: case 0:
1168: pat->flags |= VAR_SUB_MATCHED;
1169: if (pat->matches[0].rm_so > 0) {
1170: MAYBE_ADD_SPACE();
1.16 espie 1171: Buf_AddChars(buf, pat->matches[0].rm_so, wp);
1.6 millert 1172: }
1173:
1174: for (rp = pat->replace; *rp; rp++) {
1175: if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
1176: MAYBE_ADD_SPACE();
1.16 espie 1177: Buf_AddChar(buf, rp[1]);
1.6 millert 1178: rp++;
1179: }
1180: else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) {
1181: int n;
1182: char *subbuf;
1183: int sublen;
1184: char errstr[3];
1185:
1186: if (*rp == '&') {
1187: n = 0;
1188: errstr[0] = '&';
1189: errstr[1] = '\0';
1190: } else {
1191: n = rp[1] - '0';
1192: errstr[0] = '\\';
1193: errstr[1] = rp[1];
1194: errstr[2] = '\0';
1195: rp++;
1196: }
1197:
1198: if (n > pat->nsub) {
1199: Error("No subexpression %s", &errstr[0]);
1200: subbuf = "";
1201: sublen = 0;
1202: } else if ((pat->matches[n].rm_so == -1) &&
1203: (pat->matches[n].rm_eo == -1)) {
1204: Error("No match for subexpression %s", &errstr[0]);
1205: subbuf = "";
1206: sublen = 0;
1207: } else {
1208: subbuf = wp + pat->matches[n].rm_so;
1209: sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
1210: }
1211:
1212: if (sublen > 0) {
1213: MAYBE_ADD_SPACE();
1.16 espie 1214: Buf_AddChars(buf, sublen, subbuf);
1.6 millert 1215: }
1216: } else {
1217: MAYBE_ADD_SPACE();
1.16 espie 1218: Buf_AddChar(buf, *rp);
1.6 millert 1219: }
1220: }
1221: wp += pat->matches[0].rm_eo;
1222: if (pat->flags & VAR_SUB_GLOBAL)
1223: goto tryagain;
1224: if (*wp) {
1225: MAYBE_ADD_SPACE();
1.16 espie 1226: Buf_AddChars(buf, strlen(wp), wp);
1.6 millert 1227: }
1228: break;
1229: default:
1230: VarREError(xrv, &pat->re, "Unexpected regex error");
1231: /* fall through */
1232: case REG_NOMATCH:
1233: if (*wp) {
1234: MAYBE_ADD_SPACE();
1.16 espie 1235: Buf_AddChars(buf, strlen(wp), wp);
1.6 millert 1236: }
1237: break;
1238: }
1239: return(addSpace||added);
1240: }
1241: #endif
1242:
1.1 deraadt 1243: /*-
1244: *-----------------------------------------------------------------------
1245: * VarModify --
1246: * Modify each of the words of the passed string using the given
1247: * function. Used to implement all modifiers.
1248: *
1249: * Results:
1250: * A string of all the words modified appropriately.
1251: *
1252: * Side Effects:
1253: * None.
1254: *
1255: *-----------------------------------------------------------------------
1256: */
1257: static char *
1258: VarModify (str, modProc, datum)
1259: char *str; /* String whose words should be trimmed */
1260: /* Function to use to modify them */
1261: Boolean (*modProc) __P((char *, Boolean, Buffer, ClientData));
1262: ClientData datum; /* Datum to pass it */
1263: {
1264: Buffer buf; /* Buffer for the new string */
1265: Boolean addSpace; /* TRUE if need to add a space to the
1266: * buffer before adding the trimmed
1267: * word */
1.10 espie 1268: char **av; /* word list */
1269: char *as; /* word list memory */
1.1 deraadt 1270: int ac, i;
1271:
1.16 espie 1272: buf = Buf_Init(0);
1.1 deraadt 1273: addSpace = FALSE;
1274:
1.10 espie 1275: av = brk_string(str, &ac, FALSE, &as);
1.1 deraadt 1276:
1.10 espie 1277: for (i = 0; i < ac; i++)
1.1 deraadt 1278: addSpace = (*modProc)(av[i], addSpace, buf, datum);
1.5 millert 1279:
1.10 espie 1280: free(as);
1281: free(av);
1.16 espie 1282: Buf_AddChar(buf, '\0');
1283: str = Buf_GetAll(buf, NULL);
1284: Buf_Destroy(buf, FALSE);
1.1 deraadt 1285: return (str);
1286: }
1287:
1288: /*-
1289: *-----------------------------------------------------------------------
1.6 millert 1290: * VarGetPattern --
1291: * Pass through the tstr looking for 1) escaped delimiters,
1292: * '$'s and backslashes (place the escaped character in
1293: * uninterpreted) and 2) unescaped $'s that aren't before
1294: * the delimiter (expand the variable substitution).
1295: * Return the expanded string or NULL if the delimiter was missing
1.10 espie 1296: * If pattern is specified, handle escaped ampersands, and replace
1.6 millert 1297: * unescaped ampersands with the lhs of the pattern.
1298: *
1299: * Results:
1300: * A string of all the words modified appropriately.
1301: * If length is specified, return the string length of the buffer
1302: * If flags is specified and the last character of the pattern is a
1303: * $ set the VAR_MATCH_END bit of flags.
1304: *
1305: * Side Effects:
1306: * None.
1307: *-----------------------------------------------------------------------
1308: */
1309: static char *
1310: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
1311: GNode *ctxt;
1312: int err;
1313: char **tstr;
1314: int delim;
1315: int *flags;
1.16 espie 1316: size_t *length;
1.6 millert 1317: VarPattern *pattern;
1318: {
1319: char *cp;
1320: Buffer buf = Buf_Init(0);
1321: int junk;
1322: if (length == NULL)
1323: length = &junk;
1324:
1325: #define IS_A_MATCH(cp, delim) \
1326: ((cp[0] == '\\') && ((cp[1] == delim) || \
1327: (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
1328:
1329: /*
1330: * Skim through until the matching delimiter is found;
1331: * pick up variable substitutions on the way. Also allow
1332: * backslashes to quote the delimiter, $, and \, but don't
1333: * touch other backslashes.
1334: */
1335: for (cp = *tstr; *cp && (*cp != delim); cp++) {
1336: if (IS_A_MATCH(cp, delim)) {
1.16 espie 1337: Buf_AddChar(buf, cp[1]);
1.6 millert 1338: cp++;
1339: } else if (*cp == '$') {
1340: if (cp[1] == delim) {
1341: if (flags == NULL)
1.16 espie 1342: Buf_AddChar(buf, *cp);
1.6 millert 1343: else
1344: /*
1345: * Unescaped $ at end of pattern => anchor
1346: * pattern at end.
1347: */
1348: *flags |= VAR_MATCH_END;
1349: }
1350: else {
1351: char *cp2;
1352: int len;
1353: Boolean freeIt;
1354:
1355: /*
1356: * If unescaped dollar sign not before the
1357: * delimiter, assume it's a variable
1358: * substitution and recurse.
1359: */
1360: cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
1.19 espie 1361: Buf_AddString(buf, cp2);
1.6 millert 1362: if (freeIt)
1363: free(cp2);
1364: cp += len - 1;
1365: }
1366: }
1367: else if (pattern && *cp == '&')
1.16 espie 1368: Buf_AddChars(buf, pattern->leftLen, pattern->lhs);
1.6 millert 1369: else
1.16 espie 1370: Buf_AddChar(buf, *cp);
1.6 millert 1371: }
1372:
1.16 espie 1373: Buf_AddChar(buf, '\0');
1.6 millert 1374:
1375: if (*cp != delim) {
1376: *tstr = cp;
1377: *length = 0;
1378: return NULL;
1379: }
1380: else {
1381: *tstr = ++cp;
1.16 espie 1382: cp = Buf_GetAll(buf, length);
1.6 millert 1383: *length -= 1; /* Don't count the NULL */
1384: Buf_Destroy(buf, FALSE);
1385: return cp;
1386: }
1387: }
1388:
1389: /*-
1390: *-----------------------------------------------------------------------
1391: * VarQuote --
1392: * Quote shell meta-characters in the string
1393: *
1394: * Results:
1395: * The quoted string
1396: *
1397: * Side Effects:
1398: * None.
1399: *
1400: *-----------------------------------------------------------------------
1401: */
1402: static char *
1403: VarQuote(str)
1404: char *str;
1405: {
1406:
1407: Buffer buf;
1408: /* This should cover most shells :-( */
1409: static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1410:
1.16 espie 1411: buf = Buf_Init(MAKE_BSIZE);
1.6 millert 1412: for (; *str; str++) {
1413: if (strchr(meta, *str) != NULL)
1.16 espie 1414: Buf_AddChar(buf, '\\');
1415: Buf_AddChar(buf, *str);
1.6 millert 1416: }
1.16 espie 1417: Buf_AddChar(buf, '\0');
1418: str = Buf_GetAll(buf, NULL);
1419: Buf_Destroy(buf, FALSE);
1.6 millert 1420: return str;
1421: }
1422:
1423: /*-
1424: *-----------------------------------------------------------------------
1.1 deraadt 1425: * Var_Parse --
1426: * Given the start of a variable invocation, extract the variable
1427: * name and find its value, then modify it according to the
1428: * specification.
1429: *
1430: * Results:
1431: * The (possibly-modified) value of the variable or var_Error if the
1432: * specification is invalid. The length of the specification is
1433: * placed in *lengthPtr (for invalid specifications, this is just
1434: * 2...?).
1435: * A Boolean in *freePtr telling whether the returned string should
1436: * be freed by the caller.
1437: *
1438: * Side Effects:
1439: * None.
1440: *
1441: *-----------------------------------------------------------------------
1442: */
1443: char *
1444: Var_Parse (str, ctxt, err, lengthPtr, freePtr)
1445: char *str; /* The string to parse */
1446: GNode *ctxt; /* The context for the variable */
1447: Boolean err; /* TRUE if undefined variables are an error */
1448: int *lengthPtr; /* OUT: The length of the specification */
1449: Boolean *freePtr; /* OUT: TRUE if caller should free result */
1450: {
1451: register char *tstr; /* Pointer into str */
1452: Var *v; /* Variable in invocation */
1.6 millert 1453: char *cp; /* Secondary pointer into str (place marker
1.1 deraadt 1454: * for tstr) */
1455: Boolean haveModifier;/* TRUE if have modifiers for the variable */
1456: register char endc; /* Ending character when variable in parens
1457: * or braces */
1.2 deraadt 1458: register char startc=0; /* Starting character when variable in parens
1.1 deraadt 1459: * or braces */
1460: int cnt; /* Used to count brace pairs when variable in
1461: * in parens or braces */
1462: char *start;
1.6 millert 1463: char delim;
1.1 deraadt 1464: Boolean dynamic; /* TRUE if the variable is local and we're
1465: * expanding it in a non-local context. This
1466: * is done to support dynamic sources. The
1467: * result is just the invocation, unaltered */
1.5 millert 1468:
1.1 deraadt 1469: *freePtr = FALSE;
1470: dynamic = FALSE;
1471: start = str;
1.5 millert 1472:
1.1 deraadt 1473: if (str[1] != '(' && str[1] != '{') {
1474: /*
1475: * If it's not bounded by braces of some sort, life is much simpler.
1476: * We just need to check for the first character and return the
1477: * value if it exists.
1478: */
1479: char name[2];
1480:
1481: name[0] = str[1];
1482: name[1] = '\0';
1483:
1484: v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1485: if (v == (Var *)NIL) {
1486: *lengthPtr = 2;
1.5 millert 1487:
1.1 deraadt 1488: if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
1489: /*
1490: * If substituting a local variable in a non-local context,
1491: * assume it's for dynamic source stuff. We have to handle
1492: * this specially and return the longhand for the variable
1493: * with the dollar sign escaped so it makes it back to the
1494: * caller. Only four of the local variables are treated
1495: * specially as they are the only four that will be set
1496: * when dynamic sources are expanded.
1497: */
1498: switch (str[1]) {
1499: case '@':
1500: return("$(.TARGET)");
1501: case '%':
1502: return("$(.ARCHIVE)");
1503: case '*':
1504: return("$(.PREFIX)");
1505: case '!':
1506: return("$(.MEMBER)");
1507: }
1508: }
1509: /*
1510: * Error
1511: */
1512: return (err ? var_Error : varNoError);
1513: } else {
1514: haveModifier = FALSE;
1515: tstr = &str[1];
1516: endc = str[1];
1517: }
1518: } else {
1519: startc = str[1];
1520: endc = startc == '(' ? ')' : '}';
1521:
1522: /*
1523: * Skip to the end character or a colon, whichever comes first.
1524: */
1525: for (tstr = str + 2;
1526: *tstr != '\0' && *tstr != endc && *tstr != ':';
1527: tstr++)
1528: {
1529: continue;
1530: }
1531: if (*tstr == ':') {
1532: haveModifier = TRUE;
1533: } else if (*tstr != '\0') {
1534: haveModifier = FALSE;
1535: } else {
1536: /*
1537: * If we never did find the end character, return NULL
1538: * right now, setting the length to be the distance to
1539: * the end of the string, since that's what make does.
1540: */
1541: *lengthPtr = tstr - str;
1542: return (var_Error);
1543: }
1544: *tstr = '\0';
1.5 millert 1545:
1.1 deraadt 1546: v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1547: if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
1548: ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D'))
1549: {
1550: /*
1551: * Check for bogus D and F forms of local variables since we're
1552: * in a local context and the name is the right length.
1553: */
1554: switch(str[2]) {
1555: case '@':
1556: case '%':
1557: case '*':
1558: case '!':
1559: case '>':
1560: case '<':
1561: {
1562: char vname[2];
1563: char *val;
1564:
1565: /*
1566: * Well, it's local -- go look for it.
1567: */
1568: vname[0] = str[2];
1569: vname[1] = '\0';
1570: v = VarFind(vname, ctxt, 0);
1.5 millert 1571:
1.1 deraadt 1572: if (v != (Var *)NIL) {
1573: /*
1574: * No need for nested expansion or anything, as we're
1575: * the only one who sets these things and we sure don't
1576: * but nested invocations in them...
1577: */
1.16 espie 1578: val = Buf_GetAll(v->val, NULL);
1.5 millert 1579:
1.1 deraadt 1580: if (str[3] == 'D') {
1581: val = VarModify(val, VarHead, (ClientData)0);
1582: } else {
1583: val = VarModify(val, VarTail, (ClientData)0);
1584: }
1585: /*
1586: * Resulting string is dynamically allocated, so
1587: * tell caller to free it.
1588: */
1589: *freePtr = TRUE;
1590: *lengthPtr = tstr-start+1;
1591: *tstr = endc;
1592: return(val);
1593: }
1594: break;
1595: }
1596: }
1597: }
1.5 millert 1598:
1.1 deraadt 1599: if (v == (Var *)NIL) {
1600: if ((((tstr-str) == 3) ||
1601: ((((tstr-str) == 4) && (str[3] == 'F' ||
1602: str[3] == 'D')))) &&
1603: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1604: {
1605: /*
1606: * If substituting a local variable in a non-local context,
1607: * assume it's for dynamic source stuff. We have to handle
1608: * this specially and return the longhand for the variable
1609: * with the dollar sign escaped so it makes it back to the
1610: * caller. Only four of the local variables are treated
1611: * specially as they are the only four that will be set
1612: * when dynamic sources are expanded.
1613: */
1614: switch (str[2]) {
1615: case '@':
1616: case '%':
1617: case '*':
1618: case '!':
1619: dynamic = TRUE;
1620: break;
1621: }
1622: } else if (((tstr-str) > 4) && (str[2] == '.') &&
1623: isupper((unsigned char) str[3]) &&
1624: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1625: {
1626: int len;
1.5 millert 1627:
1.1 deraadt 1628: len = (tstr-str) - 3;
1629: if ((strncmp(str+2, ".TARGET", len) == 0) ||
1630: (strncmp(str+2, ".ARCHIVE", len) == 0) ||
1631: (strncmp(str+2, ".PREFIX", len) == 0) ||
1632: (strncmp(str+2, ".MEMBER", len) == 0))
1633: {
1634: dynamic = TRUE;
1635: }
1636: }
1.5 millert 1637:
1.1 deraadt 1638: if (!haveModifier) {
1639: /*
1640: * No modifiers -- have specification length so we can return
1641: * now.
1642: */
1643: *lengthPtr = tstr - start + 1;
1644: *tstr = endc;
1645: if (dynamic) {
1646: str = emalloc(*lengthPtr + 1);
1647: strncpy(str, start, *lengthPtr);
1648: str[*lengthPtr] = '\0';
1649: *freePtr = TRUE;
1650: return(str);
1651: } else {
1652: return (err ? var_Error : varNoError);
1653: }
1654: } else {
1655: /*
1656: * Still need to get to the end of the variable specification,
1657: * so kludge up a Var structure for the modifications
1658: */
1659: v = (Var *) emalloc(sizeof(Var));
1660: v->name = &str[1];
1661: v->val = Buf_Init(1);
1662: v->flags = VAR_JUNK;
1663: }
1664: }
1665: }
1666:
1667: if (v->flags & VAR_IN_USE) {
1668: Fatal("Variable %s is recursive.", v->name);
1669: /*NOTREACHED*/
1670: } else {
1671: v->flags |= VAR_IN_USE;
1672: }
1673: /*
1674: * Before doing any modification, we have to make sure the value
1675: * has been fully expanded. If it looks like recursion might be
1676: * necessary (there's a dollar sign somewhere in the variable's value)
1677: * we just call Var_Subst to do any other substitutions that are
1678: * necessary. Note that the value returned by Var_Subst will have
1679: * been dynamically-allocated, so it will need freeing when we
1680: * return.
1681: */
1.16 espie 1682: str = Buf_GetAll(v->val, NULL);
1.1 deraadt 1683: if (strchr (str, '$') != (char *)NULL) {
1684: str = Var_Subst(NULL, str, ctxt, err);
1685: *freePtr = TRUE;
1686: }
1.5 millert 1687:
1.1 deraadt 1688: v->flags &= ~VAR_IN_USE;
1.5 millert 1689:
1.1 deraadt 1690: /*
1691: * Now we need to apply any modifiers the user wants applied.
1692: * These are:
1693: * :M<pattern> words which match the given <pattern>.
1694: * <pattern> is of the standard file
1695: * wildcarding form.
1696: * :S<d><pat1><d><pat2><d>[g]
1697: * Substitute <pat2> for <pat1> in the value
1.6 millert 1698: * :C<d><pat1><d><pat2><d>[g]
1699: * Substitute <pat2> for regex <pat1> in the value
1.1 deraadt 1700: * :H Substitute the head of each word
1701: * :T Substitute the tail of each word
1702: * :E Substitute the extension (minus '.') of
1703: * each word
1704: * :R Substitute the root of each word
1705: * (pathname minus the suffix).
1706: * :lhs=rhs Like :S, but the rhs goes to the end of
1707: * the invocation.
1708: */
1709: if ((str != (char *)NULL) && haveModifier) {
1710: /*
1711: * Skip initial colon while putting it back.
1712: */
1713: *tstr++ = ':';
1714: while (*tstr != endc) {
1715: char *newStr; /* New value to return */
1716: char termc; /* Character which terminated scan */
1.5 millert 1717:
1.1 deraadt 1718: if (DEBUG(VAR)) {
1719: printf("Applying :%c to \"%s\"\n", *tstr, str);
1720: }
1721: switch (*tstr) {
1722: case 'N':
1723: case 'M':
1724: {
1725: char *pattern;
1726: char *cp2;
1727: Boolean copy;
1728:
1729: copy = FALSE;
1730: for (cp = tstr + 1;
1731: *cp != '\0' && *cp != ':' && *cp != endc;
1732: cp++)
1733: {
1734: if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
1735: copy = TRUE;
1736: cp++;
1737: }
1738: }
1739: termc = *cp;
1740: *cp = '\0';
1741: if (copy) {
1742: /*
1743: * Need to compress the \:'s out of the pattern, so
1744: * allocate enough room to hold the uncompressed
1745: * pattern (note that cp started at tstr+1, so
1746: * cp - tstr takes the null byte into account) and
1747: * compress the pattern into the space.
1748: */
1749: pattern = emalloc(cp - tstr);
1750: for (cp2 = pattern, cp = tstr + 1;
1751: *cp != '\0';
1752: cp++, cp2++)
1753: {
1754: if ((*cp == '\\') &&
1755: (cp[1] == ':' || cp[1] == endc)) {
1756: cp++;
1757: }
1758: *cp2 = *cp;
1759: }
1760: *cp2 = '\0';
1761: } else {
1762: pattern = &tstr[1];
1763: }
1764: if (*tstr == 'M' || *tstr == 'm') {
1765: newStr = VarModify(str, VarMatch, (ClientData)pattern);
1766: } else {
1767: newStr = VarModify(str, VarNoMatch,
1768: (ClientData)pattern);
1769: }
1770: if (copy) {
1771: free(pattern);
1772: }
1773: break;
1774: }
1775: case 'S':
1776: {
1777: VarPattern pattern;
1778:
1779: pattern.flags = 0;
1780: delim = tstr[1];
1781: tstr += 2;
1.6 millert 1782:
1.1 deraadt 1783: /*
1784: * If pattern begins with '^', it is anchored to the
1785: * start of the word -- skip over it and flag pattern.
1786: */
1787: if (*tstr == '^') {
1788: pattern.flags |= VAR_MATCH_START;
1789: tstr += 1;
1790: }
1791:
1.6 millert 1792: cp = tstr;
1793: if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
1794: &pattern.flags, &pattern.leftLen, NULL)) == NULL)
1795: goto cleanup;
1796:
1797: if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
1798: NULL, &pattern.rightLen, &pattern)) == NULL)
1799: goto cleanup;
1.5 millert 1800:
1.1 deraadt 1801: /*
1.6 millert 1802: * Check for global substitution. If 'g' after the final
1803: * delimiter, substitution is global and is marked that
1804: * way.
1.1 deraadt 1805: */
1.6 millert 1806: for (;; cp++) {
1807: switch (*cp) {
1808: case 'g':
1809: pattern.flags |= VAR_SUB_GLOBAL;
1810: continue;
1811: case '1':
1812: pattern.flags |= VAR_SUB_ONE;
1813: continue;
1.1 deraadt 1814: }
1.6 millert 1815: break;
1.1 deraadt 1816: }
1817:
1.6 millert 1818: termc = *cp;
1819: newStr = VarModify(str, VarSubstitute,
1820: (ClientData)&pattern);
1.5 millert 1821:
1.1 deraadt 1822: /*
1.6 millert 1823: * Free the two strings.
1.1 deraadt 1824: */
1.6 millert 1825: free(pattern.lhs);
1826: free(pattern.rhs);
1827: break;
1828: }
1829: #ifndef MAKE_BOOTSTRAP
1830: case 'C':
1831: {
1832: VarREPattern pattern;
1833: char *re;
1834: int error;
1835:
1836: pattern.flags = 0;
1837: delim = tstr[1];
1838: tstr += 2;
1.1 deraadt 1839:
1.6 millert 1840: cp = tstr;
1.1 deraadt 1841:
1.6 millert 1842: if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
1843: NULL, NULL)) == NULL)
1844: goto cleanup;
1845:
1846: if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
1847: delim, NULL, NULL, NULL)) == NULL) {
1848: free(re);
1849: goto cleanup;
1850: }
1.5 millert 1851:
1.6 millert 1852: for (;; cp++) {
1853: switch (*cp) {
1854: case 'g':
1855: pattern.flags |= VAR_SUB_GLOBAL;
1856: continue;
1857: case '1':
1858: pattern.flags |= VAR_SUB_ONE;
1859: continue;
1.1 deraadt 1860: }
1.6 millert 1861: break;
1.1 deraadt 1862: }
1863:
1.6 millert 1864: termc = *cp;
1.5 millert 1865:
1.6 millert 1866: error = regcomp(&pattern.re, re, REG_EXTENDED);
1867: free(re);
1868: if (error) {
1.1 deraadt 1869: *lengthPtr = cp - start + 1;
1.6 millert 1870: VarREError(error, &pattern.re, "RE substitution error");
1871: free(pattern.replace);
1.1 deraadt 1872: return (var_Error);
1873: }
1874:
1.6 millert 1875: pattern.nsub = pattern.re.re_nsub + 1;
1876: if (pattern.nsub < 1)
1877: pattern.nsub = 1;
1878: if (pattern.nsub > 10)
1879: pattern.nsub = 10;
1880: pattern.matches = emalloc(pattern.nsub *
1881: sizeof(regmatch_t));
1882: newStr = VarModify(str, VarRESubstitute,
1883: (ClientData) &pattern);
1884: regfree(&pattern.re);
1885: free(pattern.replace);
1886: free(pattern.matches);
1.1 deraadt 1887: break;
1888: }
1.6 millert 1889: #endif
1890: case 'Q':
1891: if (tstr[1] == endc || tstr[1] == ':') {
1892: newStr = VarQuote (str);
1893: cp = tstr + 1;
1894: termc = *cp;
1895: break;
1896: }
1897: /*FALLTHRU*/
1.1 deraadt 1898: case 'T':
1899: if (tstr[1] == endc || tstr[1] == ':') {
1900: newStr = VarModify (str, VarTail, (ClientData)0);
1901: cp = tstr + 1;
1902: termc = *cp;
1903: break;
1904: }
1905: /*FALLTHRU*/
1906: case 'H':
1907: if (tstr[1] == endc || tstr[1] == ':') {
1908: newStr = VarModify (str, VarHead, (ClientData)0);
1909: cp = tstr + 1;
1910: termc = *cp;
1911: break;
1912: }
1913: /*FALLTHRU*/
1914: case 'E':
1915: if (tstr[1] == endc || tstr[1] == ':') {
1916: newStr = VarModify (str, VarSuffix, (ClientData)0);
1917: cp = tstr + 1;
1918: termc = *cp;
1919: break;
1920: }
1921: /*FALLTHRU*/
1922: case 'R':
1923: if (tstr[1] == endc || tstr[1] == ':') {
1924: newStr = VarModify (str, VarRoot, (ClientData)0);
1.11 espie 1925: cp = tstr + 1;
1926: termc = *cp;
1927: break;
1928: }
1929: /*FALLTHRU*/
1930: case 'U':
1931: if (tstr[1] == endc || tstr[1] == ':') {
1932: newStr = VarModify (str, VarUppercase, (ClientData)0);
1933: cp = tstr + 1;
1934: termc = *cp;
1935: break;
1936: }
1937: /*FALLTHRU*/
1938: case 'L':
1939: if (tstr[1] == endc || tstr[1] == ':') {
1940: newStr = VarModify (str, VarLowercase, (ClientData)0);
1.1 deraadt 1941: cp = tstr + 1;
1942: termc = *cp;
1943: break;
1944: }
1945: /*FALLTHRU*/
1.4 briggs 1946: #ifdef SUNSHCMD
1947: case 's':
1948: if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
1949: char *err;
1950: newStr = Cmd_Exec (str, &err);
1951: if (err)
1952: Error (err, str);
1953: cp = tstr + 2;
1954: termc = *cp;
1955: break;
1956: }
1957: /*FALLTHRU*/
1958: #endif
1959: default:
1960: {
1961: #ifdef SYSVVARSUB
1.1 deraadt 1962: /*
1963: * This can either be a bogus modifier or a System-V
1964: * substitution command.
1965: */
1966: VarPattern pattern;
1967: Boolean eqFound;
1.5 millert 1968:
1.1 deraadt 1969: pattern.flags = 0;
1970: eqFound = FALSE;
1971: /*
1972: * First we make a pass through the string trying
1973: * to verify it is a SYSV-make-style translation:
1974: * it must be: <string1>=<string2>)
1975: */
1976: cp = tstr;
1977: cnt = 1;
1978: while (*cp != '\0' && cnt) {
1979: if (*cp == '=') {
1980: eqFound = TRUE;
1981: /* continue looking for endc */
1982: }
1983: else if (*cp == endc)
1984: cnt--;
1985: else if (*cp == startc)
1986: cnt++;
1987: if (cnt)
1988: cp++;
1989: }
1990: if (*cp == endc && eqFound) {
1.5 millert 1991:
1.1 deraadt 1992: /*
1993: * Now we break this sucker into the lhs and
1994: * rhs. We must null terminate them of course.
1995: */
1996: for (cp = tstr; *cp != '='; cp++)
1997: continue;
1998: pattern.lhs = tstr;
1999: pattern.leftLen = cp - tstr;
2000: *cp++ = '\0';
1.5 millert 2001:
1.1 deraadt 2002: pattern.rhs = cp;
2003: cnt = 1;
2004: while (cnt) {
2005: if (*cp == endc)
2006: cnt--;
2007: else if (*cp == startc)
2008: cnt++;
2009: if (cnt)
2010: cp++;
2011: }
2012: pattern.rightLen = cp - pattern.rhs;
2013: *cp = '\0';
1.5 millert 2014:
1.1 deraadt 2015: /*
2016: * SYSV modifications happen through the whole
2017: * string. Note the pattern is anchored at the end.
2018: */
2019: newStr = VarModify(str, VarSYSVMatch,
2020: (ClientData)&pattern);
2021:
2022: /*
2023: * Restore the nulled characters
2024: */
2025: pattern.lhs[pattern.leftLen] = '=';
2026: pattern.rhs[pattern.rightLen] = endc;
2027: termc = endc;
1.4 briggs 2028: } else
2029: #endif
2030: {
1.1 deraadt 2031: Error ("Unknown modifier '%c'\n", *tstr);
2032: for (cp = tstr+1;
2033: *cp != ':' && *cp != endc && *cp != '\0';
1.5 millert 2034: cp++)
1.1 deraadt 2035: continue;
2036: termc = *cp;
2037: newStr = var_Error;
2038: }
2039: }
2040: }
2041: if (DEBUG(VAR)) {
2042: printf("Result is \"%s\"\n", newStr);
2043: }
1.5 millert 2044:
1.1 deraadt 2045: if (*freePtr) {
2046: free (str);
2047: }
2048: str = newStr;
2049: if (str != var_Error) {
2050: *freePtr = TRUE;
2051: } else {
2052: *freePtr = FALSE;
2053: }
2054: if (termc == '\0') {
2055: Error("Unclosed variable specification for %s", v->name);
2056: } else if (termc == ':') {
2057: *cp++ = termc;
2058: } else {
2059: *cp = termc;
2060: }
2061: tstr = cp;
2062: }
2063: *lengthPtr = tstr - start + 1;
2064: } else {
2065: *lengthPtr = tstr - start + 1;
2066: *tstr = endc;
2067: }
1.5 millert 2068:
1.17 espie 2069: if (v->flags & VAR_JUNK) {
1.1 deraadt 2070: /*
2071: * Perform any free'ing needed and set *freePtr to FALSE so the caller
2072: * doesn't try to free a static pointer.
2073: */
2074: if (*freePtr) {
2075: free(str);
2076: }
2077: *freePtr = FALSE;
2078: Buf_Destroy(v->val, TRUE);
2079: free((Address)v);
2080: if (dynamic) {
2081: str = emalloc(*lengthPtr + 1);
2082: strncpy(str, start, *lengthPtr);
2083: str[*lengthPtr] = '\0';
2084: *freePtr = TRUE;
2085: } else {
1.12 espie 2086: str = err ? var_Error : varNoError;
1.1 deraadt 2087: }
2088: }
2089: return (str);
1.6 millert 2090:
2091: cleanup:
2092: *lengthPtr = cp - start + 1;
2093: if (*freePtr)
2094: free(str);
2095: Error("Unclosed substitution for %s (%c missing)",
2096: v->name, delim);
2097: return (var_Error);
1.1 deraadt 2098: }
2099:
2100: /*-
2101: *-----------------------------------------------------------------------
2102: * Var_Subst --
2103: * Substitute for all variables in the given string in the given context
2104: * If undefErr is TRUE, Parse_Error will be called when an undefined
2105: * variable is encountered.
2106: *
2107: * Results:
2108: * The resulting string.
2109: *
2110: * Side Effects:
2111: * None. The old string must be freed by the caller
2112: *-----------------------------------------------------------------------
2113: */
2114: char *
2115: Var_Subst (var, str, ctxt, undefErr)
2116: char *var; /* Named variable || NULL for all */
2117: char *str; /* the string in which to substitute */
2118: GNode *ctxt; /* the context wherein to find variables */
2119: Boolean undefErr; /* TRUE if undefineds are an error */
2120: {
2121: Buffer buf; /* Buffer for forming things */
2122: char *val; /* Value to substitute for a variable */
2123: int length; /* Length of the variable invocation */
2124: Boolean doFree; /* Set true if val should be freed */
2125: static Boolean errorReported; /* Set true if an error has already
2126: * been reported to prevent a plethora
2127: * of messages when recursing */
2128:
1.16 espie 2129: buf = Buf_Init(MAKE_BSIZE);
1.1 deraadt 2130: errorReported = FALSE;
2131:
2132: while (*str) {
2133: if (var == NULL && (*str == '$') && (str[1] == '$')) {
2134: /*
2135: * A dollar sign may be escaped either with another dollar sign.
2136: * In such a case, we skip over the escape character and store the
2137: * dollar sign into the buffer directly.
2138: */
2139: str++;
1.16 espie 2140: Buf_AddChar(buf, *str);
1.1 deraadt 2141: str++;
2142: } else if (*str != '$') {
2143: /*
2144: * Skip as many characters as possible -- either to the end of
2145: * the string or to the next dollar sign (variable invocation).
2146: */
2147: char *cp;
2148:
2149: for (cp = str++; *str != '$' && *str != '\0'; str++)
2150: continue;
1.19 espie 2151: Buf_AddInterval(buf, cp, str);
1.1 deraadt 2152: } else {
2153: if (var != NULL) {
2154: int expand;
2155: for (;;) {
2156: if (str[1] != '(' && str[1] != '{') {
1.13 espie 2157: if (str[1] != *var || var[1] != '\0') {
1.16 espie 2158: Buf_AddChars(buf, 2, str);
1.1 deraadt 2159: str += 2;
2160: expand = FALSE;
2161: }
2162: else
2163: expand = TRUE;
2164: break;
2165: }
2166: else {
2167: char *p;
2168:
2169: /*
2170: * Scan up to the end of the variable name.
2171: */
1.5 millert 2172: for (p = &str[2]; *p &&
1.1 deraadt 2173: *p != ':' && *p != ')' && *p != '}'; p++)
1.5 millert 2174: if (*p == '$')
1.1 deraadt 2175: break;
2176: /*
2177: * A variable inside the variable. We cannot expand
2178: * the external variable yet, so we try again with
2179: * the nested one
2180: */
2181: if (*p == '$') {
1.19 espie 2182: Buf_AddInterval(buf, str, p);
1.1 deraadt 2183: str = p;
2184: continue;
2185: }
1.5 millert 2186:
2187: if (strncmp(var, str + 2, p - str - 2) != 0 ||
1.1 deraadt 2188: var[p - str - 2] != '\0') {
2189: /*
2190: * Not the variable we want to expand, scan
2191: * until the next variable
2192: */
1.5 millert 2193: for (;*p != '$' && *p != '\0'; p++)
1.1 deraadt 2194: continue;
1.19 espie 2195: Buf_AddInterval(buf, str, p);
1.1 deraadt 2196: str = p;
2197: expand = FALSE;
2198: }
2199: else
2200: expand = TRUE;
2201: break;
2202: }
2203: }
2204: if (!expand)
2205: continue;
2206: }
1.5 millert 2207:
1.1 deraadt 2208: val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
2209:
2210: /*
2211: * When we come down here, val should either point to the
2212: * value of this variable, suitably modified, or be NULL.
2213: * Length should be the total length of the potential
2214: * variable invocation (from $ to end character...)
2215: */
2216: if (val == var_Error || val == varNoError) {
2217: /*
2218: * If performing old-time variable substitution, skip over
2219: * the variable and continue with the substitution. Otherwise,
2220: * store the dollar sign and advance str so we continue with
2221: * the string...
2222: */
2223: if (oldVars) {
2224: str += length;
2225: } else if (undefErr) {
2226: /*
2227: * If variable is undefined, complain and skip the
2228: * variable. The complaint will stop us from doing anything
2229: * when the file is parsed.
2230: */
2231: if (!errorReported) {
2232: Parse_Error (PARSE_FATAL,
2233: "Undefined variable \"%.*s\"",length,str);
2234: }
2235: str += length;
2236: errorReported = TRUE;
2237: } else {
1.16 espie 2238: Buf_AddChar(buf, *str);
1.1 deraadt 2239: str += 1;
2240: }
2241: } else {
2242: /*
2243: * We've now got a variable structure to store in. But first,
2244: * advance the string pointer.
2245: */
2246: str += length;
1.5 millert 2247:
1.1 deraadt 2248: /*
2249: * Copy all the characters from the variable value straight
2250: * into the new string.
2251: */
1.19 espie 2252: Buf_AddString(buf, val);
1.1 deraadt 2253: if (doFree) {
2254: free ((Address)val);
2255: }
2256: }
2257: }
2258: }
1.5 millert 2259:
1.16 espie 2260: Buf_AddChar(buf, '\0');
2261: str = Buf_GetAll(buf, NULL);
2262: Buf_Destroy(buf, FALSE);
1.1 deraadt 2263: return (str);
2264: }
2265:
2266: /*-
2267: *-----------------------------------------------------------------------
2268: * Var_GetTail --
2269: * Return the tail from each of a list of words. Used to set the
2270: * System V local variables.
2271: *
2272: * Results:
2273: * The resulting string.
2274: *
2275: * Side Effects:
2276: * None.
2277: *
2278: *-----------------------------------------------------------------------
2279: */
2280: char *
2281: Var_GetTail(file)
2282: char *file; /* Filename to modify */
2283: {
2284: return(VarModify(file, VarTail, (ClientData)0));
2285: }
2286:
2287: /*-
2288: *-----------------------------------------------------------------------
2289: * Var_GetHead --
2290: * Find the leading components of a (list of) filename(s).
2291: * XXX: VarHead does not replace foo by ., as (sun) System V make
2292: * does.
2293: *
2294: * Results:
2295: * The leading components.
2296: *
2297: * Side Effects:
2298: * None.
2299: *
2300: *-----------------------------------------------------------------------
2301: */
2302: char *
2303: Var_GetHead(file)
2304: char *file; /* Filename to manipulate */
2305: {
2306: return(VarModify(file, VarHead, (ClientData)0));
2307: }
2308:
2309: /*-
2310: *-----------------------------------------------------------------------
2311: * Var_Init --
2312: * Initialize the module
2313: *
2314: * Results:
2315: * None
2316: *
2317: * Side Effects:
1.5 millert 2318: * The VAR_CMD and VAR_GLOBAL contexts are created
1.1 deraadt 2319: *-----------------------------------------------------------------------
2320: */
2321: void
2322: Var_Init ()
2323: {
1.17 espie 2324: VAR_GLOBAL = Targ_NewGN("Global");
2325: VAR_CMD = Targ_NewGN("Command");
2326: VAR_ENV = Targ_NewGN("Environment");
1.1 deraadt 2327: allVars = Lst_Init(FALSE);
2328:
2329: }
2330:
2331:
2332: void
2333: Var_End ()
2334: {
2335: Lst_Destroy(allVars, VarDelete);
2336: }
1.5 millert 2337:
1.1 deraadt 2338:
2339: /****************** PRINT DEBUGGING INFO *****************/
2340: static int
2341: VarPrintVar (vp, dummy)
2342: ClientData vp;
2343: ClientData dummy;
2344: {
2345: Var *v = (Var *) vp;
1.16 espie 2346: printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, NULL));
1.1 deraadt 2347: return (dummy ? 0 : 0);
2348: }
2349:
2350: /*-
2351: *-----------------------------------------------------------------------
2352: * Var_Dump --
2353: * print all variables in a context
2354: *-----------------------------------------------------------------------
2355: */
2356: void
2357: Var_Dump (ctxt)
2358: GNode *ctxt;
2359: {
2360: Lst_ForEach (ctxt->context, VarPrintVar, (ClientData) 0);
2361: }