19b50d902SRodney W. Grimes /*- 29b50d902SRodney W. Grimes * Copyright (c) 1992 Diomidis Spinellis. 39b50d902SRodney W. Grimes * Copyright (c) 1992, 1993 49b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 59b50d902SRodney W. Grimes * 69b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 79b50d902SRodney W. Grimes * Diomidis Spinellis of Imperial College, University of London. 89b50d902SRodney W. Grimes * 99b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 109b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 119b50d902SRodney W. Grimes * are met: 129b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 149b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 159b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 169b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 179b50d902SRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 189b50d902SRodney W. Grimes * must display the following acknowledgement: 199b50d902SRodney W. Grimes * This product includes software developed by the University of 209b50d902SRodney W. Grimes * California, Berkeley and its contributors. 219b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 229b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 239b50d902SRodney W. Grimes * without specific prior written permission. 249b50d902SRodney W. Grimes * 259b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 269b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 279b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 289b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 299b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 309b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 319b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 329b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 339b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 349b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 359b50d902SRodney W. Grimes * SUCH DAMAGE. 369b50d902SRodney W. Grimes */ 379b50d902SRodney W. Grimes 389b50d902SRodney W. Grimes #ifndef lint 3973a08bb2SPhilippe Charnier #if 0 409b50d902SRodney W. Grimes static char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; 4173a08bb2SPhilippe Charnier #endif 4273a08bb2SPhilippe Charnier static const char rcsid[] = 431a6583daSArchie Cobbs "$Id: compile.c,v 1.10 1998/09/22 18:39:47 brian Exp $"; 449b50d902SRodney W. Grimes #endif /* not lint */ 459b50d902SRodney W. Grimes 469b50d902SRodney W. Grimes #include <sys/types.h> 479b50d902SRodney W. Grimes #include <sys/stat.h> 489b50d902SRodney W. Grimes 499b50d902SRodney W. Grimes #include <ctype.h> 5073a08bb2SPhilippe Charnier #include <err.h> 519b50d902SRodney W. Grimes #include <fcntl.h> 529b50d902SRodney W. Grimes #include <limits.h> 539b50d902SRodney W. Grimes #include <regex.h> 549b50d902SRodney W. Grimes #include <stdio.h> 559b50d902SRodney W. Grimes #include <stdlib.h> 569b50d902SRodney W. Grimes #include <string.h> 579b50d902SRodney W. Grimes 589b50d902SRodney W. Grimes #include "defs.h" 599b50d902SRodney W. Grimes #include "extern.h" 609b50d902SRodney W. Grimes 619b50d902SRodney W. Grimes #define LHSZ 128 629b50d902SRodney W. Grimes #define LHMASK (LHSZ - 1) 639b50d902SRodney W. Grimes static struct labhash { 649b50d902SRodney W. Grimes struct labhash *lh_next; 659b50d902SRodney W. Grimes u_int lh_hash; 669b50d902SRodney W. Grimes struct s_command *lh_cmd; 679b50d902SRodney W. Grimes int lh_ref; 689b50d902SRodney W. Grimes } *labels[LHSZ]; 699b50d902SRodney W. Grimes 709b50d902SRodney W. Grimes static char *compile_addr __P((char *, struct s_addr *)); 71ce19262dSJordan K. Hubbard static char *compile_ccl __P((char **, char *)); 729b50d902SRodney W. Grimes static char *compile_delimited __P((char *, char *)); 739b50d902SRodney W. Grimes static char *compile_flags __P((char *, struct s_subst *)); 749b50d902SRodney W. Grimes static char *compile_re __P((char *, regex_t **)); 759b50d902SRodney W. Grimes static char *compile_subst __P((char *, struct s_subst *)); 769b50d902SRodney W. Grimes static char *compile_text __P((void)); 779b50d902SRodney W. Grimes static char *compile_tr __P((char *, char **)); 789b50d902SRodney W. Grimes static struct s_command 79ce19262dSJordan K. Hubbard **compile_stream __P((struct s_command **)); 809b50d902SRodney W. Grimes static char *duptoeol __P((char *, char *)); 819b50d902SRodney W. Grimes static void enterlabel __P((struct s_command *)); 829b50d902SRodney W. Grimes static struct s_command 839b50d902SRodney W. Grimes *findlabel __P((char *)); 849b50d902SRodney W. Grimes static void fixuplabel __P((struct s_command *, struct s_command *)); 859b50d902SRodney W. Grimes static void uselabel __P((void)); 869b50d902SRodney W. Grimes 879b50d902SRodney W. Grimes /* 889b50d902SRodney W. Grimes * Command specification. This is used to drive the command parser. 899b50d902SRodney W. Grimes */ 909b50d902SRodney W. Grimes struct s_format { 919b50d902SRodney W. Grimes char code; /* Command code */ 929b50d902SRodney W. Grimes int naddr; /* Number of address args */ 939b50d902SRodney W. Grimes enum e_args args; /* Argument type */ 949b50d902SRodney W. Grimes }; 959b50d902SRodney W. Grimes 969b50d902SRodney W. Grimes static struct s_format cmd_fmts[] = { 979b50d902SRodney W. Grimes {'{', 2, GROUP}, 98ce19262dSJordan K. Hubbard {'}', 0, ENDGROUP}, 999b50d902SRodney W. Grimes {'a', 1, TEXT}, 1009b50d902SRodney W. Grimes {'b', 2, BRANCH}, 1019b50d902SRodney W. Grimes {'c', 2, TEXT}, 1029b50d902SRodney W. Grimes {'d', 2, EMPTY}, 1039b50d902SRodney W. Grimes {'D', 2, EMPTY}, 1049b50d902SRodney W. Grimes {'g', 2, EMPTY}, 1059b50d902SRodney W. Grimes {'G', 2, EMPTY}, 1069b50d902SRodney W. Grimes {'h', 2, EMPTY}, 1079b50d902SRodney W. Grimes {'H', 2, EMPTY}, 1089b50d902SRodney W. Grimes {'i', 1, TEXT}, 1099b50d902SRodney W. Grimes {'l', 2, EMPTY}, 1109b50d902SRodney W. Grimes {'n', 2, EMPTY}, 1119b50d902SRodney W. Grimes {'N', 2, EMPTY}, 1129b50d902SRodney W. Grimes {'p', 2, EMPTY}, 1139b50d902SRodney W. Grimes {'P', 2, EMPTY}, 1149b50d902SRodney W. Grimes {'q', 1, EMPTY}, 1159b50d902SRodney W. Grimes {'r', 1, RFILE}, 1169b50d902SRodney W. Grimes {'s', 2, SUBST}, 1179b50d902SRodney W. Grimes {'t', 2, BRANCH}, 1189b50d902SRodney W. Grimes {'w', 2, WFILE}, 1199b50d902SRodney W. Grimes {'x', 2, EMPTY}, 1209b50d902SRodney W. Grimes {'y', 2, TR}, 1219b50d902SRodney W. Grimes {'!', 2, NONSEL}, 1229b50d902SRodney W. Grimes {':', 0, LABEL}, 1239b50d902SRodney W. Grimes {'#', 0, COMMENT}, 1249b50d902SRodney W. Grimes {'=', 1, EMPTY}, 1259b50d902SRodney W. Grimes {'\0', 0, COMMENT}, 1269b50d902SRodney W. Grimes }; 1279b50d902SRodney W. Grimes 1289b50d902SRodney W. Grimes /* The compiled program. */ 1299b50d902SRodney W. Grimes struct s_command *prog; 1309b50d902SRodney W. Grimes 1319b50d902SRodney W. Grimes /* 1329b50d902SRodney W. Grimes * Compile the program into prog. 1339b50d902SRodney W. Grimes * Initialise appends. 1349b50d902SRodney W. Grimes */ 1359b50d902SRodney W. Grimes void 1369b50d902SRodney W. Grimes compile() 1379b50d902SRodney W. Grimes { 138ce19262dSJordan K. Hubbard *compile_stream(&prog) = NULL; 1399b50d902SRodney W. Grimes fixuplabel(prog, NULL); 1409b50d902SRodney W. Grimes uselabel(); 1419b50d902SRodney W. Grimes appends = xmalloc(sizeof(struct s_appends) * appendnum); 1429b50d902SRodney W. Grimes match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); 1439b50d902SRodney W. Grimes } 1449b50d902SRodney W. Grimes 1459b50d902SRodney W. Grimes #define EATSPACE() do { \ 1469b50d902SRodney W. Grimes if (p) \ 147726aebe5SAndrey A. Chernov while (*p && isspace((unsigned char)*p)) \ 1489b50d902SRodney W. Grimes p++; \ 1499b50d902SRodney W. Grimes } while (0) 1509b50d902SRodney W. Grimes 1519b50d902SRodney W. Grimes static struct s_command ** 152ce19262dSJordan K. Hubbard compile_stream(link) 1539b50d902SRodney W. Grimes struct s_command **link; 1549b50d902SRodney W. Grimes { 155ce19262dSJordan K. Hubbard register char *p; 1569b50d902SRodney W. Grimes static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 157ce19262dSJordan K. Hubbard struct s_command *cmd, *cmd2, *stack; 1589b50d902SRodney W. Grimes struct s_format *fp; 1599b50d902SRodney W. Grimes int naddr; /* Number of addresses */ 1609b50d902SRodney W. Grimes 161ce19262dSJordan K. Hubbard stack = 0; 1629b50d902SRodney W. Grimes for (;;) { 1639b50d902SRodney W. Grimes if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { 164ce19262dSJordan K. Hubbard if (stack != 0) 16573a08bb2SPhilippe Charnier errx(1, "%lu: %s: unexpected EOF (pending }'s)", 16673a08bb2SPhilippe Charnier linenum, fname); 1679b50d902SRodney W. Grimes return (link); 1689b50d902SRodney W. Grimes } 1699b50d902SRodney W. Grimes 1709b50d902SRodney W. Grimes semicolon: EATSPACE(); 1719b50d902SRodney W. Grimes if (p && (*p == '#' || *p == '\0')) 1729b50d902SRodney W. Grimes continue; 1739b50d902SRodney W. Grimes *link = cmd = xmalloc(sizeof(struct s_command)); 1749b50d902SRodney W. Grimes link = &cmd->next; 1759b50d902SRodney W. Grimes cmd->nonsel = cmd->inrange = 0; 1769b50d902SRodney W. Grimes /* First parse the addresses */ 1779b50d902SRodney W. Grimes naddr = 0; 1789b50d902SRodney W. Grimes 1799b50d902SRodney W. Grimes /* Valid characters to start an address */ 1809b50d902SRodney W. Grimes #define addrchar(c) (strchr("0123456789/\\$", (c))) 1819b50d902SRodney W. Grimes if (addrchar(*p)) { 1829b50d902SRodney W. Grimes naddr++; 1839b50d902SRodney W. Grimes cmd->a1 = xmalloc(sizeof(struct s_addr)); 1849b50d902SRodney W. Grimes p = compile_addr(p, cmd->a1); 1859b50d902SRodney W. Grimes EATSPACE(); /* EXTENSION */ 1869b50d902SRodney W. Grimes if (*p == ',') { 1879b50d902SRodney W. Grimes p++; 1889b50d902SRodney W. Grimes EATSPACE(); /* EXTENSION */ 189ce19262dSJordan K. Hubbard naddr++; 1909b50d902SRodney W. Grimes cmd->a2 = xmalloc(sizeof(struct s_addr)); 1919b50d902SRodney W. Grimes p = compile_addr(p, cmd->a2); 192ce19262dSJordan K. Hubbard EATSPACE(); 193ce19262dSJordan K. Hubbard } else 194ce19262dSJordan K. Hubbard cmd->a2 = 0; 195ce19262dSJordan K. Hubbard } else 196ce19262dSJordan K. Hubbard cmd->a1 = cmd->a2 = 0; 1979b50d902SRodney W. Grimes 1989b50d902SRodney W. Grimes nonsel: /* Now parse the command */ 1999b50d902SRodney W. Grimes if (!*p) 20073a08bb2SPhilippe Charnier errx(1, "%lu: %s: command expected", linenum, fname); 2019b50d902SRodney W. Grimes cmd->code = *p; 2029b50d902SRodney W. Grimes for (fp = cmd_fmts; fp->code; fp++) 2039b50d902SRodney W. Grimes if (fp->code == *p) 2049b50d902SRodney W. Grimes break; 2059b50d902SRodney W. Grimes if (!fp->code) 20673a08bb2SPhilippe Charnier errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); 2079b50d902SRodney W. Grimes if (naddr > fp->naddr) 20873a08bb2SPhilippe Charnier errx(1, 20973a08bb2SPhilippe Charnier "%lu: %s: command %c expects up to %d address(es), found %d", 21073a08bb2SPhilippe Charnier linenum, fname, *p, fp->naddr, naddr); 2119b50d902SRodney W. Grimes switch (fp->args) { 2129b50d902SRodney W. Grimes case NONSEL: /* ! */ 2139b50d902SRodney W. Grimes p++; 214ce19262dSJordan K. Hubbard EATSPACE(); 215ce19262dSJordan K. Hubbard cmd->nonsel = ! cmd->nonsel; 2169b50d902SRodney W. Grimes goto nonsel; 2179b50d902SRodney W. Grimes case GROUP: /* { */ 2189b50d902SRodney W. Grimes p++; 2199b50d902SRodney W. Grimes EATSPACE(); 220ce19262dSJordan K. Hubbard cmd->next = stack; 221ce19262dSJordan K. Hubbard stack = cmd; 222ce19262dSJordan K. Hubbard link = &cmd->u.c; 223ce19262dSJordan K. Hubbard if (*p) 224ce19262dSJordan K. Hubbard goto semicolon; 2259b50d902SRodney W. Grimes break; 226ce19262dSJordan K. Hubbard case ENDGROUP: 227ce19262dSJordan K. Hubbard /* 228ce19262dSJordan K. Hubbard * Short-circuit command processing, since end of 229ce19262dSJordan K. Hubbard * group is really just a noop. 230ce19262dSJordan K. Hubbard */ 231ce19262dSJordan K. Hubbard cmd->nonsel = 1; 232ce19262dSJordan K. Hubbard if (stack == 0) 23373a08bb2SPhilippe Charnier errx(1, "%lu: %s: unexpected }", linenum, fname); 234ce19262dSJordan K. Hubbard cmd2 = stack; 235ce19262dSJordan K. Hubbard stack = cmd2->next; 236ce19262dSJordan K. Hubbard cmd2->next = cmd; 237ce19262dSJordan K. Hubbard /*FALLTHROUGH*/ 2389b50d902SRodney W. Grimes case EMPTY: /* d D g G h H l n N p P q x = \0 */ 2399b50d902SRodney W. Grimes p++; 2409b50d902SRodney W. Grimes EATSPACE(); 2419b50d902SRodney W. Grimes if (*p == ';') { 2429b50d902SRodney W. Grimes p++; 2439b50d902SRodney W. Grimes link = &cmd->next; 2449b50d902SRodney W. Grimes goto semicolon; 2459b50d902SRodney W. Grimes } 2469b50d902SRodney W. Grimes if (*p) 24773a08bb2SPhilippe Charnier errx(1, "%lu: %s: extra characters at the end of %c command", 24873a08bb2SPhilippe Charnier linenum, fname, cmd->code); 2499b50d902SRodney W. Grimes break; 2509b50d902SRodney W. Grimes case TEXT: /* a c i */ 2519b50d902SRodney W. Grimes p++; 2529b50d902SRodney W. Grimes EATSPACE(); 2539b50d902SRodney W. Grimes if (*p != '\\') 25473a08bb2SPhilippe Charnier errx(1, 25573a08bb2SPhilippe Charnier "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); 2569b50d902SRodney W. Grimes p++; 2579b50d902SRodney W. Grimes EATSPACE(); 2589b50d902SRodney W. Grimes if (*p) 25973a08bb2SPhilippe Charnier errx(1, 26073a08bb2SPhilippe Charnier "%lu: %s: extra characters after \\ at the end of %c command", 26173a08bb2SPhilippe Charnier linenum, fname, cmd->code); 2629b50d902SRodney W. Grimes cmd->t = compile_text(); 2639b50d902SRodney W. Grimes break; 2649b50d902SRodney W. Grimes case COMMENT: /* \0 # */ 2659b50d902SRodney W. Grimes break; 2669b50d902SRodney W. Grimes case WFILE: /* w */ 2679b50d902SRodney W. Grimes p++; 2689b50d902SRodney W. Grimes EATSPACE(); 2699b50d902SRodney W. Grimes if (*p == '\0') 27073a08bb2SPhilippe Charnier errx(1, "%lu: %s: filename expected", linenum, fname); 2719b50d902SRodney W. Grimes cmd->t = duptoeol(p, "w command"); 2729b50d902SRodney W. Grimes if (aflag) 2739b50d902SRodney W. Grimes cmd->u.fd = -1; 2749b50d902SRodney W. Grimes else if ((cmd->u.fd = open(p, 2759b50d902SRodney W. Grimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 2769b50d902SRodney W. Grimes DEFFILEMODE)) == -1) 27773a08bb2SPhilippe Charnier err(1, "%s", p); 2789b50d902SRodney W. Grimes break; 2799b50d902SRodney W. Grimes case RFILE: /* r */ 2809b50d902SRodney W. Grimes p++; 2819b50d902SRodney W. Grimes EATSPACE(); 2829b50d902SRodney W. Grimes if (*p == '\0') 28373a08bb2SPhilippe Charnier errx(1, "%lu: %s: filename expected", linenum, fname); 2849b50d902SRodney W. Grimes else 2859b50d902SRodney W. Grimes cmd->t = duptoeol(p, "read command"); 2869b50d902SRodney W. Grimes break; 2879b50d902SRodney W. Grimes case BRANCH: /* b t */ 2889b50d902SRodney W. Grimes p++; 2899b50d902SRodney W. Grimes EATSPACE(); 2909b50d902SRodney W. Grimes if (*p == '\0') 2919b50d902SRodney W. Grimes cmd->t = NULL; 2929b50d902SRodney W. Grimes else 2939b50d902SRodney W. Grimes cmd->t = duptoeol(p, "branch"); 2949b50d902SRodney W. Grimes break; 2959b50d902SRodney W. Grimes case LABEL: /* : */ 2969b50d902SRodney W. Grimes p++; 2979b50d902SRodney W. Grimes EATSPACE(); 2989b50d902SRodney W. Grimes cmd->t = duptoeol(p, "label"); 2999b50d902SRodney W. Grimes if (strlen(p) == 0) 30073a08bb2SPhilippe Charnier errx(1, "%lu: %s: empty label", linenum, fname); 3019b50d902SRodney W. Grimes enterlabel(cmd); 3029b50d902SRodney W. Grimes break; 3039b50d902SRodney W. Grimes case SUBST: /* s */ 3049b50d902SRodney W. Grimes p++; 3059b50d902SRodney W. Grimes if (*p == '\0' || *p == '\\') 30673a08bb2SPhilippe Charnier errx(1, 30773a08bb2SPhilippe Charnier "%lu: %s: substitute pattern can not be delimited by newline or backslash", 30873a08bb2SPhilippe Charnier linenum, fname); 3099b50d902SRodney W. Grimes cmd->u.s = xmalloc(sizeof(struct s_subst)); 3109b50d902SRodney W. Grimes p = compile_re(p, &cmd->u.s->re); 3119b50d902SRodney W. Grimes if (p == NULL) 31273a08bb2SPhilippe Charnier errx(1, 31373a08bb2SPhilippe Charnier "%lu: %s: unterminated substitute pattern", linenum, fname); 3149b50d902SRodney W. Grimes --p; 3159b50d902SRodney W. Grimes p = compile_subst(p, cmd->u.s); 3169b50d902SRodney W. Grimes p = compile_flags(p, cmd->u.s); 3179b50d902SRodney W. Grimes EATSPACE(); 3189b50d902SRodney W. Grimes if (*p == ';') { 3199b50d902SRodney W. Grimes p++; 3209b50d902SRodney W. Grimes link = &cmd->next; 3219b50d902SRodney W. Grimes goto semicolon; 3229b50d902SRodney W. Grimes } 3239b50d902SRodney W. Grimes break; 3249b50d902SRodney W. Grimes case TR: /* y */ 3259b50d902SRodney W. Grimes p++; 3269b50d902SRodney W. Grimes p = compile_tr(p, (char **)&cmd->u.y); 3279b50d902SRodney W. Grimes EATSPACE(); 3289b50d902SRodney W. Grimes if (*p == ';') { 3299b50d902SRodney W. Grimes p++; 3309b50d902SRodney W. Grimes link = &cmd->next; 3319b50d902SRodney W. Grimes goto semicolon; 3329b50d902SRodney W. Grimes } 3339b50d902SRodney W. Grimes if (*p) 33473a08bb2SPhilippe Charnier errx(1, 33573a08bb2SPhilippe Charnier "%lu: %s: extra text at the end of a transform command", linenum, fname); 3369b50d902SRodney W. Grimes break; 3379b50d902SRodney W. Grimes } 3389b50d902SRodney W. Grimes } 3399b50d902SRodney W. Grimes } 3409b50d902SRodney W. Grimes 3419b50d902SRodney W. Grimes /* 3429b50d902SRodney W. Grimes * Get a delimited string. P points to the delimeter of the string; d points 3439b50d902SRodney W. Grimes * to a buffer area. Newline and delimiter escapes are processed; other 3449b50d902SRodney W. Grimes * escapes are ignored. 3459b50d902SRodney W. Grimes * 3469b50d902SRodney W. Grimes * Returns a pointer to the first character after the final delimiter or NULL 3479b50d902SRodney W. Grimes * in the case of a non-terminated string. The character array d is filled 3489b50d902SRodney W. Grimes * with the processed string. 3499b50d902SRodney W. Grimes */ 3509b50d902SRodney W. Grimes static char * 3519b50d902SRodney W. Grimes compile_delimited(p, d) 3529b50d902SRodney W. Grimes char *p, *d; 3539b50d902SRodney W. Grimes { 3549b50d902SRodney W. Grimes char c; 3559b50d902SRodney W. Grimes 3569b50d902SRodney W. Grimes c = *p++; 3579b50d902SRodney W. Grimes if (c == '\0') 3589b50d902SRodney W. Grimes return (NULL); 3599b50d902SRodney W. Grimes else if (c == '\\') 36073a08bb2SPhilippe Charnier errx(1, "%lu: %s: \\ can not be used as a string delimiter", 36173a08bb2SPhilippe Charnier linenum, fname); 3629b50d902SRodney W. Grimes else if (c == '\n') 36373a08bb2SPhilippe Charnier errx(1, "%lu: %s: newline can not be used as a string delimiter", 36473a08bb2SPhilippe Charnier linenum, fname); 3659b50d902SRodney W. Grimes while (*p) { 366ce19262dSJordan K. Hubbard if (*p == '[') { 367ce19262dSJordan K. Hubbard if ((d = compile_ccl(&p, d)) == NULL) 36873a08bb2SPhilippe Charnier errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname); 369ce19262dSJordan K. Hubbard continue; 370ce19262dSJordan K. Hubbard } else if (*p == '\\' && p[1] == '[') { 371ce19262dSJordan K. Hubbard *d++ = *p++; 372ce19262dSJordan K. Hubbard } else if (*p == '\\' && p[1] == c) 3739b50d902SRodney W. Grimes p++; 3749b50d902SRodney W. Grimes else if (*p == '\\' && p[1] == 'n') { 3759b50d902SRodney W. Grimes *d++ = '\n'; 3769b50d902SRodney W. Grimes p += 2; 3779b50d902SRodney W. Grimes continue; 3789b50d902SRodney W. Grimes } else if (*p == '\\' && p[1] == '\\') 3799b50d902SRodney W. Grimes *d++ = *p++; 3809b50d902SRodney W. Grimes else if (*p == c) { 3819b50d902SRodney W. Grimes *d = '\0'; 3829b50d902SRodney W. Grimes return (p + 1); 3839b50d902SRodney W. Grimes } 3849b50d902SRodney W. Grimes *d++ = *p++; 3859b50d902SRodney W. Grimes } 3869b50d902SRodney W. Grimes return (NULL); 3879b50d902SRodney W. Grimes } 3889b50d902SRodney W. Grimes 389ce19262dSJordan K. Hubbard 390ce19262dSJordan K. Hubbard /* compile_ccl: expand a POSIX character class */ 391ce19262dSJordan K. Hubbard static char * 392ce19262dSJordan K. Hubbard compile_ccl(sp, t) 393ce19262dSJordan K. Hubbard char **sp; 394ce19262dSJordan K. Hubbard char *t; 395ce19262dSJordan K. Hubbard { 396ce19262dSJordan K. Hubbard int c, d; 397ce19262dSJordan K. Hubbard char *s = *sp; 398ce19262dSJordan K. Hubbard 399ce19262dSJordan K. Hubbard *t++ = *s++; 400ce19262dSJordan K. Hubbard if (*s == '^') 401ce19262dSJordan K. Hubbard *t++ = *s++; 402ce19262dSJordan K. Hubbard if (*s == ']') 403ce19262dSJordan K. Hubbard *t++ = *s++; 404ce19262dSJordan K. Hubbard for (; *s && (*t = *s) != ']'; s++, t++) 405ce19262dSJordan K. Hubbard if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { 406ce19262dSJordan K. Hubbard *++t = *++s, t++, s++; 407ce19262dSJordan K. Hubbard for (c = *s; (*t = *s) != ']' || c != d; s++, t++) 408ce19262dSJordan K. Hubbard if ((c = *s) == '\0') 409ce19262dSJordan K. Hubbard return NULL; 410ce19262dSJordan K. Hubbard } else if (*s == '\\' && s[1] == 'n') 411ce19262dSJordan K. Hubbard *t = '\n', s++; 412ce19262dSJordan K. Hubbard return (*s == ']') ? *sp = ++s, ++t : NULL; 413ce19262dSJordan K. Hubbard } 414ce19262dSJordan K. Hubbard 4159b50d902SRodney W. Grimes /* 4169b50d902SRodney W. Grimes * Get a regular expression. P points to the delimiter of the regular 4179b50d902SRodney W. Grimes * expression; repp points to the address of a regexp pointer. Newline 4189b50d902SRodney W. Grimes * and delimiter escapes are processed; other escapes are ignored. 4199b50d902SRodney W. Grimes * Returns a pointer to the first character after the final delimiter 4209b50d902SRodney W. Grimes * or NULL in the case of a non terminated regular expression. The regexp 4219b50d902SRodney W. Grimes * pointer is set to the compiled regular expression. 4229b50d902SRodney W. Grimes * Cflags are passed to regcomp. 4239b50d902SRodney W. Grimes */ 4249b50d902SRodney W. Grimes static char * 4259b50d902SRodney W. Grimes compile_re(p, repp) 4269b50d902SRodney W. Grimes char *p; 4279b50d902SRodney W. Grimes regex_t **repp; 4289b50d902SRodney W. Grimes { 4299b50d902SRodney W. Grimes int eval; 4309b50d902SRodney W. Grimes char re[_POSIX2_LINE_MAX + 1]; 4319b50d902SRodney W. Grimes 4329b50d902SRodney W. Grimes p = compile_delimited(p, re); 4339b50d902SRodney W. Grimes if (p && strlen(re) == 0) { 4349b50d902SRodney W. Grimes *repp = NULL; 4359b50d902SRodney W. Grimes return (p); 4369b50d902SRodney W. Grimes } 4379b50d902SRodney W. Grimes *repp = xmalloc(sizeof(regex_t)); 4389b50d902SRodney W. Grimes if (p && (eval = regcomp(*repp, re, 0)) != 0) 43973a08bb2SPhilippe Charnier errx(1, "%lu: %s: RE error: %s", 44073a08bb2SPhilippe Charnier linenum, fname, strregerror(eval, *repp)); 4419b50d902SRodney W. Grimes if (maxnsub < (*repp)->re_nsub) 4429b50d902SRodney W. Grimes maxnsub = (*repp)->re_nsub; 4439b50d902SRodney W. Grimes return (p); 4449b50d902SRodney W. Grimes } 4459b50d902SRodney W. Grimes 4469b50d902SRodney W. Grimes /* 4479b50d902SRodney W. Grimes * Compile the substitution string of a regular expression and set res to 4489b50d902SRodney W. Grimes * point to a saved copy of it. Nsub is the number of parenthesized regular 4499b50d902SRodney W. Grimes * expressions. 4509b50d902SRodney W. Grimes */ 4519b50d902SRodney W. Grimes static char * 4529b50d902SRodney W. Grimes compile_subst(p, s) 4539b50d902SRodney W. Grimes char *p; 4549b50d902SRodney W. Grimes struct s_subst *s; 4559b50d902SRodney W. Grimes { 4569b50d902SRodney W. Grimes static char lbuf[_POSIX2_LINE_MAX + 1]; 4579b50d902SRodney W. Grimes int asize, ref, size; 4589b50d902SRodney W. Grimes char c, *text, *op, *sp; 4599b50d902SRodney W. Grimes 4609b50d902SRodney W. Grimes c = *p++; /* Terminator character */ 4619b50d902SRodney W. Grimes if (c == '\0') 4629b50d902SRodney W. Grimes return (NULL); 4639b50d902SRodney W. Grimes 4649b50d902SRodney W. Grimes s->maxbref = 0; 4659b50d902SRodney W. Grimes s->linenum = linenum; 4669b50d902SRodney W. Grimes asize = 2 * _POSIX2_LINE_MAX + 1; 4679b50d902SRodney W. Grimes text = xmalloc(asize); 4689b50d902SRodney W. Grimes size = 0; 4699b50d902SRodney W. Grimes do { 4709b50d902SRodney W. Grimes op = sp = text + size; 4719b50d902SRodney W. Grimes for (; *p; p++) { 4729b50d902SRodney W. Grimes if (*p == '\\') { 4739b50d902SRodney W. Grimes p++; 4749b50d902SRodney W. Grimes if (strchr("123456789", *p) != NULL) { 4759b50d902SRodney W. Grimes *sp++ = '\\'; 4769b50d902SRodney W. Grimes ref = *p - '0'; 4779b50d902SRodney W. Grimes if (s->re != NULL && 4789b50d902SRodney W. Grimes ref > s->re->re_nsub) 47973a08bb2SPhilippe Charnier errx(1, "%lu: %s: \\%c not defined in the RE", 48073a08bb2SPhilippe Charnier linenum, fname, *p); 4819b50d902SRodney W. Grimes if (s->maxbref < ref) 4829b50d902SRodney W. Grimes s->maxbref = ref; 4839b50d902SRodney W. Grimes } else if (*p == '&' || *p == '\\') 4849b50d902SRodney W. Grimes *sp++ = '\\'; 4859b50d902SRodney W. Grimes } else if (*p == c) { 4861a6583daSArchie Cobbs if (*++p == '\0') { 4871a6583daSArchie Cobbs if (cu_fgets(lbuf, sizeof(lbuf))) 4881a6583daSArchie Cobbs p = lbuf; 4891a6583daSArchie Cobbs } 4909b50d902SRodney W. Grimes *sp++ = '\0'; 4919b50d902SRodney W. Grimes size += sp - op; 4929b50d902SRodney W. Grimes s->new = xrealloc(text, size); 4939b50d902SRodney W. Grimes return (p); 4949b50d902SRodney W. Grimes } else if (*p == '\n') { 49573a08bb2SPhilippe Charnier errx(1, 49673a08bb2SPhilippe Charnier "%lu: %s: unescaped newline inside substitute pattern", linenum, fname); 4979b50d902SRodney W. Grimes /* NOTREACHED */ 4989b50d902SRodney W. Grimes } 4999b50d902SRodney W. Grimes *sp++ = *p; 5009b50d902SRodney W. Grimes } 5019b50d902SRodney W. Grimes size += sp - op; 5029b50d902SRodney W. Grimes if (asize - size < _POSIX2_LINE_MAX + 1) { 5039b50d902SRodney W. Grimes asize *= 2; 5041a6583daSArchie Cobbs text = xrealloc(text, asize); 5059b50d902SRodney W. Grimes } 5069b50d902SRodney W. Grimes } while (cu_fgets(p = lbuf, sizeof(lbuf))); 50773a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated substitute in regular expression", 50873a08bb2SPhilippe Charnier linenum, fname); 5099b50d902SRodney W. Grimes /* NOTREACHED */ 5109b50d902SRodney W. Grimes } 5119b50d902SRodney W. Grimes 5129b50d902SRodney W. Grimes /* 5139b50d902SRodney W. Grimes * Compile the flags of the s command 5149b50d902SRodney W. Grimes */ 5159b50d902SRodney W. Grimes static char * 5169b50d902SRodney W. Grimes compile_flags(p, s) 5179b50d902SRodney W. Grimes char *p; 5189b50d902SRodney W. Grimes struct s_subst *s; 5199b50d902SRodney W. Grimes { 5209b50d902SRodney W. Grimes int gn; /* True if we have seen g or n */ 5219b50d902SRodney W. Grimes char wfile[_POSIX2_LINE_MAX + 1], *q; 5229b50d902SRodney W. Grimes 5239b50d902SRodney W. Grimes s->n = 1; /* Default */ 5249b50d902SRodney W. Grimes s->p = 0; 5259b50d902SRodney W. Grimes s->wfile = NULL; 5269b50d902SRodney W. Grimes s->wfd = -1; 5279b50d902SRodney W. Grimes for (gn = 0;;) { 5289b50d902SRodney W. Grimes EATSPACE(); /* EXTENSION */ 5299b50d902SRodney W. Grimes switch (*p) { 5309b50d902SRodney W. Grimes case 'g': 5319b50d902SRodney W. Grimes if (gn) 53273a08bb2SPhilippe Charnier errx(1, 53373a08bb2SPhilippe Charnier "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 5349b50d902SRodney W. Grimes gn = 1; 5359b50d902SRodney W. Grimes s->n = 0; 5369b50d902SRodney W. Grimes break; 5379b50d902SRodney W. Grimes case '\0': 5389b50d902SRodney W. Grimes case '\n': 5399b50d902SRodney W. Grimes case ';': 5409b50d902SRodney W. Grimes return (p); 5419b50d902SRodney W. Grimes case 'p': 5429b50d902SRodney W. Grimes s->p = 1; 5439b50d902SRodney W. Grimes break; 5449b50d902SRodney W. Grimes case '1': case '2': case '3': 5459b50d902SRodney W. Grimes case '4': case '5': case '6': 5469b50d902SRodney W. Grimes case '7': case '8': case '9': 5479b50d902SRodney W. Grimes if (gn) 54873a08bb2SPhilippe Charnier errx(1, 54973a08bb2SPhilippe Charnier "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 5509b50d902SRodney W. Grimes gn = 1; 5519b50d902SRodney W. Grimes /* XXX Check for overflow */ 5529b50d902SRodney W. Grimes s->n = (int)strtol(p, &p, 10); 5539b50d902SRodney W. Grimes break; 5549b50d902SRodney W. Grimes case 'w': 5559b50d902SRodney W. Grimes p++; 5569b50d902SRodney W. Grimes #ifdef HISTORIC_PRACTICE 5579b50d902SRodney W. Grimes if (*p != ' ') { 55873a08bb2SPhilippe Charnier warnx("%lu: %s: space missing before w wfile", linenum, fname); 5599b50d902SRodney W. Grimes return (p); 5609b50d902SRodney W. Grimes } 5619b50d902SRodney W. Grimes #endif 5629b50d902SRodney W. Grimes EATSPACE(); 5639b50d902SRodney W. Grimes q = wfile; 5649b50d902SRodney W. Grimes while (*p) { 5659b50d902SRodney W. Grimes if (*p == '\n') 5669b50d902SRodney W. Grimes break; 5679b50d902SRodney W. Grimes *q++ = *p++; 5689b50d902SRodney W. Grimes } 5699b50d902SRodney W. Grimes *q = '\0'; 5709b50d902SRodney W. Grimes if (q == wfile) 57173a08bb2SPhilippe Charnier errx(1, "%lu: %s: no wfile specified", linenum, fname); 5729b50d902SRodney W. Grimes s->wfile = strdup(wfile); 5739b50d902SRodney W. Grimes if (!aflag && (s->wfd = open(wfile, 5749b50d902SRodney W. Grimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 5759b50d902SRodney W. Grimes DEFFILEMODE)) == -1) 57673a08bb2SPhilippe Charnier err(1, "%s", wfile); 5779b50d902SRodney W. Grimes return (p); 5789b50d902SRodney W. Grimes default: 57973a08bb2SPhilippe Charnier errx(1, "%lu: %s: bad flag in substitute command: '%c'", 58073a08bb2SPhilippe Charnier linenum, fname, *p); 5819b50d902SRodney W. Grimes break; 5829b50d902SRodney W. Grimes } 5839b50d902SRodney W. Grimes p++; 5849b50d902SRodney W. Grimes } 5859b50d902SRodney W. Grimes } 5869b50d902SRodney W. Grimes 5879b50d902SRodney W. Grimes /* 5889b50d902SRodney W. Grimes * Compile a translation set of strings into a lookup table. 5899b50d902SRodney W. Grimes */ 5909b50d902SRodney W. Grimes static char * 5919b50d902SRodney W. Grimes compile_tr(p, transtab) 5929b50d902SRodney W. Grimes char *p; 5939b50d902SRodney W. Grimes char **transtab; 5949b50d902SRodney W. Grimes { 5959b50d902SRodney W. Grimes int i; 5969b50d902SRodney W. Grimes char *lt, *op, *np; 5979b50d902SRodney W. Grimes char old[_POSIX2_LINE_MAX + 1]; 5989b50d902SRodney W. Grimes char new[_POSIX2_LINE_MAX + 1]; 5999b50d902SRodney W. Grimes 6009b50d902SRodney W. Grimes if (*p == '\0' || *p == '\\') 60173a08bb2SPhilippe Charnier errx(1, 60273a08bb2SPhilippe Charnier "%lu: %s: transform pattern can not be delimited by newline or backslash", 60373a08bb2SPhilippe Charnier linenum, fname); 6049b50d902SRodney W. Grimes p = compile_delimited(p, old); 60573a08bb2SPhilippe Charnier if (p == NULL) 60673a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated transform source string", 60773a08bb2SPhilippe Charnier linenum, fname); 6089b50d902SRodney W. Grimes p = compile_delimited(--p, new); 60973a08bb2SPhilippe Charnier if (p == NULL) 61073a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated transform target string", 61173a08bb2SPhilippe Charnier linenum, fname); 6129b50d902SRodney W. Grimes EATSPACE(); 61373a08bb2SPhilippe Charnier if (strlen(new) != strlen(old)) 61473a08bb2SPhilippe Charnier errx(1, "%lu: %s: transform strings are not the same length", 61573a08bb2SPhilippe Charnier linenum, fname); 6169b50d902SRodney W. Grimes /* We assume characters are 8 bits */ 6179b50d902SRodney W. Grimes lt = xmalloc(UCHAR_MAX); 6189b50d902SRodney W. Grimes for (i = 0; i <= UCHAR_MAX; i++) 6199b50d902SRodney W. Grimes lt[i] = (char)i; 6209b50d902SRodney W. Grimes for (op = old, np = new; *op; op++, np++) 6219b50d902SRodney W. Grimes lt[(u_char)*op] = *np; 6229b50d902SRodney W. Grimes *transtab = lt; 6239b50d902SRodney W. Grimes return (p); 6249b50d902SRodney W. Grimes } 6259b50d902SRodney W. Grimes 6269b50d902SRodney W. Grimes /* 6279b50d902SRodney W. Grimes * Compile the text following an a or i command. 6289b50d902SRodney W. Grimes */ 6299b50d902SRodney W. Grimes static char * 6309b50d902SRodney W. Grimes compile_text() 6319b50d902SRodney W. Grimes { 63249e65599SBruce Evans int asize, esc_nl, size; 6339b50d902SRodney W. Grimes char *text, *p, *op, *s; 6349b50d902SRodney W. Grimes char lbuf[_POSIX2_LINE_MAX + 1]; 6359b50d902SRodney W. Grimes 6369b50d902SRodney W. Grimes asize = 2 * _POSIX2_LINE_MAX + 1; 6379b50d902SRodney W. Grimes text = xmalloc(asize); 6389b50d902SRodney W. Grimes size = 0; 6399b50d902SRodney W. Grimes while (cu_fgets(lbuf, sizeof(lbuf))) { 6409b50d902SRodney W. Grimes op = s = text + size; 6419b50d902SRodney W. Grimes p = lbuf; 6429b50d902SRodney W. Grimes EATSPACE(); 64349e65599SBruce Evans for (esc_nl = 0; *p != '\0'; p++) { 64449e65599SBruce Evans if (*p == '\\' && p[1] != '\0' && *++p == '\n') 64549e65599SBruce Evans esc_nl = 1; 6469b50d902SRodney W. Grimes *s++ = *p; 6479b50d902SRodney W. Grimes } 6489b50d902SRodney W. Grimes size += s - op; 64949e65599SBruce Evans if (!esc_nl) { 6509b50d902SRodney W. Grimes *s = '\0'; 6519b50d902SRodney W. Grimes break; 6529b50d902SRodney W. Grimes } 6539b50d902SRodney W. Grimes if (asize - size < _POSIX2_LINE_MAX + 1) { 6549b50d902SRodney W. Grimes asize *= 2; 6559b50d902SRodney W. Grimes text = xmalloc(asize); 6569b50d902SRodney W. Grimes } 6579b50d902SRodney W. Grimes } 65813ede3c0SBrian Somers text[size] = '\0'; 6599b50d902SRodney W. Grimes return (xrealloc(text, size + 1)); 6609b50d902SRodney W. Grimes } 6619b50d902SRodney W. Grimes 6629b50d902SRodney W. Grimes /* 6639b50d902SRodney W. Grimes * Get an address and return a pointer to the first character after 6649b50d902SRodney W. Grimes * it. Fill the structure pointed to according to the address. 6659b50d902SRodney W. Grimes */ 6669b50d902SRodney W. Grimes static char * 6679b50d902SRodney W. Grimes compile_addr(p, a) 6689b50d902SRodney W. Grimes char *p; 6699b50d902SRodney W. Grimes struct s_addr *a; 6709b50d902SRodney W. Grimes { 6719b50d902SRodney W. Grimes char *end; 6729b50d902SRodney W. Grimes 6739b50d902SRodney W. Grimes switch (*p) { 6749b50d902SRodney W. Grimes case '\\': /* Context address */ 6759b50d902SRodney W. Grimes ++p; 6769b50d902SRodney W. Grimes /* FALLTHROUGH */ 6779b50d902SRodney W. Grimes case '/': /* Context address */ 6789b50d902SRodney W. Grimes p = compile_re(p, &a->u.r); 6799b50d902SRodney W. Grimes if (p == NULL) 68073a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated regular expression", linenum, fname); 6819b50d902SRodney W. Grimes a->type = AT_RE; 6829b50d902SRodney W. Grimes return (p); 6839b50d902SRodney W. Grimes 6849b50d902SRodney W. Grimes case '$': /* Last line */ 6859b50d902SRodney W. Grimes a->type = AT_LAST; 6869b50d902SRodney W. Grimes return (p + 1); 6879b50d902SRodney W. Grimes /* Line number */ 6889b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 6899b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 6909b50d902SRodney W. Grimes a->type = AT_LINE; 6919b50d902SRodney W. Grimes a->u.l = strtol(p, &end, 10); 6929b50d902SRodney W. Grimes return (end); 6939b50d902SRodney W. Grimes default: 69473a08bb2SPhilippe Charnier errx(1, "%lu: %s: expected context address", linenum, fname); 6959b50d902SRodney W. Grimes return (NULL); 6969b50d902SRodney W. Grimes } 6979b50d902SRodney W. Grimes } 6989b50d902SRodney W. Grimes 6999b50d902SRodney W. Grimes /* 7009b50d902SRodney W. Grimes * duptoeol -- 7019b50d902SRodney W. Grimes * Return a copy of all the characters up to \n or \0. 7029b50d902SRodney W. Grimes */ 7039b50d902SRodney W. Grimes static char * 7049b50d902SRodney W. Grimes duptoeol(s, ctype) 7059b50d902SRodney W. Grimes register char *s; 7069b50d902SRodney W. Grimes char *ctype; 7079b50d902SRodney W. Grimes { 7089b50d902SRodney W. Grimes size_t len; 7099b50d902SRodney W. Grimes int ws; 7109b50d902SRodney W. Grimes char *start; 7119b50d902SRodney W. Grimes 7129b50d902SRodney W. Grimes ws = 0; 7139b50d902SRodney W. Grimes for (start = s; *s != '\0' && *s != '\n'; ++s) 714726aebe5SAndrey A. Chernov ws = isspace((unsigned char)*s); 7159b50d902SRodney W. Grimes *s = '\0'; 7169b50d902SRodney W. Grimes if (ws) 71773a08bb2SPhilippe Charnier warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); 7189b50d902SRodney W. Grimes len = s - start + 1; 7199b50d902SRodney W. Grimes return (memmove(xmalloc(len), start, len)); 7209b50d902SRodney W. Grimes } 7219b50d902SRodney W. Grimes 7229b50d902SRodney W. Grimes /* 7239b50d902SRodney W. Grimes * Convert goto label names to addresses, and count a and r commands, in 7249b50d902SRodney W. Grimes * the given subset of the script. Free the memory used by labels in b 7259b50d902SRodney W. Grimes * and t commands (but not by :). 7269b50d902SRodney W. Grimes * 7279b50d902SRodney W. Grimes * TODO: Remove } nodes 7289b50d902SRodney W. Grimes */ 7299b50d902SRodney W. Grimes static void 7309b50d902SRodney W. Grimes fixuplabel(cp, end) 7319b50d902SRodney W. Grimes struct s_command *cp, *end; 7329b50d902SRodney W. Grimes { 7339b50d902SRodney W. Grimes 7349b50d902SRodney W. Grimes for (; cp != end; cp = cp->next) 7359b50d902SRodney W. Grimes switch (cp->code) { 7369b50d902SRodney W. Grimes case 'a': 7379b50d902SRodney W. Grimes case 'r': 7389b50d902SRodney W. Grimes appendnum++; 7399b50d902SRodney W. Grimes break; 7409b50d902SRodney W. Grimes case 'b': 7419b50d902SRodney W. Grimes case 't': 7429b50d902SRodney W. Grimes /* Resolve branch target. */ 7439b50d902SRodney W. Grimes if (cp->t == NULL) { 7449b50d902SRodney W. Grimes cp->u.c = NULL; 7459b50d902SRodney W. Grimes break; 7469b50d902SRodney W. Grimes } 7479b50d902SRodney W. Grimes if ((cp->u.c = findlabel(cp->t)) == NULL) 74873a08bb2SPhilippe Charnier errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); 7499b50d902SRodney W. Grimes free(cp->t); 7509b50d902SRodney W. Grimes break; 7519b50d902SRodney W. Grimes case '{': 7529b50d902SRodney W. Grimes /* Do interior commands. */ 7539b50d902SRodney W. Grimes fixuplabel(cp->u.c, cp->next); 7549b50d902SRodney W. Grimes break; 7559b50d902SRodney W. Grimes } 7569b50d902SRodney W. Grimes } 7579b50d902SRodney W. Grimes 7589b50d902SRodney W. Grimes /* 7599b50d902SRodney W. Grimes * Associate the given command label for later lookup. 7609b50d902SRodney W. Grimes */ 7619b50d902SRodney W. Grimes static void 7629b50d902SRodney W. Grimes enterlabel(cp) 7639b50d902SRodney W. Grimes struct s_command *cp; 7649b50d902SRodney W. Grimes { 7659b50d902SRodney W. Grimes register struct labhash **lhp, *lh; 7669b50d902SRodney W. Grimes register u_char *p; 7679b50d902SRodney W. Grimes register u_int h, c; 7689b50d902SRodney W. Grimes 7699b50d902SRodney W. Grimes for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) 7709b50d902SRodney W. Grimes h = (h << 5) + h + c; 7719b50d902SRodney W. Grimes lhp = &labels[h & LHMASK]; 7729b50d902SRodney W. Grimes for (lh = *lhp; lh != NULL; lh = lh->lh_next) 7739b50d902SRodney W. Grimes if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) 77473a08bb2SPhilippe Charnier errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); 7759b50d902SRodney W. Grimes lh = xmalloc(sizeof *lh); 7769b50d902SRodney W. Grimes lh->lh_next = *lhp; 7779b50d902SRodney W. Grimes lh->lh_hash = h; 7789b50d902SRodney W. Grimes lh->lh_cmd = cp; 7799b50d902SRodney W. Grimes lh->lh_ref = 0; 7809b50d902SRodney W. Grimes *lhp = lh; 7819b50d902SRodney W. Grimes } 7829b50d902SRodney W. Grimes 7839b50d902SRodney W. Grimes /* 7849b50d902SRodney W. Grimes * Find the label contained in the command l in the command linked 7859b50d902SRodney W. Grimes * list cp. L is excluded from the search. Return NULL if not found. 7869b50d902SRodney W. Grimes */ 7879b50d902SRodney W. Grimes static struct s_command * 7889b50d902SRodney W. Grimes findlabel(name) 7899b50d902SRodney W. Grimes char *name; 7909b50d902SRodney W. Grimes { 7919b50d902SRodney W. Grimes register struct labhash *lh; 7929b50d902SRodney W. Grimes register u_char *p; 7939b50d902SRodney W. Grimes register u_int h, c; 7949b50d902SRodney W. Grimes 7959b50d902SRodney W. Grimes for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) 7969b50d902SRodney W. Grimes h = (h << 5) + h + c; 7979b50d902SRodney W. Grimes for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { 7989b50d902SRodney W. Grimes if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { 7999b50d902SRodney W. Grimes lh->lh_ref = 1; 8009b50d902SRodney W. Grimes return (lh->lh_cmd); 8019b50d902SRodney W. Grimes } 8029b50d902SRodney W. Grimes } 8039b50d902SRodney W. Grimes return (NULL); 8049b50d902SRodney W. Grimes } 8059b50d902SRodney W. Grimes 8069b50d902SRodney W. Grimes /* 8079b50d902SRodney W. Grimes * Warn about any unused labels. As a side effect, release the label hash 8089b50d902SRodney W. Grimes * table space. 8099b50d902SRodney W. Grimes */ 8109b50d902SRodney W. Grimes static void 8119b50d902SRodney W. Grimes uselabel() 8129b50d902SRodney W. Grimes { 8139b50d902SRodney W. Grimes register struct labhash *lh, *next; 8149b50d902SRodney W. Grimes register int i; 8159b50d902SRodney W. Grimes 8169b50d902SRodney W. Grimes for (i = 0; i < LHSZ; i++) { 8179b50d902SRodney W. Grimes for (lh = labels[i]; lh != NULL; lh = next) { 8189b50d902SRodney W. Grimes next = lh->lh_next; 8199b50d902SRodney W. Grimes if (!lh->lh_ref) 82073a08bb2SPhilippe Charnier warnx("%lu: %s: unused label '%s'", 82173a08bb2SPhilippe Charnier linenum, fname, lh->lh_cmd->t); 8229b50d902SRodney W. Grimes free(lh); 8239b50d902SRodney W. Grimes } 8249b50d902SRodney W. Grimes } 8259b50d902SRodney W. Grimes } 826