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 "$FreeBSD$"; 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)) == 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, rflags)) != 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 int more = 0; 460 461 c = *p++; /* Terminator character */ 462 if (c == '\0') 463 return (NULL); 464 465 s->maxbref = 0; 466 s->linenum = linenum; 467 asize = 2 * _POSIX2_LINE_MAX + 1; 468 text = xmalloc(asize); 469 size = 0; 470 do { 471 op = sp = text + size; 472 for (; *p; p++) { 473 if (*p == '\\') { 474 p++; 475 if (strchr("123456789", *p) != NULL) { 476 *sp++ = '\\'; 477 ref = *p - '0'; 478 if (s->re != NULL && 479 ref > s->re->re_nsub) 480 errx(1, "%lu: %s: \\%c not defined in the RE", 481 linenum, fname, *p); 482 if (s->maxbref < ref) 483 s->maxbref = ref; 484 } else if (*p == '&' || *p == '\\') 485 *sp++ = '\\'; 486 } else if (*p == c) { 487 if (*++p == '\0' && more) { 488 if (cu_fgets(lbuf, sizeof(lbuf), &more)) 489 p = lbuf; 490 } 491 *sp++ = '\0'; 492 size += sp - op; 493 s->new = xrealloc(text, size); 494 return (p); 495 } else if (*p == '\n') { 496 errx(1, 497 "%lu: %s: unescaped newline inside substitute pattern", linenum, fname); 498 /* NOTREACHED */ 499 } 500 *sp++ = *p; 501 } 502 size += sp - op; 503 if (asize - size < _POSIX2_LINE_MAX + 1) { 504 asize *= 2; 505 text = xrealloc(text, asize); 506 } 507 } while (cu_fgets(p = lbuf, sizeof(lbuf), &more)); 508 errx(1, "%lu: %s: unterminated substitute in regular expression", 509 linenum, fname); 510 /* NOTREACHED */ 511 } 512 513 /* 514 * Compile the flags of the s command 515 */ 516 static char * 517 compile_flags(p, s) 518 char *p; 519 struct s_subst *s; 520 { 521 int gn; /* True if we have seen g or n */ 522 char wfile[_POSIX2_LINE_MAX + 1], *q; 523 524 s->n = 1; /* Default */ 525 s->p = 0; 526 s->wfile = NULL; 527 s->wfd = -1; 528 for (gn = 0;;) { 529 EATSPACE(); /* EXTENSION */ 530 switch (*p) { 531 case 'g': 532 if (gn) 533 errx(1, 534 "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 535 gn = 1; 536 s->n = 0; 537 break; 538 case '\0': 539 case '\n': 540 case ';': 541 return (p); 542 case 'p': 543 s->p = 1; 544 break; 545 case '1': case '2': case '3': 546 case '4': case '5': case '6': 547 case '7': case '8': case '9': 548 if (gn) 549 errx(1, 550 "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 551 gn = 1; 552 /* XXX Check for overflow */ 553 s->n = (int)strtol(p, &p, 10); 554 break; 555 case 'w': 556 p++; 557 #ifdef HISTORIC_PRACTICE 558 if (*p != ' ') { 559 warnx("%lu: %s: space missing before w wfile", linenum, fname); 560 return (p); 561 } 562 #endif 563 EATSPACE(); 564 q = wfile; 565 while (*p) { 566 if (*p == '\n') 567 break; 568 *q++ = *p++; 569 } 570 *q = '\0'; 571 if (q == wfile) 572 errx(1, "%lu: %s: no wfile specified", linenum, fname); 573 s->wfile = strdup(wfile); 574 if (!aflag && (s->wfd = open(wfile, 575 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 576 DEFFILEMODE)) == -1) 577 err(1, "%s", wfile); 578 return (p); 579 default: 580 errx(1, "%lu: %s: bad flag in substitute command: '%c'", 581 linenum, fname, *p); 582 break; 583 } 584 p++; 585 } 586 } 587 588 /* 589 * Compile a translation set of strings into a lookup table. 590 */ 591 static char * 592 compile_tr(p, transtab) 593 char *p; 594 char **transtab; 595 { 596 int i; 597 char *lt, *op, *np; 598 char old[_POSIX2_LINE_MAX + 1]; 599 char new[_POSIX2_LINE_MAX + 1]; 600 601 if (*p == '\0' || *p == '\\') 602 errx(1, 603 "%lu: %s: transform pattern can not be delimited by newline or backslash", 604 linenum, fname); 605 p = compile_delimited(p, old); 606 if (p == NULL) 607 errx(1, "%lu: %s: unterminated transform source string", 608 linenum, fname); 609 p = compile_delimited(--p, new); 610 if (p == NULL) 611 errx(1, "%lu: %s: unterminated transform target string", 612 linenum, fname); 613 EATSPACE(); 614 if (strlen(new) != strlen(old)) 615 errx(1, "%lu: %s: transform strings are not the same length", 616 linenum, fname); 617 /* We assume characters are 8 bits */ 618 lt = xmalloc(UCHAR_MAX); 619 for (i = 0; i <= UCHAR_MAX; i++) 620 lt[i] = (char)i; 621 for (op = old, np = new; *op; op++, np++) 622 lt[(u_char)*op] = *np; 623 *transtab = lt; 624 return (p); 625 } 626 627 /* 628 * Compile the text following an a or i command. 629 */ 630 static char * 631 compile_text() 632 { 633 int asize, esc_nl, size; 634 char *text, *p, *op, *s; 635 char lbuf[_POSIX2_LINE_MAX + 1]; 636 637 asize = 2 * _POSIX2_LINE_MAX + 1; 638 text = xmalloc(asize); 639 size = 0; 640 while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { 641 op = s = text + size; 642 p = lbuf; 643 EATSPACE(); 644 for (esc_nl = 0; *p != '\0'; p++) { 645 if (*p == '\\' && p[1] != '\0' && *++p == '\n') 646 esc_nl = 1; 647 *s++ = *p; 648 } 649 size += s - op; 650 if (!esc_nl) { 651 *s = '\0'; 652 break; 653 } 654 if (asize - size < _POSIX2_LINE_MAX + 1) { 655 asize *= 2; 656 text = xmalloc(asize); 657 } 658 } 659 text[size] = '\0'; 660 return (xrealloc(text, size + 1)); 661 } 662 663 /* 664 * Get an address and return a pointer to the first character after 665 * it. Fill the structure pointed to according to the address. 666 */ 667 static char * 668 compile_addr(p, a) 669 char *p; 670 struct s_addr *a; 671 { 672 char *end; 673 674 switch (*p) { 675 case '\\': /* Context address */ 676 ++p; 677 /* FALLTHROUGH */ 678 case '/': /* Context address */ 679 p = compile_re(p, &a->u.r); 680 if (p == NULL) 681 errx(1, "%lu: %s: unterminated regular expression", linenum, fname); 682 a->type = AT_RE; 683 return (p); 684 685 case '$': /* Last line */ 686 a->type = AT_LAST; 687 return (p + 1); 688 /* Line number */ 689 case '0': case '1': case '2': case '3': case '4': 690 case '5': case '6': case '7': case '8': case '9': 691 a->type = AT_LINE; 692 a->u.l = strtol(p, &end, 10); 693 return (end); 694 default: 695 errx(1, "%lu: %s: expected context address", linenum, fname); 696 return (NULL); 697 } 698 } 699 700 /* 701 * duptoeol -- 702 * Return a copy of all the characters up to \n or \0. 703 */ 704 static char * 705 duptoeol(s, ctype) 706 register char *s; 707 char *ctype; 708 { 709 size_t len; 710 int ws; 711 char *start; 712 713 ws = 0; 714 for (start = s; *s != '\0' && *s != '\n'; ++s) 715 ws = isspace((unsigned char)*s); 716 *s = '\0'; 717 if (ws) 718 warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); 719 len = s - start + 1; 720 return (memmove(xmalloc(len), start, len)); 721 } 722 723 /* 724 * Convert goto label names to addresses, and count a and r commands, in 725 * the given subset of the script. Free the memory used by labels in b 726 * and t commands (but not by :). 727 * 728 * TODO: Remove } nodes 729 */ 730 static void 731 fixuplabel(cp, end) 732 struct s_command *cp, *end; 733 { 734 735 for (; cp != end; cp = cp->next) 736 switch (cp->code) { 737 case 'a': 738 case 'r': 739 appendnum++; 740 break; 741 case 'b': 742 case 't': 743 /* Resolve branch target. */ 744 if (cp->t == NULL) { 745 cp->u.c = NULL; 746 break; 747 } 748 if ((cp->u.c = findlabel(cp->t)) == NULL) 749 errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); 750 free(cp->t); 751 break; 752 case '{': 753 /* Do interior commands. */ 754 fixuplabel(cp->u.c, cp->next); 755 break; 756 } 757 } 758 759 /* 760 * Associate the given command label for later lookup. 761 */ 762 static void 763 enterlabel(cp) 764 struct s_command *cp; 765 { 766 register struct labhash **lhp, *lh; 767 register u_char *p; 768 register u_int h, c; 769 770 for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) 771 h = (h << 5) + h + c; 772 lhp = &labels[h & LHMASK]; 773 for (lh = *lhp; lh != NULL; lh = lh->lh_next) 774 if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) 775 errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); 776 lh = xmalloc(sizeof *lh); 777 lh->lh_next = *lhp; 778 lh->lh_hash = h; 779 lh->lh_cmd = cp; 780 lh->lh_ref = 0; 781 *lhp = lh; 782 } 783 784 /* 785 * Find the label contained in the command l in the command linked 786 * list cp. L is excluded from the search. Return NULL if not found. 787 */ 788 static struct s_command * 789 findlabel(name) 790 char *name; 791 { 792 register struct labhash *lh; 793 register u_char *p; 794 register u_int h, c; 795 796 for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) 797 h = (h << 5) + h + c; 798 for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { 799 if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { 800 lh->lh_ref = 1; 801 return (lh->lh_cmd); 802 } 803 } 804 return (NULL); 805 } 806 807 /* 808 * Warn about any unused labels. As a side effect, release the label hash 809 * table space. 810 */ 811 static void 812 uselabel() 813 { 814 register struct labhash *lh, *next; 815 register int i; 816 817 for (i = 0; i < LHSZ; i++) { 818 for (lh = labels[i]; lh != NULL; lh = next) { 819 next = lh->lh_next; 820 if (!lh->lh_ref) 821 warnx("%lu: %s: unused label '%s'", 822 linenum, fname, lh->lh_cmd->t); 823 free(lh); 824 } 825 } 826 } 827