1 /* $OpenBSD: varmodifiers.c,v 1.39 2014/09/21 13:43:25 espie Exp $ */ 2 /* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1999-2010 Marc Espie. 6 * 7 * Extensive code changes for the OpenBSD project. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 22 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 /* 31 * Copyright (c) 1988, 1989, 1990, 1993 32 * The Regents of the University of California. All rights reserved. 33 * Copyright (c) 1989 by Berkeley Softworks 34 * All rights reserved. 35 * 36 * This code is derived from software contributed to Berkeley by 37 * Adam de Boor. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 /* VarModifiers_Apply is mostly a constituent function of Var_Parse, it 65 * is also called directly by Var_SubstVar. */ 66 67 68 #include <ctype.h> 69 #include <sys/types.h> 70 #ifndef MAKE_BOOTSTRAP 71 #include <regex.h> 72 #endif 73 #include <stddef.h> 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include "config.h" 78 #include "defines.h" 79 #include "buf.h" 80 #include "var.h" 81 #include "varmodifiers.h" 82 #include "varname.h" 83 #include "targ.h" 84 #include "error.h" 85 #include "str.h" 86 #include "cmd_exec.h" 87 #include "memory.h" 88 #include "gnode.h" 89 90 91 /* Var*Pattern flags */ 92 #define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ 93 #define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ 94 #define VAR_SUB_MATCHED 0x04 /* There was a match */ 95 #define VAR_MATCH_START 0x08 /* Match at start of word */ 96 #define VAR_MATCH_END 0x10 /* Match at end of word */ 97 98 /* Modifiers flags */ 99 #define VAR_EQUAL 0x20 100 #define VAR_MAY_EQUAL 0x40 101 #define VAR_ADD_EQUAL 0x80 102 #define VAR_BANG_EQUAL 0x100 103 104 typedef struct { 105 char *lbuffer; /* left string to free */ 106 char *lhs; /* String to match */ 107 size_t leftLen; /* Length of string */ 108 char *rhs; /* Replacement string (w/ &'s removed) */ 109 size_t rightLen; /* Length of replacement */ 110 int flags; 111 } VarPattern; 112 113 struct LoopStuff { 114 struct LoopVar *var; 115 char *expand; 116 bool err; 117 }; 118 119 static bool VarHead(struct Name *, bool, Buffer, void *); 120 static bool VarTail(struct Name *, bool, Buffer, void *); 121 static bool VarSuffix(struct Name *, bool, Buffer, void *); 122 static bool VarRoot(struct Name *, bool, Buffer, void *); 123 static bool VarMatch(struct Name *, bool, Buffer, void *); 124 static bool VarSYSVMatch(struct Name *, bool, Buffer, void *); 125 static bool VarNoMatch(struct Name *, bool, Buffer, void *); 126 static bool VarUniq(struct Name *, bool, Buffer, void *); 127 static bool VarLoop(struct Name *, bool, Buffer, void *); 128 129 130 #ifndef MAKE_BOOTSTRAP 131 static void VarREError(int, regex_t *, const char *); 132 static bool VarRESubstitute(struct Name *, bool, Buffer, void *); 133 static char *do_regex(const char *, const struct Name *, void *); 134 135 typedef struct { 136 regex_t re; 137 int nsub; 138 regmatch_t *matches; 139 char *replace; 140 int flags; 141 } VarREPattern; 142 #endif 143 144 static bool VarSubstitute(struct Name *, bool, Buffer, void *); 145 static char *VarGetPattern(SymTable *, int, const char **, int, int, 146 size_t *, VarPattern *); 147 static char *VarQuote(const char *, const struct Name *, void *); 148 static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *); 149 150 static void *check_empty(const char **, SymTable *, bool, int); 151 static void *check_quote(const char **, SymTable *, bool, int); 152 static char *do_upper(const char *, const struct Name *, void *); 153 static char *do_lower(const char *, const struct Name *, void *); 154 static void *check_shcmd(const char **, SymTable *, bool, int); 155 static char *do_shcmd(const char *, const struct Name *, void *); 156 static char *do_sort(const char *, const struct Name *, void *); 157 static char *finish_loop(const char *, const struct Name *, void *); 158 static int NameCompare(const void *, const void *); 159 static char *do_label(const char *, const struct Name *, void *); 160 static char *do_path(const char *, const struct Name *, void *); 161 static char *do_def(const char *, const struct Name *, void *); 162 static char *do_undef(const char *, const struct Name *, void *); 163 static char *do_assign(const char *, const struct Name *, void *); 164 static char *do_exec(const char *, const struct Name *, void *); 165 166 static void *assign_get_value(const char **, SymTable *, bool, int); 167 static void *get_cmd(const char **, SymTable *, bool, int); 168 static void *get_value(const char **, SymTable *, bool, int); 169 static void *get_stringarg(const char **, SymTable *, bool, int); 170 static void free_stringarg(void *); 171 static void *get_patternarg(const char **, SymTable *, bool, int); 172 static void *get_spatternarg(const char **, SymTable *, bool, int); 173 static void *common_get_patternarg(const char **, SymTable *, bool, int, bool); 174 static void free_patternarg(void *); 175 static void free_looparg(void *); 176 static void *get_sysvpattern(const char **, SymTable *, bool, int); 177 static void *get_loop(const char **, SymTable *, bool, int); 178 static char *LoopGrab(const char **); 179 180 static struct Name dummy; 181 static struct Name *dummy_arg = &dummy; 182 183 static struct modifier { 184 bool atstart; 185 void * (*getarg)(const char **, SymTable *, bool, int); 186 char * (*apply)(const char *, const struct Name *, void *); 187 bool (*word_apply)(struct Name *, bool, Buffer, void *); 188 void (*freearg)(void *); 189 } *choose_mod[256], 190 match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg}, 191 nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg}, 192 subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg}, 193 #ifndef MAKE_BOOTSTRAP 194 resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg}, 195 #endif 196 quote_mod = {false, check_quote, VarQuote, NULL , free}, 197 tail_mod = {false, check_empty, NULL, VarTail, NULL}, 198 head_mod = {false, check_empty, NULL, VarHead, NULL}, 199 suffix_mod = {false, check_empty, NULL, VarSuffix, NULL}, 200 root_mod = {false, check_empty, NULL, VarRoot, NULL}, 201 upper_mod = {false, check_empty, do_upper, NULL, NULL}, 202 lower_mod = {false, check_empty, do_lower, NULL, NULL}, 203 shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL}, 204 sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}, 205 uniq_mod = {false, check_empty, NULL, VarUniq, NULL}, 206 sort_mod = {false, check_empty, do_sort, NULL, NULL}, 207 loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg}, 208 undef_mod = {true, get_value, do_undef, NULL, NULL}, 209 def_mod = {true, get_value, do_def, NULL, NULL}, 210 label_mod = {true, check_empty, do_label, NULL, NULL}, 211 path_mod = {true, check_empty, do_path, NULL, NULL}, 212 assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg}, 213 exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg} 214 ; 215 216 void 217 VarModifiers_Init() 218 { 219 choose_mod['M'] = &match_mod; 220 choose_mod['N'] = &nomatch_mod; 221 choose_mod['S'] = &subst_mod; 222 #ifndef MAKE_BOOTSTRAP 223 choose_mod['C'] = &resubst_mod; 224 #endif 225 choose_mod['Q'] = "e_mod; 226 choose_mod['T'] = &tail_mod; 227 choose_mod['H'] = &head_mod; 228 choose_mod['E'] = &suffix_mod; 229 choose_mod['R'] = &root_mod; 230 if (FEATURES(FEATURE_UPPERLOWER)) { 231 choose_mod['U'] = &upper_mod; 232 choose_mod['L'] = &lower_mod; 233 } 234 if (FEATURES(FEATURE_SUNSHCMD)) 235 choose_mod['s'] = &shcmd_mod; 236 if (FEATURES(FEATURE_UNIQ)) 237 choose_mod['u'] = &uniq_mod; 238 if (FEATURES(FEATURE_SORT)) 239 choose_mod['O'] = &sort_mod; 240 if (FEATURES(FEATURE_ODE)) { 241 choose_mod['@'] = &loop_mod; 242 choose_mod['D'] = &def_mod; 243 choose_mod['U'] = &undef_mod; 244 choose_mod['L'] = &label_mod; 245 choose_mod['P'] = &path_mod; 246 } 247 if (FEATURES(FEATURE_ASSIGN)) 248 choose_mod[':'] = &assign_mod; 249 if (FEATURES(FEATURE_EXECMOD)) 250 choose_mod['!'] = &exec_mod; 251 } 252 253 /* All modifiers handle addSpace (need to add a space before placing the 254 * next word into the buffer) and propagate it when necessary. 255 */ 256 257 /*- 258 *----------------------------------------------------------------------- 259 * VarHead -- 260 * Remove the tail of the given word and add the result to the given 261 * buffer. 262 *----------------------------------------------------------------------- 263 */ 264 static bool 265 VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) 266 { 267 const char *slash; 268 269 slash = Str_rchri(word->s, word->e, '/'); 270 if (slash != NULL) { 271 if (addSpace) 272 Buf_AddSpace(buf); 273 Buf_Addi(buf, word->s, slash); 274 } else { 275 /* If no directory part, give . (q.v. the POSIX standard). */ 276 if (addSpace) 277 Buf_AddString(buf, " ."); 278 else 279 Buf_AddChar(buf, '.'); 280 } 281 return true; 282 } 283 284 /*- 285 *----------------------------------------------------------------------- 286 * VarTail -- 287 * Remove the head of the given word add the result to the given 288 * buffer. 289 *----------------------------------------------------------------------- 290 */ 291 static bool 292 VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) 293 { 294 const char *slash; 295 296 if (addSpace) 297 Buf_AddSpace(buf); 298 slash = Str_rchri(word->s, word->e, '/'); 299 if (slash != NULL) 300 Buf_Addi(buf, slash+1, word->e); 301 else 302 Buf_Addi(buf, word->s, word->e); 303 return true; 304 } 305 306 /*- 307 *----------------------------------------------------------------------- 308 * VarSuffix -- 309 * Add the suffix of the given word to the given buffer. 310 *----------------------------------------------------------------------- 311 */ 312 static bool 313 VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) 314 { 315 const char *dot; 316 317 dot = Str_rchri(word->s, word->e, '.'); 318 if (dot != NULL) { 319 if (addSpace) 320 Buf_AddSpace(buf); 321 Buf_Addi(buf, dot+1, word->e); 322 addSpace = true; 323 } 324 return addSpace; 325 } 326 327 /*- 328 *----------------------------------------------------------------------- 329 * VarRoot -- 330 * Remove the suffix of the given word and add the result to the 331 * buffer. 332 *----------------------------------------------------------------------- 333 */ 334 static bool 335 VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) 336 { 337 const char *dot; 338 339 if (addSpace) 340 Buf_AddSpace(buf); 341 dot = Str_rchri(word->s, word->e, '.'); 342 if (dot != NULL) 343 Buf_Addi(buf, word->s, dot); 344 else 345 Buf_Addi(buf, word->s, word->e); 346 return true; 347 } 348 349 /*- 350 *----------------------------------------------------------------------- 351 * VarMatch -- 352 * Add the word to the buffer if it matches the given pattern. 353 *----------------------------------------------------------------------- 354 */ 355 static bool 356 VarMatch(struct Name *word, bool addSpace, Buffer buf, 357 void *pattern) /* Pattern the word must match */ 358 { 359 const char *pat = (const char *)pattern; 360 361 if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { 362 if (addSpace) 363 Buf_AddSpace(buf); 364 Buf_Addi(buf, word->s, word->e); 365 return true; 366 } else 367 return addSpace; 368 } 369 370 /*- 371 *----------------------------------------------------------------------- 372 * VarNoMatch -- 373 * Add the word to the buffer if it doesn't match the given pattern. 374 *----------------------------------------------------------------------- 375 */ 376 static bool 377 VarNoMatch(struct Name *word, bool addSpace, Buffer buf, 378 void *pattern) /* Pattern the word must not match */ 379 { 380 const char *pat = (const char *)pattern; 381 382 if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { 383 if (addSpace) 384 Buf_AddSpace(buf); 385 Buf_Addi(buf, word->s, word->e); 386 return true; 387 } else 388 return addSpace; 389 } 390 391 static bool 392 VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp) 393 { 394 struct Name *last = (struct Name *)lastp; 395 396 /* does not match */ 397 if (last->s == NULL || last->e - last->s != word->e - word->s || 398 strncmp(word->s, last->s, word->e - word->s) != 0) { 399 if (addSpace) 400 Buf_AddSpace(buf); 401 Buf_Addi(buf, word->s, word->e); 402 addSpace = true; 403 } 404 last->s = word->s; 405 last->e = word->e; 406 return addSpace; 407 } 408 409 static bool 410 VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp) 411 { 412 struct LoopStuff *v = (struct LoopStuff *)vp; 413 414 if (addSpace) 415 Buf_AddSpace(buf); 416 Var_SubstVar(buf, v->expand, v->var, word->s); 417 return true; 418 } 419 420 static char * 421 finish_loop(const char *s, const struct Name *n UNUSED , void *p) 422 { 423 struct LoopStuff *l = (struct LoopStuff *)p; 424 425 return Var_Subst(s, NULL, l->err); 426 } 427 428 static int 429 NameCompare(const void *ap, const void *bp) 430 { 431 struct Name *a, *b; 432 size_t n, m; 433 int c; 434 435 a = (struct Name *)ap; 436 b = (struct Name *)bp; 437 n = a->e - a->s; 438 m = b->e - b->s; 439 if (n < m) { 440 c = strncmp(a->s, b->s, n); 441 if (c != 0) 442 return c; 443 else 444 return -1; 445 } else if (m < n) { 446 c = strncmp(a->s, b->s, m); 447 if (c != 0) 448 return c; 449 else 450 return 1; 451 } else 452 return strncmp(a->s, b->s, n); 453 } 454 455 static char * 456 do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED) 457 { 458 struct Name *t; 459 unsigned long n, i, j; 460 const char *start, *end; 461 462 n = 1024; /* start at 1024 words */ 463 t = ereallocarray(NULL, n, sizeof(struct Name)); 464 start = s; 465 end = start; 466 467 for (i = 0;; i++) { 468 if (i == n) { 469 n *= 2; 470 t = ereallocarray(t, n, sizeof(struct Name)); 471 } 472 start = iterate_words(&end); 473 if (start == NULL) 474 break; 475 t[i].s = start; 476 t[i].e = end; 477 } 478 if (i > 0) { 479 BUFFER buf; 480 481 Buf_Init(&buf, end - s); 482 qsort(t, i, sizeof(struct Name), NameCompare); 483 Buf_Addi(&buf, t[0].s, t[0].e); 484 for (j = 1; j < i; j++) { 485 Buf_AddSpace(&buf); 486 Buf_Addi(&buf, t[j].s, t[j].e); 487 } 488 free(t); 489 return Buf_Retrieve(&buf); 490 } else { 491 free(t); 492 return ""; 493 } 494 } 495 496 static char * 497 do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED) 498 { 499 return Str_dupi(n->s, n->e); 500 } 501 502 static char * 503 do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED) 504 { 505 GNode *gn; 506 507 gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE); 508 if (gn == NULL) 509 return Str_dupi(n->s, n->e); 510 else 511 return strdup(gn->path); 512 } 513 514 static char * 515 do_def(const char *s, const struct Name *n UNUSED, void *arg) 516 { 517 VarPattern *v = (VarPattern *)arg; 518 if (s == NULL) { 519 free_patternarg(v); 520 return NULL; 521 } else 522 return v->lbuffer; 523 } 524 525 static char * 526 do_undef(const char *s, const struct Name *n UNUSED, void *arg) 527 { 528 VarPattern *v = (VarPattern *)arg; 529 if (s != NULL) { 530 free_patternarg(v); 531 return NULL; 532 } else 533 return v->lbuffer; 534 } 535 536 static char * 537 do_assign(const char *s, const struct Name *n, void *arg) 538 { 539 VarPattern *v = (VarPattern *)arg; 540 char *msg; 541 char *result; 542 543 switch (v->flags) { 544 case VAR_EQUAL: 545 Var_Seti(n->s, n->e, v->lbuffer); 546 break; 547 case VAR_MAY_EQUAL: 548 if (s == NULL) 549 Var_Seti(n->s, n->e, v->lbuffer); 550 break; 551 case VAR_ADD_EQUAL: 552 if (s == NULL) 553 Var_Seti(n->s, n->e, v->lbuffer); 554 else 555 Var_Appendi(n->s, n->e, v->lbuffer); 556 break; 557 case VAR_BANG_EQUAL: 558 result = Cmd_Exec(v->lbuffer, &msg); 559 if (result != NULL) { 560 Var_Seti(n->s, n->e, result); 561 free(result); 562 } else 563 Error(msg, v->lbuffer); 564 break; 565 566 } 567 return NULL; 568 } 569 570 static char * 571 do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg) 572 { 573 VarPattern *v = (VarPattern *)arg; 574 char *msg; 575 char *result; 576 577 result = Cmd_Exec(v->lbuffer, &msg); 578 if (result == NULL) 579 Error(msg, v->lbuffer); 580 return result; 581 } 582 583 /*- 584 *----------------------------------------------------------------------- 585 * VarSYSVMatch -- 586 * Add the word to the buffer if it matches the given pattern. 587 * Used to implement the System V % modifiers. 588 *----------------------------------------------------------------------- 589 */ 590 static bool 591 VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp) 592 { 593 size_t len; 594 const char *ptr; 595 VarPattern *pat = (VarPattern *)patp; 596 597 if (*word->s != '\0') { 598 if (addSpace) 599 Buf_AddSpace(buf); 600 if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL) 601 Str_SYSVSubst(buf, pat->rhs, ptr, len); 602 else 603 Buf_Addi(buf, word->s, word->e); 604 return true; 605 } else 606 return addSpace; 607 } 608 609 void * 610 get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err, int endc) 611 { 612 VarPattern *pattern; 613 const char *cp, *cp2; 614 BUFFER buf; 615 int cnt = 0; 616 char startc = endc == ')' ? '(' : '{'; 617 for (cp = *p;; cp++) { 618 if (*cp == '=' && cnt == 0) 619 break; 620 if (*cp == '\0') 621 return NULL; 622 if (*cp == startc) 623 cnt++; 624 else if (*cp == endc) { 625 cnt--; 626 if (cnt < 0) 627 return NULL; 628 } 629 } 630 Buf_Init(&buf, 0); 631 for (cp2 = cp+1;; cp2++) { 632 if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) && 633 cnt == 0) 634 break; 635 if (*cp2 == '\0') { 636 Buf_Destroy(&buf); 637 return NULL; 638 } 639 if (*cp2 == startc) 640 cnt++; 641 else if (*cp2 == endc) { 642 cnt--; 643 if (cnt < 0) { 644 Buf_Destroy(&buf); 645 return NULL; 646 } 647 } else if (*cp2 == '$') { 648 if (cp2[1] == '$') 649 cp2++; 650 else { 651 size_t len; 652 (void)Var_ParseBuffer(&buf, cp2, ctxt, err, 653 &len); 654 cp2 += len - 1; 655 continue; 656 } 657 } 658 Buf_AddChar(&buf, *cp2); 659 } 660 661 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 662 pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp); 663 pattern->leftLen = cp - *p; 664 pattern->rhs = Buf_Retrieve(&buf); 665 pattern->rightLen = Buf_Size(&buf); 666 pattern->flags = 0; 667 *p = cp2; 668 return pattern; 669 } 670 671 672 /*- 673 *----------------------------------------------------------------------- 674 * VarSubstitute -- 675 * Perform a string-substitution on the given word, Adding the 676 * result to the given buffer. 677 *----------------------------------------------------------------------- 678 */ 679 static bool 680 VarSubstitute(struct Name *word, bool addSpace, Buffer buf, 681 void *patternp) /* Pattern for substitution */ 682 { 683 size_t wordLen; /* Length of word */ 684 const char *cp; /* General pointer */ 685 VarPattern *pattern = (VarPattern *)patternp; 686 687 wordLen = word->e - word->s; 688 if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != 689 (VAR_SUB_ONE|VAR_SUB_MATCHED)) { 690 /* Still substituting -- break it down into simple anchored cases 691 * and if none of them fits, perform the general substitution case. */ 692 if ((pattern->flags & VAR_MATCH_START) && 693 (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) { 694 /* Anchored at start and beginning of word matches pattern. */ 695 if ((pattern->flags & VAR_MATCH_END) && 696 (wordLen == pattern->leftLen)) { 697 /* Also anchored at end and matches to the end (word 698 * is same length as pattern) add space and rhs only 699 * if rhs is non-null. */ 700 if (pattern->rightLen != 0) { 701 if (addSpace) 702 Buf_AddSpace(buf); 703 addSpace = true; 704 Buf_AddChars(buf, pattern->rightLen, 705 pattern->rhs); 706 } 707 pattern->flags |= VAR_SUB_MATCHED; 708 } else if (pattern->flags & VAR_MATCH_END) { 709 /* Doesn't match to end -- copy word wholesale. */ 710 goto nosub; 711 } else { 712 /* Matches at start but need to copy in 713 * trailing characters. */ 714 if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ 715 if (addSpace) 716 Buf_AddSpace(buf); 717 addSpace = true; 718 } 719 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 720 Buf_AddChars(buf, wordLen - pattern->leftLen, 721 word->s + pattern->leftLen); 722 pattern->flags |= VAR_SUB_MATCHED; 723 } 724 } else if (pattern->flags & VAR_MATCH_START) { 725 /* Had to match at start of word and didn't -- copy whole word. */ 726 goto nosub; 727 } else if (pattern->flags & VAR_MATCH_END) { 728 /* Anchored at end, Find only place match could occur (leftLen 729 * characters from the end of the word) and see if it does. Note 730 * that because the $ will be left at the end of the lhs, we have 731 * to use strncmp. */ 732 cp = word->s + (wordLen - pattern->leftLen); 733 if (cp >= word->s && 734 strncmp(cp, pattern->lhs, pattern->leftLen) == 0) { 735 /* Match found. If we will place characters in the buffer, 736 * add a space before hand as indicated by addSpace, then 737 * stuff in the initial, unmatched part of the word followed 738 * by the right-hand-side. */ 739 if (((cp - word->s) + pattern->rightLen) != 0) { 740 if (addSpace) 741 Buf_AddSpace(buf); 742 addSpace = true; 743 } 744 Buf_Addi(buf, word->s, cp); 745 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 746 pattern->flags |= VAR_SUB_MATCHED; 747 } else { 748 /* Had to match at end and didn't. Copy entire word. */ 749 goto nosub; 750 } 751 } else { 752 /* Pattern is unanchored: search for the pattern in the word using 753 * strstr, copying unmatched portions and the 754 * right-hand-side for each match found, handling non-global 755 * substitutions correctly, etc. When the loop is done, any 756 * remaining part of the word (word and wordLen are adjusted 757 * accordingly through the loop) is copied straight into the 758 * buffer. 759 * addSpace is set to false as soon as a space is added to the 760 * buffer. */ 761 bool done; 762 size_t origSize; 763 764 done = false; 765 origSize = Buf_Size(buf); 766 while (!done) { 767 cp = strstr(word->s, pattern->lhs); 768 if (cp != NULL) { 769 if (addSpace && (cp - word->s) + pattern->rightLen != 0){ 770 Buf_AddSpace(buf); 771 addSpace = false; 772 } 773 Buf_Addi(buf, word->s, cp); 774 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 775 wordLen -= (cp - word->s) + pattern->leftLen; 776 word->s = cp + pattern->leftLen; 777 if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0) 778 done = true; 779 pattern->flags |= VAR_SUB_MATCHED; 780 } else 781 done = true; 782 } 783 if (wordLen != 0) { 784 if (addSpace) 785 Buf_AddSpace(buf); 786 Buf_AddChars(buf, wordLen, word->s); 787 } 788 /* If added characters to the buffer, need to add a space 789 * before we add any more. If we didn't add any, just return 790 * the previous value of addSpace. */ 791 return Buf_Size(buf) != origSize || addSpace; 792 } 793 return addSpace; 794 } 795 nosub: 796 if (addSpace) 797 Buf_AddSpace(buf); 798 Buf_AddChars(buf, wordLen, word->s); 799 return true; 800 } 801 802 #ifndef MAKE_BOOTSTRAP 803 /*- 804 *----------------------------------------------------------------------- 805 * VarREError -- 806 * Print the error caused by a regcomp or regexec call. 807 *----------------------------------------------------------------------- 808 */ 809 static void 810 VarREError(int err, regex_t *pat, const char *str) 811 { 812 char *errbuf; 813 int errlen; 814 815 errlen = regerror(err, pat, 0, 0); 816 errbuf = emalloc(errlen); 817 regerror(err, pat, errbuf, errlen); 818 Error("%s: %s", str, errbuf); 819 free(errbuf); 820 } 821 822 /*- 823 *----------------------------------------------------------------------- 824 * VarRESubstitute -- 825 * Perform a regex substitution on the given word, placing the 826 * result in the passed buffer. 827 *----------------------------------------------------------------------- 828 */ 829 static bool 830 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) 831 { 832 VarREPattern *pat; 833 int xrv; 834 const char *wp; 835 char *rp; 836 int added; 837 838 #define MAYBE_ADD_SPACE() \ 839 if (addSpace && !added) \ 840 Buf_AddSpace(buf); \ 841 added = 1 842 843 added = 0; 844 wp = word->s; 845 pat = patternp; 846 847 if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == 848 (VAR_SUB_ONE|VAR_SUB_MATCHED)) 849 xrv = REG_NOMATCH; 850 else { 851 tryagain: 852 xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); 853 } 854 855 switch (xrv) { 856 case 0: 857 pat->flags |= VAR_SUB_MATCHED; 858 if (pat->matches[0].rm_so > 0) { 859 MAYBE_ADD_SPACE(); 860 Buf_AddChars(buf, pat->matches[0].rm_so, wp); 861 } 862 863 for (rp = pat->replace; *rp; rp++) { 864 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 865 MAYBE_ADD_SPACE(); 866 Buf_AddChar(buf,rp[1]); 867 rp++; 868 } 869 else if (*rp == '&' || 870 (*rp == '\\' && ISDIGIT(rp[1]))) { 871 int n; 872 const char *subbuf; 873 int sublen; 874 char errstr[3]; 875 876 if (*rp == '&') { 877 n = 0; 878 errstr[0] = '&'; 879 errstr[1] = '\0'; 880 } else { 881 n = rp[1] - '0'; 882 errstr[0] = '\\'; 883 errstr[1] = rp[1]; 884 errstr[2] = '\0'; 885 rp++; 886 } 887 888 if (n > pat->nsub) { 889 Error("No subexpression %s", 890 &errstr[0]); 891 subbuf = ""; 892 sublen = 0; 893 } else if (pat->matches[n].rm_so == -1 && 894 pat->matches[n].rm_eo == -1) { 895 Error("No match for subexpression %s", 896 &errstr[0]); 897 subbuf = ""; 898 sublen = 0; 899 } else { 900 subbuf = wp + pat->matches[n].rm_so; 901 sublen = pat->matches[n].rm_eo - 902 pat->matches[n].rm_so; 903 } 904 905 if (sublen > 0) { 906 MAYBE_ADD_SPACE(); 907 Buf_AddChars(buf, sublen, subbuf); 908 } 909 } else { 910 MAYBE_ADD_SPACE(); 911 Buf_AddChar(buf, *rp); 912 } 913 } 914 wp += pat->matches[0].rm_eo; 915 if (pat->flags & VAR_SUB_GLOBAL) { 916 /* like most modern tools, empty string matches 917 * should advance one char at a time... 918 */ 919 if (pat->matches[0].rm_eo == 0) { 920 if (*wp) { 921 MAYBE_ADD_SPACE(); 922 Buf_AddChar(buf, *wp++); 923 } else 924 break; 925 } 926 goto tryagain; 927 } 928 if (*wp) { 929 MAYBE_ADD_SPACE(); 930 Buf_AddString(buf, wp); 931 } 932 break; 933 default: 934 VarREError(xrv, &pat->re, "Unexpected regex error"); 935 /* FALLTHROUGH */ 936 case REG_NOMATCH: 937 if (*wp) { 938 MAYBE_ADD_SPACE(); 939 Buf_AddString(buf, wp); 940 } 941 break; 942 } 943 return addSpace||added; 944 } 945 #endif 946 947 /*- 948 *----------------------------------------------------------------------- 949 * VarModify -- 950 * Modify each of the words of the passed string using the given 951 * function. Used to implement all modifiers. 952 * 953 * Results: 954 * A string of all the words modified appropriately. 955 *----------------------------------------------------------------------- 956 */ 957 static char * 958 VarModify(char *str, /* String whose words should be trimmed */ 959 /* Function to use to modify them */ 960 bool (*modProc)(struct Name *, bool, Buffer, void *), 961 void *datum) /* Datum to pass it */ 962 { 963 BUFFER buf; /* Buffer for the new string */ 964 bool addSpace; /* true if need to add a space to the 965 * buffer before adding the trimmed 966 * word */ 967 struct Name word; 968 969 Buf_Init(&buf, 0); 970 addSpace = false; 971 972 word.e = str; 973 974 while ((word.s = iterate_words(&word.e)) != NULL) { 975 char termc; 976 977 termc = *word.e; 978 *((char *)(word.e)) = '\0'; 979 addSpace = (*modProc)(&word, addSpace, &buf, datum); 980 *((char *)(word.e)) = termc; 981 } 982 return Buf_Retrieve(&buf); 983 } 984 985 /*- 986 *----------------------------------------------------------------------- 987 * VarGetPattern -- 988 * Pass through the tstr looking for 1) escaped delimiters, 989 * '$'s and backslashes (place the escaped character in 990 * uninterpreted) and 2) unescaped $'s that aren't before 991 * the delimiter (expand the variable substitution). 992 * Return the expanded string or NULL if the delimiter was missing 993 * If pattern is specified, handle escaped ampersands, and replace 994 * unescaped ampersands with the lhs of the pattern. 995 * 996 * Results: 997 * A string of all the words modified appropriately. 998 * If length is specified, return the string length of the buffer 999 *----------------------------------------------------------------------- 1000 */ 1001 static char * 1002 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, 1003 int delim2, size_t *length, VarPattern *pattern) 1004 { 1005 const char *cp; 1006 char *result; 1007 BUFFER buf; 1008 size_t junk; 1009 1010 Buf_Init(&buf, 0); 1011 if (length == NULL) 1012 length = &junk; 1013 1014 #define IS_A_MATCH(cp, delim1, delim2) \ 1015 (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ 1016 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) 1017 1018 /* 1019 * Skim through until the matching delimiter is found; 1020 * pick up variable substitutions on the way. Also allow 1021 * backslashes to quote the delimiter, $, and \, but don't 1022 * touch other backslashes. 1023 */ 1024 for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { 1025 if (IS_A_MATCH(cp, delim1, delim2)) { 1026 Buf_AddChar(&buf, cp[1]); 1027 cp++; 1028 } else if (*cp == '$') { 1029 /* Allowed at end of pattern */ 1030 if (cp[1] == delim1 || cp[1] == delim2) 1031 Buf_AddChar(&buf, *cp); 1032 else { 1033 size_t len; 1034 1035 /* If unescaped dollar sign not before the 1036 * delimiter, assume it's a variable 1037 * substitution and recurse. */ 1038 (void)Var_ParseBuffer(&buf, cp, ctxt, err, 1039 &len); 1040 cp += len - 1; 1041 } 1042 } else if (pattern && *cp == '&') 1043 Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); 1044 else 1045 Buf_AddChar(&buf, *cp); 1046 } 1047 1048 *length = Buf_Size(&buf); 1049 result = Buf_Retrieve(&buf); 1050 1051 if (*cp != delim1 && *cp != delim2) { 1052 *tstr = cp; 1053 *length = 0; 1054 free(result); 1055 return NULL; 1056 } 1057 else { 1058 *tstr = ++cp; 1059 return result; 1060 } 1061 } 1062 1063 /*- 1064 *----------------------------------------------------------------------- 1065 * VarQuote -- 1066 * Quote shell meta-characters in the string 1067 * 1068 * Results: 1069 * The quoted string 1070 *----------------------------------------------------------------------- 1071 */ 1072 static char * 1073 VarQuote(const char *str, const struct Name *n UNUSED, void *islistp) 1074 { 1075 int islist = *((int *)islistp); 1076 1077 BUFFER buf; 1078 /* This should cover most shells :-( */ 1079 static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1080 char *rep = meta; 1081 if (islist) 1082 rep += 3; 1083 1084 Buf_Init(&buf, MAKE_BSIZE); 1085 for (; *str; str++) { 1086 if (strchr(rep, *str) != NULL) 1087 Buf_AddChar(&buf, '\\'); 1088 Buf_AddChar(&buf, *str); 1089 } 1090 return Buf_Retrieve(&buf); 1091 } 1092 1093 static void * 1094 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1095 { 1096 dummy_arg->s = NULL; 1097 if ((*p)[1] == endc || (*p)[1] == ':') { 1098 (*p)++; 1099 return dummy_arg; 1100 } else 1101 return NULL; 1102 } 1103 1104 static void * 1105 check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1106 { 1107 int *qargs = emalloc(sizeof(int)); 1108 *qargs = 0; 1109 if ((*p)[1] == 'L') { 1110 *qargs = 1; 1111 (*p)++; 1112 } 1113 if ((*p)[1] == endc || (*p)[1] == ':') { 1114 (*p)++; 1115 return qargs; 1116 } else { 1117 free(qargs); 1118 return NULL; 1119 } 1120 } 1121 1122 static void * 1123 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1124 { 1125 if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { 1126 (*p)+=2; 1127 return dummy_arg; 1128 } else 1129 return NULL; 1130 } 1131 1132 1133 static char * 1134 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1135 { 1136 char *err; 1137 char *t; 1138 1139 t = Cmd_Exec(s, &err); 1140 if (err) 1141 Error(err, s); 1142 return t; 1143 } 1144 1145 static void * 1146 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1147 { 1148 const char *cp; 1149 char *s; 1150 1151 for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { 1152 if (*cp == '\\') { 1153 if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') 1154 cp++; 1155 } else if (*cp == '\0') 1156 return NULL; 1157 } 1158 s = escape_dupi(*p+1, cp, ":)}"); 1159 *p = cp; 1160 return s; 1161 } 1162 1163 static void 1164 free_stringarg(void *arg) 1165 { 1166 free(arg); 1167 } 1168 1169 static char * 1170 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1171 { 1172 size_t len, i; 1173 char *t; 1174 1175 len = strlen(s); 1176 t = emalloc(len+1); 1177 for (i = 0; i < len; i++) 1178 t[i] = TOUPPER(s[i]); 1179 t[len] = '\0'; 1180 return t; 1181 } 1182 1183 static char * 1184 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1185 { 1186 size_t len, i; 1187 char *t; 1188 1189 len = strlen(s); 1190 t = emalloc(len+1); 1191 for (i = 0; i < len; i++) 1192 t[i] = TOLOWER(s[i]); 1193 t[len] = '\0'; 1194 return t; 1195 } 1196 1197 static void * 1198 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) 1199 { 1200 return common_get_patternarg(p, ctxt, err, endc, false); 1201 } 1202 1203 /* Extract anchors */ 1204 static void * 1205 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) 1206 { 1207 VarPattern *pattern; 1208 1209 pattern = common_get_patternarg(p, ctxt, err, endc, true); 1210 if (pattern != NULL && pattern->leftLen > 0) { 1211 if (pattern->lhs[pattern->leftLen-1] == '$') { 1212 pattern->leftLen--; 1213 pattern->flags |= VAR_MATCH_END; 1214 } 1215 if (pattern->lhs[0] == '^') { 1216 pattern->lhs++; 1217 pattern->leftLen--; 1218 pattern->flags |= VAR_MATCH_START; 1219 } 1220 } 1221 return pattern; 1222 } 1223 1224 static void 1225 free_looparg(void *arg) 1226 { 1227 struct LoopStuff *l = (struct LoopStuff *)arg; 1228 1229 Var_DeleteLoopVar(l->var); 1230 free(l->expand); 1231 } 1232 1233 static char * 1234 LoopGrab(const char **s) 1235 { 1236 const char *p, *start; 1237 1238 start = *s; 1239 for (p = start; *p != '@'; p++) { 1240 if (*p == '\\') 1241 p++; 1242 if (*p == 0) 1243 return NULL; 1244 } 1245 *s = p+1; 1246 return escape_dupi(start, p, "@\\"); 1247 } 1248 1249 static void * 1250 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) 1251 { 1252 static struct LoopStuff loop; 1253 const char *s; 1254 const char *var; 1255 1256 s = *p +1; 1257 1258 loop.var = NULL; 1259 loop.expand = NULL; 1260 loop.err = err; 1261 var = LoopGrab(&s); 1262 if (var != NULL) { 1263 loop.expand = LoopGrab(&s); 1264 if (*s == endc || *s == ':') { 1265 *p = s; 1266 loop.var = Var_NewLoopVar(var, NULL); 1267 return &loop; 1268 } 1269 } 1270 free_looparg(&loop); 1271 return NULL; 1272 } 1273 1274 static void * 1275 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, 1276 bool dosubst) 1277 { 1278 VarPattern *pattern; 1279 char delim; 1280 const char *s; 1281 1282 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1283 pattern->flags = 0; 1284 s = *p; 1285 1286 delim = s[1]; 1287 if (delim == '\0') 1288 return NULL; 1289 s += 2; 1290 1291 pattern->rhs = NULL; 1292 pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, 1293 &pattern->leftLen, NULL); 1294 pattern->lbuffer = pattern->lhs; 1295 if (pattern->lhs != NULL) { 1296 pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, 1297 &pattern->rightLen, dosubst ? pattern: NULL); 1298 if (pattern->rhs != NULL) { 1299 /* Check for global substitution. If 'g' after the 1300 * final delimiter, substitution is global and is 1301 * marked that way. */ 1302 for (;; s++) { 1303 switch (*s) { 1304 case 'g': 1305 pattern->flags |= VAR_SUB_GLOBAL; 1306 continue; 1307 case '1': 1308 pattern->flags |= VAR_SUB_ONE; 1309 continue; 1310 } 1311 break; 1312 } 1313 if (*s == endc || *s == ':') { 1314 *p = s; 1315 return pattern; 1316 } 1317 } 1318 } 1319 free_patternarg(pattern); 1320 return NULL; 1321 } 1322 1323 static void * 1324 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) 1325 { 1326 const char *s; 1327 int flags; 1328 VarPattern *arg; 1329 1330 s = *p + 1; 1331 if (s[0] == '=') 1332 flags = VAR_EQUAL; 1333 else if (s[0] == '?' && s[1] == '=') 1334 flags = VAR_MAY_EQUAL; 1335 else if (s[0] == '+' && s[1] == '=') 1336 flags = VAR_ADD_EQUAL; 1337 else if (s[0] == '!' && s[1] == '=') 1338 flags = VAR_BANG_EQUAL; 1339 else 1340 return NULL; 1341 1342 arg = get_value(&s, ctxt, err, endc); 1343 if (arg != NULL) { 1344 *p = s; 1345 arg->flags = flags; 1346 } 1347 return arg; 1348 } 1349 1350 static void * 1351 get_value(const char **p, SymTable *ctxt, bool err, int endc) 1352 { 1353 VarPattern *pattern; 1354 const char *s; 1355 1356 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1357 s = *p + 1; 1358 pattern->rhs = NULL; 1359 pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, 1360 &pattern->leftLen, NULL); 1361 if (s[-1] == endc || s[-1] == ':') { 1362 *p = s-1; 1363 return pattern; 1364 } 1365 free_patternarg(pattern); 1366 return NULL; 1367 } 1368 1369 static void * 1370 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) 1371 { 1372 VarPattern *pattern; 1373 const char *s; 1374 1375 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1376 s = *p + 1; 1377 pattern->rhs = NULL; 1378 pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', 1379 &pattern->leftLen, NULL); 1380 if (s[-1] == '!') { 1381 *p = s-1; 1382 return pattern; 1383 } 1384 free_patternarg(pattern); 1385 return NULL; 1386 } 1387 1388 static void 1389 free_patternarg(void *p) 1390 { 1391 VarPattern *vp = (VarPattern *)p; 1392 1393 free(vp->lbuffer); 1394 free(vp->rhs); 1395 free(vp); 1396 } 1397 1398 #ifndef MAKE_BOOTSTRAP 1399 static char * 1400 do_regex(const char *s, const struct Name *n UNUSED, void *arg) 1401 { 1402 VarREPattern p2; 1403 VarPattern *p = (VarPattern *)arg; 1404 int error; 1405 char *result; 1406 1407 error = regcomp(&p2.re, p->lhs, REG_EXTENDED); 1408 if (error) { 1409 VarREError(error, &p2.re, "RE substitution error"); 1410 return var_Error; 1411 } 1412 p2.nsub = p2.re.re_nsub + 1; 1413 p2.replace = p->rhs; 1414 p2.flags = p->flags; 1415 if (p2.nsub < 1) 1416 p2.nsub = 1; 1417 if (p2.nsub > 10) 1418 p2.nsub = 10; 1419 p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t)); 1420 result = VarModify((char *)s, VarRESubstitute, &p2); 1421 regfree(&p2.re); 1422 free(p2.matches); 1423 return result; 1424 } 1425 #endif 1426 1427 char * 1428 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, 1429 bool err, bool *freePtr, const char **pscan, int paren) 1430 { 1431 const char *tstr; 1432 bool atstart; /* Some ODE modifiers only make sense at start */ 1433 char endc = paren == '(' ? ')' : '}'; 1434 const char *start = *pscan; 1435 1436 tstr = start; 1437 /* 1438 * Now we need to apply any modifiers the user wants applied. 1439 * These are: 1440 * :M<pattern> words which match the given <pattern>. 1441 * <pattern> is of the standard file 1442 * wildcarding form. 1443 * :S<d><pat1><d><pat2><d>[g] 1444 * Substitute <pat2> for <pat1> in the 1445 * value 1446 * :C<d><pat1><d><pat2><d>[g] 1447 * Substitute <pat2> for regex <pat1> in 1448 * the value 1449 * :H Substitute the head of each word 1450 * :T Substitute the tail of each word 1451 * :E Substitute the extension (minus '.') of 1452 * each word 1453 * :R Substitute the root of each word 1454 * (pathname minus the suffix). 1455 * :lhs=rhs Like :S, but the rhs goes to the end of 1456 * the invocation. 1457 */ 1458 1459 atstart = true; 1460 while (*tstr != endc && *tstr != '\0') { 1461 struct modifier *mod; 1462 void *arg; 1463 char *newStr; 1464 1465 tstr++; 1466 if (DEBUG(VAR)) 1467 printf("Applying :%c to \"%s\"\n", *tstr, str); 1468 1469 mod = choose_mod[(unsigned char)*tstr]; 1470 arg = NULL; 1471 1472 if (mod != NULL && (!mod->atstart || atstart)) 1473 arg = mod->getarg(&tstr, ctxt, err, endc); 1474 if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { 1475 mod = &sysv_mod; 1476 arg = mod->getarg(&tstr, ctxt, err, endc); 1477 } 1478 atstart = false; 1479 if (arg != NULL) { 1480 if (str != NULL || (mod->atstart && name != NULL)) { 1481 if (mod->word_apply != NULL) { 1482 newStr = VarModify(str, 1483 mod->word_apply, arg); 1484 if (mod->apply != NULL) { 1485 char *newStr2; 1486 1487 newStr2 = mod->apply(newStr, 1488 name, arg); 1489 free(newStr); 1490 newStr = newStr2; 1491 } 1492 } else 1493 newStr = mod->apply(str, name, arg); 1494 if (*freePtr) 1495 free(str); 1496 str = newStr; 1497 if (str != var_Error) 1498 *freePtr = true; 1499 else 1500 *freePtr = false; 1501 } 1502 if (mod->freearg != NULL) 1503 mod->freearg(arg); 1504 } else { 1505 Error("Bad modifier: %s", tstr); 1506 /* Try skipping to end of var... */ 1507 for (tstr++; *tstr != endc && *tstr != '\0';) 1508 tstr++; 1509 if (str != NULL && *freePtr) 1510 free(str); 1511 str = var_Error; 1512 *freePtr = false; 1513 break; 1514 } 1515 if (DEBUG(VAR)) 1516 printf("Result is \"%s\"\n", str); 1517 } 1518 if (*tstr == '\0') 1519 Parse_Error(PARSE_FATAL, "Unclosed variable specification"); 1520 else 1521 tstr++; 1522 1523 *pscan = tstr; 1524 return str; 1525 } 1526 1527 char * 1528 Var_GetHead(char *s) 1529 { 1530 return VarModify(s, VarHead, NULL); 1531 } 1532 1533 char * 1534 Var_GetTail(char *s) 1535 { 1536 return VarModify(s, VarTail, NULL); 1537 } 1538