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