1 /* $OpenPackages$ */ 2 /* $OpenBSD: varmodifiers.c,v 1.25 2007/11/03 15:42:11 deraadt Exp $ */ 3 /* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ 4 5 /* 6 * Copyright (c) 1999 Marc Espie. 7 * 8 * Extensive code changes for the OpenBSD project. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 23 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 /* 32 * Copyright (c) 1988, 1989, 1990, 1993 33 * The Regents of the University of California. All rights reserved. 34 * Copyright (c) 1989 by Berkeley Softworks 35 * All rights reserved. 36 * 37 * This code is derived from software contributed to Berkeley by 38 * Adam de Boor. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 /* VarModifiers_Apply is mostly a constituent function of Var_Parse, it 66 * is also called directly by Var_SubstVar. */ 67 68 69 #include <ctype.h> 70 #include <sys/types.h> 71 #ifndef MAKE_BOOTSTRAP 72 #include <regex.h> 73 #endif 74 #include <stddef.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <string.h> 78 #include "config.h" 79 #include "defines.h" 80 #include "buf.h" 81 #include "var.h" 82 #include "varmodifiers.h" 83 #include "varname.h" 84 #include "targ.h" 85 #include "error.h" 86 #include "str.h" 87 #include "cmd_exec.h" 88 #include "memory.h" 89 #include "gnode.h" 90 91 92 /* Var*Pattern flags */ 93 #define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ 94 #define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ 95 #define VAR_SUB_MATCHED 0x04 /* There was a match */ 96 #define VAR_MATCH_START 0x08 /* Match at start of word */ 97 #define VAR_MATCH_END 0x10 /* Match at end of word */ 98 99 /* Modifiers flags */ 100 #define VAR_EQUAL 0x20 101 #define VAR_MAY_EQUAL 0x40 102 #define VAR_ADD_EQUAL 0x80 103 #define VAR_BANG_EQUAL 0x100 104 105 typedef struct { 106 char *lbuffer; /* left string to free */ 107 char *lhs; /* String to match */ 108 size_t leftLen; /* Length of string */ 109 char *rhs; /* Replacement string (w/ &'s removed) */ 110 size_t rightLen; /* Length of replacement */ 111 int flags; 112 } VarPattern; 113 114 struct LoopStuff { 115 struct LoopVar *var; 116 char *expand; 117 bool err; 118 }; 119 120 static bool VarHead(struct Name *, bool, Buffer, void *); 121 static bool VarTail(struct Name *, bool, Buffer, void *); 122 static bool VarSuffix(struct Name *, bool, Buffer, void *); 123 static bool VarRoot(struct Name *, bool, Buffer, void *); 124 static bool VarMatch(struct Name *, bool, Buffer, void *); 125 static bool VarSYSVMatch(struct Name *, bool, Buffer, void *); 126 static bool VarNoMatch(struct Name *, bool, Buffer, void *); 127 static bool VarUniq(struct Name *, bool, Buffer, void *); 128 static bool VarLoop(struct Name *, bool, Buffer, void *); 129 130 131 #ifndef MAKE_BOOTSTRAP 132 static void VarREError(int, regex_t *, const char *); 133 static bool VarRESubstitute(struct Name *, bool, Buffer, void *); 134 static char *do_regex(const char *, const struct Name *, void *); 135 136 typedef struct { 137 regex_t re; 138 int nsub; 139 regmatch_t *matches; 140 char *replace; 141 int flags; 142 } VarREPattern; 143 #endif 144 145 static bool VarSubstitute(struct Name *, bool, Buffer, void *); 146 static char *VarGetPattern(SymTable *, int, const char **, int, int, 147 size_t *, VarPattern *); 148 static char *VarQuote(const char *, const struct Name *, void *); 149 static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *); 150 151 static void *check_empty(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_empty, VarQuote, NULL , NULL}, 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 UNUSED, 611 int endc) 612 { 613 VarPattern *pattern; 614 const char *cp, *cp2; 615 int cnt = 0; 616 char startc = endc == ')' ? '(' : '{'; 617 618 for (cp = *p;; cp++) { 619 if (*cp == '=' && cnt == 0) 620 break; 621 if (*cp == '\0') 622 return NULL; 623 if (*cp == startc) 624 cnt++; 625 else if (*cp == endc) { 626 cnt--; 627 if (cnt < 0) 628 return NULL; 629 } 630 } 631 for (cp2 = cp+1;; cp2++) { 632 if ((*cp2 == ':' || *cp2 == endc) && cnt == 0) 633 break; 634 if (*cp2 == '\0') 635 return NULL; 636 if (*cp2 == startc) 637 cnt++; 638 else if (*cp2 == endc) { 639 cnt--; 640 if (cnt < 0) 641 return NULL; 642 } 643 } 644 645 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 646 pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp); 647 pattern->leftLen = cp - *p; 648 pattern->rhs = Str_dupi(cp+1, cp2); 649 pattern->rightLen = cp2 - (cp+1); 650 pattern->flags = 0; 651 *p = cp2; 652 return pattern; 653 } 654 655 656 /*- 657 *----------------------------------------------------------------------- 658 * VarSubstitute -- 659 * Perform a string-substitution on the given word, Adding the 660 * result to the given buffer. 661 *----------------------------------------------------------------------- 662 */ 663 static bool 664 VarSubstitute(struct Name *word, bool addSpace, Buffer buf, 665 void *patternp) /* Pattern for substitution */ 666 { 667 size_t wordLen; /* Length of word */ 668 const char *cp; /* General pointer */ 669 VarPattern *pattern = (VarPattern *)patternp; 670 671 wordLen = word->e - word->s; 672 if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != 673 (VAR_SUB_ONE|VAR_SUB_MATCHED)) { 674 /* Still substituting -- break it down into simple anchored cases 675 * and if none of them fits, perform the general substitution case. */ 676 if ((pattern->flags & VAR_MATCH_START) && 677 (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) { 678 /* Anchored at start and beginning of word matches pattern. */ 679 if ((pattern->flags & VAR_MATCH_END) && 680 (wordLen == pattern->leftLen)) { 681 /* Also anchored at end and matches to the end (word 682 * is same length as pattern) add space and rhs only 683 * if rhs is non-null. */ 684 if (pattern->rightLen != 0) { 685 if (addSpace) 686 Buf_AddSpace(buf); 687 addSpace = true; 688 Buf_AddChars(buf, pattern->rightLen, 689 pattern->rhs); 690 } 691 pattern->flags |= VAR_SUB_MATCHED; 692 } else if (pattern->flags & VAR_MATCH_END) { 693 /* Doesn't match to end -- copy word wholesale. */ 694 goto nosub; 695 } else { 696 /* Matches at start but need to copy in 697 * trailing characters. */ 698 if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ 699 if (addSpace) 700 Buf_AddSpace(buf); 701 addSpace = true; 702 } 703 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 704 Buf_AddChars(buf, wordLen - pattern->leftLen, 705 word->s + pattern->leftLen); 706 pattern->flags |= VAR_SUB_MATCHED; 707 } 708 } else if (pattern->flags & VAR_MATCH_START) { 709 /* Had to match at start of word and didn't -- copy whole word. */ 710 goto nosub; 711 } else if (pattern->flags & VAR_MATCH_END) { 712 /* Anchored at end, Find only place match could occur (leftLen 713 * characters from the end of the word) and see if it does. Note 714 * that because the $ will be left at the end of the lhs, we have 715 * to use strncmp. */ 716 cp = word->s + (wordLen - pattern->leftLen); 717 if (cp >= word->s && 718 strncmp(cp, pattern->lhs, pattern->leftLen) == 0) { 719 /* Match found. If we will place characters in the buffer, 720 * add a space before hand as indicated by addSpace, then 721 * stuff in the initial, unmatched part of the word followed 722 * by the right-hand-side. */ 723 if (((cp - word->s) + pattern->rightLen) != 0) { 724 if (addSpace) 725 Buf_AddSpace(buf); 726 addSpace = true; 727 } 728 Buf_Addi(buf, word->s, cp); 729 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 730 pattern->flags |= VAR_SUB_MATCHED; 731 } else { 732 /* Had to match at end and didn't. Copy entire word. */ 733 goto nosub; 734 } 735 } else { 736 /* Pattern is unanchored: search for the pattern in the word using 737 * strstr, copying unmatched portions and the 738 * right-hand-side for each match found, handling non-global 739 * substitutions correctly, etc. When the loop is done, any 740 * remaining part of the word (word and wordLen are adjusted 741 * accordingly through the loop) is copied straight into the 742 * buffer. 743 * addSpace is set to false as soon as a space is added to the 744 * buffer. */ 745 bool done; 746 size_t origSize; 747 748 done = false; 749 origSize = Buf_Size(buf); 750 while (!done) { 751 cp = strstr(word->s, pattern->lhs); 752 if (cp != NULL) { 753 if (addSpace && (cp - word->s) + pattern->rightLen != 0){ 754 Buf_AddSpace(buf); 755 addSpace = false; 756 } 757 Buf_Addi(buf, word->s, cp); 758 Buf_AddChars(buf, pattern->rightLen, pattern->rhs); 759 wordLen -= (cp - word->s) + pattern->leftLen; 760 word->s = cp + pattern->leftLen; 761 if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0) 762 done = true; 763 pattern->flags |= VAR_SUB_MATCHED; 764 } else 765 done = true; 766 } 767 if (wordLen != 0) { 768 if (addSpace) 769 Buf_AddSpace(buf); 770 Buf_AddChars(buf, wordLen, word->s); 771 } 772 /* If added characters to the buffer, need to add a space 773 * before we add any more. If we didn't add any, just return 774 * the previous value of addSpace. */ 775 return Buf_Size(buf) != origSize || addSpace; 776 } 777 return addSpace; 778 } 779 nosub: 780 if (addSpace) 781 Buf_AddSpace(buf); 782 Buf_AddChars(buf, wordLen, word->s); 783 return true; 784 } 785 786 #ifndef MAKE_BOOTSTRAP 787 /*- 788 *----------------------------------------------------------------------- 789 * VarREError -- 790 * Print the error caused by a regcomp or regexec call. 791 *----------------------------------------------------------------------- 792 */ 793 static void 794 VarREError(int err, regex_t *pat, const char *str) 795 { 796 char *errbuf; 797 int errlen; 798 799 errlen = regerror(err, pat, 0, 0); 800 errbuf = emalloc(errlen); 801 regerror(err, pat, errbuf, errlen); 802 Error("%s: %s", str, errbuf); 803 free(errbuf); 804 } 805 806 /*- 807 *----------------------------------------------------------------------- 808 * VarRESubstitute -- 809 * Perform a regex substitution on the given word, placing the 810 * result in the passed buffer. 811 *----------------------------------------------------------------------- 812 */ 813 static bool 814 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) 815 { 816 VarREPattern *pat; 817 int xrv; 818 const char *wp; 819 char *rp; 820 int added; 821 822 #define MAYBE_ADD_SPACE() \ 823 if (addSpace && !added) \ 824 Buf_AddSpace(buf); \ 825 added = 1 826 827 added = 0; 828 wp = word->s; 829 pat = patternp; 830 831 if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == 832 (VAR_SUB_ONE|VAR_SUB_MATCHED)) 833 xrv = REG_NOMATCH; 834 else { 835 tryagain: 836 xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); 837 } 838 839 switch (xrv) { 840 case 0: 841 pat->flags |= VAR_SUB_MATCHED; 842 if (pat->matches[0].rm_so > 0) { 843 MAYBE_ADD_SPACE(); 844 Buf_AddChars(buf, pat->matches[0].rm_so, wp); 845 } 846 847 for (rp = pat->replace; *rp; rp++) { 848 if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { 849 MAYBE_ADD_SPACE(); 850 Buf_AddChar(buf,rp[1]); 851 rp++; 852 } 853 else if (*rp == '&' || 854 (*rp == '\\' && isdigit(rp[1]))) { 855 int n; 856 const char *subbuf; 857 int sublen; 858 char errstr[3]; 859 860 if (*rp == '&') { 861 n = 0; 862 errstr[0] = '&'; 863 errstr[1] = '\0'; 864 } else { 865 n = rp[1] - '0'; 866 errstr[0] = '\\'; 867 errstr[1] = rp[1]; 868 errstr[2] = '\0'; 869 rp++; 870 } 871 872 if (n > pat->nsub) { 873 Error("No subexpression %s", 874 &errstr[0]); 875 subbuf = ""; 876 sublen = 0; 877 } else if (pat->matches[n].rm_so == -1 && 878 pat->matches[n].rm_eo == -1) { 879 Error("No match for subexpression %s", 880 &errstr[0]); 881 subbuf = ""; 882 sublen = 0; 883 } else { 884 subbuf = wp + pat->matches[n].rm_so; 885 sublen = pat->matches[n].rm_eo - 886 pat->matches[n].rm_so; 887 } 888 889 if (sublen > 0) { 890 MAYBE_ADD_SPACE(); 891 Buf_AddChars(buf, sublen, subbuf); 892 } 893 } else { 894 MAYBE_ADD_SPACE(); 895 Buf_AddChar(buf, *rp); 896 } 897 } 898 wp += pat->matches[0].rm_eo; 899 if (pat->flags & VAR_SUB_GLOBAL) 900 goto tryagain; 901 if (*wp) { 902 MAYBE_ADD_SPACE(); 903 Buf_AddString(buf, wp); 904 } 905 break; 906 default: 907 VarREError(xrv, &pat->re, "Unexpected regex error"); 908 /* FALLTHROUGH */ 909 case REG_NOMATCH: 910 if (*wp) { 911 MAYBE_ADD_SPACE(); 912 Buf_AddString(buf, wp); 913 } 914 break; 915 } 916 return addSpace||added; 917 } 918 #endif 919 920 /*- 921 *----------------------------------------------------------------------- 922 * VarModify -- 923 * Modify each of the words of the passed string using the given 924 * function. Used to implement all modifiers. 925 * 926 * Results: 927 * A string of all the words modified appropriately. 928 *----------------------------------------------------------------------- 929 */ 930 static char * 931 VarModify(char *str, /* String whose words should be trimmed */ 932 /* Function to use to modify them */ 933 bool (*modProc)(struct Name *, bool, Buffer, void *), 934 void *datum) /* Datum to pass it */ 935 { 936 BUFFER buf; /* Buffer for the new string */ 937 bool addSpace; /* true if need to add a space to the 938 * buffer before adding the trimmed 939 * word */ 940 struct Name word; 941 942 Buf_Init(&buf, 0); 943 addSpace = false; 944 945 word.e = str; 946 947 while ((word.s = iterate_words(&word.e)) != NULL) { 948 char termc; 949 950 termc = *word.e; 951 *((char *)(word.e)) = '\0'; 952 addSpace = (*modProc)(&word, addSpace, &buf, datum); 953 *((char *)(word.e)) = termc; 954 } 955 return Buf_Retrieve(&buf); 956 } 957 958 /*- 959 *----------------------------------------------------------------------- 960 * VarGetPattern -- 961 * Pass through the tstr looking for 1) escaped delimiters, 962 * '$'s and backslashes (place the escaped character in 963 * uninterpreted) and 2) unescaped $'s that aren't before 964 * the delimiter (expand the variable substitution). 965 * Return the expanded string or NULL if the delimiter was missing 966 * If pattern is specified, handle escaped ampersands, and replace 967 * unescaped ampersands with the lhs of the pattern. 968 * 969 * Results: 970 * A string of all the words modified appropriately. 971 * If length is specified, return the string length of the buffer 972 *----------------------------------------------------------------------- 973 */ 974 static char * 975 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, 976 int delim2, size_t *length, VarPattern *pattern) 977 { 978 const char *cp; 979 char *result; 980 BUFFER buf; 981 size_t junk; 982 983 Buf_Init(&buf, 0); 984 if (length == NULL) 985 length = &junk; 986 987 #define IS_A_MATCH(cp, delim1, delim2) \ 988 (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ 989 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) 990 991 /* 992 * Skim through until the matching delimiter is found; 993 * pick up variable substitutions on the way. Also allow 994 * backslashes to quote the delimiter, $, and \, but don't 995 * touch other backslashes. 996 */ 997 for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { 998 if (IS_A_MATCH(cp, delim1, delim2)) { 999 Buf_AddChar(&buf, cp[1]); 1000 cp++; 1001 } else if (*cp == '$') { 1002 /* Allowed at end of pattern */ 1003 if (cp[1] == delim1 || cp[1] == delim2) 1004 Buf_AddChar(&buf, *cp); 1005 else { 1006 size_t len; 1007 1008 /* If unescaped dollar sign not before the 1009 * delimiter, assume it's a variable 1010 * substitution and recurse. */ 1011 (void)Var_ParseBuffer(&buf, cp, ctxt, err, 1012 &len); 1013 cp += len - 1; 1014 } 1015 } else if (pattern && *cp == '&') 1016 Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); 1017 else 1018 Buf_AddChar(&buf, *cp); 1019 } 1020 1021 *length = Buf_Size(&buf); 1022 result = Buf_Retrieve(&buf); 1023 1024 if (*cp != delim1 && *cp != delim2) { 1025 *tstr = cp; 1026 *length = 0; 1027 free(result); 1028 return NULL; 1029 } 1030 else { 1031 *tstr = ++cp; 1032 return result; 1033 } 1034 } 1035 1036 /*- 1037 *----------------------------------------------------------------------- 1038 * VarQuote -- 1039 * Quote shell meta-characters in the string 1040 * 1041 * Results: 1042 * The quoted string 1043 *----------------------------------------------------------------------- 1044 */ 1045 static char * 1046 VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED) 1047 { 1048 1049 BUFFER buf; 1050 /* This should cover most shells :-( */ 1051 static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; 1052 1053 Buf_Init(&buf, MAKE_BSIZE); 1054 for (; *str; str++) { 1055 if (strchr(meta, *str) != NULL) 1056 Buf_AddChar(&buf, '\\'); 1057 Buf_AddChar(&buf, *str); 1058 } 1059 return Buf_Retrieve(&buf); 1060 } 1061 1062 static void * 1063 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1064 { 1065 dummy_arg->s = NULL; 1066 if ((*p)[1] == endc || (*p)[1] == ':') { 1067 (*p)++; 1068 return dummy_arg; 1069 } else 1070 return NULL; 1071 } 1072 1073 static void * 1074 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1075 { 1076 if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { 1077 (*p)+=2; 1078 return dummy_arg; 1079 } else 1080 return NULL; 1081 } 1082 1083 1084 static char * 1085 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1086 { 1087 char *err; 1088 char *t; 1089 1090 t = Cmd_Exec(s, &err); 1091 if (err) 1092 Error(err, s); 1093 return t; 1094 } 1095 1096 static void * 1097 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) 1098 { 1099 const char *cp; 1100 char *s; 1101 1102 for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { 1103 if (*cp == '\\') { 1104 if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') 1105 cp++; 1106 } else if (*cp == '\0') 1107 return NULL; 1108 } 1109 s = escape_dupi(*p+1, cp, ":)}"); 1110 *p = cp; 1111 return s; 1112 } 1113 1114 static void 1115 free_stringarg(void *arg) 1116 { 1117 free(arg); 1118 } 1119 1120 static char * 1121 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1122 { 1123 size_t len, i; 1124 char *t; 1125 1126 len = strlen(s); 1127 t = emalloc(len+1); 1128 for (i = 0; i < len; i++) 1129 t[i] = toupper(s[i]); 1130 t[len] = '\0'; 1131 return t; 1132 } 1133 1134 static char * 1135 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) 1136 { 1137 size_t len, i; 1138 char *t; 1139 1140 len = strlen(s); 1141 t = emalloc(len+1); 1142 for (i = 0; i < len; i++) 1143 t[i] = tolower(s[i]); 1144 t[len] = '\0'; 1145 return t; 1146 } 1147 1148 static void * 1149 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) 1150 { 1151 return common_get_patternarg(p, ctxt, err, endc, false); 1152 } 1153 1154 /* Extract anchors */ 1155 static void * 1156 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) 1157 { 1158 VarPattern *pattern; 1159 1160 pattern = common_get_patternarg(p, ctxt, err, endc, true); 1161 if (pattern != NULL && pattern->leftLen > 0) { 1162 if (pattern->lhs[pattern->leftLen-1] == '$') { 1163 pattern->leftLen--; 1164 pattern->flags |= VAR_MATCH_END; 1165 } 1166 if (pattern->lhs[0] == '^') { 1167 pattern->lhs++; 1168 pattern->leftLen--; 1169 pattern->flags |= VAR_MATCH_START; 1170 } 1171 } 1172 return pattern; 1173 } 1174 1175 static void 1176 free_looparg(void *arg) 1177 { 1178 struct LoopStuff *l = (struct LoopStuff *)arg; 1179 1180 Var_DeleteLoopVar(l->var); 1181 free(l->expand); 1182 } 1183 1184 static char * 1185 LoopGrab(const char **s) 1186 { 1187 const char *p, *start; 1188 1189 start = *s; 1190 for (p = start; *p != '@'; p++) { 1191 if (*p == '\\') 1192 p++; 1193 if (*p == 0) 1194 return NULL; 1195 } 1196 *s = p+1; 1197 return escape_dupi(start, p, "@\\"); 1198 } 1199 1200 static void * 1201 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) 1202 { 1203 static struct LoopStuff loop; 1204 const char *s; 1205 const char *var; 1206 1207 s = *p +1; 1208 1209 loop.var = NULL; 1210 loop.expand = NULL; 1211 loop.err = err; 1212 var = LoopGrab(&s); 1213 if (var != NULL) { 1214 loop.expand = LoopGrab(&s); 1215 if (*s == endc || *s == ':') { 1216 *p = s; 1217 loop.var = Var_NewLoopVar(var, NULL); 1218 return &loop; 1219 } 1220 } 1221 free_looparg(&loop); 1222 return NULL; 1223 } 1224 1225 static void * 1226 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, 1227 bool dosubst) 1228 { 1229 VarPattern *pattern; 1230 char delim; 1231 const char *s; 1232 1233 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1234 pattern->flags = 0; 1235 s = *p; 1236 1237 delim = s[1]; 1238 if (delim == '\0') 1239 return NULL; 1240 s += 2; 1241 1242 pattern->rhs = NULL; 1243 pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, 1244 &pattern->leftLen, NULL); 1245 pattern->lbuffer = pattern->lhs; 1246 if (pattern->lhs != NULL) { 1247 pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, 1248 &pattern->rightLen, dosubst ? pattern: NULL); 1249 if (pattern->rhs != NULL) { 1250 /* Check for global substitution. If 'g' after the 1251 * final delimiter, substitution is global and is 1252 * marked that way. */ 1253 for (;; s++) { 1254 switch (*s) { 1255 case 'g': 1256 pattern->flags |= VAR_SUB_GLOBAL; 1257 continue; 1258 case '1': 1259 pattern->flags |= VAR_SUB_ONE; 1260 continue; 1261 } 1262 break; 1263 } 1264 if (*s == endc || *s == ':') { 1265 *p = s; 1266 return pattern; 1267 } 1268 } 1269 } 1270 free_patternarg(pattern); 1271 return NULL; 1272 } 1273 1274 static void * 1275 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) 1276 { 1277 const char *s; 1278 int flags; 1279 VarPattern *arg; 1280 1281 s = *p + 1; 1282 if (s[0] == '=') 1283 flags = VAR_EQUAL; 1284 else if (s[0] == '?' && s[1] == '=') 1285 flags = VAR_MAY_EQUAL; 1286 else if (s[0] == '+' && s[1] == '=') 1287 flags = VAR_ADD_EQUAL; 1288 else if (s[0] == '!' && s[1] == '=') 1289 flags = VAR_BANG_EQUAL; 1290 else 1291 return NULL; 1292 1293 arg = get_value(&s, ctxt, err, endc); 1294 if (arg != NULL) { 1295 *p = s; 1296 arg->flags = flags; 1297 } 1298 return arg; 1299 } 1300 1301 static void * 1302 get_value(const char **p, SymTable *ctxt, bool err, int endc) 1303 { 1304 VarPattern *pattern; 1305 const char *s; 1306 1307 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1308 s = *p + 1; 1309 pattern->rhs = NULL; 1310 pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, 1311 &pattern->leftLen, NULL); 1312 if (s[-1] == endc || s[-1] == ':') { 1313 *p = s-1; 1314 return pattern; 1315 } 1316 free_patternarg(pattern); 1317 return NULL; 1318 } 1319 1320 static void * 1321 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) 1322 { 1323 VarPattern *pattern; 1324 const char *s; 1325 1326 pattern = (VarPattern *)emalloc(sizeof(VarPattern)); 1327 s = *p + 1; 1328 pattern->rhs = NULL; 1329 pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', 1330 &pattern->leftLen, NULL); 1331 if (s[-1] == '!') { 1332 *p = s-1; 1333 return pattern; 1334 } 1335 free_patternarg(pattern); 1336 return NULL; 1337 } 1338 1339 static void 1340 free_patternarg(void *p) 1341 { 1342 VarPattern *vp = (VarPattern *)p; 1343 1344 free(vp->lbuffer); 1345 free(vp->rhs); 1346 free(vp); 1347 } 1348 1349 #ifndef MAKE_BOOTSTRAP 1350 static char * 1351 do_regex(const char *s, const struct Name *n UNUSED, void *arg) 1352 { 1353 VarREPattern p2; 1354 VarPattern *p = (VarPattern *)arg; 1355 int error; 1356 char *result; 1357 1358 error = regcomp(&p2.re, p->lhs, REG_EXTENDED); 1359 if (error) { 1360 VarREError(error, &p2.re, "RE substitution error"); 1361 return var_Error; 1362 } 1363 p2.nsub = p2.re.re_nsub + 1; 1364 p2.replace = p->rhs; 1365 p2.flags = p->flags; 1366 if (p2.nsub < 1) 1367 p2.nsub = 1; 1368 if (p2.nsub > 10) 1369 p2.nsub = 10; 1370 p2.matches = emalloc(p2.nsub * sizeof(regmatch_t)); 1371 result = VarModify((char *)s, VarRESubstitute, &p2); 1372 regfree(&p2.re); 1373 free(p2.matches); 1374 return result; 1375 } 1376 #endif 1377 1378 char * 1379 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, 1380 bool err, bool *freePtr, const char **pscan, int paren) 1381 { 1382 const char *tstr; 1383 bool atstart; /* Some ODE modifiers only make sense at start */ 1384 char endc = paren == '(' ? ')' : '}'; 1385 const char *start = *pscan; 1386 1387 tstr = start; 1388 /* 1389 * Now we need to apply any modifiers the user wants applied. 1390 * These are: 1391 * :M<pattern> words which match the given <pattern>. 1392 * <pattern> is of the standard file 1393 * wildcarding form. 1394 * :S<d><pat1><d><pat2><d>[g] 1395 * Substitute <pat2> for <pat1> in the 1396 * value 1397 * :C<d><pat1><d><pat2><d>[g] 1398 * Substitute <pat2> for regex <pat1> in 1399 * the value 1400 * :H Substitute the head of each word 1401 * :T Substitute the tail of each word 1402 * :E Substitute the extension (minus '.') of 1403 * each word 1404 * :R Substitute the root of each word 1405 * (pathname minus the suffix). 1406 * :lhs=rhs Like :S, but the rhs goes to the end of 1407 * the invocation. 1408 */ 1409 1410 atstart = true; 1411 while (*tstr != endc && *tstr != '\0') { 1412 struct modifier *mod; 1413 void *arg; 1414 char *newStr; 1415 1416 tstr++; 1417 if (DEBUG(VAR)) 1418 printf("Applying :%c to \"%s\"\n", *tstr, str); 1419 1420 mod = choose_mod[*tstr]; 1421 arg = NULL; 1422 1423 if (mod != NULL && (!mod->atstart || atstart)) 1424 arg = mod->getarg(&tstr, ctxt, err, endc); 1425 if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { 1426 mod = &sysv_mod; 1427 arg = mod->getarg(&tstr, ctxt, err, endc); 1428 } 1429 atstart = false; 1430 if (arg != NULL) { 1431 if (str != NULL || (mod->atstart && name != NULL)) { 1432 if (mod->word_apply != NULL) { 1433 newStr = VarModify(str, 1434 mod->word_apply, arg); 1435 if (mod->apply != NULL) { 1436 char *newStr2; 1437 1438 newStr2 = mod->apply(newStr, 1439 name, arg); 1440 free(newStr); 1441 newStr = newStr2; 1442 } 1443 } else 1444 newStr = mod->apply(str, name, arg); 1445 if (*freePtr) 1446 free(str); 1447 str = newStr; 1448 if (str != var_Error) 1449 *freePtr = true; 1450 else 1451 *freePtr = false; 1452 } 1453 if (mod->freearg != NULL) 1454 mod->freearg(arg); 1455 } else { 1456 Error("Bad modifier: %s\n", tstr); 1457 /* Try skipping to end of var... */ 1458 for (tstr++; *tstr != endc && *tstr != '\0';) 1459 tstr++; 1460 if (str != NULL && *freePtr) 1461 free(str); 1462 str = var_Error; 1463 *freePtr = false; 1464 break; 1465 } 1466 if (DEBUG(VAR)) 1467 printf("Result is \"%s\"\n", str); 1468 } 1469 if (*tstr == '\0') 1470 Error("Unclosed variable specification"); 1471 else 1472 tstr++; 1473 1474 *pscan = tstr; 1475 return str; 1476 } 1477 1478 char * 1479 Var_GetHead(char *s) 1480 { 1481 return VarModify(s, VarHead, NULL); 1482 } 1483 1484 char * 1485 Var_GetTail(char *s) 1486 { 1487 return VarModify(s, VarTail, NULL); 1488 } 1489