1 /* $OpenBSD: varmodifiers.c,v 1.30 2011/08/16 14:18: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 = (struct Name *)emalloc(sizeof(struct Name) * n); 464 start = s; 465 end = start; 466 467 for (i = 0;; i++) { 468 if (i == n) { 469 n *= 2; 470 t = (struct Name *)erealloc(t, sizeof(struct Name) * n); 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 goto tryagain; 917 if (*wp) { 918 MAYBE_ADD_SPACE(); 919 Buf_AddString(buf, wp); 920 } 921 break; 922 default: 923 VarREError(xrv, &pat->re, "Unexpected regex error"); 924 /* FALLTHROUGH */ 925 case REG_NOMATCH: 926 if (*wp) { 927 MAYBE_ADD_SPACE(); 928 Buf_AddString(buf, wp); 929 } 930 break; 931 } 932 return addSpace||added; 933 } 934 #endif 935 936 /*- 937 *----------------------------------------------------------------------- 938 * VarModify -- 939 * Modify each of the words of the passed string using the given 940 * function. Used to implement all modifiers. 941 * 942 * Results: 943 * A string of all the words modified appropriately. 944 *----------------------------------------------------------------------- 945 */ 946 static char * 947 VarModify(char *str, /* String whose words should be trimmed */ 948 /* Function to use to modify them */ 949 bool (*modProc)(struct Name *, bool, Buffer, void *), 950 void *datum) /* Datum to pass it */ 951 { 952 BUFFER buf; /* Buffer for the new string */ 953 bool addSpace; /* true if need to add a space to the 954 * buffer before adding the trimmed 955 * word */ 956 struct Name word; 957 958 Buf_Init(&buf, 0); 959 addSpace = false; 960 961 word.e = str; 962 963 while ((word.s = iterate_words(&word.e)) != NULL) { 964 char termc; 965 966 termc = *word.e; 967 *((char *)(word.e)) = '\0'; 968 addSpace = (*modProc)(&word, addSpace, &buf, datum); 969 *((char *)(word.e)) = termc; 970 } 971 return Buf_Retrieve(&buf); 972 } 973 974 /*- 975 *----------------------------------------------------------------------- 976 * VarGetPattern -- 977 * Pass through the tstr looking for 1) escaped delimiters, 978 * '$'s and backslashes (place the escaped character in 979 * uninterpreted) and 2) unescaped $'s that aren't before 980 * the delimiter (expand the variable substitution). 981 * Return the expanded string or NULL if the delimiter was missing 982 * If pattern is specified, handle escaped ampersands, and replace 983 * unescaped ampersands with the lhs of the pattern. 984 * 985 * Results: 986 * A string of all the words modified appropriately. 987 * If length is specified, return the string length of the buffer 988 *----------------------------------------------------------------------- 989 */ 990 static char * 991 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, 992 int delim2, size_t *length, VarPattern *pattern) 993 { 994 const char *cp; 995 char *result; 996 BUFFER buf; 997 size_t junk; 998 999 Buf_Init(&buf, 0); 1000 if (length == NULL) 1001 length = &junk; 1002 1003 #define IS_A_MATCH(cp, delim1, delim2) \ 1004 (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ 1005 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) 1006 1007 /* 1008 * Skim through until the matching delimiter is found; 1009 * pick up variable substitutions on the way. Also allow 1010 * backslashes to quote the delimiter, $, and \, but don't 1011 * touch other backslashes. 1012 */ 1013 for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { 1014 if (IS_A_MATCH(cp, delim1, delim2)) { 1015 Buf_AddChar(&buf, cp[1]); 1016 cp++; 1017 } else if (*cp == '$') { 1018 /* Allowed at end of pattern */ 1019 if (cp[1] == delim1 || cp[1] == delim2) 1020 Buf_AddChar(&buf, *cp); 1021 else { 1022 size_t len; 1023 1024 /* If unescaped dollar sign not before the 1025 * delimiter, assume it's a variable 1026 * substitution and recurse. */ 1027 (void)Var_ParseBuffer(&buf, cp, ctxt, err, 1028 &len); 1029 cp += len - 1; 1030 } 1031 } else if (pattern && *cp == '&') 1032 Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); 1033 else 1034 Buf_AddChar(&buf, *cp); 1035 } 1036 1037 *length = Buf_Size(&buf); 1038 result = Buf_Retrieve(&buf); 1039 1040 if (*cp != delim1 && *cp != delim2) { 1041 *tstr = cp; 1042 *length = 0; 1043 free(result); 1044 return NULL; 1045 } 1046 else { 1047 *tstr = ++cp; 1048 return result; 1049 } 1050 } 1051 1052 /*- 1053 *----------------------------------------------------------------------- 1054 * VarQuote -- 1055 * Quote shell meta-characters in the string 1056 * 1057 * Results: 1058 * The quoted string 1059 *----------------------------------------------------------------------- 1060 */ 1061 static char * 1062 VarQuote(const char *str, const struct Name *n UNUSED, void *islistp) 1063 { 1064 int islist = *((int *)islistp); 1065 1066 BUFFER buf; 1067 /* This should cover most shells :-( */ 1068 static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1069 char *rep = meta; 1070 if (islist) 1071 rep += 3; 1072 1073 Buf_Init(&buf, MAKE_BSIZE); 1074 for (; *str; str++) { 1075 if (strchr(rep, *str) != NULL) 1076 Buf_AddChar(&buf, '\\'); 1077 Buf_AddChar(&buf, *str); 1078 } 1079 return Buf_Retrieve(&buf); 1080 } 1081 1082 static void * 1083 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1084 { 1085 dummy_arg->s = NULL; 1086 if ((*p)[1] == endc || (*p)[1] == ':') { 1087 (*p)++; 1088 return dummy_arg; 1089 } else 1090 return NULL; 1091 } 1092 1093 static void * 1094 check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1095 { 1096 int *qargs = emalloc(sizeof(int)); 1097 *qargs = 0; 1098 if ((*p)[1] == 'L') { 1099 *qargs = 1; 1100 (*p)++; 1101 } 1102 if ((*p)[1] == endc || (*p)[1] == ':') { 1103 (*p)++; 1104 return qargs; 1105 } else 1106 return NULL; 1107 } 1108 1109 static void * 1110 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1111 { 1112 if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { 1113 (*p)+=2; 1114 return dummy_arg; 1115 } else 1116 return NULL; 1117 } 1118 1119 1120 static char * 1121 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1122 { 1123 char *err; 1124 char *t; 1125 1126 t = Cmd_Exec(s, &err); 1127 if (err) 1128 Error(err, s); 1129 return t; 1130 } 1131 1132 static void * 1133 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1134 { 1135 const char *cp; 1136 char *s; 1137 1138 for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { 1139 if (*cp == '\\') { 1140 if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') 1141 cp++; 1142 } else if (*cp == '\0') 1143 return NULL; 1144 } 1145 s = escape_dupi(*p+1, cp, ":)}"); 1146 *p = cp; 1147 return s; 1148 } 1149 1150 static void 1151 free_stringarg(void *arg) 1152 { 1153 free(arg); 1154 } 1155 1156 static char * 1157 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1158 { 1159 size_t len, i; 1160 char *t; 1161 1162 len = strlen(s); 1163 t = emalloc(len+1); 1164 for (i = 0; i < len; i++) 1165 t[i] = toupper(s[i]); 1166 t[len] = '\0'; 1167 return t; 1168 } 1169 1170 static char * 1171 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1172 { 1173 size_t len, i; 1174 char *t; 1175 1176 len = strlen(s); 1177 t = emalloc(len+1); 1178 for (i = 0; i < len; i++) 1179 t[i] = tolower(s[i]); 1180 t[len] = '\0'; 1181 return t; 1182 } 1183 1184 static void * 1185 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) 1186 { 1187 return common_get_patternarg(p, ctxt, err, endc, false); 1188 } 1189 1190 /* Extract anchors */ 1191 static void * 1192 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) 1193 { 1194 VarPattern *pattern; 1195 1196 pattern = common_get_patternarg(p, ctxt, err, endc, true); 1197 if (pattern != NULL && pattern->leftLen > 0) { 1198 if (pattern->lhs[pattern->leftLen-1] == '$') { 1199 pattern->leftLen--; 1200 pattern->flags |= VAR_MATCH_END; 1201 } 1202 if (pattern->lhs[0] == '^') { 1203 pattern->lhs++; 1204 pattern->leftLen--; 1205 pattern->flags |= VAR_MATCH_START; 1206 } 1207 } 1208 return pattern; 1209 } 1210 1211 static void 1212 free_looparg(void *arg) 1213 { 1214 struct LoopStuff *l = (struct LoopStuff *)arg; 1215 1216 Var_DeleteLoopVar(l->var); 1217 free(l->expand); 1218 } 1219 1220 static char * 1221 LoopGrab(const char **s) 1222 { 1223 const char *p, *start; 1224 1225 start = *s; 1226 for (p = start; *p != '@'; p++) { 1227 if (*p == '\\') 1228 p++; 1229 if (*p == 0) 1230 return NULL; 1231 } 1232 *s = p+1; 1233 return escape_dupi(start, p, "@\\"); 1234 } 1235 1236 static void * 1237 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) 1238 { 1239 static struct LoopStuff loop; 1240 const char *s; 1241 const char *var; 1242 1243 s = *p +1; 1244 1245 loop.var = NULL; 1246 loop.expand = NULL; 1247 loop.err = err; 1248 var = LoopGrab(&s); 1249 if (var != NULL) { 1250 loop.expand = LoopGrab(&s); 1251 if (*s == endc || *s == ':') { 1252 *p = s; 1253 loop.var = Var_NewLoopVar(var, NULL); 1254 return &loop; 1255 } 1256 } 1257 free_looparg(&loop); 1258 return NULL; 1259 } 1260 1261 static void * 1262 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, 1263 bool dosubst) 1264 { 1265 VarPattern *pattern; 1266 char delim; 1267 const char *s; 1268 1269 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1270 pattern->flags = 0; 1271 s = *p; 1272 1273 delim = s[1]; 1274 if (delim == '\0') 1275 return NULL; 1276 s += 2; 1277 1278 pattern->rhs = NULL; 1279 pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, 1280 &pattern->leftLen, NULL); 1281 pattern->lbuffer = pattern->lhs; 1282 if (pattern->lhs != NULL) { 1283 pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, 1284 &pattern->rightLen, dosubst ? pattern: NULL); 1285 if (pattern->rhs != NULL) { 1286 /* Check for global substitution. If 'g' after the 1287 * final delimiter, substitution is global and is 1288 * marked that way. */ 1289 for (;; s++) { 1290 switch (*s) { 1291 case 'g': 1292 pattern->flags |= VAR_SUB_GLOBAL; 1293 continue; 1294 case '1': 1295 pattern->flags |= VAR_SUB_ONE; 1296 continue; 1297 } 1298 break; 1299 } 1300 if (*s == endc || *s == ':') { 1301 *p = s; 1302 return pattern; 1303 } 1304 } 1305 } 1306 free_patternarg(pattern); 1307 return NULL; 1308 } 1309 1310 static void * 1311 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) 1312 { 1313 const char *s; 1314 int flags; 1315 VarPattern *arg; 1316 1317 s = *p + 1; 1318 if (s[0] == '=') 1319 flags = VAR_EQUAL; 1320 else if (s[0] == '?' && s[1] == '=') 1321 flags = VAR_MAY_EQUAL; 1322 else if (s[0] == '+' && s[1] == '=') 1323 flags = VAR_ADD_EQUAL; 1324 else if (s[0] == '!' && s[1] == '=') 1325 flags = VAR_BANG_EQUAL; 1326 else 1327 return NULL; 1328 1329 arg = get_value(&s, ctxt, err, endc); 1330 if (arg != NULL) { 1331 *p = s; 1332 arg->flags = flags; 1333 } 1334 return arg; 1335 } 1336 1337 static void * 1338 get_value(const char **p, SymTable *ctxt, bool err, int endc) 1339 { 1340 VarPattern *pattern; 1341 const char *s; 1342 1343 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1344 s = *p + 1; 1345 pattern->rhs = NULL; 1346 pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, 1347 &pattern->leftLen, NULL); 1348 if (s[-1] == endc || s[-1] == ':') { 1349 *p = s-1; 1350 return pattern; 1351 } 1352 free_patternarg(pattern); 1353 return NULL; 1354 } 1355 1356 static void * 1357 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) 1358 { 1359 VarPattern *pattern; 1360 const char *s; 1361 1362 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1363 s = *p + 1; 1364 pattern->rhs = NULL; 1365 pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', 1366 &pattern->leftLen, NULL); 1367 if (s[-1] == '!') { 1368 *p = s-1; 1369 return pattern; 1370 } 1371 free_patternarg(pattern); 1372 return NULL; 1373 } 1374 1375 static void 1376 free_patternarg(void *p) 1377 { 1378 VarPattern *vp = (VarPattern *)p; 1379 1380 free(vp->lbuffer); 1381 free(vp->rhs); 1382 free(vp); 1383 } 1384 1385 #ifndef MAKE_BOOTSTRAP 1386 static char * 1387 do_regex(const char *s, const struct Name *n UNUSED, void *arg) 1388 { 1389 VarREPattern p2; 1390 VarPattern *p = (VarPattern *)arg; 1391 int error; 1392 char *result; 1393 1394 error = regcomp(&p2.re, p->lhs, REG_EXTENDED); 1395 if (error) { 1396 VarREError(error, &p2.re, "RE substitution error"); 1397 return var_Error; 1398 } 1399 p2.nsub = p2.re.re_nsub + 1; 1400 p2.replace = p->rhs; 1401 p2.flags = p->flags; 1402 if (p2.nsub < 1) 1403 p2.nsub = 1; 1404 if (p2.nsub > 10) 1405 p2.nsub = 10; 1406 p2.matches = emalloc(p2.nsub * sizeof(regmatch_t)); 1407 result = VarModify((char *)s, VarRESubstitute, &p2); 1408 regfree(&p2.re); 1409 free(p2.matches); 1410 return result; 1411 } 1412 #endif 1413 1414 char * 1415 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, 1416 bool err, bool *freePtr, const char **pscan, int paren) 1417 { 1418 const char *tstr; 1419 bool atstart; /* Some ODE modifiers only make sense at start */ 1420 char endc = paren == '(' ? ')' : '}'; 1421 const char *start = *pscan; 1422 1423 tstr = start; 1424 /* 1425 * Now we need to apply any modifiers the user wants applied. 1426 * These are: 1427 * :M<pattern> words which match the given <pattern>. 1428 * <pattern> is of the standard file 1429 * wildcarding form. 1430 * :S<d><pat1><d><pat2><d>[g] 1431 * Substitute <pat2> for <pat1> in the 1432 * value 1433 * :C<d><pat1><d><pat2><d>[g] 1434 * Substitute <pat2> for regex <pat1> in 1435 * the value 1436 * :H Substitute the head of each word 1437 * :T Substitute the tail of each word 1438 * :E Substitute the extension (minus '.') of 1439 * each word 1440 * :R Substitute the root of each word 1441 * (pathname minus the suffix). 1442 * :lhs=rhs Like :S, but the rhs goes to the end of 1443 * the invocation. 1444 */ 1445 1446 atstart = true; 1447 while (*tstr != endc && *tstr != '\0') { 1448 struct modifier *mod; 1449 void *arg; 1450 char *newStr; 1451 1452 tstr++; 1453 if (DEBUG(VAR)) 1454 printf("Applying :%c to \"%s\"\n", *tstr, str); 1455 1456 mod = choose_mod[*tstr]; 1457 arg = NULL; 1458 1459 if (mod != NULL && (!mod->atstart || atstart)) 1460 arg = mod->getarg(&tstr, ctxt, err, endc); 1461 if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { 1462 mod = &sysv_mod; 1463 arg = mod->getarg(&tstr, ctxt, err, endc); 1464 } 1465 atstart = false; 1466 if (arg != NULL) { 1467 if (str != NULL || (mod->atstart && name != NULL)) { 1468 if (mod->word_apply != NULL) { 1469 newStr = VarModify(str, 1470 mod->word_apply, arg); 1471 if (mod->apply != NULL) { 1472 char *newStr2; 1473 1474 newStr2 = mod->apply(newStr, 1475 name, arg); 1476 free(newStr); 1477 newStr = newStr2; 1478 } 1479 } else 1480 newStr = mod->apply(str, name, arg); 1481 if (*freePtr) 1482 free(str); 1483 str = newStr; 1484 if (str != var_Error) 1485 *freePtr = true; 1486 else 1487 *freePtr = false; 1488 } 1489 if (mod->freearg != NULL) 1490 mod->freearg(arg); 1491 } else { 1492 Error("Bad modifier: %s\n", tstr); 1493 /* Try skipping to end of var... */ 1494 for (tstr++; *tstr != endc && *tstr != '\0';) 1495 tstr++; 1496 if (str != NULL && *freePtr) 1497 free(str); 1498 str = var_Error; 1499 *freePtr = false; 1500 break; 1501 } 1502 if (DEBUG(VAR)) 1503 printf("Result is \"%s\"\n", str); 1504 } 1505 if (*tstr == '\0') 1506 Error("Unclosed variable specification"); 1507 else 1508 tstr++; 1509 1510 *pscan = tstr; 1511 return str; 1512 } 1513 1514 char * 1515 Var_GetHead(char *s) 1516 { 1517 return VarModify(s, VarHead, NULL); 1518 } 1519 1520 char * 1521 Var_GetTail(char *s) 1522 { 1523 return VarModify(s, VarTail, NULL); 1524 } 1525