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