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