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