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