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