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