1 /* $OpenBSD: varmodifiers.c,v 1.43 2015/11/15 06:19:22 daniel 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; 607 int cnt = 0; 608 char startc = endc == ')' ? '(' : '{'; 609 for (cp = *p;; cp++) { 610 if (*cp == '=' && cnt == 0) 611 break; 612 if (*cp == '\0') 613 return NULL; 614 if (*cp == startc) 615 cnt++; 616 else if (*cp == endc) { 617 cnt--; 618 if (cnt < 0) 619 return NULL; 620 } 621 } 622 Buf_Init(&buf, 0); 623 for (cp2 = cp+1;; cp2++) { 624 if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) && 625 cnt == 0) 626 break; 627 if (*cp2 == '\0') { 628 Buf_Destroy(&buf); 629 return NULL; 630 } 631 if (*cp2 == startc) 632 cnt++; 633 else if (*cp2 == endc) { 634 cnt--; 635 if (cnt < 0) { 636 Buf_Destroy(&buf); 637 return NULL; 638 } 639 } else if (*cp2 == '$') { 640 if (cp2[1] == '$') 641 cp2++; 642 else { 643 size_t len; 644 (void)Var_ParseBuffer(&buf, cp2, ctxt, err, 645 &len); 646 cp2 += len - 1; 647 continue; 648 } 649 } 650 Buf_AddChar(&buf, *cp2); 651 } 652 653 pattern = emalloc(sizeof(VarPattern)); 654 pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp); 655 pattern->leftLen = cp - *p; 656 pattern->rhs = Buf_Retrieve(&buf); 657 pattern->rightLen = Buf_Size(&buf); 658 pattern->flags = 0; 659 *p = cp2; 660 return pattern; 661 } 662 663 664 /*- 665 *----------------------------------------------------------------------- 666 * VarSubstitute -- 667 * Perform a string-substitution on the given word, Adding the 668 * result to the given buffer. 669 *----------------------------------------------------------------------- 670 */ 671 static bool 672 VarSubstitute(struct Name *word, bool addSpace, Buffer buf, 673 void *patternp) /* Pattern for substitution */ 674 { 675 size_t wordLen; /* Length of word */ 676 const char *cp; /* General pointer */ 677 VarPattern *pattern = patternp; 678 679 wordLen = word->e - word->s; 680 if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != 681 (VAR_SUB_ONE|VAR_SUB_MATCHED)) { 682 /* Still substituting -- break it down into simple anchored cases 683 * and if none of them fits, perform the general substitution case. */ 684 if ((pattern->flags & VAR_MATCH_START) && 685 (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) { 686 /* Anchored at start and beginning of word matches pattern. */ 687 if ((pattern->flags & VAR_MATCH_END) && 688 (wordLen == pattern->leftLen)) { 689 /* Also anchored at end and matches to the end (word 690 * is same length as pattern) add space and rhs only 691 * if rhs is non-null. */ 692 if (pattern->rightLen != 0) { 693 if (addSpace) 694 Buf_AddSpace(buf); 695 addSpace = true; 696 Buf_AddChars(buf, pattern->rightLen, 697 pattern->rhs); 698 } 699 pattern->flags |= VAR_SUB_MATCHED; 700 } else if (pattern->flags & VAR_MATCH_END) { 701 /* Doesn't match to end -- copy word wholesale. */ 702 goto nosub; 703 } else { 704 /* Matches at start but need to copy in 705 * trailing characters. */ 706 if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ 707 if (addSpace) 708 Buf_AddSpace(buf); 709 addSpace = true; 710 } 711 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 712 Buf_AddChars(buf, wordLen - pattern->leftLen, 713 word->s + pattern->leftLen); 714 pattern->flags |= VAR_SUB_MATCHED; 715 } 716 } else if (pattern->flags & VAR_MATCH_START) { 717 /* Had to match at start of word and didn't -- copy whole word. */ 718 goto nosub; 719 } else if (pattern->flags & VAR_MATCH_END) { 720 /* Anchored at end, Find only place match could occur (leftLen 721 * characters from the end of the word) and see if it does. Note 722 * that because the $ will be left at the end of the lhs, we have 723 * to use strncmp. */ 724 cp = word->s + (wordLen - pattern->leftLen); 725 if (cp >= word->s && 726 strncmp(cp, pattern->lhs, pattern->leftLen) == 0) { 727 /* Match found. If we will place characters in the buffer, 728 * add a space before hand as indicated by addSpace, then 729 * stuff in the initial, unmatched part of the word followed 730 * by the right-hand-side. */ 731 if (((cp - word->s) + pattern->rightLen) != 0) { 732 if (addSpace) 733 Buf_AddSpace(buf); 734 addSpace = true; 735 } 736 Buf_Addi(buf, word->s, cp); 737 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 738 pattern->flags |= VAR_SUB_MATCHED; 739 } else { 740 /* Had to match at end and didn't. Copy entire word. */ 741 goto nosub; 742 } 743 } else { 744 /* Pattern is unanchored: search for the pattern in the word using 745 * strstr, copying unmatched portions and the 746 * right-hand-side for each match found, handling non-global 747 * substitutions correctly, etc. When the loop is done, any 748 * remaining part of the word (word and wordLen are adjusted 749 * accordingly through the loop) is copied straight into the 750 * buffer. 751 * addSpace is set to false as soon as a space is added to the 752 * buffer. */ 753 bool done; 754 size_t origSize; 755 756 done = false; 757 origSize = Buf_Size(buf); 758 while (!done) { 759 cp = strstr(word->s, pattern->lhs); 760 if (cp != NULL) { 761 if (addSpace && (cp - word->s) + pattern->rightLen != 0){ 762 Buf_AddSpace(buf); 763 addSpace = false; 764 } 765 Buf_Addi(buf, word->s, cp); 766 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 767 wordLen -= (cp - word->s) + pattern->leftLen; 768 word->s = cp + pattern->leftLen; 769 if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0) 770 done = true; 771 pattern->flags |= VAR_SUB_MATCHED; 772 } else 773 done = true; 774 } 775 if (wordLen != 0) { 776 if (addSpace) 777 Buf_AddSpace(buf); 778 Buf_AddChars(buf, wordLen, word->s); 779 } 780 /* If added characters to the buffer, need to add a space 781 * before we add any more. If we didn't add any, just return 782 * the previous value of addSpace. */ 783 return Buf_Size(buf) != origSize || addSpace; 784 } 785 return addSpace; 786 } 787 nosub: 788 if (addSpace) 789 Buf_AddSpace(buf); 790 Buf_AddChars(buf, wordLen, word->s); 791 return true; 792 } 793 794 /*- 795 *----------------------------------------------------------------------- 796 * VarREError -- 797 * Print the error caused by a regcomp or regexec call. 798 *----------------------------------------------------------------------- 799 */ 800 static void 801 VarREError(int err, regex_t *pat, const char *str) 802 { 803 char *errbuf; 804 int errlen; 805 806 errlen = regerror(err, pat, 0, 0); 807 errbuf = emalloc(errlen); 808 regerror(err, pat, errbuf, errlen); 809 Error("%s: %s", str, errbuf); 810 free(errbuf); 811 } 812 813 /*- 814 *----------------------------------------------------------------------- 815 * VarRESubstitute -- 816 * Perform a regex substitution on the given word, placing the 817 * result in the passed buffer. 818 *----------------------------------------------------------------------- 819 */ 820 static bool 821 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) 822 { 823 VarREPattern *pat; 824 int xrv; 825 const char *wp; 826 char *rp; 827 int added; 828 829 #define MAYBE_ADD_SPACE() \ 830 if (addSpace && !added) \ 831 Buf_AddSpace(buf); \ 832 added = 1 833 834 added = 0; 835 wp = word->s; 836 pat = patternp; 837 838 if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == 839 (VAR_SUB_ONE|VAR_SUB_MATCHED)) 840 xrv = REG_NOMATCH; 841 else { 842 tryagain: 843 xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); 844 } 845 846 switch (xrv) { 847 case 0: 848 pat->flags |= VAR_SUB_MATCHED; 849 if (pat->matches[0].rm_so > 0) { 850 MAYBE_ADD_SPACE(); 851 Buf_AddChars(buf, pat->matches[0].rm_so, wp); 852 } 853 854 for (rp = pat->replace; *rp; rp++) { 855 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 856 MAYBE_ADD_SPACE(); 857 Buf_AddChar(buf,rp[1]); 858 rp++; 859 } 860 else if (*rp == '&' || 861 (*rp == '\\' && ISDIGIT(rp[1]))) { 862 int n; 863 const char *subbuf; 864 int sublen; 865 char errstr[3]; 866 867 if (*rp == '&') { 868 n = 0; 869 errstr[0] = '&'; 870 errstr[1] = '\0'; 871 } else { 872 n = rp[1] - '0'; 873 errstr[0] = '\\'; 874 errstr[1] = rp[1]; 875 errstr[2] = '\0'; 876 rp++; 877 } 878 879 if (n > pat->nsub) { 880 Error("No subexpression %s", 881 &errstr[0]); 882 subbuf = ""; 883 sublen = 0; 884 } else if (pat->matches[n].rm_so == -1 && 885 pat->matches[n].rm_eo == -1) { 886 Error("No match for subexpression %s", 887 &errstr[0]); 888 subbuf = ""; 889 sublen = 0; 890 } else { 891 subbuf = wp + pat->matches[n].rm_so; 892 sublen = pat->matches[n].rm_eo - 893 pat->matches[n].rm_so; 894 } 895 896 if (sublen > 0) { 897 MAYBE_ADD_SPACE(); 898 Buf_AddChars(buf, sublen, subbuf); 899 } 900 } else { 901 MAYBE_ADD_SPACE(); 902 Buf_AddChar(buf, *rp); 903 } 904 } 905 wp += pat->matches[0].rm_eo; 906 if (pat->flags & VAR_SUB_GLOBAL) { 907 /* like most modern tools, empty string matches 908 * should advance one char at a time... 909 */ 910 if (pat->matches[0].rm_eo == 0) { 911 if (*wp) { 912 MAYBE_ADD_SPACE(); 913 Buf_AddChar(buf, *wp++); 914 } else 915 break; 916 } 917 goto tryagain; 918 } 919 if (*wp) { 920 MAYBE_ADD_SPACE(); 921 Buf_AddString(buf, wp); 922 } 923 break; 924 default: 925 VarREError(xrv, &pat->re, "Unexpected regex error"); 926 /* FALLTHROUGH */ 927 case REG_NOMATCH: 928 if (*wp) { 929 MAYBE_ADD_SPACE(); 930 Buf_AddString(buf, wp); 931 } 932 break; 933 } 934 return addSpace||added; 935 } 936 937 /*- 938 *----------------------------------------------------------------------- 939 * VarModify -- 940 * Modify each of the words of the passed string using the given 941 * function. Used to implement all modifiers. 942 * 943 * Results: 944 * A string of all the words modified appropriately. 945 *----------------------------------------------------------------------- 946 */ 947 static char * 948 VarModify(char *str, /* String whose words should be trimmed */ 949 /* Function to use to modify them */ 950 bool (*modProc)(struct Name *, bool, Buffer, void *), 951 void *datum) /* Datum to pass it */ 952 { 953 BUFFER buf; /* Buffer for the new string */ 954 bool addSpace; /* true if need to add a space to the 955 * buffer before adding the trimmed 956 * word */ 957 struct Name word; 958 959 Buf_Init(&buf, 0); 960 addSpace = false; 961 962 word.e = str; 963 964 while ((word.s = iterate_words(&word.e)) != NULL) { 965 char termc; 966 967 termc = *word.e; 968 *((char *)(word.e)) = '\0'; 969 addSpace = (*modProc)(&word, addSpace, &buf, datum); 970 *((char *)(word.e)) = termc; 971 } 972 return Buf_Retrieve(&buf); 973 } 974 975 /*- 976 *----------------------------------------------------------------------- 977 * VarGetPattern -- 978 * Pass through the tstr looking for 1) escaped delimiters, 979 * '$'s and backslashes (place the escaped character in 980 * uninterpreted) and 2) unescaped $'s that aren't before 981 * the delimiter (expand the variable substitution). 982 * Return the expanded string or NULL if the delimiter was missing 983 * If pattern is specified, handle escaped ampersands, and replace 984 * unescaped ampersands with the lhs of the pattern. 985 * 986 * Results: 987 * A string of all the words modified appropriately. 988 * If length is specified, return the string length of the buffer 989 *----------------------------------------------------------------------- 990 */ 991 static char * 992 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, 993 int delim2, size_t *length, VarPattern *pattern) 994 { 995 const char *cp; 996 char *result; 997 BUFFER buf; 998 size_t junk; 999 1000 Buf_Init(&buf, 0); 1001 if (length == NULL) 1002 length = &junk; 1003 1004 #define IS_A_MATCH(cp, delim1, delim2) \ 1005 (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ 1006 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) 1007 1008 /* 1009 * Skim through until the matching delimiter is found; 1010 * pick up variable substitutions on the way. Also allow 1011 * backslashes to quote the delimiter, $, and \, but don't 1012 * touch other backslashes. 1013 */ 1014 for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { 1015 if (IS_A_MATCH(cp, delim1, delim2)) { 1016 Buf_AddChar(&buf, cp[1]); 1017 cp++; 1018 } else if (*cp == '$') { 1019 /* Allowed at end of pattern */ 1020 if (cp[1] == delim1 || cp[1] == delim2) 1021 Buf_AddChar(&buf, *cp); 1022 else { 1023 size_t len; 1024 1025 /* If unescaped dollar sign not before the 1026 * delimiter, assume it's a variable 1027 * substitution and recurse. */ 1028 (void)Var_ParseBuffer(&buf, cp, ctxt, err, 1029 &len); 1030 cp += len - 1; 1031 } 1032 } else if (pattern && *cp == '&') 1033 Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); 1034 else 1035 Buf_AddChar(&buf, *cp); 1036 } 1037 1038 *length = Buf_Size(&buf); 1039 result = Buf_Retrieve(&buf); 1040 1041 if (*cp != delim1 && *cp != delim2) { 1042 *tstr = cp; 1043 *length = 0; 1044 free(result); 1045 return NULL; 1046 } 1047 else { 1048 *tstr = ++cp; 1049 return result; 1050 } 1051 } 1052 1053 /*- 1054 *----------------------------------------------------------------------- 1055 * VarQuote -- 1056 * Quote shell meta-characters in the string 1057 * 1058 * Results: 1059 * The quoted string 1060 *----------------------------------------------------------------------- 1061 */ 1062 static char * 1063 VarQuote(const char *str, const struct Name *n UNUSED, void *islistp) 1064 { 1065 int *p = islistp; 1066 int islist = *p; 1067 1068 BUFFER buf; 1069 /* This should cover most shells :-( */ 1070 static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1071 char *rep = meta; 1072 if (islist) 1073 rep += 3; 1074 1075 Buf_Init(&buf, MAKE_BSIZE); 1076 for (; *str; str++) { 1077 if (strchr(rep, *str) != NULL) 1078 Buf_AddChar(&buf, '\\'); 1079 Buf_AddChar(&buf, *str); 1080 } 1081 return Buf_Retrieve(&buf); 1082 } 1083 1084 static void * 1085 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1086 { 1087 dummy_arg->s = NULL; 1088 if ((*p)[1] == endc || (*p)[1] == ':') { 1089 (*p)++; 1090 return dummy_arg; 1091 } else 1092 return NULL; 1093 } 1094 1095 static void * 1096 check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1097 { 1098 int *qargs = emalloc(sizeof(int)); 1099 *qargs = 0; 1100 if ((*p)[1] == 'L') { 1101 *qargs = 1; 1102 (*p)++; 1103 } 1104 if ((*p)[1] == endc || (*p)[1] == ':') { 1105 (*p)++; 1106 return qargs; 1107 } else { 1108 free(qargs); 1109 return NULL; 1110 } 1111 } 1112 1113 static void * 1114 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1115 { 1116 if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { 1117 (*p)+=2; 1118 return dummy_arg; 1119 } else 1120 return NULL; 1121 } 1122 1123 1124 static char * 1125 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1126 { 1127 char *err; 1128 char *t; 1129 1130 t = Cmd_Exec(s, &err); 1131 if (err) 1132 Error(err, s); 1133 return t; 1134 } 1135 1136 static void * 1137 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1138 { 1139 const char *cp; 1140 char *s; 1141 1142 for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { 1143 if (*cp == '\\') { 1144 if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') 1145 cp++; 1146 } else if (*cp == '\0') 1147 return NULL; 1148 } 1149 s = escape_dupi(*p+1, cp, ":)}"); 1150 *p = cp; 1151 return s; 1152 } 1153 1154 static void 1155 free_stringarg(void *arg) 1156 { 1157 free(arg); 1158 } 1159 1160 static char * 1161 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1162 { 1163 size_t len, i; 1164 char *t; 1165 1166 len = strlen(s); 1167 t = emalloc(len+1); 1168 for (i = 0; i < len; i++) 1169 t[i] = TOUPPER(s[i]); 1170 t[len] = '\0'; 1171 return t; 1172 } 1173 1174 static char * 1175 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1176 { 1177 size_t len, i; 1178 char *t; 1179 1180 len = strlen(s); 1181 t = emalloc(len+1); 1182 for (i = 0; i < len; i++) 1183 t[i] = TOLOWER(s[i]); 1184 t[len] = '\0'; 1185 return t; 1186 } 1187 1188 static void * 1189 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) 1190 { 1191 return common_get_patternarg(p, ctxt, err, endc, false); 1192 } 1193 1194 /* Extract anchors */ 1195 static void * 1196 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) 1197 { 1198 VarPattern *pattern; 1199 1200 pattern = common_get_patternarg(p, ctxt, err, endc, true); 1201 if (pattern != NULL && pattern->leftLen > 0) { 1202 if (pattern->lhs[pattern->leftLen-1] == '$') { 1203 pattern->leftLen--; 1204 pattern->flags |= VAR_MATCH_END; 1205 } 1206 if (pattern->lhs[0] == '^') { 1207 pattern->lhs++; 1208 pattern->leftLen--; 1209 pattern->flags |= VAR_MATCH_START; 1210 } 1211 } 1212 return pattern; 1213 } 1214 1215 static void 1216 free_looparg(void *arg) 1217 { 1218 struct LoopStuff *l = arg; 1219 1220 Var_DeleteLoopVar(l->var); 1221 free(l->expand); 1222 } 1223 1224 static char * 1225 LoopGrab(const char **s) 1226 { 1227 const char *p, *start; 1228 1229 start = *s; 1230 for (p = start; *p != '@'; p++) { 1231 if (*p == '\\') 1232 p++; 1233 if (*p == 0) 1234 return NULL; 1235 } 1236 *s = p+1; 1237 return escape_dupi(start, p, "@\\"); 1238 } 1239 1240 static void * 1241 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) 1242 { 1243 static struct LoopStuff loop; 1244 const char *s; 1245 const char *var; 1246 1247 s = *p +1; 1248 1249 loop.var = NULL; 1250 loop.expand = NULL; 1251 loop.err = err; 1252 var = LoopGrab(&s); 1253 if (var != NULL) { 1254 loop.expand = LoopGrab(&s); 1255 if (*s == endc || *s == ':') { 1256 *p = s; 1257 loop.var = Var_NewLoopVar(var, NULL); 1258 return &loop; 1259 } 1260 } 1261 free_looparg(&loop); 1262 return NULL; 1263 } 1264 1265 static void * 1266 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, 1267 bool dosubst) 1268 { 1269 VarPattern *pattern; 1270 char delim; 1271 const char *s; 1272 1273 pattern = emalloc(sizeof(VarPattern)); 1274 pattern->flags = 0; 1275 s = *p; 1276 1277 delim = s[1]; 1278 if (delim == '\0') 1279 return NULL; 1280 s += 2; 1281 1282 pattern->rhs = NULL; 1283 pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, 1284 &pattern->leftLen, NULL); 1285 pattern->lbuffer = pattern->lhs; 1286 if (pattern->lhs != NULL) { 1287 pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, 1288 &pattern->rightLen, dosubst ? pattern: NULL); 1289 if (pattern->rhs != NULL) { 1290 /* Check for global substitution. If 'g' after the 1291 * final delimiter, substitution is global and is 1292 * marked that way. */ 1293 for (;; s++) { 1294 switch (*s) { 1295 case 'g': 1296 pattern->flags |= VAR_SUB_GLOBAL; 1297 continue; 1298 case '1': 1299 pattern->flags |= VAR_SUB_ONE; 1300 continue; 1301 } 1302 break; 1303 } 1304 if (*s == endc || *s == ':') { 1305 *p = s; 1306 return pattern; 1307 } 1308 } 1309 } 1310 free_patternarg(pattern); 1311 return NULL; 1312 } 1313 1314 static void * 1315 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) 1316 { 1317 const char *s; 1318 int flags; 1319 VarPattern *arg; 1320 1321 s = *p + 1; 1322 if (s[0] == '=') 1323 flags = VAR_EQUAL; 1324 else if (s[0] == '?' && s[1] == '=') 1325 flags = VAR_MAY_EQUAL; 1326 else if (s[0] == '+' && s[1] == '=') 1327 flags = VAR_ADD_EQUAL; 1328 else if (s[0] == '!' && s[1] == '=') 1329 flags = VAR_BANG_EQUAL; 1330 else 1331 return NULL; 1332 1333 arg = get_value(&s, ctxt, err, endc); 1334 if (arg != NULL) { 1335 *p = s; 1336 arg->flags = flags; 1337 } 1338 return arg; 1339 } 1340 1341 static void * 1342 get_value(const char **p, SymTable *ctxt, bool err, int endc) 1343 { 1344 VarPattern *pattern; 1345 const char *s; 1346 1347 pattern = emalloc(sizeof(VarPattern)); 1348 s = *p + 1; 1349 pattern->rhs = NULL; 1350 pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, 1351 &pattern->leftLen, NULL); 1352 if (s[-1] == endc || s[-1] == ':') { 1353 *p = s-1; 1354 return pattern; 1355 } 1356 free_patternarg(pattern); 1357 return NULL; 1358 } 1359 1360 static void * 1361 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) 1362 { 1363 VarPattern *pattern; 1364 const char *s; 1365 1366 pattern = emalloc(sizeof(VarPattern)); 1367 s = *p + 1; 1368 pattern->rhs = NULL; 1369 pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', 1370 &pattern->leftLen, NULL); 1371 if (s[-1] == '!') { 1372 *p = s-1; 1373 return pattern; 1374 } 1375 free_patternarg(pattern); 1376 return NULL; 1377 } 1378 1379 static void 1380 free_patternarg(void *p) 1381 { 1382 VarPattern *vp = p; 1383 1384 free(vp->lbuffer); 1385 free(vp->rhs); 1386 free(vp); 1387 } 1388 1389 static char * 1390 do_regex(const char *s, const struct Name *n UNUSED, void *arg) 1391 { 1392 VarREPattern p2; 1393 VarPattern *p = arg; 1394 int error; 1395 char *result; 1396 1397 error = regcomp(&p2.re, p->lhs, REG_EXTENDED); 1398 if (error) { 1399 VarREError(error, &p2.re, "RE substitution error"); 1400 return var_Error; 1401 } 1402 p2.nsub = p2.re.re_nsub + 1; 1403 p2.replace = p->rhs; 1404 p2.flags = p->flags; 1405 if (p2.nsub < 1) 1406 p2.nsub = 1; 1407 if (p2.nsub > 10) 1408 p2.nsub = 10; 1409 p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t)); 1410 result = VarModify((char *)s, VarRESubstitute, &p2); 1411 regfree(&p2.re); 1412 free(p2.matches); 1413 return result; 1414 } 1415 1416 char * 1417 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, 1418 bool err, bool *freePtr, const char **pscan, int paren) 1419 { 1420 const char *tstr; 1421 bool atstart; /* Some ODE modifiers only make sense at start */ 1422 char endc = paren == '(' ? ')' : '}'; 1423 const char *start = *pscan; 1424 1425 tstr = start; 1426 /* 1427 * Now we need to apply any modifiers the user wants applied. 1428 * These are: 1429 * :M<pattern> words which match the given <pattern>. 1430 * <pattern> is of the standard file 1431 * wildcarding form. 1432 * :S<d><pat1><d><pat2><d>[g] 1433 * Substitute <pat2> for <pat1> in the 1434 * value 1435 * :C<d><pat1><d><pat2><d>[g] 1436 * Substitute <pat2> for regex <pat1> in 1437 * the value 1438 * :H Substitute the head of each word 1439 * :T Substitute the tail of each word 1440 * :E Substitute the extension (minus '.') of 1441 * each word 1442 * :R Substitute the root of each word 1443 * (pathname minus the suffix). 1444 * :lhs=rhs Like :S, but the rhs goes to the end of 1445 * the invocation. 1446 */ 1447 1448 atstart = true; 1449 while (*tstr != endc && *tstr != '\0') { 1450 struct modifier *mod; 1451 void *arg; 1452 char *newStr; 1453 1454 tstr++; 1455 if (DEBUG(VAR)) 1456 printf("Applying :%c to \"%s\"\n", *tstr, str); 1457 1458 mod = choose_mod[(unsigned char)*tstr]; 1459 arg = NULL; 1460 1461 if (mod != NULL && (!mod->atstart || atstart)) 1462 arg = mod->getarg(&tstr, ctxt, err, endc); 1463 if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { 1464 mod = &sysv_mod; 1465 arg = mod->getarg(&tstr, ctxt, err, endc); 1466 } 1467 atstart = false; 1468 if (arg != NULL) { 1469 if (str != NULL || (mod->atstart && name != NULL)) { 1470 if (mod->word_apply != NULL) { 1471 newStr = VarModify(str, 1472 mod->word_apply, arg); 1473 if (mod->apply != NULL) { 1474 char *newStr2; 1475 1476 newStr2 = mod->apply(newStr, 1477 name, arg); 1478 free(newStr); 1479 newStr = newStr2; 1480 } 1481 } else 1482 newStr = mod->apply(str, name, arg); 1483 if (*freePtr) 1484 free(str); 1485 str = newStr; 1486 if (str != var_Error) 1487 *freePtr = true; 1488 else 1489 *freePtr = false; 1490 } 1491 if (mod->freearg != NULL) 1492 mod->freearg(arg); 1493 } else { 1494 Error("Bad modifier: %s", tstr); 1495 /* Try skipping to end of var... */ 1496 for (tstr++; *tstr != endc && *tstr != '\0';) 1497 tstr++; 1498 if (str != NULL && *freePtr) 1499 free(str); 1500 str = var_Error; 1501 *freePtr = false; 1502 break; 1503 } 1504 if (DEBUG(VAR)) 1505 printf("Result is \"%s\"\n", str); 1506 } 1507 if (*tstr == '\0') 1508 Parse_Error(PARSE_FATAL, "Unclosed variable specification"); 1509 else 1510 tstr++; 1511 1512 *pscan = tstr; 1513 return str; 1514 } 1515 1516 char * 1517 Var_GetHead(char *s) 1518 { 1519 return VarModify(s, VarHead, NULL); 1520 } 1521 1522 char * 1523 Var_GetTail(char *s) 1524 { 1525 return VarModify(s, VarTail, NULL); 1526 } 1527