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