Annotation of src/usr.bin/make/var.c, Revision 1.23
1.23 ! espie 1: /* $OpenBSD: var.c,v 1.22 1999/12/16 16:52:11 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.23 ! espie 73: static char rcsid[] = "$OpenBSD: var.c,v 1.22 1999/12/16 16:52:11 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 */
1.23 ! espie 170: BUFFER val; /* its value */
1.1 deraadt 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.23 ! espie 205: #define VarValue(v) Buf_Retrieve(&((v)->val))
1.1 deraadt 206: static int VarCmp __P((ClientData, ClientData));
207: static Var *VarFind __P((char *, GNode *, int));
1.17 espie 208: static Var *VarAdd __P((char *, char *, GNode *));
1.1 deraadt 209: static void VarDelete __P((ClientData));
210: static Boolean VarHead __P((char *, Boolean, Buffer, ClientData));
211: static Boolean VarTail __P((char *, Boolean, Buffer, ClientData));
212: static Boolean VarSuffix __P((char *, Boolean, Buffer, ClientData));
213: static Boolean VarRoot __P((char *, Boolean, Buffer, ClientData));
214: static Boolean VarMatch __P((char *, Boolean, Buffer, ClientData));
1.4 briggs 215: #ifdef SYSVVARSUB
1.1 deraadt 216: static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData));
1.4 briggs 217: #endif
1.1 deraadt 218: static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData));
1.6 millert 219: #ifndef MAKE_BOOTSTRAP
220: static void VarREError __P((int, regex_t *, const char *));
221: static Boolean VarRESubstitute __P((char *, Boolean, Buffer, ClientData));
222: #endif
1.1 deraadt 223: static Boolean VarSubstitute __P((char *, Boolean, Buffer, ClientData));
1.16 espie 224: static char *VarGetPattern __P((GNode *, int, char **, int, int *, size_t *,
1.6 millert 225: VarPattern *));
226: static char *VarQuote __P((char *));
1.1 deraadt 227: static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer,
228: ClientData),
229: ClientData));
230: static int VarPrintVar __P((ClientData, ClientData));
1.14 espie 231: static Boolean VarUppercase __P((char *word, Boolean addSpace, Buffer buf, ClientData dummy));
232: static Boolean VarLowercase __P((char *word, Boolean addSpace, Buffer buf, ClientData dummy));
1.1 deraadt 233:
234: /*-
235: *-----------------------------------------------------------------------
236: * VarCmp --
237: * See if the given variable matches the named one. Called from
238: * Lst_Find when searching for a variable of a given name.
239: *
240: * Results:
241: * 0 if they match. non-zero otherwise.
242: *
243: * Side Effects:
244: * none
245: *-----------------------------------------------------------------------
246: */
247: static int
248: VarCmp (v, name)
249: ClientData v; /* VAR structure to compare */
250: ClientData name; /* name to look for */
251: {
252: return (strcmp ((char *) name, ((Var *) v)->name));
253: }
254:
255: /*-
256: *-----------------------------------------------------------------------
257: * VarFind --
258: * Find the given variable in the given context and any other contexts
259: * indicated.
260: *
261: * Results:
262: * A pointer to the structure describing the desired variable or
263: * NIL if the variable does not exist.
264: *
265: * Side Effects:
1.17 espie 266: * Caches env variables in the VAR_ENV context.
1.1 deraadt 267: *-----------------------------------------------------------------------
268: */
269: static Var *
270: VarFind (name, ctxt, flags)
271: char *name; /* name to find */
272: GNode *ctxt; /* context in which to find it */
273: int flags; /* FIND_GLOBAL set means to look in the
274: * VAR_GLOBAL context as well.
275: * FIND_CMD set means to look in the VAR_CMD
276: * context also.
277: * FIND_ENV set means to look in the
1.17 espie 278: * environment/VAR_ENV context. */
1.1 deraadt 279: {
280: LstNode var;
281: Var *v;
282:
283: /*
284: * If the variable name begins with a '.', it could very well be one of
285: * the local ones. We check the name against all the local variables
286: * and substitute the short version in for 'name' if it matches one of
287: * them.
288: */
289: if (*name == '.' && isupper((unsigned char) name[1]))
290: switch (name[1]) {
291: case 'A':
292: if (!strcmp(name, ".ALLSRC"))
293: name = ALLSRC;
294: if (!strcmp(name, ".ARCHIVE"))
295: name = ARCHIVE;
296: break;
297: case 'I':
298: if (!strcmp(name, ".IMPSRC"))
299: name = IMPSRC;
300: break;
301: case 'M':
302: if (!strcmp(name, ".MEMBER"))
303: name = MEMBER;
304: break;
305: case 'O':
306: if (!strcmp(name, ".OODATE"))
307: name = OODATE;
308: break;
309: case 'P':
310: if (!strcmp(name, ".PREFIX"))
311: name = PREFIX;
312: break;
313: case 'T':
314: if (!strcmp(name, ".TARGET"))
315: name = TARGET;
316: break;
317: }
318: /*
319: * First look for the variable in the given context. If it's not there,
320: * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
321: * depending on the FIND_* flags in 'flags'
322: */
1.17 espie 323: var = Lst_Find(ctxt->context, (ClientData)name, VarCmp);
1.1 deraadt 324:
1.17 espie 325: if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD))
1.1 deraadt 326: var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp);
327: if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) &&
1.17 espie 328: (ctxt != VAR_GLOBAL)) {
1.1 deraadt 329: var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
330: }
331: if ((var == NILLNODE) && (flags & FIND_ENV)) {
1.17 espie 332: var = Lst_Find(VAR_ENV->context, (ClientData)name, VarCmp);
333: if (var == NILLNODE) {
334: char *env;
335:
336: if ((env = getenv(name)) != NULL)
337: return VarAdd(name, env, VAR_ENV);
1.1 deraadt 338: }
1.17 espie 339: }
340: if (var == NILLNODE && checkEnvFirst && (flags & FIND_GLOBAL) &&
341: (ctxt != VAR_GLOBAL))
342: var = Lst_Find(VAR_GLOBAL->context, (ClientData)name, VarCmp);
343: if (var == NILLNODE)
1.1 deraadt 344: return ((Var *) NIL);
1.17 espie 345: else
1.1 deraadt 346: return ((Var *) Lst_Datum (var));
347: }
348:
349: /*-
350: *-----------------------------------------------------------------------
351: * VarAdd --
352: * Add a new variable of name name and value val to the given context
353: *
354: * Results:
1.17 espie 355: * The added variable
1.1 deraadt 356: *
357: * Side Effects:
358: * The new variable is placed at the front of the given context
359: * The name and val arguments are duplicated so they may
360: * safely be freed.
361: *-----------------------------------------------------------------------
362: */
1.17 espie 363: static Var *
364: VarAdd(name, val, ctxt)
1.1 deraadt 365: char *name; /* name of variable to add */
366: char *val; /* value to set it to */
367: GNode *ctxt; /* context in which to set it */
368: {
369: register Var *v;
370: int len;
371:
372: v = (Var *) emalloc (sizeof (Var));
373:
1.4 briggs 374: v->name = estrdup (name);
1.1 deraadt 375:
376: len = val ? strlen(val) : 0;
1.23 ! espie 377: Buf_Init(&(v->val), len+1);
! 378: Buf_AddChars(&(v->val), len, val);
1.1 deraadt 379:
380: v->flags = 0;
381:
382: (void) Lst_AtFront (ctxt->context, (ClientData)v);
383: (void) Lst_AtEnd (allVars, (ClientData) v);
384: if (DEBUG(VAR)) {
385: printf("%s:%s = %s\n", ctxt->name, name, val);
386: }
1.17 espie 387: return v;
1.1 deraadt 388: }
389:
390:
391: /*-
392: *-----------------------------------------------------------------------
393: * VarDelete --
394: * Delete a variable and all the space associated with it.
395: *
396: * Results:
397: * None
398: *
399: * Side Effects:
400: * None
401: *-----------------------------------------------------------------------
402: */
403: static void
404: VarDelete(vp)
405: ClientData vp;
406: {
407: Var *v = (Var *) vp;
408: free(v->name);
1.23 ! espie 409: Buf_Destroy(&(v->val));
! 410: free(v);
1.1 deraadt 411: }
412:
413:
414:
415: /*-
416: *-----------------------------------------------------------------------
417: * Var_Delete --
418: * Remove a variable from a context.
419: *
420: * Results:
421: * None.
422: *
423: * Side Effects:
424: * The Var structure is removed and freed.
425: *
426: *-----------------------------------------------------------------------
427: */
428: void
429: Var_Delete(name, ctxt)
430: char *name;
431: GNode *ctxt;
432: {
433: LstNode ln;
434:
435: if (DEBUG(VAR)) {
436: printf("%s:delete %s\n", ctxt->name, name);
437: }
438: ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp);
439: if (ln != NILLNODE) {
440: register Var *v;
441:
442: v = (Var *)Lst_Datum(ln);
443: Lst_Remove(ctxt->context, ln);
444: ln = Lst_Member(allVars, v);
445: Lst_Remove(allVars, ln);
446: VarDelete((ClientData) v);
447: }
448: }
449:
450: /*-
451: *-----------------------------------------------------------------------
452: * Var_Set --
453: * Set the variable name to the value val in the given context.
454: *
455: * Results:
456: * None.
457: *
458: * Side Effects:
459: * If the variable doesn't yet exist, a new record is created for it.
460: * Else the old value is freed and the new one stuck in its place
461: *
462: * Notes:
463: * The variable is searched for only in its context before being
464: * created in that context. I.e. if the context is VAR_GLOBAL,
465: * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
466: * VAR_CMD->context is searched. This is done to avoid the literally
467: * thousands of unnecessary strcmp's that used to be done to
468: * set, say, $(@) or $(<).
469: *-----------------------------------------------------------------------
470: */
471: void
472: Var_Set (name, val, ctxt)
473: char *name; /* name of variable to set */
474: char *val; /* value to give to the variable */
475: GNode *ctxt; /* context in which to set it */
476: {
477: register Var *v;
478:
479: /*
480: * We only look for a variable in the given context since anything set
481: * here will override anything in a lower context, so there's not much
482: * point in searching them all just to save a bit of memory...
483: */
484: v = VarFind (name, ctxt, 0);
485: if (v == (Var *) NIL) {
1.17 espie 486: (void)VarAdd(name, val, ctxt);
1.1 deraadt 487: } else {
1.23 ! espie 488: Buf_Reset(&(v->val));
! 489: Buf_AddString(&(v->val), val);
1.1 deraadt 490:
491: if (DEBUG(VAR)) {
492: printf("%s:%s = %s\n", ctxt->name, name, val);
493: }
494: }
495: /*
496: * Any variables given on the command line are automatically exported
1.17 espie 497: * to the environment (as per POSIX standard).
498: * We put them into the env cache directly.
499: * (Note that additions to VAR_CMD occur very early, so VAR_ENV is
500: * actually empty at this point).
1.1 deraadt 501: */
502: if (ctxt == VAR_CMD) {
503: setenv(name, val, 1);
1.17 espie 504: (void)VarAdd(name, val, VAR_ENV);
1.1 deraadt 505: }
506: }
507:
508: /*-
509: *-----------------------------------------------------------------------
510: * Var_Append --
511: * The variable of the given name has the given value appended to it in
512: * the given context.
513: *
514: * Results:
515: * None
516: *
517: * Side Effects:
518: * If the variable doesn't exist, it is created. Else the strings
519: * are concatenated (with a space in between).
520: *
521: * Notes:
522: * Only if the variable is being sought in the global context is the
523: * environment searched.
524: * XXX: Knows its calling circumstances in that if called with ctxt
525: * an actual target, it will only search that context since only
526: * a local variable could be being appended to. This is actually
527: * a big win and must be tolerated.
528: *-----------------------------------------------------------------------
529: */
530: void
531: Var_Append (name, val, ctxt)
532: char *name; /* Name of variable to modify */
533: char *val; /* String to append to it */
534: GNode *ctxt; /* Context in which this should occur */
535: {
536: register Var *v;
537:
538: v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
539:
540: if (v == (Var *) NIL) {
1.17 espie 541: (void)VarAdd(name, val, ctxt);
1.1 deraadt 542: } else {
1.23 ! espie 543: Buf_AddSpace(&(v->val));
! 544: Buf_AddString(&(v->val), val);
1.1 deraadt 545:
546: if (DEBUG(VAR)) {
1.23 ! espie 547: printf("%s:%s = %s\n", ctxt->name, name, VarValue(v));
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);
1.23 ! espie 601: if (v != (Var *)NIL)
! 602: return VarValue(v);
1.21 espie 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: {
1.23 ! espie 1263: BUFFER buf; /* Buffer for the new string */
1.1 deraadt 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.23 ! espie 1271: Buf_Init(&buf, 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.23 ! espie 1277: addSpace = (*modProc)(av[i], addSpace, &buf, datum);
1.5 millert 1278:
1.10 espie 1279: free(as);
1280: free(av);
1.23 ! espie 1281: return Buf_Retrieve(&buf);
1.1 deraadt 1282: }
1283:
1284: /*-
1285: *-----------------------------------------------------------------------
1.6 millert 1286: * VarGetPattern --
1287: * Pass through the tstr looking for 1) escaped delimiters,
1288: * '$'s and backslashes (place the escaped character in
1289: * uninterpreted) and 2) unescaped $'s that aren't before
1290: * the delimiter (expand the variable substitution).
1291: * Return the expanded string or NULL if the delimiter was missing
1.10 espie 1292: * If pattern is specified, handle escaped ampersands, and replace
1.6 millert 1293: * unescaped ampersands with the lhs of the pattern.
1294: *
1295: * Results:
1296: * A string of all the words modified appropriately.
1297: * If length is specified, return the string length of the buffer
1298: * If flags is specified and the last character of the pattern is a
1299: * $ set the VAR_MATCH_END bit of flags.
1300: *
1301: * Side Effects:
1302: * None.
1303: *-----------------------------------------------------------------------
1304: */
1305: static char *
1306: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
1307: GNode *ctxt;
1308: int err;
1309: char **tstr;
1310: int delim;
1311: int *flags;
1.16 espie 1312: size_t *length;
1.6 millert 1313: VarPattern *pattern;
1314: {
1315: char *cp;
1.23 ! espie 1316: BUFFER buf;
1.6 millert 1317: int junk;
1.23 ! espie 1318:
! 1319: Buf_Init(&buf, 0);
1.6 millert 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.23 ! 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.23 ! 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.23 ! 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.23 ! espie 1366: Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
1.6 millert 1367: else
1.23 ! 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.23 ! espie 1378: *length = Buf_Size(&buf);
! 1379: return Buf_Retrieve(&buf);
1.6 millert 1380: }
1381: }
1382:
1383: /*-
1384: *-----------------------------------------------------------------------
1385: * VarQuote --
1386: * Quote shell meta-characters in the string
1387: *
1388: * Results:
1389: * The quoted string
1390: *
1391: * Side Effects:
1392: * None.
1393: *
1394: *-----------------------------------------------------------------------
1395: */
1396: static char *
1397: VarQuote(str)
1398: char *str;
1399: {
1400:
1.23 ! espie 1401: BUFFER buf;
1.6 millert 1402: /* This should cover most shells :-( */
1403: static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1404:
1.23 ! espie 1405: Buf_Init(&buf, MAKE_BSIZE);
1.6 millert 1406: for (; *str; str++) {
1407: if (strchr(meta, *str) != NULL)
1.23 ! espie 1408: Buf_AddChar(&buf, '\\');
! 1409: Buf_AddChar(&buf, *str);
1.6 millert 1410: }
1.23 ! espie 1411: return Buf_Retrieve(&buf);
1.6 millert 1412: }
1413:
1414: /*-
1415: *-----------------------------------------------------------------------
1.1 deraadt 1416: * Var_Parse --
1417: * Given the start of a variable invocation, extract the variable
1418: * name and find its value, then modify it according to the
1419: * specification.
1420: *
1421: * Results:
1422: * The (possibly-modified) value of the variable or var_Error if the
1423: * specification is invalid. The length of the specification is
1424: * placed in *lengthPtr (for invalid specifications, this is just
1425: * 2...?).
1426: * A Boolean in *freePtr telling whether the returned string should
1427: * be freed by the caller.
1428: *
1429: * Side Effects:
1430: * None.
1431: *
1432: *-----------------------------------------------------------------------
1433: */
1434: char *
1435: Var_Parse (str, ctxt, err, lengthPtr, freePtr)
1436: char *str; /* The string to parse */
1437: GNode *ctxt; /* The context for the variable */
1438: Boolean err; /* TRUE if undefined variables are an error */
1439: int *lengthPtr; /* OUT: The length of the specification */
1440: Boolean *freePtr; /* OUT: TRUE if caller should free result */
1441: {
1442: register char *tstr; /* Pointer into str */
1443: Var *v; /* Variable in invocation */
1.6 millert 1444: char *cp; /* Secondary pointer into str (place marker
1.1 deraadt 1445: * for tstr) */
1446: Boolean haveModifier;/* TRUE if have modifiers for the variable */
1447: register char endc; /* Ending character when variable in parens
1448: * or braces */
1.2 deraadt 1449: register char startc=0; /* Starting character when variable in parens
1.1 deraadt 1450: * or braces */
1451: int cnt; /* Used to count brace pairs when variable in
1452: * in parens or braces */
1453: char *start;
1.6 millert 1454: char delim;
1.1 deraadt 1455: Boolean dynamic; /* TRUE if the variable is local and we're
1456: * expanding it in a non-local context. This
1457: * is done to support dynamic sources. The
1458: * result is just the invocation, unaltered */
1.5 millert 1459:
1.1 deraadt 1460: *freePtr = FALSE;
1461: dynamic = FALSE;
1462: start = str;
1.5 millert 1463:
1.1 deraadt 1464: if (str[1] != '(' && str[1] != '{') {
1465: /*
1466: * If it's not bounded by braces of some sort, life is much simpler.
1467: * We just need to check for the first character and return the
1468: * value if it exists.
1469: */
1470: char name[2];
1471:
1472: name[0] = str[1];
1473: name[1] = '\0';
1474:
1475: v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1476: if (v == (Var *)NIL) {
1477: *lengthPtr = 2;
1.5 millert 1478:
1.1 deraadt 1479: if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
1480: /*
1481: * If substituting a local variable in a non-local context,
1482: * assume it's for dynamic source stuff. We have to handle
1483: * this specially and return the longhand for the variable
1484: * with the dollar sign escaped so it makes it back to the
1485: * caller. Only four of the local variables are treated
1486: * specially as they are the only four that will be set
1487: * when dynamic sources are expanded.
1488: */
1489: switch (str[1]) {
1490: case '@':
1491: return("$(.TARGET)");
1492: case '%':
1493: return("$(.ARCHIVE)");
1494: case '*':
1495: return("$(.PREFIX)");
1496: case '!':
1497: return("$(.MEMBER)");
1498: }
1499: }
1500: /*
1501: * Error
1502: */
1503: return (err ? var_Error : varNoError);
1504: } else {
1505: haveModifier = FALSE;
1506: tstr = &str[1];
1507: endc = str[1];
1508: }
1509: } else {
1510: startc = str[1];
1511: endc = startc == '(' ? ')' : '}';
1512:
1513: /*
1514: * Skip to the end character or a colon, whichever comes first.
1515: */
1516: for (tstr = str + 2;
1517: *tstr != '\0' && *tstr != endc && *tstr != ':';
1518: tstr++)
1519: {
1520: continue;
1521: }
1522: if (*tstr == ':') {
1523: haveModifier = TRUE;
1524: } else if (*tstr != '\0') {
1525: haveModifier = FALSE;
1526: } else {
1527: /*
1528: * If we never did find the end character, return NULL
1529: * right now, setting the length to be the distance to
1530: * the end of the string, since that's what make does.
1531: */
1532: *lengthPtr = tstr - str;
1533: return (var_Error);
1534: }
1535: *tstr = '\0';
1.5 millert 1536:
1.1 deraadt 1537: v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
1538: if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
1539: ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D'))
1540: {
1541: /*
1542: * Check for bogus D and F forms of local variables since we're
1543: * in a local context and the name is the right length.
1544: */
1545: switch(str[2]) {
1546: case '@':
1547: case '%':
1548: case '*':
1549: case '!':
1550: case '>':
1551: case '<':
1552: {
1553: char vname[2];
1554: char *val;
1555:
1556: /*
1557: * Well, it's local -- go look for it.
1558: */
1559: vname[0] = str[2];
1560: vname[1] = '\0';
1561: v = VarFind(vname, ctxt, 0);
1.5 millert 1562:
1.1 deraadt 1563: if (v != (Var *)NIL) {
1564: /*
1565: * No need for nested expansion or anything, as we're
1566: * the only one who sets these things and we sure don't
1567: * but nested invocations in them...
1568: */
1.23 ! espie 1569: val = VarValue(v);
1.5 millert 1570:
1.1 deraadt 1571: if (str[3] == 'D') {
1572: val = VarModify(val, VarHead, (ClientData)0);
1573: } else {
1574: val = VarModify(val, VarTail, (ClientData)0);
1575: }
1576: /*
1577: * Resulting string is dynamically allocated, so
1578: * tell caller to free it.
1579: */
1580: *freePtr = TRUE;
1581: *lengthPtr = tstr-start+1;
1582: *tstr = endc;
1583: return(val);
1584: }
1585: break;
1586: }
1587: }
1588: }
1.5 millert 1589:
1.1 deraadt 1590: if (v == (Var *)NIL) {
1591: if ((((tstr-str) == 3) ||
1592: ((((tstr-str) == 4) && (str[3] == 'F' ||
1593: str[3] == 'D')))) &&
1594: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1595: {
1596: /*
1597: * If substituting a local variable in a non-local context,
1598: * assume it's for dynamic source stuff. We have to handle
1599: * this specially and return the longhand for the variable
1600: * with the dollar sign escaped so it makes it back to the
1601: * caller. Only four of the local variables are treated
1602: * specially as they are the only four that will be set
1603: * when dynamic sources are expanded.
1604: */
1605: switch (str[2]) {
1606: case '@':
1607: case '%':
1608: case '*':
1609: case '!':
1610: dynamic = TRUE;
1611: break;
1612: }
1613: } else if (((tstr-str) > 4) && (str[2] == '.') &&
1614: isupper((unsigned char) str[3]) &&
1615: ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
1616: {
1617: int len;
1.5 millert 1618:
1.1 deraadt 1619: len = (tstr-str) - 3;
1620: if ((strncmp(str+2, ".TARGET", len) == 0) ||
1621: (strncmp(str+2, ".ARCHIVE", len) == 0) ||
1622: (strncmp(str+2, ".PREFIX", len) == 0) ||
1623: (strncmp(str+2, ".MEMBER", len) == 0))
1624: {
1625: dynamic = TRUE;
1626: }
1627: }
1.5 millert 1628:
1.1 deraadt 1629: if (!haveModifier) {
1630: /*
1631: * No modifiers -- have specification length so we can return
1632: * now.
1633: */
1634: *lengthPtr = tstr - start + 1;
1635: *tstr = endc;
1636: if (dynamic) {
1637: str = emalloc(*lengthPtr + 1);
1638: strncpy(str, start, *lengthPtr);
1639: str[*lengthPtr] = '\0';
1640: *freePtr = TRUE;
1641: return(str);
1642: } else {
1643: return (err ? var_Error : varNoError);
1644: }
1645: } else {
1646: /*
1647: * Still need to get to the end of the variable specification,
1648: * so kludge up a Var structure for the modifications
1649: */
1650: v = (Var *) emalloc(sizeof(Var));
1651: v->name = &str[1];
1.23 ! espie 1652: Buf_Init(&(v->val), 1);
1.1 deraadt 1653: v->flags = VAR_JUNK;
1654: }
1655: }
1656: }
1657:
1658: if (v->flags & VAR_IN_USE) {
1659: Fatal("Variable %s is recursive.", v->name);
1660: /*NOTREACHED*/
1661: } else {
1662: v->flags |= VAR_IN_USE;
1663: }
1664: /*
1665: * Before doing any modification, we have to make sure the value
1666: * has been fully expanded. If it looks like recursion might be
1667: * necessary (there's a dollar sign somewhere in the variable's value)
1668: * we just call Var_Subst to do any other substitutions that are
1669: * necessary. Note that the value returned by Var_Subst will have
1670: * been dynamically-allocated, so it will need freeing when we
1671: * return.
1672: */
1.23 ! espie 1673: str = VarValue(v);
1.1 deraadt 1674: if (strchr (str, '$') != (char *)NULL) {
1675: str = Var_Subst(NULL, str, ctxt, err);
1676: *freePtr = TRUE;
1677: }
1.5 millert 1678:
1.1 deraadt 1679: v->flags &= ~VAR_IN_USE;
1.5 millert 1680:
1.1 deraadt 1681: /*
1682: * Now we need to apply any modifiers the user wants applied.
1683: * These are:
1684: * :M<pattern> words which match the given <pattern>.
1685: * <pattern> is of the standard file
1686: * wildcarding form.
1687: * :S<d><pat1><d><pat2><d>[g]
1688: * Substitute <pat2> for <pat1> in the value
1.6 millert 1689: * :C<d><pat1><d><pat2><d>[g]
1690: * Substitute <pat2> for regex <pat1> in the value
1.1 deraadt 1691: * :H Substitute the head of each word
1692: * :T Substitute the tail of each word
1693: * :E Substitute the extension (minus '.') of
1694: * each word
1695: * :R Substitute the root of each word
1696: * (pathname minus the suffix).
1697: * :lhs=rhs Like :S, but the rhs goes to the end of
1698: * the invocation.
1699: */
1700: if ((str != (char *)NULL) && haveModifier) {
1701: /*
1702: * Skip initial colon while putting it back.
1703: */
1704: *tstr++ = ':';
1705: while (*tstr != endc) {
1706: char *newStr; /* New value to return */
1707: char termc; /* Character which terminated scan */
1.5 millert 1708:
1.1 deraadt 1709: if (DEBUG(VAR)) {
1710: printf("Applying :%c to \"%s\"\n", *tstr, str);
1711: }
1712: switch (*tstr) {
1713: case 'N':
1714: case 'M':
1715: {
1716: char *pattern;
1717: char *cp2;
1718: Boolean copy;
1719:
1720: copy = FALSE;
1721: for (cp = tstr + 1;
1722: *cp != '\0' && *cp != ':' && *cp != endc;
1723: cp++)
1724: {
1725: if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
1726: copy = TRUE;
1727: cp++;
1728: }
1729: }
1730: termc = *cp;
1731: *cp = '\0';
1732: if (copy) {
1733: /*
1734: * Need to compress the \:'s out of the pattern, so
1735: * allocate enough room to hold the uncompressed
1736: * pattern (note that cp started at tstr+1, so
1737: * cp - tstr takes the null byte into account) and
1738: * compress the pattern into the space.
1739: */
1740: pattern = emalloc(cp - tstr);
1741: for (cp2 = pattern, cp = tstr + 1;
1742: *cp != '\0';
1743: cp++, cp2++)
1744: {
1745: if ((*cp == '\\') &&
1746: (cp[1] == ':' || cp[1] == endc)) {
1747: cp++;
1748: }
1749: *cp2 = *cp;
1750: }
1751: *cp2 = '\0';
1752: } else {
1753: pattern = &tstr[1];
1754: }
1755: if (*tstr == 'M' || *tstr == 'm') {
1756: newStr = VarModify(str, VarMatch, (ClientData)pattern);
1757: } else {
1758: newStr = VarModify(str, VarNoMatch,
1759: (ClientData)pattern);
1760: }
1761: if (copy) {
1762: free(pattern);
1763: }
1764: break;
1765: }
1766: case 'S':
1767: {
1768: VarPattern pattern;
1769:
1770: pattern.flags = 0;
1771: delim = tstr[1];
1772: tstr += 2;
1.6 millert 1773:
1.1 deraadt 1774: /*
1775: * If pattern begins with '^', it is anchored to the
1776: * start of the word -- skip over it and flag pattern.
1777: */
1778: if (*tstr == '^') {
1779: pattern.flags |= VAR_MATCH_START;
1780: tstr += 1;
1781: }
1782:
1.6 millert 1783: cp = tstr;
1784: if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
1785: &pattern.flags, &pattern.leftLen, NULL)) == NULL)
1786: goto cleanup;
1787:
1788: if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
1789: NULL, &pattern.rightLen, &pattern)) == NULL)
1790: goto cleanup;
1.5 millert 1791:
1.1 deraadt 1792: /*
1.6 millert 1793: * Check for global substitution. If 'g' after the final
1794: * delimiter, substitution is global and is marked that
1795: * way.
1.1 deraadt 1796: */
1.6 millert 1797: for (;; cp++) {
1798: switch (*cp) {
1799: case 'g':
1800: pattern.flags |= VAR_SUB_GLOBAL;
1801: continue;
1802: case '1':
1803: pattern.flags |= VAR_SUB_ONE;
1804: continue;
1.1 deraadt 1805: }
1.6 millert 1806: break;
1.1 deraadt 1807: }
1808:
1.6 millert 1809: termc = *cp;
1810: newStr = VarModify(str, VarSubstitute,
1811: (ClientData)&pattern);
1.5 millert 1812:
1.1 deraadt 1813: /*
1.6 millert 1814: * Free the two strings.
1.1 deraadt 1815: */
1.6 millert 1816: free(pattern.lhs);
1817: free(pattern.rhs);
1818: break;
1819: }
1820: #ifndef MAKE_BOOTSTRAP
1821: case 'C':
1822: {
1823: VarREPattern pattern;
1824: char *re;
1825: int error;
1826:
1827: pattern.flags = 0;
1828: delim = tstr[1];
1829: tstr += 2;
1.1 deraadt 1830:
1.6 millert 1831: cp = tstr;
1.1 deraadt 1832:
1.6 millert 1833: if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
1834: NULL, NULL)) == NULL)
1835: goto cleanup;
1836:
1837: if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
1838: delim, NULL, NULL, NULL)) == NULL) {
1839: free(re);
1840: goto cleanup;
1841: }
1.5 millert 1842:
1.6 millert 1843: for (;; cp++) {
1844: switch (*cp) {
1845: case 'g':
1846: pattern.flags |= VAR_SUB_GLOBAL;
1847: continue;
1848: case '1':
1849: pattern.flags |= VAR_SUB_ONE;
1850: continue;
1.1 deraadt 1851: }
1.6 millert 1852: break;
1.1 deraadt 1853: }
1854:
1.6 millert 1855: termc = *cp;
1.5 millert 1856:
1.6 millert 1857: error = regcomp(&pattern.re, re, REG_EXTENDED);
1858: free(re);
1859: if (error) {
1.1 deraadt 1860: *lengthPtr = cp - start + 1;
1.6 millert 1861: VarREError(error, &pattern.re, "RE substitution error");
1862: free(pattern.replace);
1.1 deraadt 1863: return (var_Error);
1864: }
1865:
1.6 millert 1866: pattern.nsub = pattern.re.re_nsub + 1;
1867: if (pattern.nsub < 1)
1868: pattern.nsub = 1;
1869: if (pattern.nsub > 10)
1870: pattern.nsub = 10;
1871: pattern.matches = emalloc(pattern.nsub *
1872: sizeof(regmatch_t));
1873: newStr = VarModify(str, VarRESubstitute,
1874: (ClientData) &pattern);
1875: regfree(&pattern.re);
1876: free(pattern.replace);
1877: free(pattern.matches);
1.1 deraadt 1878: break;
1879: }
1.6 millert 1880: #endif
1881: case 'Q':
1882: if (tstr[1] == endc || tstr[1] == ':') {
1883: newStr = VarQuote (str);
1884: cp = tstr + 1;
1885: termc = *cp;
1886: break;
1887: }
1888: /*FALLTHRU*/
1.1 deraadt 1889: case 'T':
1890: if (tstr[1] == endc || tstr[1] == ':') {
1891: newStr = VarModify (str, VarTail, (ClientData)0);
1892: cp = tstr + 1;
1893: termc = *cp;
1894: break;
1895: }
1896: /*FALLTHRU*/
1897: case 'H':
1898: if (tstr[1] == endc || tstr[1] == ':') {
1899: newStr = VarModify (str, VarHead, (ClientData)0);
1900: cp = tstr + 1;
1901: termc = *cp;
1902: break;
1903: }
1904: /*FALLTHRU*/
1905: case 'E':
1906: if (tstr[1] == endc || tstr[1] == ':') {
1907: newStr = VarModify (str, VarSuffix, (ClientData)0);
1908: cp = tstr + 1;
1909: termc = *cp;
1910: break;
1911: }
1912: /*FALLTHRU*/
1913: case 'R':
1914: if (tstr[1] == endc || tstr[1] == ':') {
1915: newStr = VarModify (str, VarRoot, (ClientData)0);
1.11 espie 1916: cp = tstr + 1;
1917: termc = *cp;
1918: break;
1919: }
1920: /*FALLTHRU*/
1921: case 'U':
1922: if (tstr[1] == endc || tstr[1] == ':') {
1923: newStr = VarModify (str, VarUppercase, (ClientData)0);
1924: cp = tstr + 1;
1925: termc = *cp;
1926: break;
1927: }
1928: /*FALLTHRU*/
1929: case 'L':
1930: if (tstr[1] == endc || tstr[1] == ':') {
1931: newStr = VarModify (str, VarLowercase, (ClientData)0);
1.1 deraadt 1932: cp = tstr + 1;
1933: termc = *cp;
1934: break;
1935: }
1936: /*FALLTHRU*/
1.4 briggs 1937: #ifdef SUNSHCMD
1938: case 's':
1939: if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
1940: char *err;
1941: newStr = Cmd_Exec (str, &err);
1942: if (err)
1943: Error (err, str);
1944: cp = tstr + 2;
1945: termc = *cp;
1946: break;
1947: }
1948: /*FALLTHRU*/
1949: #endif
1950: default:
1951: {
1952: #ifdef SYSVVARSUB
1.1 deraadt 1953: /*
1954: * This can either be a bogus modifier or a System-V
1955: * substitution command.
1956: */
1957: VarPattern pattern;
1958: Boolean eqFound;
1.5 millert 1959:
1.1 deraadt 1960: pattern.flags = 0;
1961: eqFound = FALSE;
1962: /*
1963: * First we make a pass through the string trying
1964: * to verify it is a SYSV-make-style translation:
1965: * it must be: <string1>=<string2>)
1966: */
1967: cp = tstr;
1968: cnt = 1;
1969: while (*cp != '\0' && cnt) {
1970: if (*cp == '=') {
1971: eqFound = TRUE;
1972: /* continue looking for endc */
1973: }
1974: else if (*cp == endc)
1975: cnt--;
1976: else if (*cp == startc)
1977: cnt++;
1978: if (cnt)
1979: cp++;
1980: }
1981: if (*cp == endc && eqFound) {
1.5 millert 1982:
1.1 deraadt 1983: /*
1984: * Now we break this sucker into the lhs and
1985: * rhs. We must null terminate them of course.
1986: */
1987: for (cp = tstr; *cp != '='; cp++)
1988: continue;
1989: pattern.lhs = tstr;
1990: pattern.leftLen = cp - tstr;
1991: *cp++ = '\0';
1.5 millert 1992:
1.1 deraadt 1993: pattern.rhs = cp;
1994: cnt = 1;
1995: while (cnt) {
1996: if (*cp == endc)
1997: cnt--;
1998: else if (*cp == startc)
1999: cnt++;
2000: if (cnt)
2001: cp++;
2002: }
2003: pattern.rightLen = cp - pattern.rhs;
2004: *cp = '\0';
1.5 millert 2005:
1.1 deraadt 2006: /*
2007: * SYSV modifications happen through the whole
2008: * string. Note the pattern is anchored at the end.
2009: */
2010: newStr = VarModify(str, VarSYSVMatch,
2011: (ClientData)&pattern);
2012:
2013: /*
2014: * Restore the nulled characters
2015: */
2016: pattern.lhs[pattern.leftLen] = '=';
2017: pattern.rhs[pattern.rightLen] = endc;
2018: termc = endc;
1.4 briggs 2019: } else
2020: #endif
2021: {
1.1 deraadt 2022: Error ("Unknown modifier '%c'\n", *tstr);
2023: for (cp = tstr+1;
2024: *cp != ':' && *cp != endc && *cp != '\0';
1.5 millert 2025: cp++)
1.1 deraadt 2026: continue;
2027: termc = *cp;
2028: newStr = var_Error;
2029: }
2030: }
2031: }
2032: if (DEBUG(VAR)) {
2033: printf("Result is \"%s\"\n", newStr);
2034: }
1.5 millert 2035:
1.1 deraadt 2036: if (*freePtr) {
2037: free (str);
2038: }
2039: str = newStr;
2040: if (str != var_Error) {
2041: *freePtr = TRUE;
2042: } else {
2043: *freePtr = FALSE;
2044: }
2045: if (termc == '\0') {
2046: Error("Unclosed variable specification for %s", v->name);
2047: } else if (termc == ':') {
2048: *cp++ = termc;
2049: } else {
2050: *cp = termc;
2051: }
2052: tstr = cp;
2053: }
2054: *lengthPtr = tstr - start + 1;
2055: } else {
2056: *lengthPtr = tstr - start + 1;
2057: *tstr = endc;
2058: }
1.5 millert 2059:
1.17 espie 2060: if (v->flags & VAR_JUNK) {
1.1 deraadt 2061: /*
2062: * Perform any free'ing needed and set *freePtr to FALSE so the caller
2063: * doesn't try to free a static pointer.
2064: */
2065: if (*freePtr) {
2066: free(str);
2067: }
2068: *freePtr = FALSE;
1.23 ! espie 2069: Buf_Destroy(&(v->val));
! 2070: free(v);
1.1 deraadt 2071: if (dynamic) {
2072: str = emalloc(*lengthPtr + 1);
2073: strncpy(str, start, *lengthPtr);
2074: str[*lengthPtr] = '\0';
2075: *freePtr = TRUE;
2076: } else {
1.12 espie 2077: str = err ? var_Error : varNoError;
1.1 deraadt 2078: }
2079: }
2080: return (str);
1.6 millert 2081:
2082: cleanup:
2083: *lengthPtr = cp - start + 1;
2084: if (*freePtr)
2085: free(str);
2086: Error("Unclosed substitution for %s (%c missing)",
2087: v->name, delim);
2088: return (var_Error);
1.1 deraadt 2089: }
2090:
2091: /*-
2092: *-----------------------------------------------------------------------
2093: * Var_Subst --
2094: * Substitute for all variables in the given string in the given context
2095: * If undefErr is TRUE, Parse_Error will be called when an undefined
2096: * variable is encountered.
2097: *
2098: * Results:
2099: * The resulting string.
2100: *
2101: * Side Effects:
2102: * None. The old string must be freed by the caller
2103: *-----------------------------------------------------------------------
2104: */
2105: char *
2106: Var_Subst (var, str, ctxt, undefErr)
2107: char *var; /* Named variable || NULL for all */
2108: char *str; /* the string in which to substitute */
2109: GNode *ctxt; /* the context wherein to find variables */
2110: Boolean undefErr; /* TRUE if undefineds are an error */
2111: {
1.23 ! espie 2112: BUFFER buf; /* Buffer for forming things */
1.1 deraadt 2113: char *val; /* Value to substitute for a variable */
2114: int length; /* Length of the variable invocation */
2115: Boolean doFree; /* Set true if val should be freed */
2116: static Boolean errorReported; /* Set true if an error has already
2117: * been reported to prevent a plethora
2118: * of messages when recursing */
2119:
1.23 ! espie 2120: Buf_Init(&buf, MAKE_BSIZE);
1.1 deraadt 2121: errorReported = FALSE;
2122:
2123: while (*str) {
2124: if (var == NULL && (*str == '$') && (str[1] == '$')) {
2125: /*
2126: * A dollar sign may be escaped either with another dollar sign.
2127: * In such a case, we skip over the escape character and store the
2128: * dollar sign into the buffer directly.
2129: */
2130: str++;
1.23 ! espie 2131: Buf_AddChar(&buf, *str);
1.1 deraadt 2132: str++;
2133: } else if (*str != '$') {
2134: /*
2135: * Skip as many characters as possible -- either to the end of
2136: * the string or to the next dollar sign (variable invocation).
2137: */
2138: char *cp;
2139:
2140: for (cp = str++; *str != '$' && *str != '\0'; str++)
2141: continue;
1.23 ! espie 2142: Buf_AddInterval(&buf, cp, str);
1.1 deraadt 2143: } else {
2144: if (var != NULL) {
2145: int expand;
2146: for (;;) {
2147: if (str[1] != '(' && str[1] != '{') {
1.13 espie 2148: if (str[1] != *var || var[1] != '\0') {
1.23 ! espie 2149: Buf_AddChars(&buf, 2, str);
1.1 deraadt 2150: str += 2;
2151: expand = FALSE;
2152: }
2153: else
2154: expand = TRUE;
2155: break;
2156: }
2157: else {
2158: char *p;
2159:
2160: /*
2161: * Scan up to the end of the variable name.
2162: */
1.5 millert 2163: for (p = &str[2]; *p &&
1.1 deraadt 2164: *p != ':' && *p != ')' && *p != '}'; p++)
1.5 millert 2165: if (*p == '$')
1.1 deraadt 2166: break;
2167: /*
2168: * A variable inside the variable. We cannot expand
2169: * the external variable yet, so we try again with
2170: * the nested one
2171: */
2172: if (*p == '$') {
1.23 ! espie 2173: Buf_AddInterval(&buf, str, p);
1.1 deraadt 2174: str = p;
2175: continue;
2176: }
1.5 millert 2177:
2178: if (strncmp(var, str + 2, p - str - 2) != 0 ||
1.1 deraadt 2179: var[p - str - 2] != '\0') {
2180: /*
2181: * Not the variable we want to expand, scan
2182: * until the next variable
2183: */
1.5 millert 2184: for (;*p != '$' && *p != '\0'; p++)
1.1 deraadt 2185: continue;
1.23 ! espie 2186: Buf_AddInterval(&buf, str, p);
1.1 deraadt 2187: str = p;
2188: expand = FALSE;
2189: }
2190: else
2191: expand = TRUE;
2192: break;
2193: }
2194: }
2195: if (!expand)
2196: continue;
2197: }
1.5 millert 2198:
1.1 deraadt 2199: val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
2200:
2201: /*
2202: * When we come down here, val should either point to the
2203: * value of this variable, suitably modified, or be NULL.
2204: * Length should be the total length of the potential
2205: * variable invocation (from $ to end character...)
2206: */
2207: if (val == var_Error || val == varNoError) {
2208: /*
2209: * If performing old-time variable substitution, skip over
2210: * the variable and continue with the substitution. Otherwise,
2211: * store the dollar sign and advance str so we continue with
2212: * the string...
2213: */
2214: if (oldVars) {
2215: str += length;
2216: } else if (undefErr) {
2217: /*
2218: * If variable is undefined, complain and skip the
2219: * variable. The complaint will stop us from doing anything
2220: * when the file is parsed.
2221: */
2222: if (!errorReported) {
2223: Parse_Error (PARSE_FATAL,
2224: "Undefined variable \"%.*s\"",length,str);
2225: }
2226: str += length;
2227: errorReported = TRUE;
2228: } else {
1.23 ! espie 2229: Buf_AddChar(&buf, *str);
1.1 deraadt 2230: str += 1;
2231: }
2232: } else {
2233: /*
2234: * We've now got a variable structure to store in. But first,
2235: * advance the string pointer.
2236: */
2237: str += length;
1.5 millert 2238:
1.1 deraadt 2239: /*
2240: * Copy all the characters from the variable value straight
2241: * into the new string.
2242: */
1.23 ! espie 2243: Buf_AddString(&buf, val);
! 2244: if (doFree)
! 2245: free(val);
1.1 deraadt 2246: }
2247: }
2248: }
1.5 millert 2249:
1.23 ! espie 2250: return Buf_Retrieve(&buf);
1.1 deraadt 2251: }
2252:
2253: /*-
2254: *-----------------------------------------------------------------------
2255: * Var_GetTail --
2256: * Return the tail from each of a list of words. Used to set the
2257: * System V local variables.
2258: *
2259: * Results:
2260: * The resulting string.
2261: *
2262: * Side Effects:
2263: * None.
2264: *
2265: *-----------------------------------------------------------------------
2266: */
2267: char *
2268: Var_GetTail(file)
2269: char *file; /* Filename to modify */
2270: {
2271: return(VarModify(file, VarTail, (ClientData)0));
2272: }
2273:
2274: /*-
2275: *-----------------------------------------------------------------------
2276: * Var_GetHead --
2277: * Find the leading components of a (list of) filename(s).
2278: * XXX: VarHead does not replace foo by ., as (sun) System V make
2279: * does.
2280: *
2281: * Results:
2282: * The leading components.
2283: *
2284: * Side Effects:
2285: * None.
2286: *
2287: *-----------------------------------------------------------------------
2288: */
2289: char *
2290: Var_GetHead(file)
2291: char *file; /* Filename to manipulate */
2292: {
2293: return(VarModify(file, VarHead, (ClientData)0));
2294: }
2295:
2296: /*-
2297: *-----------------------------------------------------------------------
2298: * Var_Init --
2299: * Initialize the module
2300: *
2301: * Results:
2302: * None
2303: *
2304: * Side Effects:
1.5 millert 2305: * The VAR_CMD and VAR_GLOBAL contexts are created
1.1 deraadt 2306: *-----------------------------------------------------------------------
2307: */
2308: void
2309: Var_Init ()
2310: {
1.17 espie 2311: VAR_GLOBAL = Targ_NewGN("Global");
2312: VAR_CMD = Targ_NewGN("Command");
2313: VAR_ENV = Targ_NewGN("Environment");
1.1 deraadt 2314: allVars = Lst_Init(FALSE);
2315:
2316: }
2317:
2318:
2319: void
2320: Var_End ()
2321: {
2322: Lst_Destroy(allVars, VarDelete);
2323: }
1.5 millert 2324:
1.1 deraadt 2325:
2326: /****************** PRINT DEBUGGING INFO *****************/
2327: static int
1.23 ! espie 2328: VarPrintVar(vp, dummy)
1.1 deraadt 2329: ClientData vp;
2330: ClientData dummy;
2331: {
2332: Var *v = (Var *) vp;
1.23 ! espie 2333: printf("%-16s = %s\n", v->name, VarValue(v));
1.1 deraadt 2334: return (dummy ? 0 : 0);
2335: }
2336:
2337: /*-
2338: *-----------------------------------------------------------------------
2339: * Var_Dump --
2340: * print all variables in a context
2341: *-----------------------------------------------------------------------
2342: */
2343: void
2344: Var_Dump (ctxt)
2345: GNode *ctxt;
2346: {
2347: Lst_ForEach (ctxt->context, VarPrintVar, (ClientData) 0);
2348: }