1 /* $NetBSD: compile.c,v 1.40 2014/05/05 17:12:11 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Diomidis Spinellis of Imperial College, University of London. 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 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /*- 36 * Copyright (c) 1992 Diomidis Spinellis. 37 * 38 * This code is derived from software contributed to Berkeley by 39 * Diomidis Spinellis of Imperial College, University of London. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. All advertising materials mentioning features or use of this software 50 * must display the following acknowledgement: 51 * This product includes software developed by the University of 52 * California, Berkeley and its contributors. 53 * 4. Neither the name of the University nor the names of its contributors 54 * may be used to endorse or promote products derived from this software 55 * without specific prior written permission. 56 * 57 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * SUCH DAMAGE. 68 */ 69 70 #if HAVE_NBTOOL_CONFIG_H 71 #include "nbtool_config.h" 72 #endif 73 74 #include <sys/cdefs.h> 75 #ifndef lint 76 #if 0 77 static char sccsid[] = "@(#)compile.c 8.2 (Berkeley) 4/28/95"; 78 #else 79 __RCSID("$NetBSD: compile.c,v 1.40 2014/05/05 17:12:11 christos Exp $"); 80 #endif 81 #endif /* not lint */ 82 83 #include <sys/types.h> 84 #include <sys/stat.h> 85 86 #include <ctype.h> 87 #include <errno.h> 88 #include <fcntl.h> 89 #include <limits.h> 90 #include <regex.h> 91 #include <stdio.h> 92 #include <stdlib.h> 93 #include <string.h> 94 95 #include "defs.h" 96 #include "extern.h" 97 98 #ifndef _POSIX2_LINE_MAX 99 #define _POSIX2_LINE_MAX (2 * BUFSIZ) 100 #endif 101 102 #define LHSZ 128 103 #define LHMASK (LHSZ - 1) 104 static struct labhash { 105 struct labhash *lh_next; 106 u_int lh_hash; 107 struct s_command *lh_cmd; 108 int lh_ref; 109 } *labels[LHSZ]; 110 111 static char *compile_addr(char *, struct s_addr *); 112 static char *compile_ccl(char **, char *); 113 static char *compile_delimited(char *, char *); 114 static char *compile_flags(char *, struct s_subst *); 115 static char *compile_re(char *, regex_t **); 116 static char *compile_subst(char *, struct s_subst *); 117 static char *compile_text(void); 118 static char *compile_tr(char *, char **); 119 static struct s_command 120 **compile_stream(struct s_command **); 121 static char *duptoeol(char *, const char *); 122 static void enterlabel(struct s_command *); 123 static struct s_command 124 *findlabel(char *); 125 static void fixuplabel(struct s_command *, struct s_command *); 126 static void uselabel(void); 127 128 /* 129 * Command specification. This is used to drive the command parser. 130 */ 131 struct s_format { 132 char code; /* Command code */ 133 int naddr; /* Number of address args */ 134 enum e_args args; /* Argument type */ 135 }; 136 137 static struct s_format cmd_fmts[] = { 138 {'{', 2, GROUP}, 139 {'}', 0, ENDGROUP}, 140 {'a', 1, TEXT}, 141 {'b', 2, BRANCH}, 142 {'c', 2, TEXT}, 143 {'d', 2, EMPTY}, 144 {'D', 2, EMPTY}, 145 {'g', 2, EMPTY}, 146 {'G', 2, EMPTY}, 147 {'h', 2, EMPTY}, 148 {'H', 2, EMPTY}, 149 {'i', 1, TEXT}, 150 {'l', 2, EMPTY}, 151 {'n', 2, EMPTY}, 152 {'N', 2, EMPTY}, 153 {'p', 2, EMPTY}, 154 {'P', 2, EMPTY}, 155 {'q', 1, EMPTY}, 156 {'r', 1, RFILE}, 157 {'s', 2, SUBST}, 158 {'t', 2, BRANCH}, 159 {'w', 2, WFILE}, 160 {'x', 2, EMPTY}, 161 {'y', 2, TR}, 162 {'!', 2, NONSEL}, 163 {':', 0, LABEL}, 164 {'#', 0, COMMENT}, 165 {'=', 1, EMPTY}, 166 {'\0', 0, COMMENT}, 167 }; 168 169 /* The compiled program. */ 170 struct s_command *prog; 171 172 /* 173 * Compile the program into prog. 174 * Initialise appends. 175 */ 176 void 177 compile(void) 178 { 179 *compile_stream(&prog) = NULL; 180 fixuplabel(prog, NULL); 181 uselabel(); 182 if (appendnum > 0) 183 appends = xmalloc(sizeof(struct s_appends) * appendnum); 184 match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); 185 } 186 187 #define EATSPACE() \ 188 while (*p && isascii((unsigned char)*p) && \ 189 isspace((unsigned char)*p)) \ 190 p++ \ 191 192 static struct s_command ** 193 compile_stream(struct s_command **link) 194 { 195 char *p; 196 static char *lbuf; /* To avoid excessive malloc calls */ 197 static size_t bufsize; 198 struct s_command *cmd, *cmd2, *stack; 199 struct s_format *fp; 200 int naddr; /* Number of addresses */ 201 202 stack = 0; 203 for (;;) { 204 if ((p = cu_fgets(&lbuf, &bufsize)) == NULL) { 205 if (stack != 0) 206 err(COMPILE, "unexpected EOF (pending }'s)"); 207 return (link); 208 } 209 210 semicolon: EATSPACE(); 211 if (*p == '#' || *p == '\0') 212 continue; 213 else if (*p == ';') { 214 p++; 215 goto semicolon; 216 } 217 *link = cmd = xmalloc(sizeof(struct s_command)); 218 link = &cmd->next; 219 cmd->nonsel = cmd->inrange = 0; 220 /* First parse the addresses */ 221 naddr = 0; 222 223 /* Valid characters to start an address */ 224 #define addrchar(c) (strchr("0123456789/\\$", (c))) 225 if (addrchar(*p)) { 226 naddr++; 227 cmd->a1 = xmalloc(sizeof(struct s_addr)); 228 p = compile_addr(p, cmd->a1); 229 EATSPACE(); /* EXTENSION */ 230 if (*p == ',') { 231 p++; 232 EATSPACE(); /* EXTENSION */ 233 naddr++; 234 cmd->a2 = xmalloc(sizeof(struct s_addr)); 235 p = compile_addr(p, cmd->a2); 236 EATSPACE(); 237 } else 238 cmd->a2 = 0; 239 } else 240 cmd->a1 = cmd->a2 = 0; 241 242 nonsel: /* Now parse the command */ 243 if (!*p) 244 err(COMPILE, "command expected"); 245 cmd->code = *p; 246 for (fp = cmd_fmts; fp->code; fp++) 247 if (fp->code == *p) 248 break; 249 if (!fp->code) 250 err(COMPILE, "invalid command code %c", *p); 251 if (naddr > fp->naddr) 252 err(COMPILE, 253 "command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); 254 switch (fp->args) { 255 case NONSEL: /* ! */ 256 p++; 257 EATSPACE(); 258 cmd->nonsel = ! cmd->nonsel; 259 goto nonsel; 260 case GROUP: /* { */ 261 p++; 262 EATSPACE(); 263 cmd->next = stack; 264 stack = cmd; 265 link = &cmd->u.c; 266 if (*p) 267 goto semicolon; 268 break; 269 case ENDGROUP: 270 /* 271 * Short-circuit command processing, since end of 272 * group is really just a noop. 273 */ 274 cmd->nonsel = 1; 275 if (stack == 0) 276 err(COMPILE, "unexpected }"); 277 cmd2 = stack; 278 stack = cmd2->next; 279 cmd2->next = cmd; 280 /*FALLTHROUGH*/ 281 case EMPTY: /* d D g G h H l n N p P q x = \0 */ 282 p++; 283 EATSPACE(); 284 switch (*p) { 285 case ';': 286 p++; 287 link = &cmd->next; 288 goto semicolon; 289 case '}': 290 goto semicolon; 291 case '\0': 292 break; 293 default: 294 err(COMPILE, 295 "extra characters at the end of %c command", cmd->code); 296 } 297 break; 298 case TEXT: /* a c i */ 299 p++; 300 EATSPACE(); 301 if (*p != '\\') 302 err(COMPILE, 303 "command %c expects \\ followed by text", cmd->code); 304 p++; 305 EATSPACE(); 306 if (*p) 307 err(COMPILE, 308 "extra characters after \\ at the end of %c command", cmd->code); 309 cmd->t = compile_text(); 310 break; 311 case COMMENT: /* \0 # */ 312 break; 313 case WFILE: /* w */ 314 p++; 315 EATSPACE(); 316 if (*p == '\0') 317 err(COMPILE, "filename expected"); 318 cmd->t = duptoeol(p, "w command"); 319 if (aflag) 320 cmd->u.fd = -1; 321 else if ((cmd->u.fd = open(p, 322 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 323 DEFFILEMODE)) == -1) 324 err(FATAL, "%s: %s", p, strerror(errno)); 325 break; 326 case RFILE: /* r */ 327 p++; 328 EATSPACE(); 329 if (*p == '\0') 330 err(COMPILE, "filename expected"); 331 else 332 cmd->t = duptoeol(p, "read command"); 333 break; 334 case BRANCH: /* b t */ 335 p++; 336 EATSPACE(); 337 if (*p == '\0') 338 cmd->t = NULL; 339 else 340 cmd->t = duptoeol(p, "branch"); 341 break; 342 case LABEL: /* : */ 343 p++; 344 EATSPACE(); 345 cmd->t = duptoeol(p, "label"); 346 if (strlen(p) == 0) 347 err(COMPILE, "empty label"); 348 enterlabel(cmd); 349 break; 350 case SUBST: /* s */ 351 p++; 352 if (*p == '\0' || *p == '\\') 353 err(COMPILE, 354 "substitute pattern can not be delimited by newline or backslash"); 355 cmd->u.s = xmalloc(sizeof(struct s_subst)); 356 p = compile_re(p, &cmd->u.s->re); 357 if (p == NULL) 358 err(COMPILE, "unterminated substitute pattern"); 359 --p; 360 p = compile_subst(p, cmd->u.s); 361 p = compile_flags(p, cmd->u.s); 362 EATSPACE(); 363 if (*p == ';') { 364 p++; 365 link = &cmd->next; 366 goto semicolon; 367 } 368 break; 369 case TR: /* y */ 370 p++; 371 p = compile_tr(p, (char **)(void *)&cmd->u.y); 372 EATSPACE(); 373 switch (*p) { 374 case ';': 375 p++; 376 link = &cmd->next; 377 goto semicolon; 378 case '}': 379 goto semicolon; 380 case '\0': 381 break; 382 default: 383 err(COMPILE, 384 "extra text at the end of a transform command"); 385 } 386 break; 387 } 388 } 389 } 390 391 /* 392 * Get a delimited string. P points to the delimiter of the string; d points 393 * to a buffer area. Newline and delimiter escapes are processed; other 394 * escapes are ignored. 395 * 396 * Returns a pointer to the first character after the final delimiter or NULL 397 * in the case of a non-terminated string. The character array d is filled 398 * with the processed string. 399 */ 400 static char * 401 compile_delimited(char *p, char *d) 402 { 403 char c; 404 405 c = *p++; 406 if (c == '\0') 407 return (NULL); 408 else if (c == '\\') 409 err(COMPILE, "\\ can not be used as a string delimiter"); 410 else if (c == '\n') 411 err(COMPILE, "newline can not be used as a string delimiter"); 412 while (*p) { 413 if (*p == '[') { 414 if ((d = compile_ccl(&p, d)) == NULL) 415 err(COMPILE, "unbalanced brackets ([])"); 416 continue; 417 } else if (*p == '\\' && p[1] == '[') { 418 *d++ = *p++; 419 } else if (*p == '\\' && p[1] == c) 420 p++; 421 else if (*p == '\\' && p[1] == 'n') { 422 *d++ = '\n'; 423 p += 2; 424 continue; 425 } else if (*p == '\\' && p[1] == '\\') 426 *d++ = *p++; 427 else if (*p == c) { 428 *d = '\0'; 429 return (p + 1); 430 } 431 *d++ = *p++; 432 } 433 return (NULL); 434 } 435 436 437 /* compile_ccl: expand a POSIX character class */ 438 static char * 439 compile_ccl(char **sp, char *t) 440 { 441 int c, d; 442 char *s = *sp; 443 444 *t++ = *s++; 445 if (*s == '^') 446 *t++ = *s++; 447 if (*s == ']') 448 *t++ = *s++; 449 for (; *s && (*t = *s) != ']'; s++, t++) 450 if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { 451 *++t = *++s, t++, s++; 452 for (c = *s; (*t = *s) != ']' || c != d; s++, t++) 453 if ((c = *s) == '\0') 454 return NULL; 455 } else if (*s == '\\' && s[1] == 'n') 456 *t = '\n', s++; 457 return (*s == ']') ? *sp = ++s, ++t : NULL; 458 } 459 460 /* 461 * Get a regular expression. P points to the delimiter of the regular 462 * expression; repp points to the address of a regexp pointer. Newline 463 * and delimiter escapes are processed; other escapes are ignored. 464 * Returns a pointer to the first character after the final delimiter 465 * or NULL in the case of a non terminated regular expression. The regexp 466 * pointer is set to the compiled regular expression. 467 * Cflags are passed to regcomp. 468 */ 469 static char * 470 compile_re(char *p, regex_t **repp) 471 { 472 int eval; 473 char *re; 474 475 re = xmalloc(strlen(p) + 1); /* strlen(re) <= strlen(p) */ 476 p = compile_delimited(p, re); 477 if (p && strlen(re) == 0) { 478 *repp = NULL; 479 free(re); 480 return (p); 481 } 482 *repp = xmalloc(sizeof(regex_t)); 483 if (p && (eval = regcomp(*repp, re, ere)) != 0) 484 err(COMPILE, "RE error: %s", strregerror(eval, *repp)); 485 if (maxnsub < (*repp)->re_nsub) 486 maxnsub = (*repp)->re_nsub; 487 free(re); 488 return (p); 489 } 490 491 /* 492 * Compile the substitution string of a regular expression and set res to 493 * point to a saved copy of it. Nsub is the number of parenthesized regular 494 * expressions. 495 */ 496 static char * 497 compile_subst(char *p, struct s_subst *s) 498 { 499 static char *lbuf; 500 static size_t bufsize; 501 int asize, ref, size, len; 502 char c, *text, *op, *sp; 503 int sawesc = 0; 504 505 c = *p++; /* Terminator character */ 506 if (c == '\0') 507 return (NULL); 508 509 s->maxbref = 0; 510 s->linenum = linenum; 511 text = NULL; 512 asize = size = 0; 513 do { 514 len = ROUNDLEN(strlen(p) + 1); 515 if (asize - size < len) { 516 do { 517 asize += len; 518 } while (asize - size < len); 519 text = xrealloc(text, asize); 520 } 521 op = sp = text + size; 522 for (; *p; p++) { 523 if (*p == '\\' || sawesc) { 524 /* 525 * If this is a continuation from the last 526 * buffer, we won't have a character to 527 * skip over. 528 */ 529 if (sawesc) 530 sawesc = 0; 531 else 532 p++; 533 534 if (*p == '\0') { 535 /* 536 * This escaped character is continued 537 * in the next part of the line. Note 538 * this fact, then cause the loop to 539 * exit w/ normal EOL case and reenter 540 * above with the new buffer. 541 */ 542 sawesc = 1; 543 p--; 544 continue; 545 } else if (strchr("123456789", *p) != NULL) { 546 *sp++ = '\\'; 547 ref = *p - '0'; 548 if (s->re != NULL && 549 (size_t)ref > s->re->re_nsub) 550 err(COMPILE, 551 "\\%c not defined in the RE", *p); 552 if (s->maxbref < ref) 553 s->maxbref = ref; 554 } else if (*p == '&' || *p == '\\') 555 *sp++ = '\\'; 556 } else if (*p == c) { 557 p++; 558 *sp++ = '\0'; 559 size += sp - op; 560 s->new = xrealloc(text, size); 561 return (p); 562 } else if (*p == '\n') { 563 err(COMPILE, 564 "unescaped newline inside substitute pattern"); 565 /* NOTREACHED */ 566 } 567 *sp++ = *p; 568 } 569 size += sp - op; 570 } while ((p = cu_fgets(&lbuf, &bufsize))); 571 err(COMPILE, "unterminated substitute in regular expression"); 572 /* NOTREACHED */ 573 return (NULL); 574 } 575 576 /* 577 * Compile the flags of the s command 578 */ 579 static char * 580 compile_flags(char *p, struct s_subst *s) 581 { 582 int gn; /* True if we have seen g or n */ 583 char wfile[PATH_MAX], *q; 584 585 s->n = 1; /* Default */ 586 s->p = 0; 587 s->wfile = NULL; 588 s->wfd = -1; 589 for (gn = 0;;) { 590 EATSPACE(); /* EXTENSION */ 591 switch (*p) { 592 case 'g': 593 if (gn) 594 err(COMPILE, 595 "more than one number or 'g' in substitute flags"); 596 gn = 1; 597 s->n = 0; 598 break; 599 case '\0': 600 case '\n': 601 case ';': 602 return (p); 603 case 'p': 604 s->p = 1; 605 break; 606 case '1': case '2': case '3': 607 case '4': case '5': case '6': 608 case '7': case '8': case '9': 609 if (gn) 610 err(COMPILE, 611 "more than one number or 'g' in substitute flags"); 612 gn = 1; 613 /* XXX Check for overflow */ 614 s->n = (int)strtol(p, &p, 10); 615 p--; 616 break; 617 case 'w': 618 p++; 619 #ifdef HISTORIC_PRACTICE 620 if (*p != ' ') { 621 err(WARNING, "space missing before w wfile"); 622 return (p); 623 } 624 #endif 625 EATSPACE(); 626 q = wfile; 627 while (*p) { 628 if (*p == '\n') 629 break; 630 *q++ = *p++; 631 } 632 *q = '\0'; 633 if (q == wfile) 634 err(COMPILE, "no wfile specified"); 635 s->wfile = strdup(wfile); 636 if (!aflag && (s->wfd = open(wfile, 637 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 638 DEFFILEMODE)) == -1) 639 err(FATAL, "%s: %s", wfile, strerror(errno)); 640 return (p); 641 default: 642 err(COMPILE, 643 "bad flag in substitute command: '%c'", *p); 644 break; 645 } 646 p++; 647 } 648 } 649 650 /* 651 * Compile a translation set of strings into a lookup table. 652 */ 653 static char * 654 compile_tr(char *p, char **transtab) 655 { 656 int i; 657 char *lt, *op, *np; 658 char *old = NULL, *new = NULL; 659 660 if (*p == '\0' || *p == '\\') 661 err(COMPILE, 662 "transform pattern can not be delimited by newline or backslash"); 663 old = xmalloc(strlen(p) + 1); 664 p = compile_delimited(p, old); 665 if (p == NULL) { 666 err(COMPILE, "unterminated transform source string"); 667 goto bad; 668 } 669 new = xmalloc(strlen(p) + 1); 670 p = compile_delimited(p - 1, new); 671 if (p == NULL) { 672 err(COMPILE, "unterminated transform target string"); 673 goto bad; 674 } 675 EATSPACE(); 676 if (strlen(new) != strlen(old)) { 677 err(COMPILE, "transform strings are not the same length"); 678 goto bad; 679 } 680 /* We assume characters are 8 bits */ 681 lt = xmalloc(UCHAR_MAX+1); 682 for (i = 0; i <= UCHAR_MAX; i++) 683 lt[i] = (char)i; 684 for (op = old, np = new; *op; op++, np++) 685 lt[(u_char)*op] = *np; 686 *transtab = lt; 687 free(old); 688 free(new); 689 return (p); 690 bad: 691 free(old); 692 free(new); 693 return (NULL); 694 } 695 696 /* 697 * Compile the text following an a, c, or i command. 698 */ 699 static char * 700 compile_text(void) 701 { 702 int asize, size, len; 703 char *lbuf, *text, *p, *op, *s; 704 size_t bufsize; 705 706 lbuf = text = NULL; 707 asize = size = 0; 708 while ((p = cu_fgets(&lbuf, &bufsize))) { 709 len = ROUNDLEN(strlen(p) + 1); 710 if (asize - size < len) { 711 do { 712 asize += len; 713 } while (asize - size < len); 714 text = xrealloc(text, asize); 715 } 716 op = s = text + size; 717 for (; *p; p++) { 718 if (*p == '\\') 719 p++; 720 *s++ = *p; 721 } 722 size += s - op; 723 if (p[-2] != '\\') { 724 *s = '\0'; 725 break; 726 } 727 } 728 free(lbuf); 729 return (xrealloc(text, size + 1)); 730 } 731 732 /* 733 * Get an address and return a pointer to the first character after 734 * it. Fill the structure pointed to according to the address. 735 */ 736 static char * 737 compile_addr(char *p, struct s_addr *a) 738 { 739 char *end; 740 741 switch (*p) { 742 case '\\': /* Context address */ 743 ++p; 744 /* FALLTHROUGH */ 745 case '/': /* Context address */ 746 p = compile_re(p, &a->u.r); 747 if (p == NULL) 748 err(COMPILE, "unterminated regular expression"); 749 a->type = AT_RE; 750 return (p); 751 752 case '$': /* Last line */ 753 a->type = AT_LAST; 754 return (p + 1); 755 /* Line number */ 756 case '0': case '1': case '2': case '3': case '4': 757 case '5': case '6': case '7': case '8': case '9': 758 a->type = AT_LINE; 759 a->u.l = strtol(p, &end, 10); 760 return (end); 761 default: 762 err(COMPILE, "expected context address"); 763 return (NULL); 764 } 765 } 766 767 /* 768 * duptoeol -- 769 * Return a copy of all the characters up to \n or \0. 770 */ 771 static char * 772 duptoeol(char *s, const char *ctype) 773 { 774 size_t len; 775 int ws; 776 char *start; 777 778 ws = 0; 779 for (start = s; *s != '\0' && *s != '\n'; ++s) 780 ws = isspace((unsigned char)*s); 781 *s = '\0'; 782 if (ws) 783 err(WARNING, "whitespace after %s", ctype); 784 len = s - start + 1; 785 return (memmove(xmalloc(len), start, len)); 786 } 787 788 /* 789 * Convert goto label names to addresses, and count a and r commands, in 790 * the given subset of the script. Free the memory used by labels in b 791 * and t commands (but not by :). 792 * 793 * TODO: Remove } nodes 794 */ 795 static void 796 fixuplabel(struct s_command *cp, struct s_command *end) 797 { 798 799 for (; cp != end; cp = cp->next) 800 switch (cp->code) { 801 case 'a': 802 case 'r': 803 appendnum++; 804 break; 805 case 'b': 806 case 't': 807 /* Resolve branch target. */ 808 if (cp->t == NULL) { 809 cp->u.c = NULL; 810 break; 811 } 812 if ((cp->u.c = findlabel(cp->t)) == NULL) 813 err(COMPILE2, "undefined label '%s'", cp->t); 814 free(cp->t); 815 break; 816 case '{': 817 /* Do interior commands. */ 818 fixuplabel(cp->u.c, cp->next); 819 break; 820 } 821 } 822 823 /* 824 * Associate the given command label for later lookup. 825 */ 826 static void 827 enterlabel(struct s_command *cp) 828 { 829 struct labhash **lhp, *lh; 830 u_char *p; 831 u_int h, c; 832 833 for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) 834 h = (h << 5) + h + c; 835 lhp = &labels[h & LHMASK]; 836 for (lh = *lhp; lh != NULL; lh = lh->lh_next) 837 if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) 838 err(COMPILE2, "duplicate label '%s'", cp->t); 839 lh = xmalloc(sizeof *lh); 840 lh->lh_next = *lhp; 841 lh->lh_hash = h; 842 lh->lh_cmd = cp; 843 lh->lh_ref = 0; 844 *lhp = lh; 845 } 846 847 /* 848 * Find the label contained in the command l in the command linked 849 * list cp. L is excluded from the search. Return NULL if not found. 850 */ 851 static struct s_command * 852 findlabel(char *name) 853 { 854 struct labhash *lh; 855 u_char *p; 856 u_int h, c; 857 858 for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) 859 h = (h << 5) + h + c; 860 for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { 861 if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { 862 lh->lh_ref = 1; 863 return (lh->lh_cmd); 864 } 865 } 866 return (NULL); 867 } 868 869 /* 870 * Warn about any unused labels. As a side effect, release the label hash 871 * table space. 872 */ 873 static void 874 uselabel(void) 875 { 876 struct labhash *lh, *next; 877 int i; 878 879 for (i = 0; i < LHSZ; i++) { 880 for (lh = labels[i]; lh != NULL; lh = next) { 881 next = lh->lh_next; 882 if (!lh->lh_ref) 883 err(WARNING, "unused label '%s'", 884 lh->lh_cmd->t); 885 free(lh); 886 } 887 } 888 } 889