1 /* $OpenBSD: var.c,v 1.8 1998/03/30 06:59:39 deraadt Exp $ */ 2 /* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1988, 1989, 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * Copyright (c) 1989 by Berkeley Softworks 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Adam de Boor. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; 45 #else 46 static char rcsid[] = "$OpenBSD: var.c,v 1.8 1998/03/30 06:59:39 deraadt 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 /* 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 (*word) { 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 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ 1032 done = TRUE; 1033 } 1034 pattern->flags |= VAR_SUB_MATCHED; 1035 } else { 1036 done = TRUE; 1037 } 1038 } 1039 if (wordLen != 0) { 1040 if (addSpace) { 1041 Buf_AddByte(buf, (Byte)' '); 1042 } 1043 Buf_AddBytes(buf, wordLen, (Byte *)word); 1044 } 1045 /* 1046 * If added characters to the buffer, need to add a space 1047 * before we add any more. If we didn't add any, just return 1048 * the previous value of addSpace. 1049 */ 1050 return ((Buf_Size(buf) != origSize) || addSpace); 1051 } 1052 return (addSpace); 1053 } 1054 nosub: 1055 if (addSpace) { 1056 Buf_AddByte(buf, (Byte)' '); 1057 } 1058 Buf_AddBytes(buf, wordLen, (Byte *)word); 1059 return(TRUE); 1060 } 1061 1062 #ifndef MAKE_BOOTSTRAP 1063 /*- 1064 *----------------------------------------------------------------------- 1065 * VarREError -- 1066 * Print the error caused by a regcomp or regexec call. 1067 * 1068 * Results: 1069 * None. 1070 * 1071 * Side Effects: 1072 * An error gets printed. 1073 * 1074 *----------------------------------------------------------------------- 1075 */ 1076 static void 1077 VarREError(err, pat, str) 1078 int err; 1079 regex_t *pat; 1080 const char *str; 1081 { 1082 char *errbuf; 1083 int errlen; 1084 1085 errlen = regerror(err, pat, 0, 0); 1086 errbuf = emalloc(errlen); 1087 regerror(err, pat, errbuf, errlen); 1088 Error("%s: %s", str, errbuf); 1089 free(errbuf); 1090 } 1091 1092 /*- 1093 *----------------------------------------------------------------------- 1094 * VarRESubstitute -- 1095 * Perform a regex substitution on the given word, placing the 1096 * result in the passed buffer. 1097 * 1098 * Results: 1099 * TRUE if a space is needed before more characters are added. 1100 * 1101 * Side Effects: 1102 * None. 1103 * 1104 *----------------------------------------------------------------------- 1105 */ 1106 static Boolean 1107 VarRESubstitute(word, addSpace, buf, patternp) 1108 char *word; 1109 Boolean addSpace; 1110 Buffer buf; 1111 ClientData patternp; 1112 { 1113 VarREPattern *pat; 1114 int xrv; 1115 char *wp; 1116 char *rp; 1117 int added; 1118 1119 #define MAYBE_ADD_SPACE() \ 1120 if (addSpace && !added) \ 1121 Buf_AddByte(buf, ' '); \ 1122 added = 1 1123 1124 added = 0; 1125 wp = word; 1126 pat = patternp; 1127 1128 if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == 1129 (VAR_SUB_ONE|VAR_SUB_MATCHED)) 1130 xrv = REG_NOMATCH; 1131 else { 1132 tryagain: 1133 xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); 1134 } 1135 1136 switch (xrv) { 1137 case 0: 1138 pat->flags |= VAR_SUB_MATCHED; 1139 if (pat->matches[0].rm_so > 0) { 1140 MAYBE_ADD_SPACE(); 1141 Buf_AddBytes(buf, pat->matches[0].rm_so, wp); 1142 } 1143 1144 for (rp = pat->replace; *rp; rp++) { 1145 if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { 1146 MAYBE_ADD_SPACE(); 1147 Buf_AddByte(buf,rp[1]); 1148 rp++; 1149 } 1150 else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) { 1151 int n; 1152 char *subbuf; 1153 int sublen; 1154 char errstr[3]; 1155 1156 if (*rp == '&') { 1157 n = 0; 1158 errstr[0] = '&'; 1159 errstr[1] = '\0'; 1160 } else { 1161 n = rp[1] - '0'; 1162 errstr[0] = '\\'; 1163 errstr[1] = rp[1]; 1164 errstr[2] = '\0'; 1165 rp++; 1166 } 1167 1168 if (n > pat->nsub) { 1169 Error("No subexpression %s", &errstr[0]); 1170 subbuf = ""; 1171 sublen = 0; 1172 } else if ((pat->matches[n].rm_so == -1) && 1173 (pat->matches[n].rm_eo == -1)) { 1174 Error("No match for subexpression %s", &errstr[0]); 1175 subbuf = ""; 1176 sublen = 0; 1177 } else { 1178 subbuf = wp + pat->matches[n].rm_so; 1179 sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; 1180 } 1181 1182 if (sublen > 0) { 1183 MAYBE_ADD_SPACE(); 1184 Buf_AddBytes(buf, sublen, subbuf); 1185 } 1186 } else { 1187 MAYBE_ADD_SPACE(); 1188 Buf_AddByte(buf, *rp); 1189 } 1190 } 1191 wp += pat->matches[0].rm_eo; 1192 if (pat->flags & VAR_SUB_GLOBAL) 1193 goto tryagain; 1194 if (*wp) { 1195 MAYBE_ADD_SPACE(); 1196 Buf_AddBytes(buf, strlen(wp), wp); 1197 } 1198 break; 1199 default: 1200 VarREError(xrv, &pat->re, "Unexpected regex error"); 1201 /* fall through */ 1202 case REG_NOMATCH: 1203 if (*wp) { 1204 MAYBE_ADD_SPACE(); 1205 Buf_AddBytes(buf,strlen(wp),wp); 1206 } 1207 break; 1208 } 1209 return(addSpace||added); 1210 } 1211 #endif 1212 1213 /*- 1214 *----------------------------------------------------------------------- 1215 * VarModify -- 1216 * Modify each of the words of the passed string using the given 1217 * function. Used to implement all modifiers. 1218 * 1219 * Results: 1220 * A string of all the words modified appropriately. 1221 * 1222 * Side Effects: 1223 * None. 1224 * 1225 *----------------------------------------------------------------------- 1226 */ 1227 static char * 1228 VarModify (str, modProc, datum) 1229 char *str; /* String whose words should be trimmed */ 1230 /* Function to use to modify them */ 1231 Boolean (*modProc) __P((char *, Boolean, Buffer, ClientData)); 1232 ClientData datum; /* Datum to pass it */ 1233 { 1234 Buffer buf; /* Buffer for the new string */ 1235 Boolean addSpace; /* TRUE if need to add a space to the 1236 * buffer before adding the trimmed 1237 * word */ 1238 char **av; /* word list [first word does not count] */ 1239 int ac, i; 1240 1241 buf = Buf_Init (0); 1242 addSpace = FALSE; 1243 1244 av = brk_string(str, &ac, FALSE); 1245 1246 for (i = 1; i < ac; i++) 1247 addSpace = (*modProc)(av[i], addSpace, buf, datum); 1248 1249 Buf_AddByte (buf, '\0'); 1250 str = (char *)Buf_GetAll (buf, (int *)NULL); 1251 Buf_Destroy (buf, FALSE); 1252 return (str); 1253 } 1254 1255 /*- 1256 *----------------------------------------------------------------------- 1257 * VarGetPattern -- 1258 * Pass through the tstr looking for 1) escaped delimiters, 1259 * '$'s and backslashes (place the escaped character in 1260 * uninterpreted) and 2) unescaped $'s that aren't before 1261 * the delimiter (expand the variable substitution). 1262 * Return the expanded string or NULL if the delimiter was missing 1263 * If pattern is specified, handle escaped ampersants, and replace 1264 * unescaped ampersands with the lhs of the pattern. 1265 * 1266 * Results: 1267 * A string of all the words modified appropriately. 1268 * If length is specified, return the string length of the buffer 1269 * If flags is specified and the last character of the pattern is a 1270 * $ set the VAR_MATCH_END bit of flags. 1271 * 1272 * Side Effects: 1273 * None. 1274 *----------------------------------------------------------------------- 1275 */ 1276 static char * 1277 VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern) 1278 GNode *ctxt; 1279 int err; 1280 char **tstr; 1281 int delim; 1282 int *flags; 1283 int *length; 1284 VarPattern *pattern; 1285 { 1286 char *cp; 1287 Buffer buf = Buf_Init(0); 1288 int junk; 1289 if (length == NULL) 1290 length = &junk; 1291 1292 #define IS_A_MATCH(cp, delim) \ 1293 ((cp[0] == '\\') && ((cp[1] == delim) || \ 1294 (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) 1295 1296 /* 1297 * Skim through until the matching delimiter is found; 1298 * pick up variable substitutions on the way. Also allow 1299 * backslashes to quote the delimiter, $, and \, but don't 1300 * touch other backslashes. 1301 */ 1302 for (cp = *tstr; *cp && (*cp != delim); cp++) { 1303 if (IS_A_MATCH(cp, delim)) { 1304 Buf_AddByte(buf, (Byte) cp[1]); 1305 cp++; 1306 } else if (*cp == '$') { 1307 if (cp[1] == delim) { 1308 if (flags == NULL) 1309 Buf_AddByte(buf, (Byte) *cp); 1310 else 1311 /* 1312 * Unescaped $ at end of pattern => anchor 1313 * pattern at end. 1314 */ 1315 *flags |= VAR_MATCH_END; 1316 } 1317 else { 1318 char *cp2; 1319 int len; 1320 Boolean freeIt; 1321 1322 /* 1323 * If unescaped dollar sign not before the 1324 * delimiter, assume it's a variable 1325 * substitution and recurse. 1326 */ 1327 cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); 1328 Buf_AddBytes(buf, strlen(cp2), (Byte *) cp2); 1329 if (freeIt) 1330 free(cp2); 1331 cp += len - 1; 1332 } 1333 } 1334 else if (pattern && *cp == '&') 1335 Buf_AddBytes(buf, pattern->leftLen, (Byte *)pattern->lhs); 1336 else 1337 Buf_AddByte(buf, (Byte) *cp); 1338 } 1339 1340 Buf_AddByte(buf, (Byte) '\0'); 1341 1342 if (*cp != delim) { 1343 *tstr = cp; 1344 *length = 0; 1345 return NULL; 1346 } 1347 else { 1348 *tstr = ++cp; 1349 cp = (char *) Buf_GetAll(buf, length); 1350 *length -= 1; /* Don't count the NULL */ 1351 Buf_Destroy(buf, FALSE); 1352 return cp; 1353 } 1354 } 1355 1356 /*- 1357 *----------------------------------------------------------------------- 1358 * VarQuote -- 1359 * Quote shell meta-characters in the string 1360 * 1361 * Results: 1362 * The quoted string 1363 * 1364 * Side Effects: 1365 * None. 1366 * 1367 *----------------------------------------------------------------------- 1368 */ 1369 static char * 1370 VarQuote(str) 1371 char *str; 1372 { 1373 1374 Buffer buf; 1375 /* This should cover most shells :-( */ 1376 static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1377 1378 buf = Buf_Init (MAKE_BSIZE); 1379 for (; *str; str++) { 1380 if (strchr(meta, *str) != NULL) 1381 Buf_AddByte(buf, (Byte)'\\'); 1382 Buf_AddByte(buf, (Byte)*str); 1383 } 1384 Buf_AddByte(buf, (Byte) '\0'); 1385 str = (char *)Buf_GetAll (buf, (int *)NULL); 1386 Buf_Destroy (buf, FALSE); 1387 return str; 1388 } 1389 1390 /*- 1391 *----------------------------------------------------------------------- 1392 * Var_Parse -- 1393 * Given the start of a variable invocation, extract the variable 1394 * name and find its value, then modify it according to the 1395 * specification. 1396 * 1397 * Results: 1398 * The (possibly-modified) value of the variable or var_Error if the 1399 * specification is invalid. The length of the specification is 1400 * placed in *lengthPtr (for invalid specifications, this is just 1401 * 2...?). 1402 * A Boolean in *freePtr telling whether the returned string should 1403 * be freed by the caller. 1404 * 1405 * Side Effects: 1406 * None. 1407 * 1408 *----------------------------------------------------------------------- 1409 */ 1410 char * 1411 Var_Parse (str, ctxt, err, lengthPtr, freePtr) 1412 char *str; /* The string to parse */ 1413 GNode *ctxt; /* The context for the variable */ 1414 Boolean err; /* TRUE if undefined variables are an error */ 1415 int *lengthPtr; /* OUT: The length of the specification */ 1416 Boolean *freePtr; /* OUT: TRUE if caller should free result */ 1417 { 1418 register char *tstr; /* Pointer into str */ 1419 Var *v; /* Variable in invocation */ 1420 char *cp; /* Secondary pointer into str (place marker 1421 * for tstr) */ 1422 Boolean haveModifier;/* TRUE if have modifiers for the variable */ 1423 register char endc; /* Ending character when variable in parens 1424 * or braces */ 1425 register char startc=0; /* Starting character when variable in parens 1426 * or braces */ 1427 int cnt; /* Used to count brace pairs when variable in 1428 * in parens or braces */ 1429 char *start; 1430 char delim; 1431 Boolean dynamic; /* TRUE if the variable is local and we're 1432 * expanding it in a non-local context. This 1433 * is done to support dynamic sources. The 1434 * result is just the invocation, unaltered */ 1435 1436 *freePtr = FALSE; 1437 dynamic = FALSE; 1438 start = str; 1439 1440 if (str[1] != '(' && str[1] != '{') { 1441 /* 1442 * If it's not bounded by braces of some sort, life is much simpler. 1443 * We just need to check for the first character and return the 1444 * value if it exists. 1445 */ 1446 char name[2]; 1447 1448 name[0] = str[1]; 1449 name[1] = '\0'; 1450 1451 v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); 1452 if (v == (Var *)NIL) { 1453 *lengthPtr = 2; 1454 1455 if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { 1456 /* 1457 * If substituting a local variable in a non-local context, 1458 * assume it's for dynamic source stuff. We have to handle 1459 * this specially and return the longhand for the variable 1460 * with the dollar sign escaped so it makes it back to the 1461 * caller. Only four of the local variables are treated 1462 * specially as they are the only four that will be set 1463 * when dynamic sources are expanded. 1464 */ 1465 switch (str[1]) { 1466 case '@': 1467 return("$(.TARGET)"); 1468 case '%': 1469 return("$(.ARCHIVE)"); 1470 case '*': 1471 return("$(.PREFIX)"); 1472 case '!': 1473 return("$(.MEMBER)"); 1474 } 1475 } 1476 /* 1477 * Error 1478 */ 1479 return (err ? var_Error : varNoError); 1480 } else { 1481 haveModifier = FALSE; 1482 tstr = &str[1]; 1483 endc = str[1]; 1484 } 1485 } else { 1486 startc = str[1]; 1487 endc = startc == '(' ? ')' : '}'; 1488 1489 /* 1490 * Skip to the end character or a colon, whichever comes first. 1491 */ 1492 for (tstr = str + 2; 1493 *tstr != '\0' && *tstr != endc && *tstr != ':'; 1494 tstr++) 1495 { 1496 continue; 1497 } 1498 if (*tstr == ':') { 1499 haveModifier = TRUE; 1500 } else if (*tstr != '\0') { 1501 haveModifier = FALSE; 1502 } else { 1503 /* 1504 * If we never did find the end character, return NULL 1505 * right now, setting the length to be the distance to 1506 * the end of the string, since that's what make does. 1507 */ 1508 *lengthPtr = tstr - str; 1509 return (var_Error); 1510 } 1511 *tstr = '\0'; 1512 1513 v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); 1514 if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && 1515 ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) 1516 { 1517 /* 1518 * Check for bogus D and F forms of local variables since we're 1519 * in a local context and the name is the right length. 1520 */ 1521 switch(str[2]) { 1522 case '@': 1523 case '%': 1524 case '*': 1525 case '!': 1526 case '>': 1527 case '<': 1528 { 1529 char vname[2]; 1530 char *val; 1531 1532 /* 1533 * Well, it's local -- go look for it. 1534 */ 1535 vname[0] = str[2]; 1536 vname[1] = '\0'; 1537 v = VarFind(vname, ctxt, 0); 1538 1539 if (v != (Var *)NIL) { 1540 /* 1541 * No need for nested expansion or anything, as we're 1542 * the only one who sets these things and we sure don't 1543 * but nested invocations in them... 1544 */ 1545 val = (char *)Buf_GetAll(v->val, (int *)NULL); 1546 1547 if (str[3] == 'D') { 1548 val = VarModify(val, VarHead, (ClientData)0); 1549 } else { 1550 val = VarModify(val, VarTail, (ClientData)0); 1551 } 1552 /* 1553 * Resulting string is dynamically allocated, so 1554 * tell caller to free it. 1555 */ 1556 *freePtr = TRUE; 1557 *lengthPtr = tstr-start+1; 1558 *tstr = endc; 1559 return(val); 1560 } 1561 break; 1562 } 1563 } 1564 } 1565 1566 if (v == (Var *)NIL) { 1567 if ((((tstr-str) == 3) || 1568 ((((tstr-str) == 4) && (str[3] == 'F' || 1569 str[3] == 'D')))) && 1570 ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) 1571 { 1572 /* 1573 * If substituting a local variable in a non-local context, 1574 * assume it's for dynamic source stuff. We have to handle 1575 * this specially and return the longhand for the variable 1576 * with the dollar sign escaped so it makes it back to the 1577 * caller. Only four of the local variables are treated 1578 * specially as they are the only four that will be set 1579 * when dynamic sources are expanded. 1580 */ 1581 switch (str[2]) { 1582 case '@': 1583 case '%': 1584 case '*': 1585 case '!': 1586 dynamic = TRUE; 1587 break; 1588 } 1589 } else if (((tstr-str) > 4) && (str[2] == '.') && 1590 isupper((unsigned char) str[3]) && 1591 ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) 1592 { 1593 int len; 1594 1595 len = (tstr-str) - 3; 1596 if ((strncmp(str+2, ".TARGET", len) == 0) || 1597 (strncmp(str+2, ".ARCHIVE", len) == 0) || 1598 (strncmp(str+2, ".PREFIX", len) == 0) || 1599 (strncmp(str+2, ".MEMBER", len) == 0)) 1600 { 1601 dynamic = TRUE; 1602 } 1603 } 1604 1605 if (!haveModifier) { 1606 /* 1607 * No modifiers -- have specification length so we can return 1608 * now. 1609 */ 1610 *lengthPtr = tstr - start + 1; 1611 *tstr = endc; 1612 if (dynamic) { 1613 str = emalloc(*lengthPtr + 1); 1614 strncpy(str, start, *lengthPtr); 1615 str[*lengthPtr] = '\0'; 1616 *freePtr = TRUE; 1617 return(str); 1618 } else { 1619 return (err ? var_Error : varNoError); 1620 } 1621 } else { 1622 /* 1623 * Still need to get to the end of the variable specification, 1624 * so kludge up a Var structure for the modifications 1625 */ 1626 v = (Var *) emalloc(sizeof(Var)); 1627 v->name = &str[1]; 1628 v->val = Buf_Init(1); 1629 v->flags = VAR_JUNK; 1630 } 1631 } 1632 } 1633 1634 if (v->flags & VAR_IN_USE) { 1635 Fatal("Variable %s is recursive.", v->name); 1636 /*NOTREACHED*/ 1637 } else { 1638 v->flags |= VAR_IN_USE; 1639 } 1640 /* 1641 * Before doing any modification, we have to make sure the value 1642 * has been fully expanded. If it looks like recursion might be 1643 * necessary (there's a dollar sign somewhere in the variable's value) 1644 * we just call Var_Subst to do any other substitutions that are 1645 * necessary. Note that the value returned by Var_Subst will have 1646 * been dynamically-allocated, so it will need freeing when we 1647 * return. 1648 */ 1649 str = (char *)Buf_GetAll(v->val, (int *)NULL); 1650 if (strchr (str, '$') != (char *)NULL) { 1651 str = Var_Subst(NULL, str, ctxt, err); 1652 *freePtr = TRUE; 1653 } 1654 1655 v->flags &= ~VAR_IN_USE; 1656 1657 /* 1658 * Now we need to apply any modifiers the user wants applied. 1659 * These are: 1660 * :M<pattern> words which match the given <pattern>. 1661 * <pattern> is of the standard file 1662 * wildcarding form. 1663 * :S<d><pat1><d><pat2><d>[g] 1664 * Substitute <pat2> for <pat1> in the value 1665 * :C<d><pat1><d><pat2><d>[g] 1666 * Substitute <pat2> for regex <pat1> in the value 1667 * :H Substitute the head of each word 1668 * :T Substitute the tail of each word 1669 * :E Substitute the extension (minus '.') of 1670 * each word 1671 * :R Substitute the root of each word 1672 * (pathname minus the suffix). 1673 * :lhs=rhs Like :S, but the rhs goes to the end of 1674 * the invocation. 1675 */ 1676 if ((str != (char *)NULL) && haveModifier) { 1677 /* 1678 * Skip initial colon while putting it back. 1679 */ 1680 *tstr++ = ':'; 1681 while (*tstr != endc) { 1682 char *newStr; /* New value to return */ 1683 char termc; /* Character which terminated scan */ 1684 1685 if (DEBUG(VAR)) { 1686 printf("Applying :%c to \"%s\"\n", *tstr, str); 1687 } 1688 switch (*tstr) { 1689 case 'N': 1690 case 'M': 1691 { 1692 char *pattern; 1693 char *cp2; 1694 Boolean copy; 1695 1696 copy = FALSE; 1697 for (cp = tstr + 1; 1698 *cp != '\0' && *cp != ':' && *cp != endc; 1699 cp++) 1700 { 1701 if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ 1702 copy = TRUE; 1703 cp++; 1704 } 1705 } 1706 termc = *cp; 1707 *cp = '\0'; 1708 if (copy) { 1709 /* 1710 * Need to compress the \:'s out of the pattern, so 1711 * allocate enough room to hold the uncompressed 1712 * pattern (note that cp started at tstr+1, so 1713 * cp - tstr takes the null byte into account) and 1714 * compress the pattern into the space. 1715 */ 1716 pattern = emalloc(cp - tstr); 1717 for (cp2 = pattern, cp = tstr + 1; 1718 *cp != '\0'; 1719 cp++, cp2++) 1720 { 1721 if ((*cp == '\\') && 1722 (cp[1] == ':' || cp[1] == endc)) { 1723 cp++; 1724 } 1725 *cp2 = *cp; 1726 } 1727 *cp2 = '\0'; 1728 } else { 1729 pattern = &tstr[1]; 1730 } 1731 if (*tstr == 'M' || *tstr == 'm') { 1732 newStr = VarModify(str, VarMatch, (ClientData)pattern); 1733 } else { 1734 newStr = VarModify(str, VarNoMatch, 1735 (ClientData)pattern); 1736 } 1737 if (copy) { 1738 free(pattern); 1739 } 1740 break; 1741 } 1742 case 'S': 1743 { 1744 VarPattern pattern; 1745 1746 pattern.flags = 0; 1747 delim = tstr[1]; 1748 tstr += 2; 1749 1750 /* 1751 * If pattern begins with '^', it is anchored to the 1752 * start of the word -- skip over it and flag pattern. 1753 */ 1754 if (*tstr == '^') { 1755 pattern.flags |= VAR_MATCH_START; 1756 tstr += 1; 1757 } 1758 1759 cp = tstr; 1760 if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim, 1761 &pattern.flags, &pattern.leftLen, NULL)) == NULL) 1762 goto cleanup; 1763 1764 if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim, 1765 NULL, &pattern.rightLen, &pattern)) == NULL) 1766 goto cleanup; 1767 1768 /* 1769 * Check for global substitution. If 'g' after the final 1770 * delimiter, substitution is global and is marked that 1771 * way. 1772 */ 1773 for (;; cp++) { 1774 switch (*cp) { 1775 case 'g': 1776 pattern.flags |= VAR_SUB_GLOBAL; 1777 continue; 1778 case '1': 1779 pattern.flags |= VAR_SUB_ONE; 1780 continue; 1781 } 1782 break; 1783 } 1784 1785 termc = *cp; 1786 newStr = VarModify(str, VarSubstitute, 1787 (ClientData)&pattern); 1788 1789 /* 1790 * Free the two strings. 1791 */ 1792 free(pattern.lhs); 1793 free(pattern.rhs); 1794 break; 1795 } 1796 #ifndef MAKE_BOOTSTRAP 1797 case 'C': 1798 { 1799 VarREPattern pattern; 1800 char *re; 1801 int error; 1802 1803 pattern.flags = 0; 1804 delim = tstr[1]; 1805 tstr += 2; 1806 1807 cp = tstr; 1808 1809 if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, 1810 NULL, NULL)) == NULL) 1811 goto cleanup; 1812 1813 if ((pattern.replace = VarGetPattern(ctxt, err, &cp, 1814 delim, NULL, NULL, NULL)) == NULL) { 1815 free(re); 1816 goto cleanup; 1817 } 1818 1819 for (;; cp++) { 1820 switch (*cp) { 1821 case 'g': 1822 pattern.flags |= VAR_SUB_GLOBAL; 1823 continue; 1824 case '1': 1825 pattern.flags |= VAR_SUB_ONE; 1826 continue; 1827 } 1828 break; 1829 } 1830 1831 termc = *cp; 1832 1833 error = regcomp(&pattern.re, re, REG_EXTENDED); 1834 free(re); 1835 if (error) { 1836 *lengthPtr = cp - start + 1; 1837 VarREError(error, &pattern.re, "RE substitution error"); 1838 free(pattern.replace); 1839 return (var_Error); 1840 } 1841 1842 pattern.nsub = pattern.re.re_nsub + 1; 1843 if (pattern.nsub < 1) 1844 pattern.nsub = 1; 1845 if (pattern.nsub > 10) 1846 pattern.nsub = 10; 1847 pattern.matches = emalloc(pattern.nsub * 1848 sizeof(regmatch_t)); 1849 newStr = VarModify(str, VarRESubstitute, 1850 (ClientData) &pattern); 1851 regfree(&pattern.re); 1852 free(pattern.replace); 1853 free(pattern.matches); 1854 break; 1855 } 1856 #endif 1857 case 'Q': 1858 if (tstr[1] == endc || tstr[1] == ':') { 1859 newStr = VarQuote (str); 1860 cp = tstr + 1; 1861 termc = *cp; 1862 break; 1863 } 1864 /*FALLTHRU*/ 1865 case 'T': 1866 if (tstr[1] == endc || tstr[1] == ':') { 1867 newStr = VarModify (str, VarTail, (ClientData)0); 1868 cp = tstr + 1; 1869 termc = *cp; 1870 break; 1871 } 1872 /*FALLTHRU*/ 1873 case 'H': 1874 if (tstr[1] == endc || tstr[1] == ':') { 1875 newStr = VarModify (str, VarHead, (ClientData)0); 1876 cp = tstr + 1; 1877 termc = *cp; 1878 break; 1879 } 1880 /*FALLTHRU*/ 1881 case 'E': 1882 if (tstr[1] == endc || tstr[1] == ':') { 1883 newStr = VarModify (str, VarSuffix, (ClientData)0); 1884 cp = tstr + 1; 1885 termc = *cp; 1886 break; 1887 } 1888 /*FALLTHRU*/ 1889 case 'R': 1890 if (tstr[1] == endc || tstr[1] == ':') { 1891 newStr = VarModify (str, VarRoot, (ClientData)0); 1892 cp = tstr + 1; 1893 termc = *cp; 1894 break; 1895 } 1896 /*FALLTHRU*/ 1897 #ifdef SUNSHCMD 1898 case 's': 1899 if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { 1900 char *err; 1901 newStr = Cmd_Exec (str, &err); 1902 if (err) 1903 Error (err, str); 1904 cp = tstr + 2; 1905 termc = *cp; 1906 break; 1907 } 1908 /*FALLTHRU*/ 1909 #endif 1910 default: 1911 { 1912 #ifdef SYSVVARSUB 1913 /* 1914 * This can either be a bogus modifier or a System-V 1915 * substitution command. 1916 */ 1917 VarPattern pattern; 1918 Boolean eqFound; 1919 1920 pattern.flags = 0; 1921 eqFound = FALSE; 1922 /* 1923 * First we make a pass through the string trying 1924 * to verify it is a SYSV-make-style translation: 1925 * it must be: <string1>=<string2>) 1926 */ 1927 cp = tstr; 1928 cnt = 1; 1929 while (*cp != '\0' && cnt) { 1930 if (*cp == '=') { 1931 eqFound = TRUE; 1932 /* continue looking for endc */ 1933 } 1934 else if (*cp == endc) 1935 cnt--; 1936 else if (*cp == startc) 1937 cnt++; 1938 if (cnt) 1939 cp++; 1940 } 1941 if (*cp == endc && eqFound) { 1942 1943 /* 1944 * Now we break this sucker into the lhs and 1945 * rhs. We must null terminate them of course. 1946 */ 1947 for (cp = tstr; *cp != '='; cp++) 1948 continue; 1949 pattern.lhs = tstr; 1950 pattern.leftLen = cp - tstr; 1951 *cp++ = '\0'; 1952 1953 pattern.rhs = cp; 1954 cnt = 1; 1955 while (cnt) { 1956 if (*cp == endc) 1957 cnt--; 1958 else if (*cp == startc) 1959 cnt++; 1960 if (cnt) 1961 cp++; 1962 } 1963 pattern.rightLen = cp - pattern.rhs; 1964 *cp = '\0'; 1965 1966 /* 1967 * SYSV modifications happen through the whole 1968 * string. Note the pattern is anchored at the end. 1969 */ 1970 newStr = VarModify(str, VarSYSVMatch, 1971 (ClientData)&pattern); 1972 1973 /* 1974 * Restore the nulled characters 1975 */ 1976 pattern.lhs[pattern.leftLen] = '='; 1977 pattern.rhs[pattern.rightLen] = endc; 1978 termc = endc; 1979 } else 1980 #endif 1981 { 1982 Error ("Unknown modifier '%c'\n", *tstr); 1983 for (cp = tstr+1; 1984 *cp != ':' && *cp != endc && *cp != '\0'; 1985 cp++) 1986 continue; 1987 termc = *cp; 1988 newStr = var_Error; 1989 } 1990 } 1991 } 1992 if (DEBUG(VAR)) { 1993 printf("Result is \"%s\"\n", newStr); 1994 } 1995 1996 if (*freePtr) { 1997 free (str); 1998 } 1999 str = newStr; 2000 if (str != var_Error) { 2001 *freePtr = TRUE; 2002 } else { 2003 *freePtr = FALSE; 2004 } 2005 if (termc == '\0') { 2006 Error("Unclosed variable specification for %s", v->name); 2007 } else if (termc == ':') { 2008 *cp++ = termc; 2009 } else { 2010 *cp = termc; 2011 } 2012 tstr = cp; 2013 } 2014 *lengthPtr = tstr - start + 1; 2015 } else { 2016 *lengthPtr = tstr - start + 1; 2017 *tstr = endc; 2018 } 2019 2020 if (v->flags & VAR_FROM_ENV) { 2021 Boolean destroy = FALSE; 2022 2023 if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { 2024 destroy = TRUE; 2025 } else { 2026 /* 2027 * Returning the value unmodified, so tell the caller to free 2028 * the thing. 2029 */ 2030 *freePtr = TRUE; 2031 } 2032 Buf_Destroy(v->val, destroy); 2033 free((Address)v); 2034 } else if (v->flags & VAR_JUNK) { 2035 /* 2036 * Perform any free'ing needed and set *freePtr to FALSE so the caller 2037 * doesn't try to free a static pointer. 2038 */ 2039 if (*freePtr) { 2040 free(str); 2041 } 2042 *freePtr = FALSE; 2043 Buf_Destroy(v->val, TRUE); 2044 free((Address)v); 2045 if (dynamic) { 2046 str = emalloc(*lengthPtr + 1); 2047 strncpy(str, start, *lengthPtr); 2048 str[*lengthPtr] = '\0'; 2049 *freePtr = TRUE; 2050 } else { 2051 str = var_Error; 2052 } 2053 } 2054 return (str); 2055 2056 cleanup: 2057 *lengthPtr = cp - start + 1; 2058 if (*freePtr) 2059 free(str); 2060 Error("Unclosed substitution for %s (%c missing)", 2061 v->name, delim); 2062 return (var_Error); 2063 } 2064 2065 /*- 2066 *----------------------------------------------------------------------- 2067 * Var_Subst -- 2068 * Substitute for all variables in the given string in the given context 2069 * If undefErr is TRUE, Parse_Error will be called when an undefined 2070 * variable is encountered. 2071 * 2072 * Results: 2073 * The resulting string. 2074 * 2075 * Side Effects: 2076 * None. The old string must be freed by the caller 2077 *----------------------------------------------------------------------- 2078 */ 2079 char * 2080 Var_Subst (var, str, ctxt, undefErr) 2081 char *var; /* Named variable || NULL for all */ 2082 char *str; /* the string in which to substitute */ 2083 GNode *ctxt; /* the context wherein to find variables */ 2084 Boolean undefErr; /* TRUE if undefineds are an error */ 2085 { 2086 Buffer buf; /* Buffer for forming things */ 2087 char *val; /* Value to substitute for a variable */ 2088 int length; /* Length of the variable invocation */ 2089 Boolean doFree; /* Set true if val should be freed */ 2090 static Boolean errorReported; /* Set true if an error has already 2091 * been reported to prevent a plethora 2092 * of messages when recursing */ 2093 2094 buf = Buf_Init (MAKE_BSIZE); 2095 errorReported = FALSE; 2096 2097 while (*str) { 2098 if (var == NULL && (*str == '$') && (str[1] == '$')) { 2099 /* 2100 * A dollar sign may be escaped either with another dollar sign. 2101 * In such a case, we skip over the escape character and store the 2102 * dollar sign into the buffer directly. 2103 */ 2104 str++; 2105 Buf_AddByte(buf, (Byte)*str); 2106 str++; 2107 } else if (*str != '$') { 2108 /* 2109 * Skip as many characters as possible -- either to the end of 2110 * the string or to the next dollar sign (variable invocation). 2111 */ 2112 char *cp; 2113 2114 for (cp = str++; *str != '$' && *str != '\0'; str++) 2115 continue; 2116 Buf_AddBytes(buf, str - cp, (Byte *)cp); 2117 } else { 2118 if (var != NULL) { 2119 int expand; 2120 for (;;) { 2121 if (str[1] != '(' && str[1] != '{') { 2122 if (str[1] != *var) { 2123 Buf_AddBytes(buf, 2, (Byte *) str); 2124 str += 2; 2125 expand = FALSE; 2126 } 2127 else 2128 expand = TRUE; 2129 break; 2130 } 2131 else { 2132 char *p; 2133 2134 /* 2135 * Scan up to the end of the variable name. 2136 */ 2137 for (p = &str[2]; *p && 2138 *p != ':' && *p != ')' && *p != '}'; p++) 2139 if (*p == '$') 2140 break; 2141 /* 2142 * A variable inside the variable. We cannot expand 2143 * the external variable yet, so we try again with 2144 * the nested one 2145 */ 2146 if (*p == '$') { 2147 Buf_AddBytes(buf, p - str, (Byte *) str); 2148 str = p; 2149 continue; 2150 } 2151 2152 if (strncmp(var, str + 2, p - str - 2) != 0 || 2153 var[p - str - 2] != '\0') { 2154 /* 2155 * Not the variable we want to expand, scan 2156 * until the next variable 2157 */ 2158 for (;*p != '$' && *p != '\0'; p++) 2159 continue; 2160 Buf_AddBytes(buf, p - str, (Byte *) str); 2161 str = p; 2162 expand = FALSE; 2163 } 2164 else 2165 expand = TRUE; 2166 break; 2167 } 2168 } 2169 if (!expand) 2170 continue; 2171 } 2172 2173 val = Var_Parse (str, ctxt, undefErr, &length, &doFree); 2174 2175 /* 2176 * When we come down here, val should either point to the 2177 * value of this variable, suitably modified, or be NULL. 2178 * Length should be the total length of the potential 2179 * variable invocation (from $ to end character...) 2180 */ 2181 if (val == var_Error || val == varNoError) { 2182 /* 2183 * If performing old-time variable substitution, skip over 2184 * the variable and continue with the substitution. Otherwise, 2185 * store the dollar sign and advance str so we continue with 2186 * the string... 2187 */ 2188 if (oldVars) { 2189 str += length; 2190 } else if (undefErr) { 2191 /* 2192 * If variable is undefined, complain and skip the 2193 * variable. The complaint will stop us from doing anything 2194 * when the file is parsed. 2195 */ 2196 if (!errorReported) { 2197 Parse_Error (PARSE_FATAL, 2198 "Undefined variable \"%.*s\"",length,str); 2199 } 2200 str += length; 2201 errorReported = TRUE; 2202 } else { 2203 Buf_AddByte (buf, (Byte)*str); 2204 str += 1; 2205 } 2206 } else { 2207 /* 2208 * We've now got a variable structure to store in. But first, 2209 * advance the string pointer. 2210 */ 2211 str += length; 2212 2213 /* 2214 * Copy all the characters from the variable value straight 2215 * into the new string. 2216 */ 2217 Buf_AddBytes (buf, strlen (val), (Byte *)val); 2218 if (doFree) { 2219 free ((Address)val); 2220 } 2221 } 2222 } 2223 } 2224 2225 Buf_AddByte (buf, '\0'); 2226 str = (char *)Buf_GetAll (buf, (int *)NULL); 2227 Buf_Destroy (buf, FALSE); 2228 return (str); 2229 } 2230 2231 /*- 2232 *----------------------------------------------------------------------- 2233 * Var_GetTail -- 2234 * Return the tail from each of a list of words. Used to set the 2235 * System V local variables. 2236 * 2237 * Results: 2238 * The resulting string. 2239 * 2240 * Side Effects: 2241 * None. 2242 * 2243 *----------------------------------------------------------------------- 2244 */ 2245 char * 2246 Var_GetTail(file) 2247 char *file; /* Filename to modify */ 2248 { 2249 return(VarModify(file, VarTail, (ClientData)0)); 2250 } 2251 2252 /*- 2253 *----------------------------------------------------------------------- 2254 * Var_GetHead -- 2255 * Find the leading components of a (list of) filename(s). 2256 * XXX: VarHead does not replace foo by ., as (sun) System V make 2257 * does. 2258 * 2259 * Results: 2260 * The leading components. 2261 * 2262 * Side Effects: 2263 * None. 2264 * 2265 *----------------------------------------------------------------------- 2266 */ 2267 char * 2268 Var_GetHead(file) 2269 char *file; /* Filename to manipulate */ 2270 { 2271 return(VarModify(file, VarHead, (ClientData)0)); 2272 } 2273 2274 /*- 2275 *----------------------------------------------------------------------- 2276 * Var_Init -- 2277 * Initialize the module 2278 * 2279 * Results: 2280 * None 2281 * 2282 * Side Effects: 2283 * The VAR_CMD and VAR_GLOBAL contexts are created 2284 *----------------------------------------------------------------------- 2285 */ 2286 void 2287 Var_Init () 2288 { 2289 VAR_GLOBAL = Targ_NewGN ("Global"); 2290 VAR_CMD = Targ_NewGN ("Command"); 2291 allVars = Lst_Init(FALSE); 2292 2293 } 2294 2295 2296 void 2297 Var_End () 2298 { 2299 Lst_Destroy(allVars, VarDelete); 2300 } 2301 2302 2303 /****************** PRINT DEBUGGING INFO *****************/ 2304 static int 2305 VarPrintVar (vp, dummy) 2306 ClientData vp; 2307 ClientData dummy; 2308 { 2309 Var *v = (Var *) vp; 2310 printf ("%-16s = %s\n", v->name, (char *) Buf_GetAll(v->val, (int *)NULL)); 2311 return (dummy ? 0 : 0); 2312 } 2313 2314 /*- 2315 *----------------------------------------------------------------------- 2316 * Var_Dump -- 2317 * print all variables in a context 2318 *----------------------------------------------------------------------- 2319 */ 2320 void 2321 Var_Dump (ctxt) 2322 GNode *ctxt; 2323 { 2324 Lst_ForEach (ctxt->context, VarPrintVar, (ClientData) 0); 2325 } 2326