1141104Sharti/*- 2146045Sharti * Copyright (c) 2002 Juli Mallett. 31590Srgrimes * Copyright (c) 1988, 1989, 1990, 1993 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * Copyright (c) 1989 by Berkeley Softworks 61590Srgrimes * All rights reserved. 71590Srgrimes * 81590Srgrimes * This code is derived from software contributed to Berkeley by 91590Srgrimes * Adam de Boor. 101590Srgrimes * 111590Srgrimes * Redistribution and use in source and binary forms, with or without 121590Srgrimes * modification, are permitted provided that the following conditions 131590Srgrimes * are met: 141590Srgrimes * 1. Redistributions of source code must retain the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer. 161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171590Srgrimes * notice, this list of conditions and the following disclaimer in the 181590Srgrimes * documentation and/or other materials provided with the distribution. 191590Srgrimes * 3. All advertising materials mentioning features or use of this software 201590Srgrimes * must display the following acknowledgement: 211590Srgrimes * This product includes software developed by the University of 221590Srgrimes * California, Berkeley and its contributors. 231590Srgrimes * 4. Neither the name of the University nor the names of its contributors 241590Srgrimes * may be used to endorse or promote products derived from this software 251590Srgrimes * without specific prior written permission. 261590Srgrimes * 271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 301590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 371590Srgrimes * SUCH DAMAGE. 3862833Swsanchez * 3962833Swsanchez * @(#)var.c 8.3 (Berkeley) 3/19/94 401590Srgrimes */ 411590Srgrimes 4262833Swsanchez#include <sys/cdefs.h> 4394587Sobrien__FBSDID("$FreeBSD$"); 441590Srgrimes 45146046Sharti/** 461590Srgrimes * var.c -- 471590Srgrimes * Variable-handling functions 481590Srgrimes * 491590Srgrimes * Interface: 50143813Sharti * Var_Set Set the value of a variable in the given 51143813Sharti * context. The variable is created if it doesn't 52143813Sharti * yet exist. The value and variable name need not 53143813Sharti * be preserved. 541590Srgrimes * 55143813Sharti * Var_Append Append more characters to an existing variable 56143813Sharti * in the given context. The variable needn't 57143813Sharti * exist already -- it will be created if it doesn't. 58143813Sharti * A space is placed between the old value and the 59143813Sharti * new one. 601590Srgrimes * 61143813Sharti * Var_Exists See if a variable exists. 621590Srgrimes * 63143813Sharti * Var_Value Return the value of a variable in a context or 64143813Sharti * NULL if the variable is undefined. 651590Srgrimes * 66143813Sharti * Var_Subst Substitute named variable, or all variables if 67143813Sharti * NULL in a string using 68143813Sharti * the given context as the top-most one. If the 69143813Sharti * third argument is non-zero, Parse_Error is 70143813Sharti * called if any variables are undefined. 711590Srgrimes * 72143813Sharti * Var_Parse Parse a variable expansion from a string and 73143813Sharti * return the result and the number of characters 74143813Sharti * consumed. 751590Srgrimes * 76143813Sharti * Var_Delete Delete a variable in a context. 771590Srgrimes * 78143813Sharti * Var_Init Initialize this module. 791590Srgrimes * 801590Srgrimes * Debugging: 81143813Sharti * Var_Dump Print out all variables defined in the given 82143813Sharti * context. 831590Srgrimes * 841590Srgrimes * XXX: There's a lot of duplication in these functions. 851590Srgrimes */ 861590Srgrimes 87141104Sharti#include <ctype.h> 88141104Sharti#include <stdlib.h> 89141104Sharti#include <string.h> 90146045Sharti#include <sys/types.h> 91146045Sharti#include <regex.h> 921590Srgrimes 93141104Sharti#include "buf.h" 94141104Sharti#include "config.h" 95141104Sharti#include "globals.h" 96141104Sharti#include "GNode.h" 97146054Sharti#include "job.h" 98146141Sharti#include "lst.h" 99141104Sharti#include "parse.h" 100141104Sharti#include "str.h" 101141104Sharti#include "targ.h" 102141104Sharti#include "util.h" 103141104Sharti#include "var.h" 104141104Sharti 105143811Sharti/** 106143811Sharti * 107143811Sharti */ 108143811Shartitypedef struct VarParser { 109143811Sharti const char *const input; /* pointer to input string */ 110143904Sharti const char *ptr; /* current parser pos in input str */ 111143811Sharti GNode *ctxt; 112143811Sharti Boolean err; 113146038Sharti Boolean execute; 114143811Sharti} VarParser; 115146045Sharti 116146045Shartitypedef struct Var { 117146045Sharti char *name; /* the variable's name */ 118146045Sharti struct Buffer *val; /* its value */ 119146045Sharti int flags; /* miscellaneous status flags */ 120146045Sharti 121146045Sharti#define VAR_IN_USE 1 /* Variable's value currently being used. 122146045Sharti * Used to avoid recursion */ 123146045Sharti 124146045Sharti#define VAR_JUNK 4 /* Variable is a junk variable that 125146045Sharti * should be destroyed when done with 126146045Sharti * it. Used by Var_Parse for undefined, 127146045Sharti * modified variables */ 128146045Sharti 129146045Sharti#define VAR_TO_ENV 8 /* Place variable in environment */ 130146045Sharti} Var; 131146045Sharti 132146045Shartitypedef struct { 133146045Sharti struct Buffer *lhs; /* String to match */ 134146045Sharti struct Buffer *rhs; /* Replacement string (w/ &'s removed) */ 135146045Sharti 136146045Sharti regex_t re; 137146045Sharti int nsub; 138146045Sharti regmatch_t *matches; 139146045Sharti 140146045Sharti int flags; 141146045Sharti#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ 142146045Sharti#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ 143146045Sharti#define VAR_SUB_MATCHED 0x04 /* There was a match */ 144146045Sharti#define VAR_MATCH_START 0x08 /* Match at start of word */ 145146045Sharti#define VAR_MATCH_END 0x10 /* Match at end of word */ 146146045Sharti} VarPattern; 147146045Sharti 148146045Shartitypedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *); 149146045Sharti 150143914Shartistatic char *VarParse(VarParser *, Boolean *); 151143811Sharti 1521590Srgrimes/* 1531590Srgrimes * This is a harmless return value for Var_Parse that can be used by Var_Subst 1541590Srgrimes * to determine if there was an error in parsing -- easier than returning 1551590Srgrimes * a flag, as things outside this module don't give a hoot. 1561590Srgrimes */ 157143813Shartichar var_Error[] = ""; 1581590Srgrimes 1591590Srgrimes/* 1601590Srgrimes * Similar to var_Error, but returned when the 'err' flag for Var_Parse is 1611590Srgrimes * set false. Why not just use a constant? Well, gcc likes to condense 1621590Srgrimes * identical string instances... 1631590Srgrimes */ 1641590Srgrimesstatic char varNoError[] = ""; 1651590Srgrimes 1661590Srgrimes/* 1671590Srgrimes * Internally, variables are contained in four different contexts. 1681590Srgrimes * 1) the environment. They may not be changed. If an environment 169143813Sharti * variable is appended-to, the result is placed in the global 170143813Sharti * context. 1711590Srgrimes * 2) the global context. Variables set in the Makefile are located in 172143813Sharti * the global context. It is the penultimate context searched when 173143813Sharti * substituting. 1741590Srgrimes * 3) the command-line context. All variables set on the command line 1751590Srgrimes * are placed in this context. They are UNALTERABLE once placed here. 1761590Srgrimes * 4) the local context. Each target has associated with it a context 1771590Srgrimes * list. On this list are located the structures describing such 1781590Srgrimes * local variables as $(@) and $(*) 1791590Srgrimes * The four contexts are searched in the reverse order from which they are 1801590Srgrimes * listed. 1811590Srgrimes */ 182146134Shartistatic GNode *VAR_ENV; /* variables from the environment */ 183146134ShartiGNode *VAR_GLOBAL; /* variables from the makefile */ 184146134ShartiGNode *VAR_CMD; /* variables defined on the command-line */ 1851590Srgrimes 186146134ShartiBoolean oldVars; /* variable substitution style */ 187146134ShartiBoolean checkEnvFirst; /* -e flag */ 188146134Sharti 189141583Sharti#define OPEN_PAREN '(' 190141583Sharti#define CLOSE_PAREN ')' 191141583Sharti#define OPEN_BRACE '{' 192141583Sharti#define CLOSE_BRACE '}' 193141583Sharti 194146046Sharti/** 195141564Sharti * Create a Var object. 196141564Sharti * 197141564Sharti * Params: 198141564Sharti * name Name of variable (copied). 199141564Sharti * value Value of variable (copied) or NULL. 200141564Sharti * flags Flags set on variable. 201141564Sharti * 202141564Sharti * Returns: 203141564Sharti * New variable. 204141564Sharti */ 205141564Shartistatic Var * 206141564ShartiVarCreate(const char name[], const char value[], int flags) 207141564Sharti{ 208141564Sharti Var *v; 209141564Sharti 210141564Sharti v = emalloc(sizeof(Var)); 211141564Sharti v->name = estrdup(name); 212141564Sharti v->val = Buf_Init(0); 213141564Sharti v->flags = flags; 214141564Sharti 215141564Sharti if (value != NULL) { 216141564Sharti Buf_Append(v->val, value); 217141564Sharti } 218141564Sharti return (v); 219141564Sharti} 220141564Sharti 221146046Sharti/** 222141564Sharti * Destroy a Var object. 223141564Sharti * 224141564Sharti * Params: 225146039Sharti * v Object to destroy. 226146039Sharti * f True if internal buffer in Buffer object is to be removed. 227141564Sharti */ 228141564Shartistatic void 229141564ShartiVarDestroy(Var *v, Boolean f) 230141564Sharti{ 231141564Sharti 232141564Sharti Buf_Destroy(v->val, f); 233141564Sharti free(v->name); 234141564Sharti free(v); 235141564Sharti} 236141564Sharti 237146045Sharti/** 238146046Sharti * Remove the tail of the given word and place the result in the given 239146046Sharti * buffer. 240146045Sharti * 241146045Sharti * Results: 242146045Sharti * TRUE if characters were added to the buffer (a space needs to be 243146045Sharti * added to the buffer before the next word). 244146045Sharti * 245146045Sharti * Side Effects: 246146045Sharti * The trimmed word is added to the buffer. 247146045Sharti */ 248146045Shartistatic Boolean 249146045ShartiVarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused) 250146045Sharti{ 251146045Sharti char *slash; 252146045Sharti 253146045Sharti slash = strrchr(word, '/'); 254146045Sharti if (slash != NULL) { 255146045Sharti if (addSpace) { 256146045Sharti Buf_AddByte(buf, (Byte)' '); 257146045Sharti } 258146045Sharti Buf_AppendRange(buf, word, slash); 259146045Sharti } else { 260146045Sharti /* 261146045Sharti * If no directory part, give . (q.v. the POSIX standard) 262146045Sharti */ 263146045Sharti if (addSpace) { 264146045Sharti Buf_Append(buf, " ."); 265146045Sharti } else { 266146045Sharti Buf_AddByte(buf, (Byte)'.'); 267146045Sharti } 268146045Sharti } 269146045Sharti return (TRUE); 270146045Sharti} 271146045Sharti 272146045Sharti/** 273146046Sharti * Remove the head of the given word and place the result in the given 274146046Sharti * buffer. 275146045Sharti * 276146045Sharti * Results: 277146045Sharti * TRUE if characters were added to the buffer (a space needs to be 278146045Sharti * added to the buffer before the next word). 279146045Sharti * 280146045Sharti * Side Effects: 281146045Sharti * The trimmed word is added to the buffer. 282146045Sharti */ 283146045Shartistatic Boolean 284146045ShartiVarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused) 285146045Sharti{ 286146045Sharti const char *slash; 287146045Sharti 288146045Sharti if (addSpace) { 289146045Sharti Buf_AddByte (buf, (Byte)' '); 290146045Sharti } 291146045Sharti 292146045Sharti slash = strrchr(word, '/'); 293146045Sharti if (slash != NULL) { 294146045Sharti slash++; 295146045Sharti Buf_Append(buf, slash); 296146045Sharti } else { 297146045Sharti Buf_Append(buf, word); 298146045Sharti } 299146045Sharti return (TRUE); 300146045Sharti} 301146045Sharti 302146045Sharti/** 303146046Sharti * Place the suffix of the given word in the given buffer. 304146045Sharti * 305146045Sharti * Results: 306146045Sharti * TRUE if characters were added to the buffer (a space needs to be 307146045Sharti * added to the buffer before the next word). 308146045Sharti * 309146045Sharti * Side Effects: 310146045Sharti * The suffix from the word is placed in the buffer. 311146045Sharti */ 312146045Shartistatic Boolean 313146045ShartiVarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused) 314146045Sharti{ 315146045Sharti const char *dot; 316146045Sharti 317146045Sharti dot = strrchr(word, '.'); 318146045Sharti if (dot != NULL) { 319146045Sharti if (addSpace) { 320146045Sharti Buf_AddByte(buf, (Byte)' '); 321146045Sharti } 322146045Sharti dot++; 323146045Sharti Buf_Append(buf, dot); 324146045Sharti addSpace = TRUE; 325146045Sharti } 326146045Sharti return (addSpace); 327146045Sharti} 328146045Sharti 329146045Sharti/** 330146046Sharti * Remove the suffix of the given word and place the result in the 331146046Sharti * buffer. 332146045Sharti * 333146045Sharti * Results: 334146045Sharti * TRUE if characters were added to the buffer (a space needs to be 335146045Sharti * added to the buffer before the next word). 336146045Sharti * 337146045Sharti * Side Effects: 338146045Sharti * The trimmed word is added to the buffer. 339146045Sharti */ 340146045Shartistatic Boolean 341146045ShartiVarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused) 342146045Sharti{ 343146045Sharti char *dot; 344146045Sharti 345146045Sharti if (addSpace) { 346146045Sharti Buf_AddByte(buf, (Byte)' '); 347146045Sharti } 348146045Sharti 349146045Sharti dot = strrchr(word, '.'); 350146045Sharti if (dot != NULL) { 351146045Sharti Buf_AppendRange(buf, word, dot); 352146045Sharti } else { 353146045Sharti Buf_Append(buf, word); 354146045Sharti } 355146045Sharti return (TRUE); 356146045Sharti} 357146045Sharti 358146045Sharti/** 359146046Sharti * Place the word in the buffer if it matches the given pattern. 360146046Sharti * Callback function for VarModify to implement the :M modifier. 361146046Sharti * A space will be added if requested. A pattern is supplied 362146046Sharti * which the word must match. 363146045Sharti * 364146045Sharti * Results: 365146045Sharti * TRUE if a space should be placed in the buffer before the next 366146045Sharti * word. 367146045Sharti * 368146045Sharti * Side Effects: 369146045Sharti * The word may be copied to the buffer. 370146045Sharti */ 371146045Shartistatic Boolean 372146045ShartiVarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern) 373146045Sharti{ 374146045Sharti 375146045Sharti if (Str_Match(word, pattern)) { 376146045Sharti if (addSpace) { 377146045Sharti Buf_AddByte(buf, (Byte)' '); 378146045Sharti } 379146045Sharti addSpace = TRUE; 380146045Sharti Buf_Append(buf, word); 381146045Sharti } 382146045Sharti return (addSpace); 383146045Sharti} 384146045Sharti 385146045Sharti#ifdef SYSVVARSUB 386146045Sharti/** 387146046Sharti * Place the word in the buffer if it matches the given pattern. 388146046Sharti * Callback function for VarModify to implement the System V % 389146046Sharti * modifiers. A space is added if requested. 390146045Sharti * 391146045Sharti * Results: 392146045Sharti * TRUE if a space should be placed in the buffer before the next 393146045Sharti * word. 394146045Sharti * 395146045Sharti * Side Effects: 396146045Sharti * The word may be copied to the buffer. 397146045Sharti */ 398146045Shartistatic Boolean 399146045ShartiVarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp) 400146045Sharti{ 401146045Sharti int len; 402146045Sharti const char *ptr; 403146045Sharti VarPattern *pat = (VarPattern *)patp; 404146045Sharti 405146045Sharti if (addSpace) 406146045Sharti Buf_AddByte(buf, (Byte)' '); 407146045Sharti 408146045Sharti addSpace = TRUE; 409146045Sharti 410146045Sharti if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL) 411146045Sharti Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len); 412146045Sharti else 413146045Sharti Buf_Append(buf, word); 414146045Sharti 415146045Sharti return (addSpace); 416146045Sharti} 417146045Sharti#endif 418146045Sharti 419146045Sharti/** 420146046Sharti * Place the word in the buffer if it doesn't match the given pattern. 421146046Sharti * Callback function for VarModify to implement the :N modifier. A 422146046Sharti * space is added if requested. 423146045Sharti * 424146045Sharti * Results: 425146045Sharti * TRUE if a space should be placed in the buffer before the next 426146045Sharti * word. 427146045Sharti * 428146045Sharti * Side Effects: 429146045Sharti * The word may be copied to the buffer. 430146045Sharti */ 431146045Shartistatic Boolean 432146045ShartiVarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern) 433146045Sharti{ 434146045Sharti 435146045Sharti if (!Str_Match(word, pattern)) { 436146045Sharti if (addSpace) { 437146045Sharti Buf_AddByte(buf, (Byte)' '); 438146045Sharti } 439146045Sharti addSpace = TRUE; 440146045Sharti Buf_Append(buf, word); 441146045Sharti } 442146045Sharti return (addSpace); 443146045Sharti} 444146045Sharti 445146045Sharti/** 446146046Sharti * Perform a string-substitution on the given word, placing the 447146046Sharti * result in the passed buffer. A space is added if requested. 448146045Sharti * 449146045Sharti * Results: 450146045Sharti * TRUE if a space is needed before more characters are added. 451146045Sharti */ 452146045Shartistatic Boolean 453146045ShartiVarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp) 454146045Sharti{ 455146045Sharti size_t wordLen; /* Length of word */ 456146045Sharti const char *cp; /* General pointer */ 457146045Sharti VarPattern *pattern = patternp; 458146045Sharti 459146045Sharti wordLen = strlen(word); 460146045Sharti if (1) { /* substitute in each word of the variable */ 461146045Sharti /* 462146045Sharti * Break substitution down into simple anchored cases 463146045Sharti * and if none of them fits, perform the general substitution 464146045Sharti * case. 465146045Sharti */ 466146045Sharti if ((pattern->flags & VAR_MATCH_START) && 467146045Sharti (strncmp(word, Buf_Data(pattern->lhs), 468146045Sharti Buf_Size(pattern->lhs)) == 0)) { 469146045Sharti /* 470146045Sharti * Anchored at start and beginning of word matches 471146045Sharti * pattern. 472146045Sharti */ 473146045Sharti if ((pattern->flags & VAR_MATCH_END) && 474146045Sharti (wordLen == Buf_Size(pattern->lhs))) { 475146045Sharti /* 476146045Sharti * Also anchored at end and matches to the end 477146045Sharti * (word is same length as pattern) add space 478146045Sharti * and rhs only if rhs is non-null. 479146045Sharti */ 480146045Sharti if (Buf_Size(pattern->rhs) != 0) { 481146045Sharti if (addSpace) { 482146045Sharti Buf_AddByte(buf, (Byte)' '); 483146045Sharti } 484146045Sharti addSpace = TRUE; 485146045Sharti Buf_AppendBuf(buf, pattern->rhs); 486146045Sharti } 487146045Sharti 488146045Sharti } else if (pattern->flags & VAR_MATCH_END) { 489146045Sharti /* 490146045Sharti * Doesn't match to end -- copy word wholesale 491146045Sharti */ 492146045Sharti goto nosub; 493146045Sharti 494146045Sharti } else { 495146045Sharti /* 496146045Sharti * Matches at start but need to copy in 497146045Sharti * trailing characters. 498146045Sharti */ 499146045Sharti if ((Buf_Size(pattern->rhs) + wordLen - 500146045Sharti Buf_Size(pattern->lhs)) != 0) { 501146045Sharti if (addSpace) { 502146045Sharti Buf_AddByte(buf, (Byte)' '); 503146045Sharti } 504146045Sharti addSpace = TRUE; 505146045Sharti } 506146045Sharti Buf_AppendBuf(buf, pattern->rhs); 507146045Sharti Buf_AddBytes(buf, wordLen - 508146045Sharti Buf_Size(pattern->lhs), 509146045Sharti (word + Buf_Size(pattern->lhs))); 510146045Sharti } 511146045Sharti 512146045Sharti } else if (pattern->flags & VAR_MATCH_START) { 513146045Sharti /* 514146045Sharti * Had to match at start of word and didn't -- copy 515146045Sharti * whole word. 516146045Sharti */ 517146045Sharti goto nosub; 518146045Sharti 519146045Sharti } else if (pattern->flags & VAR_MATCH_END) { 520146045Sharti /* 521146045Sharti * Anchored at end, Find only place match could occur 522146045Sharti * (leftLen characters from the end of the word) and 523146045Sharti * see if it does. Note that because the $ will be 524146045Sharti * left at the end of the lhs, we have to use strncmp. 525146045Sharti */ 526146045Sharti cp = word + (wordLen - Buf_Size(pattern->lhs)); 527146045Sharti if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs), 528146045Sharti Buf_Size(pattern->lhs)) == 0)) { 529146045Sharti /* 530146045Sharti * Match found. If we will place characters in 531146045Sharti * the buffer, add a space before hand as 532146045Sharti * indicated by addSpace, then stuff in the 533146045Sharti * initial, unmatched part of the word followed 534146045Sharti * by the right-hand-side. 535146045Sharti */ 536146045Sharti if ((cp - word) + Buf_Size(pattern->rhs) != 0) { 537146045Sharti if (addSpace) { 538146045Sharti Buf_AddByte(buf, (Byte)' '); 539146045Sharti } 540146045Sharti addSpace = TRUE; 541146045Sharti } 542146045Sharti Buf_AppendRange(buf, word, cp); 543146045Sharti Buf_AppendBuf(buf, pattern->rhs); 544146045Sharti 545146045Sharti } else { 546146045Sharti /* 547146045Sharti * Had to match at end and didn't. Copy entire 548146045Sharti * word. 549146045Sharti */ 550146045Sharti goto nosub; 551146045Sharti } 552146045Sharti } else { 553146045Sharti /* 554146045Sharti * Pattern is unanchored: search for the pattern in the 555146045Sharti * word using strstr(3), copying unmatched portions and 556146045Sharti * the right-hand-side for each match found, handling 557146045Sharti * non-global substitutions correctly, etc. When the 558146045Sharti * loop is done, any remaining part of the word (word 559146045Sharti * and wordLen are adjusted accordingly through the 560146045Sharti * loop) is copied straight into the buffer. 561146045Sharti * addSpace is set FALSE as soon as a space is added 562146045Sharti * to the buffer. 563146045Sharti */ 564146045Sharti Boolean done; 565146045Sharti size_t origSize; 566146045Sharti 567146045Sharti done = FALSE; 568146045Sharti origSize = Buf_Size(buf); 569146045Sharti while (!done) { 570146045Sharti cp = strstr(word, Buf_Data(pattern->lhs)); 571146045Sharti if (cp != NULL) { 572146045Sharti if (addSpace && (((cp - word) + 573146045Sharti Buf_Size(pattern->rhs)) != 0)) { 574146045Sharti Buf_AddByte(buf, (Byte)' '); 575146045Sharti addSpace = FALSE; 576146045Sharti } 577146045Sharti Buf_AppendRange(buf, word, cp); 578146045Sharti Buf_AppendBuf(buf, pattern->rhs); 579146045Sharti wordLen -= (cp - word) + 580146045Sharti Buf_Size(pattern->lhs); 581146045Sharti word = cp + Buf_Size(pattern->lhs); 582146045Sharti if (wordLen == 0 || (pattern->flags & 583146045Sharti VAR_SUB_GLOBAL) == 0) { 584146045Sharti done = TRUE; 585146045Sharti } 586146045Sharti } else { 587146045Sharti done = TRUE; 588146045Sharti } 589146045Sharti } 590146045Sharti if (wordLen != 0) { 591146045Sharti if (addSpace) { 592146045Sharti Buf_AddByte(buf, (Byte)' '); 593146045Sharti } 594146045Sharti Buf_AddBytes(buf, wordLen, (const Byte *)word); 595146045Sharti } 596146045Sharti 597146045Sharti /* 598146045Sharti * If added characters to the buffer, need to add a 599146045Sharti * space before we add any more. If we didn't add any, 600146045Sharti * just return the previous value of addSpace. 601146045Sharti */ 602146045Sharti return ((Buf_Size(buf) != origSize) || addSpace); 603146045Sharti } 604146045Sharti /* 605146045Sharti * Common code for anchored substitutions: 606146045Sharti * addSpace was set TRUE if characters were added to the buffer. 607146045Sharti */ 608146045Sharti return (addSpace); 609146045Sharti } 610146045Sharti nosub: 611146045Sharti if (addSpace) { 612146045Sharti Buf_AddByte(buf, (Byte)' '); 613146045Sharti } 614146045Sharti Buf_AddBytes(buf, wordLen, (const Byte *)word); 615146045Sharti return (TRUE); 616146045Sharti} 617146045Sharti 618146045Sharti/** 619146045Sharti * Print the error caused by a regcomp or regexec call. 620146045Sharti * 621146045Sharti * Side Effects: 622146045Sharti * An error gets printed. 623146045Sharti */ 624146045Shartistatic void 625146045ShartiVarREError(int err, regex_t *pat, const char *str) 626146045Sharti{ 627146045Sharti char *errbuf; 628146045Sharti int errlen; 629146045Sharti 630146045Sharti errlen = regerror(err, pat, 0, 0); 631146045Sharti errbuf = emalloc(errlen); 632146045Sharti regerror(err, pat, errbuf, errlen); 633146045Sharti Error("%s: %s", str, errbuf); 634146045Sharti free(errbuf); 635146045Sharti} 636146045Sharti 637146045Sharti 638146045Sharti/** 639146046Sharti * Perform a regex substitution on the given word, placing the 640146046Sharti * result in the passed buffer. A space is added if requested. 641146045Sharti * 642146045Sharti * Results: 643146045Sharti * TRUE if a space is needed before more characters are added. 644146045Sharti */ 645146045Shartistatic Boolean 646146045ShartiVarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp) 647146045Sharti{ 648146045Sharti VarPattern *pat; 649146045Sharti int xrv; 650146045Sharti const char *wp; 651146045Sharti char *rp; 652146045Sharti int added; 653146045Sharti int flags = 0; 654146045Sharti 655146045Sharti#define MAYBE_ADD_SPACE() \ 656146045Sharti if (addSpace && !added) \ 657146045Sharti Buf_AddByte(buf, (Byte)' '); \ 658146045Sharti added = 1 659146045Sharti 660146045Sharti added = 0; 661146045Sharti wp = word; 662146045Sharti pat = patternp; 663146045Sharti 664146045Sharti if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) == 665146045Sharti (VAR_SUB_ONE | VAR_SUB_MATCHED)) { 666146045Sharti xrv = REG_NOMATCH; 667146045Sharti } else { 668146045Sharti tryagain: 669146045Sharti xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); 670146045Sharti } 671146045Sharti 672146045Sharti switch (xrv) { 673146045Sharti case 0: 674146045Sharti pat->flags |= VAR_SUB_MATCHED; 675146045Sharti if (pat->matches[0].rm_so > 0) { 676146045Sharti MAYBE_ADD_SPACE(); 677146045Sharti Buf_AddBytes(buf, pat->matches[0].rm_so, 678146045Sharti (const Byte *)wp); 679146045Sharti } 680146045Sharti 681146045Sharti for (rp = Buf_Data(pat->rhs); *rp; rp++) { 682146045Sharti if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { 683146045Sharti MAYBE_ADD_SPACE(); 684146045Sharti Buf_AddByte(buf, (Byte)rp[1]); 685146045Sharti rp++; 686146045Sharti 687146045Sharti } else if ((*rp == '&') || 688146045Sharti ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { 689146045Sharti int n; 690146045Sharti const char *subbuf; 691146045Sharti int sublen; 692146045Sharti char errstr[3]; 693146045Sharti 694146045Sharti if (*rp == '&') { 695146045Sharti n = 0; 696146045Sharti errstr[0] = '&'; 697146045Sharti errstr[1] = '\0'; 698146045Sharti } else { 699146045Sharti n = rp[1] - '0'; 700146045Sharti errstr[0] = '\\'; 701146045Sharti errstr[1] = rp[1]; 702146045Sharti errstr[2] = '\0'; 703146045Sharti rp++; 704146045Sharti } 705146045Sharti 706146045Sharti if (n > pat->nsub) { 707146045Sharti Error("No subexpression %s", 708146045Sharti &errstr[0]); 709146045Sharti subbuf = ""; 710146045Sharti sublen = 0; 711146045Sharti 712146045Sharti } else if ((pat->matches[n].rm_so == -1) && 713146045Sharti (pat->matches[n].rm_eo == -1)) { 714146045Sharti Error("No match for subexpression %s", 715146045Sharti &errstr[0]); 716146045Sharti subbuf = ""; 717146045Sharti sublen = 0; 718146045Sharti 719146045Sharti } else { 720146045Sharti subbuf = wp + pat->matches[n].rm_so; 721146045Sharti sublen = pat->matches[n].rm_eo - 722146045Sharti pat->matches[n].rm_so; 723146045Sharti } 724146045Sharti 725146045Sharti if (sublen > 0) { 726146045Sharti MAYBE_ADD_SPACE(); 727146045Sharti Buf_AddBytes(buf, sublen, 728146045Sharti (const Byte *)subbuf); 729146045Sharti } 730146045Sharti } else { 731146045Sharti MAYBE_ADD_SPACE(); 732146045Sharti Buf_AddByte(buf, (Byte)*rp); 733146045Sharti } 734146045Sharti } 735146045Sharti wp += pat->matches[0].rm_eo; 736146045Sharti if (pat->flags & VAR_SUB_GLOBAL) { 737146045Sharti flags |= REG_NOTBOL; 738146045Sharti if (pat->matches[0].rm_so == 0 && 739146045Sharti pat->matches[0].rm_eo == 0) { 740146045Sharti MAYBE_ADD_SPACE(); 741146045Sharti Buf_AddByte(buf, (Byte)*wp); 742146045Sharti wp++; 743146045Sharti } 744146045Sharti if (*wp) 745146045Sharti goto tryagain; 746146045Sharti } 747146045Sharti if (*wp) { 748146045Sharti MAYBE_ADD_SPACE(); 749146045Sharti Buf_Append(buf, wp); 750146045Sharti } 751146045Sharti break; 752146045Sharti 753146045Sharti default: 754146045Sharti VarREError(xrv, &pat->re, "Unexpected regex error"); 755146045Sharti /* fall through */ 756146045Sharti 757146045Sharti case REG_NOMATCH: 758146045Sharti if (*wp) { 759146045Sharti MAYBE_ADD_SPACE(); 760146045Sharti Buf_Append(buf, wp); 761146045Sharti } 762146045Sharti break; 763146045Sharti } 764146045Sharti return (addSpace || added); 765146045Sharti} 766146045Sharti 767146046Sharti/** 768143975Sharti * Find a variable in a variable list. 7691590Srgrimes */ 770143975Shartistatic Var * 771143975ShartiVarLookup(Lst *vlist, const char *name) 7721590Srgrimes{ 773143975Sharti LstNode *ln; 774138232Sharti 775143975Sharti LST_FOREACH(ln, vlist) 776143975Sharti if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) 777143975Sharti return (Lst_Datum(ln)); 778143975Sharti return (NULL); 7791590Srgrimes} 7801590Srgrimes 781146046Sharti/** 782146046Sharti * Expand a variable name's embedded variables in the given context. 78398439Sjmallett * 78498439Sjmallett * Results: 78598441Sjmallett * The contents of name, possibly expanded. 78698439Sjmallett */ 787141268Shartistatic char * 788141268ShartiVarPossiblyExpand(const char *name, GNode *ctxt) 78998439Sjmallett{ 790142457Sharti Buffer *buf; 791138232Sharti 792141268Sharti if (strchr(name, '$') != NULL) { 793146027Sharti buf = Var_Subst(name, ctxt, 0); 794143959Sharti return (Buf_Peel(buf)); 795142457Sharti } else { 796143919Sharti return estrdup(name); 797142457Sharti } 79898439Sjmallett} 79998439Sjmallett 800145007Sharti/** 801145007Sharti * If the variable name begins with a '.', it could very well be 802145007Sharti * one of the local ones. We check the name against all the local 803145007Sharti * variables and substitute the short version in for 'name' if it 804145007Sharti * matches one of them. 8051590Srgrimes */ 806146046Shartistatic const char * 807145007ShartiVarLocal(const char name[]) 8081590Srgrimes{ 809143653Sharti if (name[0] == '.') { 8101590Srgrimes switch (name[1]) { 8111590Srgrimes case 'A': 8121590Srgrimes if (!strcmp(name, ".ALLSRC")) 813145007Sharti return (ALLSRC); 8141590Srgrimes if (!strcmp(name, ".ARCHIVE")) 815145007Sharti return (ARCHIVE); 8161590Srgrimes break; 8171590Srgrimes case 'I': 8181590Srgrimes if (!strcmp(name, ".IMPSRC")) 819145007Sharti return (IMPSRC); 8201590Srgrimes break; 8211590Srgrimes case 'M': 8221590Srgrimes if (!strcmp(name, ".MEMBER")) 823145007Sharti return (MEMBER); 8241590Srgrimes break; 8251590Srgrimes case 'O': 8261590Srgrimes if (!strcmp(name, ".OODATE")) 827145007Sharti return (OODATE); 8281590Srgrimes break; 8291590Srgrimes case 'P': 8301590Srgrimes if (!strcmp(name, ".PREFIX")) 831145007Sharti return (PREFIX); 8321590Srgrimes break; 8331590Srgrimes case 'T': 8341590Srgrimes if (!strcmp(name, ".TARGET")) 835145007Sharti return (TARGET); 8361590Srgrimes break; 837141461Sharti default: 838141461Sharti break; 8391590Srgrimes } 840141461Sharti } 841145007Sharti return (name); 842145007Sharti} 84349332Shoek 844145007Sharti/** 845228992Suqs * Find the given variable in the given context and the environment. 846145007Sharti * 847145007Sharti * Results: 848145007Sharti * A pointer to the structure describing the desired variable or 849145007Sharti * NULL if the variable does not exist. 850145007Sharti */ 851145007Shartistatic Var * 852145007ShartiVarFindEnv(const char name[], GNode *ctxt) 853145007Sharti{ 854145007Sharti Var *var; 855145007Sharti 856145007Sharti name = VarLocal(name); 857145007Sharti 858145007Sharti if ((var = VarLookup(&ctxt->context, name)) != NULL) 859145007Sharti return (var); 860145007Sharti 861145971Sharti if ((var = VarLookup(&VAR_ENV->context, name)) != NULL) 862145971Sharti return (var); 863145007Sharti 864145007Sharti return (NULL); 865145007Sharti} 866145007Sharti 867145007Sharti/** 868145007Sharti * Look for the variable in the given context. 869145007Sharti */ 870145007Shartistatic Var * 871145007ShartiVarFindOnly(const char name[], GNode *ctxt) 872145007Sharti{ 873145007Sharti Var *var; 874145007Sharti 875145007Sharti name = VarLocal(name); 876145007Sharti 877145007Sharti if ((var = VarLookup(&ctxt->context, name)) != NULL) 878145007Sharti return (var); 879145007Sharti 880145007Sharti return (NULL); 881145007Sharti} 882145007Sharti 883145007Sharti/** 884145007Sharti * Look for the variable in all contexts. 885145007Sharti */ 886145007Shartistatic Var * 887145007ShartiVarFindAny(const char name[], GNode *ctxt) 888145007Sharti{ 889145007Sharti Boolean localCheckEnvFirst; 890145007Sharti LstNode *ln; 891145007Sharti Var *var; 892145007Sharti 893145007Sharti name = VarLocal(name); 894145007Sharti 895141572Sharti /* 896141572Sharti * Note whether this is one of the specific variables we were told 897141572Sharti * through the -E flag to use environment-variable-override for. 898141572Sharti */ 899143975Sharti localCheckEnvFirst = FALSE; 900143975Sharti LST_FOREACH(ln, &envFirstVars) { 901143975Sharti if (strcmp(Lst_Datum(ln), name) == 0) { 902143975Sharti localCheckEnvFirst = TRUE; 903143975Sharti break; 904143975Sharti } 905141572Sharti } 90649332Shoek 907141572Sharti /* 908141572Sharti * First look for the variable in the given context. If it's not there, 909141572Sharti * look for it in VAR_CMD, VAR_GLOBAL and the environment, 910141572Sharti * in that order, depending on the FIND_* flags in 'flags' 911141572Sharti */ 912145007Sharti if ((var = VarLookup(&ctxt->context, name)) != NULL) 913143975Sharti return (var); 9141590Srgrimes 915141572Sharti /* not there - try command line context */ 916145007Sharti if (ctxt != VAR_CMD) { 917145007Sharti if ((var = VarLookup(&VAR_CMD->context, name)) != NULL) 918143975Sharti return (var); 919141572Sharti } 9201590Srgrimes 921141572Sharti /* not there - try global context, but only if not -e/-E */ 922145007Sharti if (ctxt != VAR_GLOBAL && (!checkEnvFirst && !localCheckEnvFirst)) { 923145007Sharti if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL) 924143975Sharti return (var); 925141572Sharti } 926141572Sharti 927145971Sharti if ((var = VarLookup(&VAR_ENV->context, name)) != NULL) 928145971Sharti return (var); 929141572Sharti 930141572Sharti /* deferred check for the environment (in case of -e/-E) */ 931145007Sharti if ((ctxt != VAR_GLOBAL) && (checkEnvFirst || localCheckEnvFirst)) { 932145007Sharti if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL) 933143975Sharti return (var); 934141572Sharti } 935145007Sharti 936138264Sharti return (NULL); 9371590Srgrimes} 9381590Srgrimes 939145971Sharti/** 940145971Sharti * Add a new variable of name name and value val to the given context. 9411590Srgrimes * 9421590Srgrimes * Side Effects: 9431590Srgrimes * The new variable is placed at the front of the given context 9441590Srgrimes * The name and val arguments are duplicated so they may 9451590Srgrimes * safely be freed. 9461590Srgrimes */ 947186713Sobrienstatic Var * 948141252ShartiVarAdd(const char *name, const char *val, GNode *ctxt) 9491590Srgrimes{ 950186713Sobrien Var *v; 9511590Srgrimes 952186713Sobrien Lst_AtFront(&ctxt->context, v = VarCreate(name, val, 0)); 953143813Sharti DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, name, val)); 954186713Sobrien return (v); 9551590Srgrimes} 9561590Srgrimes 957146046Sharti/** 958146046Sharti * Remove a variable from a context. 9591590Srgrimes * 9601590Srgrimes * Side Effects: 9611590Srgrimes * The Var structure is removed and freed. 9621590Srgrimes */ 9631590Srgrimesvoid 964141268ShartiVar_Delete(const char *name, GNode *ctxt) 9651590Srgrimes{ 966143813Sharti LstNode *ln; 9671590Srgrimes 968143813Sharti DEBUGF(VAR, ("%s:delete %s\n", ctxt->name, name)); 969143975Sharti LST_FOREACH(ln, &ctxt->context) { 970143975Sharti if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) { 971143975Sharti VarDestroy(Lst_Datum(ln), TRUE); 972143975Sharti Lst_Remove(&ctxt->context, ln); 973143975Sharti break; 974143975Sharti } 975143813Sharti } 9761590Srgrimes} 9771590Srgrimes 978146046Sharti/** 979146046Sharti * Set the variable name to the value val in the given context. 9801590Srgrimes * 9811590Srgrimes * Side Effects: 9821590Srgrimes * If the variable doesn't yet exist, a new record is created for it. 9831590Srgrimes * Else the old value is freed and the new one stuck in its place 9841590Srgrimes * 9851590Srgrimes * Notes: 9861590Srgrimes * The variable is searched for only in its context before being 9871590Srgrimes * created in that context. I.e. if the context is VAR_GLOBAL, 9881590Srgrimes * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only 9891590Srgrimes * VAR_CMD->context is searched. This is done to avoid the literally 9901590Srgrimes * thousands of unnecessary strcmp's that used to be done to 9911590Srgrimes * set, say, $(@) or $(<). 9921590Srgrimes */ 9931590Srgrimesvoid 994141268ShartiVar_Set(const char *name, const char *val, GNode *ctxt) 9951590Srgrimes{ 996143813Sharti Var *v; 997143813Sharti char *n; 9981590Srgrimes 999143813Sharti /* 1000143813Sharti * We only look for a variable in the given context since anything 1001143813Sharti * set here will override anything in a lower context, so there's not 1002143813Sharti * much point in searching them all just to save a bit of memory... 1003143813Sharti */ 1004143813Sharti n = VarPossiblyExpand(name, ctxt); 1005145007Sharti v = VarFindOnly(n, ctxt); 1006143813Sharti if (v == NULL) { 1007186713Sobrien v = VarAdd(n, val, ctxt); 1008143813Sharti } else { 1009143813Sharti Buf_Clear(v->val); 1010143813Sharti Buf_Append(v->val, val); 1011160450Sobrien DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, val)); 1012143813Sharti } 1013145971Sharti 1014186713Sobrien if (ctxt == VAR_CMD || (v->flags & VAR_TO_ENV)) { 1015186713Sobrien /* 1016186713Sobrien * Any variables given on the command line 1017186713Sobrien * are automatically exported to the 1018186713Sobrien * environment (as per POSIX standard) 1019186713Sobrien */ 1020186713Sobrien setenv(n, val, 1); 1021186713Sobrien } 1022186713Sobrien 1023143813Sharti free(n); 10241590Srgrimes} 10251590Srgrimes 1026146046Sharti/** 1027146145Sharti * Set the a global name variable to the value. 1028146145Sharti */ 1029146145Shartivoid 1030146145ShartiVar_SetGlobal(const char name[], const char value[]) 1031146145Sharti{ 1032146145Sharti 1033146145Sharti Var_Set(name, value, VAR_GLOBAL); 1034146145Sharti} 1035146145Sharti 1036146145Sharti 1037146145Sharti/** 1038146046Sharti * Set the VAR_TO_ENV flag on a variable 1039145971Sharti */ 1040145971Shartivoid 1041145971ShartiVar_SetEnv(const char *name, GNode *ctxt) 1042145971Sharti{ 1043145971Sharti Var *v; 1044145971Sharti 1045145971Sharti v = VarFindOnly(name, VAR_CMD); 1046145971Sharti if (v != NULL) { 1047145971Sharti /* 1048145971Sharti * Do not allow .EXPORT: to be set on variables 1049145971Sharti * from the comand line or MAKEFLAGS. 1050145971Sharti */ 1051145971Sharti Error( 1052145971Sharti "Warning: Did not set .EXPORTVAR: on %s because it " 1053145971Sharti "is from the comand line or MAKEFLAGS", name); 1054145971Sharti return; 1055145971Sharti } 1056145971Sharti 1057145971Sharti v = VarFindAny(name, ctxt); 1058145971Sharti if (v == NULL) { 1059145971Sharti Lst_AtFront(&VAR_ENV->context, 1060145971Sharti VarCreate(name, NULL, VAR_TO_ENV)); 1061145971Sharti setenv(name, "", 1); 1062145971Sharti Error("Warning: .EXPORTVAR: set on undefined variable %s", name); 1063145971Sharti } else { 1064145971Sharti if ((v->flags & VAR_TO_ENV) == 0) { 1065145971Sharti v->flags |= VAR_TO_ENV; 1066145971Sharti setenv(v->name, Buf_Data(v->val), 1); 1067145971Sharti } 1068145971Sharti } 1069145971Sharti} 1070145971Sharti 1071146046Sharti/** 1072146046Sharti * The variable of the given name has the given value appended to it in 1073146046Sharti * the given context. 10741590Srgrimes * 10751590Srgrimes * Side Effects: 10761590Srgrimes * If the variable doesn't exist, it is created. Else the strings 10771590Srgrimes * are concatenated (with a space in between). 10781590Srgrimes * 10791590Srgrimes * Notes: 10801590Srgrimes * Only if the variable is being sought in the global context is the 10811590Srgrimes * environment searched. 10821590Srgrimes * XXX: Knows its calling circumstances in that if called with ctxt 10831590Srgrimes * an actual target, it will only search that context since only 10841590Srgrimes * a local variable could be being appended to. This is actually 10851590Srgrimes * a big win and must be tolerated. 10861590Srgrimes */ 10871590Srgrimesvoid 1088141268ShartiVar_Append(const char *name, const char *val, GNode *ctxt) 10891590Srgrimes{ 1090143813Sharti Var *v; 1091143813Sharti char *n; 10921590Srgrimes 1093143813Sharti n = VarPossiblyExpand(name, ctxt); 1094145007Sharti if (ctxt == VAR_GLOBAL) { 1095145007Sharti v = VarFindEnv(n, ctxt); 1096145007Sharti } else { 1097145007Sharti v = VarFindOnly(n, ctxt); 1098145007Sharti } 1099143813Sharti if (v == NULL) { 1100143813Sharti VarAdd(n, val, ctxt); 1101143813Sharti } else { 1102143813Sharti Buf_AddByte(v->val, (Byte)' '); 1103143813Sharti Buf_Append(v->val, val); 1104143959Sharti DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, Buf_Data(v->val))); 11051590Srgrimes } 1106143813Sharti free(n); 11071590Srgrimes} 11081590Srgrimes 1109146046Sharti/** 1110146046Sharti * See if the given variable exists. 11111590Srgrimes * 11121590Srgrimes * Results: 11131590Srgrimes * TRUE if it does, FALSE if it doesn't 11141590Srgrimes */ 11151590SrgrimesBoolean 1116141268ShartiVar_Exists(const char *name, GNode *ctxt) 11171590Srgrimes{ 1118143813Sharti Var *v; 1119143813Sharti char *n; 11201590Srgrimes 1121143813Sharti n = VarPossiblyExpand(name, ctxt); 1122145007Sharti v = VarFindAny(n, ctxt); 1123143813Sharti if (v == NULL) { 1124143968Sharti free(n); 1125143813Sharti return (FALSE); 1126143968Sharti } else { 1127143968Sharti free(n); 1128143968Sharti return (TRUE); 1129143813Sharti } 11301590Srgrimes} 11311590Srgrimes 1132146046Sharti/** 1133146046Sharti * Return the value of the named variable in the given context 11341590Srgrimes * 11351590Srgrimes * Results: 1136146580Sharti * The value if the variable exists, NULL if it doesn't. 11371590Srgrimes */ 1138146581Sharticonst char * 1139146580ShartiVar_Value(const char name[], GNode *ctxt) 11401590Srgrimes{ 1141143813Sharti Var *v; 1142143813Sharti char *n; 11431590Srgrimes 1144143813Sharti n = VarPossiblyExpand(name, ctxt); 1145145007Sharti v = VarFindAny(n, ctxt); 1146146580Sharti free(n); 1147143968Sharti if (v == NULL) { 1148146580Sharti return (NULL); 1149143813Sharti } else { 1150146580Sharti return (Buf_Data(v->val)); 11515814Sjkh } 11521590Srgrimes} 11531590Srgrimes 1154146046Sharti/** 1155146046Sharti * Modify each of the words of the passed string using the given 1156146046Sharti * function. Used to implement all modifiers. 11571590Srgrimes * 11581590Srgrimes * Results: 11591590Srgrimes * A string of all the words modified appropriately. 11601590Srgrimes * 11611590Srgrimes * Side Effects: 1162143255Sharti * Uses brk_string() so it invalidates any previous call to 1163143255Sharti * brk_string(). 11641590Srgrimes */ 11651590Srgrimesstatic char * 1166143255ShartiVarModify(const char *str, VarModifyProc *modProc, void *datum) 11671590Srgrimes{ 1168146345Sharti ArgArray aa; 1169146345Sharti Buffer *buf; /* Buffer for the new string */ 1170146345Sharti int i; 1171146345Sharti Boolean addSpace; /* 1172146345Sharti * TRUE if need to add a space to 1173146345Sharti * the buffer before adding the 1174146345Sharti * trimmed word 1175146345Sharti */ 11765814Sjkh 1177146345Sharti brk_string(&aa, str, FALSE); 1178143255Sharti 1179146345Sharti addSpace = FALSE; 1180143254Sharti buf = Buf_Init(0); 1181146345Sharti for (i = 1; i < aa.argc; i++) 1182146345Sharti addSpace = (*modProc)(aa.argv[i], addSpace, buf, datum); 1183143255Sharti 1184146345Sharti ArgArray_Done(&aa); 1185143959Sharti return (Buf_Peel(buf)); 11861590Srgrimes} 11871590Srgrimes 1188146046Sharti/** 1189146046Sharti * Sort the words in the string. 1190120184Smarcel * 1191120184Smarcel * Input: 1192120184Smarcel * str String whose words should be sorted 1193120184Smarcel * cmp A comparison function to control the ordering 1194120184Smarcel * 1195120184Smarcel * Results: 1196120184Smarcel * A string containing the words sorted 1197120184Smarcel */ 1198120184Smarcelstatic char * 1199143255ShartiVarSortWords(const char *str, int (*cmp)(const void *, const void *)) 1200120184Smarcel{ 1201146345Sharti ArgArray aa; 1202146345Sharti Buffer *buf; 1203146345Sharti int i; 1204120184Smarcel 1205146345Sharti brk_string(&aa, str, FALSE); 1206146345Sharti qsort(aa.argv + 1, aa.argc - 1, sizeof(char *), cmp); 1207143255Sharti 1208143255Sharti buf = Buf_Init(0); 1209146345Sharti for (i = 1; i < aa.argc; i++) { 1210146345Sharti Buf_Append(buf, aa.argv[i]); 1211146345Sharti Buf_AddByte(buf, (Byte)((i < aa.argc - 1) ? ' ' : '\0')); 1212120184Smarcel } 1213143255Sharti 1214146345Sharti ArgArray_Done(&aa); 1215143959Sharti return (Buf_Peel(buf)); 1216120184Smarcel} 1217120184Smarcel 1218120184Smarcelstatic int 1219120184SmarcelSortIncreasing(const void *l, const void *r) 1220120184Smarcel{ 1221138232Sharti 1222120184Smarcel return (strcmp(*(const char* const*)l, *(const char* const*)r)); 1223120184Smarcel} 1224120184Smarcel 1225146046Sharti/** 1226157588Sfjoe * Remove adjacent duplicate words. 1227157588Sfjoe * 1228157588Sfjoe * Results: 1229157588Sfjoe * A string containing the resulting words. 1230157588Sfjoe */ 1231157588Sfjoestatic char * 1232157588SfjoeVarUniq(const char *str) 1233157588Sfjoe{ 1234157588Sfjoe ArgArray aa; 1235157588Sfjoe Buffer *buf; /* Buffer for new string */ 1236157588Sfjoe int i, j; 1237157588Sfjoe 1238157588Sfjoe buf = Buf_Init(0); 1239157588Sfjoe brk_string(&aa, str, FALSE); 1240157588Sfjoe 1241157588Sfjoe if (aa.argc > 2) { 1242157588Sfjoe for (j = 1, i = 2; i < aa.argc; i++) { 1243157588Sfjoe if (strcmp(aa.argv[i], aa.argv[j]) != 0 && (++j != i)) 1244157588Sfjoe aa.argv[j] = aa.argv[i]; 1245157588Sfjoe } 1246157588Sfjoe aa.argc = j + 1; 1247157588Sfjoe } 1248157588Sfjoe 1249157588Sfjoe for (i = 1; i < aa.argc; i++) { 1250157588Sfjoe Buf_AddBytes(buf, strlen(aa.argv[i]), (Byte *)aa.argv[i]); 1251157588Sfjoe if (i != aa.argc - 1) 1252157588Sfjoe Buf_AddByte(buf, ' '); 1253157588Sfjoe } 1254157588Sfjoe Buf_AddByte(buf, '\0'); 1255157588Sfjoe 1256157588Sfjoe ArgArray_Done(&aa); 1257157588Sfjoe return (Buf_Peel(buf)); 1258157588Sfjoe} 1259157588Sfjoe 1260157588Sfjoe/** 1261146046Sharti * Pass through the tstr looking for 1) escaped delimiters, 1262146046Sharti * '$'s and backslashes (place the escaped character in 1263146046Sharti * uninterpreted) and 2) unescaped $'s that aren't before 1264146046Sharti * the delimiter (expand the variable substitution). 1265146046Sharti * Return the expanded string or NULL if the delimiter was missing 1266146046Sharti * If pattern is specified, handle escaped ampersands, and replace 1267146046Sharti * unescaped ampersands with the lhs of the pattern. 126866853Swill * 126966853Swill * Results: 127066853Swill * A string of all the words modified appropriately. 127166853Swill * If length is specified, return the string length of the buffer 127266853Swill * If flags is specified and the last character of the pattern is a 127366853Swill * $ set the VAR_MATCH_END bit of flags. 127466853Swill */ 1275143966Shartistatic Buffer * 1276143966ShartiVarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt) 127766853Swill{ 1278143955Sharti Buffer *buf; 1279138232Sharti 1280143955Sharti buf = Buf_Init(0); 128166853Swill 1282143813Sharti /* 1283143813Sharti * Skim through until the matching delimiter is found; pick up 1284143813Sharti * variable substitutions on the way. Also allow backslashes to quote 1285143813Sharti * the delimiter, $, and \, but don't touch other backslashes. 1286143813Sharti */ 1287143955Sharti while (*vp->ptr != '\0') { 1288143955Sharti if (*vp->ptr == delim) { 1289143966Sharti return (buf); 1290143955Sharti 1291143955Sharti } else if ((vp->ptr[0] == '\\') && 1292143955Sharti ((vp->ptr[1] == delim) || 1293143955Sharti (vp->ptr[1] == '\\') || 1294143955Sharti (vp->ptr[1] == '$') || 1295143955Sharti (vp->ptr[1] == '&' && patt != NULL))) { 1296143955Sharti vp->ptr++; /* consume backslash */ 1297143955Sharti Buf_AddByte(buf, (Byte)vp->ptr[0]); 1298143955Sharti vp->ptr++; 1299143955Sharti 1300143955Sharti } else if (vp->ptr[0] == '$') { 1301143955Sharti if (vp->ptr[1] == delim) { 1302143955Sharti if (flags == NULL) { 1303143955Sharti Buf_AddByte(buf, (Byte)vp->ptr[0]); 1304143955Sharti vp->ptr++; 1305143955Sharti } else { 1306143813Sharti /* 1307143955Sharti * Unescaped $ at end of patt => 1308143955Sharti * anchor patt at end. 1309143813Sharti */ 1310143813Sharti *flags |= VAR_MATCH_END; 1311143955Sharti vp->ptr++; 1312143955Sharti } 1313143813Sharti } else { 1314143970Sharti VarParser subvp = { 1315143970Sharti vp->ptr, 1316143970Sharti vp->ptr, 1317143970Sharti vp->ctxt, 1318146038Sharti vp->err, 1319146038Sharti vp->execute 1320143970Sharti }; 1321143969Sharti char *rval; 1322143969Sharti Boolean rfree; 132366853Swill 1324143955Sharti /* 1325143955Sharti * If unescaped dollar sign not 1326143955Sharti * before the delimiter, assume it's 1327143955Sharti * a variable substitution and 1328143955Sharti * recurse. 1329143955Sharti */ 1330143970Sharti rval = VarParse(&subvp, &rfree); 1331143969Sharti Buf_Append(buf, rval); 1332143969Sharti if (rfree) 1333143969Sharti free(rval); 1334143970Sharti vp->ptr = subvp.ptr; 133566853Swill } 1336143955Sharti } else if (vp->ptr[0] == '&' && patt != NULL) { 1337143966Sharti Buf_AppendBuf(buf, patt->lhs); 1338143955Sharti vp->ptr++; 1339143955Sharti } else { 1340143955Sharti Buf_AddByte(buf, (Byte)vp->ptr[0]); 1341143955Sharti vp->ptr++; 1342143955Sharti } 134366853Swill } 134466853Swill 1345143955Sharti Buf_Destroy(buf, TRUE); 1346143955Sharti return (NULL); 134766853Swill} 134866853Swill 1349143811Sharti/** 1350142557Sharti * Make sure this variable is fully expanded. 13511590Srgrimes */ 1352142557Shartistatic char * 1353143811ShartiVarExpand(Var *v, VarParser *vp) 13541590Srgrimes{ 1355142557Sharti char *value; 1356142557Sharti char *result; 1357142557Sharti 1358142557Sharti if (v->flags & VAR_IN_USE) { 1359142557Sharti Fatal("Variable %s is recursive.", v->name); 1360142557Sharti /* NOTREACHED */ 1361142557Sharti } 1362142557Sharti 1363142557Sharti v->flags |= VAR_IN_USE; 1364142557Sharti 1365142557Sharti /* 1366142557Sharti * Before doing any modification, we have to make sure the 1367142557Sharti * value has been fully expanded. If it looks like recursion 1368142557Sharti * might be necessary (there's a dollar sign somewhere in the 1369142557Sharti * variable's value) we just call Var_Subst to do any other 1370142557Sharti * substitutions that are necessary. Note that the value 1371142557Sharti * returned by Var_Subst will have been 1372142557Sharti * dynamically-allocated, so it will need freeing when we 1373142557Sharti * return. 1374142557Sharti */ 1375143959Sharti value = Buf_Data(v->val); 1376142557Sharti if (strchr(value, '$') == NULL) { 1377142557Sharti result = strdup(value); 1378142557Sharti } else { 1379142557Sharti Buffer *buf; 1380142557Sharti 1381146027Sharti buf = Var_Subst(value, vp->ctxt, vp->err); 1382143959Sharti result = Buf_Peel(buf); 1383142557Sharti } 1384142557Sharti 1385142557Sharti v->flags &= ~VAR_IN_USE; 1386142557Sharti 1387142557Sharti return (result); 1388142557Sharti} 1389142557Sharti 1390143240Sharti/** 1391143812Sharti * Select only those words in value that match the modifier. 1392143240Sharti */ 1393143240Shartistatic char * 1394143914Shartimodifier_M(VarParser *vp, const char value[], char endc) 1395143240Sharti{ 1396143914Sharti char *patt; 1397143914Sharti char *ptr; 1398143914Sharti char *newValue; 1399143914Sharti char modifier; 1400143240Sharti 1401143914Sharti modifier = vp->ptr[0]; 1402143914Sharti vp->ptr++; /* consume 'M' or 'N' */ 1403143914Sharti 1404143240Sharti /* 1405143240Sharti * Compress the \:'s out of the pattern, so allocate enough 1406143240Sharti * room to hold the uncompressed pattern and compress the 1407143656Sharti * pattern into that space. 1408143240Sharti */ 1409143914Sharti patt = estrdup(vp->ptr); 1410143240Sharti ptr = patt; 1411143914Sharti while (vp->ptr[0] != '\0') { 1412143967Sharti if (vp->ptr[0] == endc || vp->ptr[0] == ':') { 1413143656Sharti break; 1414143656Sharti } 1415143967Sharti if (vp->ptr[0] == '\\' && 1416143967Sharti (vp->ptr[1] == endc || vp->ptr[1] == ':')) { 1417143919Sharti vp->ptr++; /* consume backslash */ 1418143240Sharti } 1419143914Sharti *ptr = vp->ptr[0]; 1420143240Sharti ptr++; 1421143914Sharti vp->ptr++; 1422143240Sharti } 1423143240Sharti *ptr = '\0'; 1424241280Savg DEBUGF(VAR, ("Pattern :%s\n", patt)); 1425143240Sharti 1426143914Sharti if (modifier == 'M') { 1427143240Sharti newValue = VarModify(value, VarMatch, patt); 1428143240Sharti } else { 1429143240Sharti newValue = VarModify(value, VarNoMatch, patt); 1430143240Sharti } 1431143240Sharti free(patt); 1432143240Sharti 1433143240Sharti return (newValue); 1434143240Sharti} 1435143240Sharti 1436143812Sharti/** 1437143812Sharti * Substitute the replacement string for the pattern. The substitution 1438143812Sharti * is applied to each word in value. 1439143812Sharti */ 1440143252Shartistatic char * 1441143914Shartimodifier_S(VarParser *vp, const char value[], Var *v) 1442143252Sharti{ 1443143955Sharti VarPattern patt; 1444143252Sharti char delim; 1445143252Sharti char *newValue; 1446143240Sharti 1447143955Sharti patt.flags = 0; 1448143252Sharti 1449143920Sharti vp->ptr++; /* consume 'S' */ 1450143252Sharti 1451143920Sharti delim = *vp->ptr; /* used to find end of pattern */ 1452143920Sharti vp->ptr++; /* consume 1st delim */ 1453143920Sharti 1454143252Sharti /* 1455143252Sharti * If pattern begins with '^', it is anchored to the start of the 1456143252Sharti * word -- skip over it and flag pattern. 1457143252Sharti */ 1458143920Sharti if (*vp->ptr == '^') { 1459143955Sharti patt.flags |= VAR_MATCH_START; 1460143920Sharti vp->ptr++; 1461143252Sharti } 1462143252Sharti 1463143966Sharti patt.lhs = VarGetPattern(vp, delim, &patt.flags, NULL); 1464143955Sharti if (patt.lhs == NULL) { 1465143955Sharti /* 1466143955Sharti * LHS didn't end with the delim, complain and exit. 1467143955Sharti */ 1468143955Sharti Fatal("Unclosed substitution for %s (%c missing)", 1469143955Sharti v->name, delim); 1470143955Sharti } 1471143252Sharti 1472143955Sharti vp->ptr++; /* consume 2nd delim */ 1473143252Sharti 1474143966Sharti patt.rhs = VarGetPattern(vp, delim, NULL, &patt); 1475143955Sharti if (patt.rhs == NULL) { 1476143955Sharti /* 1477143955Sharti * RHS didn't end with the delim, complain and exit. 1478143955Sharti */ 1479143955Sharti Fatal("Unclosed substitution for %s (%c missing)", 1480143955Sharti v->name, delim); 1481143252Sharti } 1482143252Sharti 1483143920Sharti vp->ptr++; /* consume last delim */ 1484143252Sharti 1485143252Sharti /* 1486143252Sharti * Check for global substitution. If 'g' after the final delimiter, 1487143252Sharti * substitution is global and is marked that way. 1488143252Sharti */ 1489143920Sharti if (vp->ptr[0] == 'g') { 1490143955Sharti patt.flags |= VAR_SUB_GLOBAL; 1491143920Sharti vp->ptr++; 1492143252Sharti } 1493143252Sharti 1494143252Sharti /* 1495143252Sharti * Global substitution of the empty string causes an infinite number 1496143252Sharti * of matches, unless anchored by '^' (start of string) or '$' (end 1497143252Sharti * of string). Catch the infinite substitution here. Note that flags 1498143252Sharti * can only contain the 3 bits we're interested in so we don't have 1499143252Sharti * to mask unrelated bits. We can test for equality. 1500143252Sharti */ 1501143966Sharti if (Buf_Size(patt.lhs) == 0 && patt.flags == VAR_SUB_GLOBAL) 1502143252Sharti Fatal("Global substitution of the empty string"); 1503143252Sharti 1504143955Sharti newValue = VarModify(value, VarSubstitute, &patt); 1505143252Sharti 1506143252Sharti /* 1507143252Sharti * Free the two strings. 1508143252Sharti */ 1509143955Sharti free(patt.lhs); 1510143955Sharti free(patt.rhs); 1511143252Sharti 1512143252Sharti return (newValue); 1513143252Sharti} 1514143252Sharti 1515143812Shartistatic char * 1516143914Shartimodifier_C(VarParser *vp, char value[], Var *v) 1517143812Sharti{ 1518143964Sharti VarPattern patt; 1519143913Sharti char delim; 1520143812Sharti int error; 1521143812Sharti char *newValue; 1522143812Sharti 1523143812Sharti patt.flags = 0; 1524143812Sharti 1525143920Sharti vp->ptr++; /* consume 'C' */ 1526143812Sharti 1527143920Sharti delim = *vp->ptr; /* delimiter between sections */ 1528143812Sharti 1529143920Sharti vp->ptr++; /* consume 1st delim */ 1530143920Sharti 1531143966Sharti patt.lhs = VarGetPattern(vp, delim, NULL, NULL); 1532143965Sharti if (patt.lhs == NULL) { 1533143812Sharti Fatal("Unclosed substitution for %s (%c missing)", 1534143813Sharti v->name, delim); 1535143812Sharti } 1536143812Sharti 1537143955Sharti vp->ptr++; /* consume 2st delim */ 1538143955Sharti 1539143966Sharti patt.rhs = VarGetPattern(vp, delim, NULL, NULL); 1540143964Sharti if (patt.rhs == NULL) { 1541143812Sharti Fatal("Unclosed substitution for %s (%c missing)", 1542143813Sharti v->name, delim); 1543143812Sharti } 1544143812Sharti 1545143955Sharti vp->ptr++; /* consume last delim */ 1546143955Sharti 1547143920Sharti switch (*vp->ptr) { 1548143920Sharti case 'g': 1549143920Sharti patt.flags |= VAR_SUB_GLOBAL; 1550143920Sharti vp->ptr++; /* consume 'g' */ 1551143812Sharti break; 1552143920Sharti case '1': 1553143920Sharti patt.flags |= VAR_SUB_ONE; 1554143920Sharti vp->ptr++; /* consume '1' */ 1555143920Sharti break; 1556143920Sharti default: 1557143920Sharti break; 1558143812Sharti } 1559143812Sharti 1560143966Sharti error = regcomp(&patt.re, Buf_Data(patt.lhs), REG_EXTENDED); 1561143812Sharti if (error) { 1562143812Sharti VarREError(error, &patt.re, "RE substitution error"); 1563143964Sharti free(patt.rhs); 1564143965Sharti free(patt.lhs); 1565143812Sharti return (var_Error); 1566143812Sharti } 1567143812Sharti 1568143812Sharti patt.nsub = patt.re.re_nsub + 1; 1569143812Sharti if (patt.nsub < 1) 1570143812Sharti patt.nsub = 1; 1571143812Sharti if (patt.nsub > 10) 1572143812Sharti patt.nsub = 10; 1573143812Sharti patt.matches = emalloc(patt.nsub * sizeof(regmatch_t)); 1574143812Sharti 1575143812Sharti newValue = VarModify(value, VarRESubstitute, &patt); 1576143812Sharti 1577143812Sharti regfree(&patt.re); 1578143812Sharti free(patt.matches); 1579143964Sharti free(patt.rhs); 1580143965Sharti free(patt.lhs); 1581143812Sharti 1582143812Sharti return (newValue); 1583143812Sharti} 1584143812Sharti 1585143918Shartistatic char * 1586143918ShartisysVvarsub(VarParser *vp, char startc, Var *v, const char value[]) 1587143918Sharti{ 1588143918Sharti#ifdef SYSVVARSUB 1589143918Sharti /* 1590143918Sharti * This can either be a bogus modifier or a System-V substitution 1591143918Sharti * command. 1592143918Sharti */ 1593143918Sharti char endc; 1594143918Sharti VarPattern patt; 1595143918Sharti Boolean eqFound; 1596143918Sharti int cnt; 1597143918Sharti char *newStr; 1598143918Sharti const char *cp; 1599143918Sharti 1600143918Sharti endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; 1601143918Sharti 1602143918Sharti patt.flags = 0; 1603143918Sharti 1604143918Sharti /* 1605143918Sharti * First we make a pass through the string trying to verify it is a 1606143918Sharti * SYSV-make-style translation: it must be: <string1>=<string2>) 1607143918Sharti */ 1608143955Sharti eqFound = FALSE; 1609143918Sharti cp = vp->ptr; 1610143918Sharti cnt = 1; 1611143918Sharti while (*cp != '\0' && cnt) { 1612143918Sharti if (*cp == '=') { 1613143918Sharti eqFound = TRUE; 1614143918Sharti /* continue looking for endc */ 1615143918Sharti } else if (*cp == endc) 1616143918Sharti cnt--; 1617143918Sharti else if (*cp == startc) 1618143918Sharti cnt++; 1619143918Sharti if (cnt) 1620143918Sharti cp++; 1621143918Sharti } 1622143918Sharti 1623143918Sharti if (*cp == endc && eqFound) { 1624143918Sharti /* 1625143918Sharti * Now we break this sucker into the lhs and rhs. 1626143918Sharti */ 1627143966Sharti patt.lhs = VarGetPattern(vp, '=', &patt.flags, NULL); 1628143918Sharti if (patt.lhs == NULL) { 1629143918Sharti Fatal("Unclosed substitution for %s (%c missing)", 1630143918Sharti v->name, '='); 1631143918Sharti } 1632143955Sharti vp->ptr++; /* consume '=' */ 1633143955Sharti 1634143966Sharti patt.rhs = VarGetPattern(vp, endc, NULL, &patt); 1635143918Sharti if (patt.rhs == NULL) { 1636143918Sharti Fatal("Unclosed substitution for %s (%c missing)", 1637143918Sharti v->name, endc); 1638143918Sharti } 1639143918Sharti 1640143918Sharti /* 1641143918Sharti * SYSV modifications happen through the whole string. Note 1642143918Sharti * the pattern is anchored at the end. 1643143918Sharti */ 1644143918Sharti newStr = VarModify(value, VarSYSVMatch, &patt); 1645143918Sharti 1646143918Sharti free(patt.lhs); 1647143918Sharti free(patt.rhs); 1648143918Sharti } else 1649143918Sharti#endif 1650143918Sharti { 1651143918Sharti Error("Unknown modifier '%c'\n", *vp->ptr); 1652143918Sharti vp->ptr++; 1653143918Sharti while (*vp->ptr != '\0') { 1654143918Sharti if (*vp->ptr == endc && *vp->ptr == ':') { 1655143918Sharti break; 1656143918Sharti } 1657143918Sharti vp->ptr++; 1658143918Sharti } 1659143918Sharti newStr = var_Error; 1660143918Sharti } 1661143918Sharti 1662143918Sharti return (newStr); 1663143918Sharti} 1664143918Sharti 1665146045Sharti/** 1666146045Sharti * Quote shell meta-characters in the string 1667146045Sharti * 1668146045Sharti * Results: 1669146045Sharti * The quoted string 1670146045Sharti */ 1671146045Shartistatic char * 1672146045ShartiVar_Quote(const char *str) 1673146045Sharti{ 1674146045Sharti Buffer *buf; 1675146045Sharti /* This should cover most shells :-( */ 1676146045Sharti static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1677146045Sharti 1678146045Sharti buf = Buf_Init(MAKE_BSIZE); 1679146045Sharti for (; *str; str++) { 1680146045Sharti if (strchr(meta, *str) != NULL) 1681146045Sharti Buf_AddByte(buf, (Byte)'\\'); 1682146045Sharti Buf_AddByte(buf, (Byte)*str); 1683146045Sharti } 1684146045Sharti 1685146045Sharti return (Buf_Peel(buf)); 1686146045Sharti} 1687146045Sharti 1688146045Sharti 1689142557Sharti/* 1690142910Sharti * Now we need to apply any modifiers the user wants applied. 1691142910Sharti * These are: 1692142910Sharti * :M<pattern> 1693142910Sharti * words which match the given <pattern>. 1694142910Sharti * <pattern> is of the standard file 1695142910Sharti * wildcarding form. 1696157589Sfjoe * :N<pattern> 1697157589Sfjoe * words which do not match the given <pattern> 1698157589Sfjoe * <pattern> is of the standard file 1699157589Sfjoe * wildcarding form. 1700142910Sharti * :S<d><pat1><d><pat2><d>[g] 1701142910Sharti * Substitute <pat2> for <pat1> in the value 1702142910Sharti * :C<d><pat1><d><pat2><d>[g] 1703142910Sharti * Substitute <pat2> for regex <pat1> in the value 1704142910Sharti * :H Substitute the head of each word 1705142910Sharti * :T Substitute the tail of each word 1706142910Sharti * :E Substitute the extension (minus '.') of 1707142910Sharti * each word 1708142910Sharti * :R Substitute the root of each word 1709142910Sharti * (pathname minus the suffix). 1710142910Sharti * :lhs=rhs 1711142910Sharti * Like :S, but the rhs goes to the end of 1712142910Sharti * the invocation. 1713142910Sharti * :U Converts variable to upper-case. 1714142910Sharti * :L Converts variable to lower-case. 1715157589Sfjoe * :O ("Order") Alphabeticaly sort words in variable. 1716157588Sfjoe * :u ("uniq") Remove adjacent duplicate words. 1717142910Sharti */ 1718142910Shartistatic char * 1719143917ShartiParseModifier(VarParser *vp, char startc, Var *v, Boolean *freeResult) 1720142910Sharti{ 1721143914Sharti char *value; 1722143914Sharti char endc; 1723142910Sharti 1724143811Sharti value = VarExpand(v, vp); 1725143917Sharti *freeResult = TRUE; 1726142910Sharti 1727143914Sharti endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; 1728143914Sharti 1729143918Sharti vp->ptr++; /* consume first colon */ 1730143918Sharti 1731143918Sharti while (*vp->ptr != '\0') { 1732143813Sharti char *newStr; /* New value to return */ 1733142910Sharti 1734143918Sharti if (*vp->ptr == endc) { 1735143918Sharti return (value); 1736143918Sharti } 1737143918Sharti 1738143914Sharti DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *vp->ptr, value)); 1739143914Sharti switch (*vp->ptr) { 1740142910Sharti case 'N': 1741142910Sharti case 'M': 1742143914Sharti newStr = modifier_M(vp, value, endc); 1743143104Sharti break; 1744142910Sharti case 'S': 1745143914Sharti newStr = modifier_S(vp, value, v); 1746143252Sharti break; 1747142910Sharti case 'C': 1748143914Sharti newStr = modifier_C(vp, value, v); 1749142910Sharti break; 1750236338Sobrien case 't': 1751236338Sobrien /* :tl :tu for OSF ODE & NetBSD make compatibility */ 1752236338Sobrien switch (vp->ptr[1]) { 1753236338Sobrien case 'l': 1754236338Sobrien vp->ptr++; 1755236338Sobrien goto mod_lower; 1756236338Sobrien break; 1757236338Sobrien case 'u': 1758236338Sobrien vp->ptr++; 1759236338Sobrien goto mod_upper; 1760236338Sobrien break; 1761236338Sobrien } 1762236338Sobrien /* FALLTHROUGH */ 1763143814Sharti default: 1764143918Sharti if (vp->ptr[1] != endc && vp->ptr[1] != ':') { 1765142910Sharti#ifdef SUNSHCMD 1766143914Sharti if ((vp->ptr[0] == 's') && 1767143914Sharti (vp->ptr[1] == 'h') && 1768143914Sharti (vp->ptr[2] == endc || vp->ptr[2] == ':')) { 1769239071Sdim const char *error = NULL; 1770142910Sharti 1771146038Sharti if (vp->execute) { 1772146038Sharti newStr = Buf_Peel( 1773146038Sharti Cmd_Exec(value, &error)); 1774146038Sharti } else { 1775146038Sharti newStr = estrdup(""); 1776146038Sharti } 1777142910Sharti 1778143814Sharti if (error) 1779143814Sharti Error(error, value); 1780143918Sharti vp->ptr += 2; 1781143814Sharti } else 1782142910Sharti#endif 1783143814Sharti { 1784143918Sharti newStr = sysVvarsub(vp, startc, v, value); 1785143918Sharti } 1786143918Sharti break; 1787143918Sharti } 1788142910Sharti 1789143918Sharti switch (vp->ptr[0]) { 1790143918Sharti case 'L': 1791236338Sobrien mod_lower: 1792143918Sharti { 1793143918Sharti const char *cp; 1794143918Sharti Buffer *buf; 1795143918Sharti buf = Buf_Init(MAKE_BSIZE); 1796143918Sharti for (cp = value; *cp; cp++) 1797143918Sharti Buf_AddByte(buf, (Byte)tolower(*cp)); 1798142910Sharti 1799143959Sharti newStr = Buf_Peel(buf); 1800142910Sharti 1801143918Sharti vp->ptr++; 1802143918Sharti break; 1803143918Sharti } 1804143918Sharti case 'O': 1805143918Sharti newStr = VarSortWords(value, SortIncreasing); 1806143918Sharti vp->ptr++; 1807143918Sharti break; 1808143918Sharti case 'Q': 1809143918Sharti newStr = Var_Quote(value); 1810143918Sharti vp->ptr++; 1811143918Sharti break; 1812143918Sharti case 'T': 1813143918Sharti newStr = VarModify(value, VarTail, NULL); 1814143918Sharti vp->ptr++; 1815143918Sharti break; 1816143918Sharti case 'U': 1817236338Sobrien mod_upper: 1818143918Sharti { 1819143918Sharti const char *cp; 1820143918Sharti Buffer *buf; 1821143918Sharti buf = Buf_Init(MAKE_BSIZE); 1822143918Sharti for (cp = value; *cp; cp++) 1823143918Sharti Buf_AddByte(buf, (Byte)toupper(*cp)); 1824142910Sharti 1825143959Sharti newStr = Buf_Peel(buf); 1826143813Sharti 1827143918Sharti vp->ptr++; 1828143918Sharti break; 1829143814Sharti } 1830143918Sharti case 'H': 1831143918Sharti newStr = VarModify(value, VarHead, NULL); 1832143918Sharti vp->ptr++; 1833143918Sharti break; 1834143918Sharti case 'E': 1835143918Sharti newStr = VarModify(value, VarSuffix, NULL); 1836143918Sharti vp->ptr++; 1837143918Sharti break; 1838143918Sharti case 'R': 1839143918Sharti newStr = VarModify(value, VarRoot, NULL); 1840143918Sharti vp->ptr++; 1841143918Sharti break; 1842157588Sfjoe case 'u': 1843157588Sfjoe newStr = VarUniq(value); 1844157588Sfjoe vp->ptr++; 1845157588Sfjoe break; 1846143918Sharti default: 1847143918Sharti newStr = sysVvarsub(vp, startc, v, value); 1848143918Sharti break; 1849143813Sharti } 1850143814Sharti break; 1851142910Sharti } 1852143104Sharti 1853143813Sharti DEBUGF(VAR, ("Result is \"%s\"\n", newStr)); 1854143917Sharti if (*freeResult) { 1855143813Sharti free(value); 1856143813Sharti } 1857143918Sharti 1858143813Sharti value = newStr; 1859143918Sharti *freeResult = (value == var_Error) ? FALSE : TRUE; 1860143918Sharti 1861143918Sharti if (vp->ptr[0] == ':') { 1862143918Sharti vp->ptr++; /* consume colon */ 1863143813Sharti } 1864142910Sharti } 1865142910Sharti 1866143916Sharti return (value); 1867142910Sharti} 1868142910Sharti 1869142557Shartistatic char * 1870143917ShartiParseRestModifier(VarParser *vp, char startc, Buffer *buf, Boolean *freeResult) 1871142557Sharti{ 1872143097Sharti const char *vname; 1873143566Sharti size_t vlen; 1874143566Sharti Var *v; 1875143915Sharti char *value; 1876143097Sharti 1877143566Sharti vname = Buf_GetAll(buf, &vlen); 1878143566Sharti 1879145007Sharti v = VarFindAny(vname, vp->ctxt); 1880143097Sharti if (v != NULL) { 1881143917Sharti value = ParseModifier(vp, startc, v, freeResult); 1882143916Sharti return (value); 1883143566Sharti } 1884142014Sharti 1885143811Sharti if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { 1886144340Sharti size_t consumed; 1887144340Sharti /* 1888144340Sharti * Still need to get to the end of the variable 1889144340Sharti * specification, so kludge up a Var structure for the 1890144340Sharti * modifications 1891144340Sharti */ 1892144340Sharti v = VarCreate(vname, NULL, VAR_JUNK); 1893144340Sharti value = ParseModifier(vp, startc, v, freeResult); 1894144340Sharti if (*freeResult) { 1895144340Sharti free(value); 1896144340Sharti } 1897144340Sharti VarDestroy(v, TRUE); 1898143916Sharti 1899144340Sharti consumed = vp->ptr - vp->input + 1; 1900143915Sharti /* 1901143916Sharti * If substituting a local variable in a non-local context, 1902143916Sharti * assume it's for dynamic source stuff. We have to handle 1903143916Sharti * this specially and return the longhand for the variable 1904143916Sharti * with the dollar sign escaped so it makes it back to the 1905143916Sharti * caller. Only four of the local variables are treated 1906143916Sharti * specially as they are the only four that will be set when 1907143916Sharti * dynamic sources are expanded. 1908143915Sharti */ 1909143967Sharti if (vlen == 1 || 1910143967Sharti (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) { 1911143566Sharti if (strchr("!%*@", vname[0]) != NULL) { 1912143915Sharti value = emalloc(consumed + 1); 1913143915Sharti strncpy(value, vp->input, consumed); 1914143915Sharti value[consumed] = '\0'; 1915143915Sharti 1916143917Sharti *freeResult = TRUE; 1917143915Sharti return (value); 1918143097Sharti } 19191590Srgrimes } 1920143967Sharti if (vlen > 2 && 1921143967Sharti vname[0] == '.' && 1922143566Sharti isupper((unsigned char)vname[1])) { 1923143566Sharti if ((strncmp(vname, ".TARGET", vlen - 1) == 0) || 1924143566Sharti (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) || 1925143566Sharti (strncmp(vname, ".PREFIX", vlen - 1) == 0) || 1926143566Sharti (strncmp(vname, ".MEMBER", vlen - 1) == 0)) { 1927143915Sharti value = emalloc(consumed + 1); 1928143915Sharti strncpy(value, vp->input, consumed); 1929143915Sharti value[consumed] = '\0'; 1930143915Sharti 1931143917Sharti *freeResult = TRUE; 1932143915Sharti return (value); 1933143566Sharti } 1934143566Sharti } 1935144340Sharti 1936144340Sharti *freeResult = FALSE; 1937144340Sharti return (vp->err ? var_Error : varNoError); 1938143566Sharti } else { 1939143566Sharti /* 1940143097Sharti * Check for D and F forms of local variables since we're in 1941143097Sharti * a local context and the name is the right length. 19421590Srgrimes */ 1943143967Sharti if (vlen == 2 && 1944143097Sharti (vname[1] == 'F' || vname[1] == 'D') && 1945143097Sharti (strchr("!%*<>@", vname[0]) != NULL)) { 1946143097Sharti char name[2]; 19478874Srgrimes 1948143097Sharti name[0] = vname[0]; 1949143097Sharti name[1] = '\0'; 1950143097Sharti 1951145007Sharti v = VarFindOnly(name, vp->ctxt); 1952143097Sharti if (v != NULL) { 1953143917Sharti value = ParseModifier(vp, startc, v, freeResult); 1954143916Sharti return (value); 1955143097Sharti } 19561590Srgrimes } 1957143566Sharti 1958144340Sharti /* 1959144340Sharti * Still need to get to the end of the variable 1960144340Sharti * specification, so kludge up a Var structure for the 1961144340Sharti * modifications 1962144340Sharti */ 1963144340Sharti v = VarCreate(vname, NULL, VAR_JUNK); 1964144340Sharti value = ParseModifier(vp, startc, v, freeResult); 1965144340Sharti if (*freeResult) { 1966144340Sharti free(value); 1967144340Sharti } 1968144340Sharti VarDestroy(v, TRUE); 1969144340Sharti 1970144340Sharti *freeResult = FALSE; 1971144340Sharti return (vp->err ? var_Error : varNoError); 1972143919Sharti } 1973143566Sharti} 1974143566Sharti 1975143566Shartistatic char * 1976143917ShartiParseRestEnd(VarParser *vp, Buffer *buf, Boolean *freeResult) 1977143566Sharti{ 1978143566Sharti const char *vname; 1979143566Sharti size_t vlen; 1980143566Sharti Var *v; 1981143653Sharti char *value; 1982143566Sharti 1983143566Sharti vname = Buf_GetAll(buf, &vlen); 1984143566Sharti 1985145007Sharti v = VarFindAny(vname, vp->ctxt); 1986143566Sharti if (v != NULL) { 1987143811Sharti value = VarExpand(v, vp); 1988143917Sharti *freeResult = TRUE; 1989143653Sharti return (value); 1990143566Sharti } 1991143566Sharti 1992143811Sharti if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { 1993143967Sharti size_t consumed = vp->ptr - vp->input + 1; 1994143916Sharti 1995143566Sharti /* 1996143916Sharti * If substituting a local variable in a non-local context, 1997143916Sharti * assume it's for dynamic source stuff. We have to handle 1998143916Sharti * this specially and return the longhand for the variable 1999143916Sharti * with the dollar sign escaped so it makes it back to the 2000143916Sharti * caller. Only four of the local variables are treated 2001143916Sharti * specially as they are the only four that will be set when 2002143566Sharti * dynamic sources are expanded. 2003143566Sharti */ 2004143967Sharti if (vlen == 1 || 2005143967Sharti (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) { 2006143097Sharti if (strchr("!%*@", vname[0]) != NULL) { 2007143910Sharti value = emalloc(consumed + 1); 2008143910Sharti strncpy(value, vp->input, consumed); 2009143910Sharti value[consumed] = '\0'; 2010143566Sharti 2011143917Sharti *freeResult = TRUE; 2012143653Sharti return (value); 2013143097Sharti } 2014143097Sharti } 2015143967Sharti if (vlen > 2 && 2016143967Sharti vname[0] == '.' && 2017143097Sharti isupper((unsigned char)vname[1])) { 2018143097Sharti if ((strncmp(vname, ".TARGET", vlen - 1) == 0) || 2019143097Sharti (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) || 2020143097Sharti (strncmp(vname, ".PREFIX", vlen - 1) == 0) || 2021143097Sharti (strncmp(vname, ".MEMBER", vlen - 1) == 0)) { 2022143910Sharti value = emalloc(consumed + 1); 2023143910Sharti strncpy(value, vp->input, consumed); 2024143910Sharti value[consumed] = '\0'; 2025143566Sharti 2026143917Sharti *freeResult = TRUE; 2027143653Sharti return (value); 2028143097Sharti } 2029143097Sharti } 2030143097Sharti } else { 2031141686Sharti /* 2032143566Sharti * Check for D and F forms of local variables since we're in 2033143566Sharti * a local context and the name is the right length. 20341590Srgrimes */ 2035143967Sharti if (vlen == 2 && 2036143566Sharti (vname[1] == 'F' || vname[1] == 'D') && 2037143566Sharti (strchr("!%*<>@", vname[0]) != NULL)) { 2038143566Sharti char name[2]; 2039142382Sharti 2040143566Sharti name[0] = vname[0]; 2041143566Sharti name[1] = '\0'; 2042142382Sharti 2043145007Sharti v = VarFindOnly(name, vp->ctxt); 2044143566Sharti if (v != NULL) { 2045143566Sharti char *val; 2046143566Sharti /* 2047143916Sharti * No need for nested expansion or anything, 2048143916Sharti * as we're the only one who sets these 2049143916Sharti * things and we sure don't put nested 2050143916Sharti * invocations in them... 2051143566Sharti */ 2052143959Sharti val = Buf_Data(v->val); 2053142382Sharti 2054143566Sharti if (vname[1] == 'D') { 2055143566Sharti val = VarModify(val, VarHead, NULL); 2056143566Sharti } else { 2057143566Sharti val = VarModify(val, VarTail, NULL); 2058143566Sharti } 2059143566Sharti 2060143917Sharti *freeResult = TRUE; 2061143566Sharti return (val); 2062143566Sharti } 2063143566Sharti } 2064143919Sharti } 2065143566Sharti 2066143919Sharti *freeResult = FALSE; 2067143919Sharti return (vp->err ? var_Error : varNoError); 2068143566Sharti} 2069143566Sharti 2070143566Sharti/** 2071143566Sharti * Parse a multi letter variable name, and return it's value. 2072143566Sharti */ 2073143566Shartistatic char * 2074143917ShartiVarParseLong(VarParser *vp, Boolean *freeResult) 2075143566Sharti{ 2076143566Sharti Buffer *buf; 2077143649Sharti char startc; 2078143649Sharti char endc; 2079143917Sharti char *value; 2080143566Sharti 2081143566Sharti buf = Buf_Init(MAKE_BSIZE); 2082143566Sharti 2083143910Sharti startc = vp->ptr[0]; 2084143919Sharti vp->ptr++; /* consume opening paren or brace */ 2085143914Sharti 2086143566Sharti endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; 2087143566Sharti 2088143919Sharti /* 2089143919Sharti * Process characters until we reach an end character or a colon, 2090143919Sharti * replacing embedded variables as we go. 2091143919Sharti */ 2092143910Sharti while (*vp->ptr != '\0') { 2093143910Sharti if (*vp->ptr == endc) { 2094143917Sharti value = ParseRestEnd(vp, buf, freeResult); 2095143910Sharti vp->ptr++; /* consume closing paren or brace */ 2096143599Sharti Buf_Destroy(buf, TRUE); 2097143917Sharti return (value); 2098143566Sharti 2099143910Sharti } else if (*vp->ptr == ':') { 2100143917Sharti value = ParseRestModifier(vp, startc, buf, freeResult); 2101143914Sharti vp->ptr++; /* consume closing paren or brace */ 2102143649Sharti Buf_Destroy(buf, TRUE); 2103143917Sharti return (value); 2104143649Sharti 2105143910Sharti } else if (*vp->ptr == '$') { 2106143970Sharti VarParser subvp = { 2107143970Sharti vp->ptr, 2108143970Sharti vp->ptr, 2109143970Sharti vp->ctxt, 2110146038Sharti vp->err, 2111146038Sharti vp->execute 2112143970Sharti }; 2113143970Sharti char *rval; 2114143566Sharti Boolean rfree; 2115143566Sharti 2116143970Sharti rval = VarParse(&subvp, &rfree); 2117143566Sharti if (rval == var_Error) { 2118143566Sharti Fatal("Error expanding embedded variable."); 2119143566Sharti } 2120143566Sharti Buf_Append(buf, rval); 2121143566Sharti if (rfree) 2122143566Sharti free(rval); 2123143970Sharti vp->ptr = subvp.ptr; 21241590Srgrimes } else { 2125143910Sharti Buf_AddByte(buf, (Byte)*vp->ptr); 2126143910Sharti vp->ptr++; 21271590Srgrimes } 2128141686Sharti } 2129143566Sharti 2130143916Sharti /* If we did not find the end character, return var_Error */ 2131143566Sharti Buf_Destroy(buf, TRUE); 2132143917Sharti *freeResult = FALSE; 2133143910Sharti return (var_Error); 2134142557Sharti} 21358874Srgrimes 2136142881Sharti/** 2137143566Sharti * Parse a single letter variable name, and return it's value. 2138142881Sharti */ 2139142557Shartistatic char * 2140143910ShartiVarParseShort(VarParser *vp, Boolean *freeResult) 2141142557Sharti{ 2142143653Sharti char vname[2]; 2143142881Sharti Var *v; 2144143653Sharti char *value; 21458874Srgrimes 2146143910Sharti vname[0] = vp->ptr[0]; 2147143653Sharti vname[1] = '\0'; 2148142557Sharti 2149143910Sharti vp->ptr++; /* consume single letter */ 2150143036Sharti 2151145007Sharti v = VarFindAny(vname, vp->ctxt); 2152142881Sharti if (v != NULL) { 2153143811Sharti value = VarExpand(v, vp); 2154143653Sharti *freeResult = TRUE; 2155143653Sharti return (value); 21561590Srgrimes } 2157142881Sharti 2158142881Sharti /* 2159142881Sharti * If substituting a local variable in a non-local context, assume 2160142881Sharti * it's for dynamic source stuff. We have to handle this specially 2161142881Sharti * and return the longhand for the variable with the dollar sign 2162142881Sharti * escaped so it makes it back to the caller. Only four of the local 2163142881Sharti * variables are treated specially as they are the only four that 2164142881Sharti * will be set when dynamic sources are expanded. 2165142881Sharti */ 2166143811Sharti if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { 2167142881Sharti 2168142881Sharti /* XXX: It looks like $% and $! are reversed here */ 2169143653Sharti switch (vname[0]) { 2170142881Sharti case '@': 2171143653Sharti *freeResult = TRUE; 2172143036Sharti return (estrdup("$(.TARGET)")); 2173142881Sharti case '%': 2174143653Sharti *freeResult = TRUE; 2175143036Sharti return (estrdup("$(.ARCHIVE)")); 2176142881Sharti case '*': 2177143653Sharti *freeResult = TRUE; 2178143036Sharti return (estrdup("$(.PREFIX)")); 2179142881Sharti case '!': 2180143653Sharti *freeResult = TRUE; 2181143036Sharti return (estrdup("$(.MEMBER)")); 2182142881Sharti default: 2183143653Sharti *freeResult = FALSE; 2184143811Sharti return (vp->err ? var_Error : varNoError); 2185142881Sharti } 2186142881Sharti } 2187142881Sharti 2188143918Sharti /* Variable name was not found. */ 2189143653Sharti *freeResult = FALSE; 2190143811Sharti return (vp->err ? var_Error : varNoError); 2191142557Sharti} 2192142557Sharti 2193143811Shartistatic char * 2194143914ShartiVarParse(VarParser *vp, Boolean *freeResult) 2195143811Sharti{ 2196143811Sharti 2197143919Sharti vp->ptr++; /* consume '$' or last letter of conditional */ 2198143811Sharti 2199143904Sharti if (vp->ptr[0] == '\0') { 2200143811Sharti /* Error, there is only a dollar sign in the input string. */ 2201143811Sharti *freeResult = FALSE; 2202143914Sharti return (vp->err ? var_Error : varNoError); 2203143811Sharti 2204143904Sharti } else if (vp->ptr[0] == OPEN_PAREN || vp->ptr[0] == OPEN_BRACE) { 2205143811Sharti /* multi letter variable name */ 2206143914Sharti return (VarParseLong(vp, freeResult)); 2207143811Sharti 2208143811Sharti } else { 2209143811Sharti /* single letter variable name */ 2210143914Sharti return (VarParseShort(vp, freeResult)); 2211143811Sharti } 2212143811Sharti} 2213143811Sharti 2214146046Sharti/** 2215146046Sharti * Given the start of a variable invocation, extract the variable 2216146046Sharti * name and find its value, then modify it according to the 2217146046Sharti * specification. 2218142557Sharti * 2219142557Sharti * Results: 2220143276Sharti * The value of the variable or var_Error if the specification 2221143566Sharti * is invalid. The number of characters in the specification 2222143566Sharti * is placed in the variable pointed to by consumed. (for 2223143566Sharti * invalid specifications, this is just 2 to skip the '$' and 2224143566Sharti * the following letter, or 1 if '$' was the last character 2225143653Sharti * in the string). A Boolean in *freeResult telling whether the 2226143566Sharti * returned string should be freed by the caller. 2227142557Sharti */ 2228142557Shartichar * 2229143276ShartiVar_Parse(const char input[], GNode *ctxt, Boolean err, 2230143653Sharti size_t *consumed, Boolean *freeResult) 2231142557Sharti{ 2232143811Sharti VarParser vp = { 2233143811Sharti input, 2234143904Sharti input, 2235143811Sharti ctxt, 2236146038Sharti err, 2237146038Sharti TRUE 2238143811Sharti }; 2239143910Sharti char *value; 2240143036Sharti 2241143914Sharti value = VarParse(&vp, freeResult); 2242143914Sharti *consumed += vp.ptr - vp.input; 2243143910Sharti return (value); 22441590Srgrimes} 22451590Srgrimes 2246146038Sharti/* 2247146046Sharti * Given the start of a variable invocation, determine the length 2248146046Sharti * of the specification. 2249146038Sharti * 2250146038Sharti * Results: 2251146038Sharti * The number of characters in the specification. For invalid 2252146038Sharti * specifications, this is just 2 to skip the '$' and the 2253146038Sharti * following letter, or 1 if '$' was the last character in the 2254146038Sharti * string. 2255146038Sharti */ 2256146038Shartisize_t 2257146038ShartiVar_Match(const char input[], GNode *ctxt) 2258146038Sharti{ 2259146038Sharti VarParser vp = { 2260146038Sharti input, 2261146038Sharti input, 2262146038Sharti ctxt, 2263146038Sharti FALSE, 2264146038Sharti FALSE 2265146038Sharti }; 2266146038Sharti char *value; 2267146038Sharti Boolean freeResult; 2268146038Sharti 2269146038Sharti value = VarParse(&vp, &freeResult); 2270146038Sharti if (freeResult) { 2271146038Sharti free(value); 2272146038Sharti } 2273146038Sharti return (vp.ptr - vp.input); 2274146038Sharti} 2275146038Sharti 2276146046Shartistatic int 2277146046Shartimatch_var(const char str[], const char var[]) 2278146046Sharti{ 2279146046Sharti const char *start = str; 2280146046Sharti size_t len; 2281146046Sharti 2282146046Sharti str++; /* consume '$' */ 2283146046Sharti 2284146046Sharti if (str[0] == OPEN_PAREN || str[0] == OPEN_BRACE) { 2285146046Sharti str++; /* consume opening paren or brace */ 2286146046Sharti 2287146046Sharti while (str[0] != '\0') { 2288146046Sharti if (str[0] == '$') { 2289146046Sharti /* 2290146046Sharti * A variable inside the variable. We cannot 2291146046Sharti * expand the external variable yet. 2292146046Sharti */ 2293146046Sharti return (str - start); 2294146046Sharti } else if (str[0] == ':' || 2295146046Sharti str[0] == CLOSE_PAREN || 2296146046Sharti str[0] == CLOSE_BRACE) { 2297146046Sharti len = str - (start + 2); 2298146046Sharti 2299152969Sfjoe if (strncmp(var, start + 2, len) == 0 && var[len] == '\0') { 2300146046Sharti return (0); /* match */ 2301146046Sharti } else { 2302146046Sharti /* 2303146046Sharti * Not the variable we want to 2304146046Sharti * expand. 2305146046Sharti */ 2306146046Sharti return (str - start); 2307146046Sharti } 2308146046Sharti } else { 2309146046Sharti ++str; 2310146046Sharti } 2311146046Sharti } 2312146046Sharti return (str - start); 2313146046Sharti } else { 2314146046Sharti /* Single letter variable name */ 2315146046Sharti if (var[1] == '\0' && var[0] == str[0]) { 2316146046Sharti return (0); /* match */ 2317146046Sharti } else { 2318146046Sharti str++; /* consume variable name */ 2319146046Sharti return (str - start); 2320146046Sharti } 2321146046Sharti } 2322146046Sharti} 2323146046Sharti 2324146046Sharti/** 2325146046Sharti * Substitute for all variables in the given string in the given 2326146046Sharti * context If err is TRUE, Parse_Error will be called when an 2327146046Sharti * undefined variable is encountered. 23281590Srgrimes * 23291590Srgrimes * Results: 23301590Srgrimes * The resulting string. 23311590Srgrimes * 23321590Srgrimes * Side Effects: 23331590Srgrimes * None. The old string must be freed by the caller 23341590Srgrimes */ 2335142457ShartiBuffer * 2336186831SobrienVar_Subst(const char *str, GNode *ctxt, Boolean err) 23371590Srgrimes{ 2338146027Sharti Boolean errorReported; 2339143813Sharti Buffer *buf; /* Buffer for forming things */ 23401590Srgrimes 2341143813Sharti /* 2342143813Sharti * Set TRUE if an error has already been reported to prevent a 2343143813Sharti * plethora of messages when recursing. XXXHB this comment sounds 2344143813Sharti * wrong. 2345143813Sharti */ 2346143813Sharti errorReported = FALSE; 2347141648Sharti 2348143813Sharti buf = Buf_Init(0); 2349146027Sharti while (str[0] != '\0') { 2350146027Sharti if ((str[0] == '$') && (str[1] == '$')) { 2351143813Sharti /* 2352146027Sharti * A dollar sign may be escaped with another dollar 2353146027Sharti * sign. In such a case, we skip over the escape 2354146027Sharti * character and store the dollar sign into the 2355146027Sharti * buffer directly. 2356143813Sharti */ 2357146027Sharti str++; 2358143813Sharti Buf_AddByte(buf, (Byte)str[0]); 2359146027Sharti str++; 2360141659Sharti 2361143813Sharti } else if (str[0] == '$') { 2362146027Sharti /* Variable invocation. */ 2363146027Sharti VarParser subvp = { 2364143970Sharti str, 2365143970Sharti str, 2366143970Sharti ctxt, 2367146038Sharti err, 2368146038Sharti TRUE 2369143970Sharti }; 2370143970Sharti char *rval; 2371143970Sharti Boolean rfree; 2372141648Sharti 2373143970Sharti rval = VarParse(&subvp, &rfree); 2374143970Sharti 23751590Srgrimes /* 2376143813Sharti * When we come down here, val should either point to 2377143813Sharti * the value of this variable, suitably modified, or 2378143813Sharti * be NULL. Length should be the total length of the 2379143813Sharti * potential variable invocation (from $ to end 2380143813Sharti * character...) 23811590Srgrimes */ 2382143969Sharti if (rval == var_Error || rval == varNoError) { 2383143813Sharti /* 2384143813Sharti * If performing old-time variable 2385143813Sharti * substitution, skip over the variable and 2386143813Sharti * continue with the substitution. Otherwise, 2387143813Sharti * store the dollar sign and advance str so 2388143813Sharti * we continue with the string... 2389143813Sharti */ 2390143813Sharti if (oldVars) { 2391143970Sharti str = subvp.ptr; 2392143969Sharti } else if (err) { 2393143813Sharti /* 2394143813Sharti * If variable is undefined, complain 2395143813Sharti * and skip the variable. The 2396143813Sharti * complaint will stop us from doing 2397143813Sharti * anything when the file is parsed. 2398143813Sharti */ 2399143813Sharti if (!errorReported) { 2400143813Sharti Parse_Error(PARSE_FATAL, 2401143970Sharti "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str); 2402143813Sharti } 2403146027Sharti errorReported = TRUE; 2404143970Sharti str = subvp.ptr; 2405143813Sharti } else { 2406146027Sharti Buf_AddByte(buf, (Byte)str[0]); 2407146027Sharti str++; 2408143813Sharti } 2409141686Sharti } else { 2410143813Sharti /* 2411143813Sharti * Copy all the characters from the variable 2412143813Sharti * value straight into the new string. 2413143813Sharti */ 2414143969Sharti Buf_Append(buf, rval); 2415143969Sharti if (rfree) { 2416143969Sharti free(rval); 2417143813Sharti } 2418146027Sharti str = subvp.ptr; 2419141686Sharti } 2420143813Sharti } else { 2421146027Sharti Buf_AddByte(buf, (Byte)str[0]); 2422146027Sharti str++; 24231590Srgrimes } 24241590Srgrimes } 24258874Srgrimes 2426143813Sharti return (buf); 24271590Srgrimes} 24281590Srgrimes 2429146046Sharti/** 2430146046Sharti * Substitute for all variables except if it is the same as 'var', 2431146046Sharti * in the given string in the given context. If err is TRUE, 2432146046Sharti * Parse_Error will be called when an undefined variable is 2433146046Sharti * encountered. 2434146046Sharti * 2435146046Sharti * Results: 2436146046Sharti * The resulting string. 2437146046Sharti * 2438146046Sharti * Side Effects: 2439146046Sharti * None. The old string must be freed by the caller 2440146046Sharti */ 2441146027ShartiBuffer * 2442146048ShartiVar_SubstOnly(const char *var, const char *str, Boolean err) 24431590Srgrimes{ 2444146048Sharti GNode *ctxt = VAR_GLOBAL; 2445146027Sharti Boolean errorReported; 2446146027Sharti Buffer *buf; /* Buffer for forming things */ 2447138232Sharti 2448146027Sharti /* 2449146027Sharti * Set TRUE if an error has already been reported to prevent a 2450146027Sharti * plethora of messages when recursing. XXXHB this comment sounds 2451146027Sharti * wrong. 2452146027Sharti */ 2453146027Sharti errorReported = FALSE; 2454146027Sharti 2455146027Sharti buf = Buf_Init(0); 2456146027Sharti while (str[0] != '\0') { 2457146027Sharti if (str[0] == '$') { 2458146027Sharti int skip; 2459146027Sharti 2460146027Sharti skip = match_var(str, var); 2461146027Sharti if (skip > 0) { 2462146027Sharti Buf_AddBytes(buf, skip, str); 2463146027Sharti str += skip; 2464146027Sharti } else { 2465146027Sharti /* Variable invocation. */ 2466146027Sharti VarParser subvp = { 2467146027Sharti str, 2468146027Sharti str, 2469146027Sharti ctxt, 2470146038Sharti err, 2471146038Sharti TRUE 2472146027Sharti }; 2473146027Sharti char *rval; 2474146027Sharti Boolean rfree; 2475146027Sharti 2476146027Sharti rval = VarParse(&subvp, &rfree); 2477146027Sharti 2478146027Sharti /* 2479146027Sharti * When we get down here, rval should either 2480146027Sharti * point to the value of this variable, or be 2481146027Sharti * NULL. 2482146027Sharti */ 2483146027Sharti if (rval == var_Error || rval == varNoError) { 2484146027Sharti /* 2485146027Sharti * If performing old-time variable 2486146027Sharti * substitution, skip over the 2487146027Sharti * variable and continue with the 2488146027Sharti * substitution. Otherwise, store the 2489146027Sharti * dollar sign and advance str so we 2490146027Sharti * continue with the string... 2491146027Sharti */ 2492146027Sharti if (oldVars) { 2493146027Sharti str = subvp.ptr; 2494146027Sharti } else if (err) { 2495146027Sharti /* 2496146027Sharti * If variable is undefined, 2497146027Sharti * complain and skip the 2498146027Sharti * variable. The complaint 2499146027Sharti * will stop us from doing 2500146027Sharti * anything when the file is 2501146027Sharti * parsed. 2502146027Sharti */ 2503146027Sharti if (!errorReported) { 2504146027Sharti Parse_Error(PARSE_FATAL, 2505146027Sharti "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str); 2506146027Sharti } 2507146027Sharti errorReported = TRUE; 2508146027Sharti str = subvp.ptr; 2509146027Sharti } else { 2510146027Sharti Buf_AddByte(buf, (Byte)str[0]); 2511146027Sharti str++; 2512146027Sharti } 2513146027Sharti } else { 2514146027Sharti /* 2515146027Sharti * Copy all the characters from the 2516146027Sharti * variable value straight into the 2517146027Sharti * new string. 2518146027Sharti */ 2519146027Sharti Buf_Append(buf, rval); 2520146027Sharti if (rfree) { 2521146027Sharti free(rval); 2522146027Sharti } 2523146027Sharti str = subvp.ptr; 2524146027Sharti } 2525146027Sharti } 2526146027Sharti } else { 2527146027Sharti Buf_AddByte(buf, (Byte)str[0]); 2528146027Sharti str++; 2529146027Sharti } 2530146027Sharti } 2531146027Sharti 2532146027Sharti return (buf); 25331590Srgrimes} 25341590Srgrimes 2535146046Sharti/** 2536146046Sharti * Initialize the module 25371590Srgrimes * 25381590Srgrimes * Side Effects: 25398874Srgrimes * The VAR_CMD and VAR_GLOBAL contexts are created 25401590Srgrimes */ 25411590Srgrimesvoid 2542145971ShartiVar_Init(char **env) 25431590Srgrimes{ 2544145971Sharti char **ptr; 2545138232Sharti 2546145971Sharti VAR_CMD = Targ_NewGN("Command"); 2547145971Sharti VAR_ENV = Targ_NewGN("Environment"); 2548143813Sharti VAR_GLOBAL = Targ_NewGN("Global"); 2549145971Sharti 2550145971Sharti /* 2551145971Sharti * Copy user environment variables into ENV context. 2552145971Sharti */ 2553145971Sharti for (ptr = env; *ptr != NULL; ++ptr) { 2554145971Sharti char *tmp = estrdup(*ptr); 2555145971Sharti const char *name = tmp; 2556145971Sharti char *sep = strchr(name, '='); 2557145971Sharti const char *value = sep + 1; 2558145971Sharti 2559145971Sharti if (sep != NULL) { 2560145971Sharti *sep = '\0'; 2561145971Sharti VarAdd(name, value, VAR_ENV); 2562145971Sharti } 2563145971Sharti free(tmp); 2564145971Sharti } 25651590Srgrimes} 25661590Srgrimes 2567146046Sharti/** 2568146046Sharti * Print all variables in global and command line contexts. 25691590Srgrimes */ 25701590Srgrimesvoid 2571146039ShartiVar_Dump(void) 25721590Srgrimes{ 2573143377Sharti const LstNode *ln; 2574143377Sharti const Var *v; 2575138232Sharti 2576146039Sharti printf("#*** Global Variables:\n"); 2577146039Sharti LST_FOREACH(ln, &VAR_GLOBAL->context) { 2578143377Sharti v = Lst_Datum(ln); 2579143959Sharti printf("%-16s = %s\n", v->name, Buf_Data(v->val)); 2580143377Sharti } 2581146039Sharti 2582146039Sharti printf("#*** Command-line Variables:\n"); 2583146039Sharti LST_FOREACH(ln, &VAR_CMD->context) { 2584146039Sharti v = Lst_Datum(ln); 2585146039Sharti printf("%-16s = %s\n", v->name, Buf_Data(v->val)); 2586146039Sharti } 25871590Srgrimes} 2588146039Sharti 2589146141Sharti/** 2590146141Sharti * Print the values of any variables requested by 2591146141Sharti * the user. 2592146141Sharti */ 2593146141Shartivoid 2594146141ShartiVar_Print(Lst *vlist, Boolean expandVars) 2595146141Sharti{ 2596146141Sharti LstNode *n; 2597236346Sobrien char *name; 2598146141Sharti 2599146141Sharti LST_FOREACH(n, vlist) { 2600146141Sharti name = Lst_Datum(n); 2601146141Sharti if (expandVars) { 2602146581Sharti char *value; 2603146581Sharti char *v; 2604146581Sharti 2605236346Sobrien if (*name == '$') { 2606236346Sobrien v = name; 2607236346Sobrien } else { 2608236346Sobrien v = emalloc(strlen(name) + 1 + 3); 2609236346Sobrien sprintf(v, "${%s}", name); 2610236346Sobrien } 2611146580Sharti value = Buf_Peel(Var_Subst(v, VAR_GLOBAL, FALSE)); 2612146141Sharti printf("%s\n", value); 2613146141Sharti 2614236346Sobrien if (v != name) 2615236346Sobrien free(v); 2616146141Sharti free(value); 2617146141Sharti } else { 2618146581Sharti const char *value = Var_Value(name, VAR_GLOBAL); 2619146141Sharti printf("%s\n", value != NULL ? value : ""); 2620146141Sharti } 2621146141Sharti } 2622146141Sharti} 2623146141Sharti 2624