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