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