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