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