19b50d902SRodney W. Grimes /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1992 Diomidis Spinellis. 59b50d902SRodney W. Grimes * Copyright (c) 1992, 1993 69b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 79b50d902SRodney W. Grimes * 89b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 99b50d902SRodney W. Grimes * Diomidis Spinellis of Imperial College, University of London. 109b50d902SRodney W. Grimes * 119b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 129b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 139b50d902SRodney W. Grimes * are met: 149b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 159b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 169b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 179b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 189b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 19fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 209b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 219b50d902SRodney W. Grimes * without specific prior written permission. 229b50d902SRodney W. Grimes * 239b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 249b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 259b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 269b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 279b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 289b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 299b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 309b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 319b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 329b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 339b50d902SRodney W. Grimes * SUCH DAMAGE. 349b50d902SRodney W. Grimes */ 359b50d902SRodney W. Grimes 36e74bf75fSMark Murray #include <sys/cdefs.h> 37e74bf75fSMark Murray __FBSDID("$FreeBSD$"); 38e74bf75fSMark Murray 399b50d902SRodney W. Grimes #ifndef lint 40e74bf75fSMark Murray static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; 4173a08bb2SPhilippe Charnier #endif 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes #include <sys/types.h> 449b50d902SRodney W. Grimes #include <sys/stat.h> 459b50d902SRodney W. Grimes 469b50d902SRodney W. Grimes #include <ctype.h> 4773a08bb2SPhilippe Charnier #include <err.h> 4881a8648aSTim J. Robbins #include <errno.h> 499b50d902SRodney W. Grimes #include <fcntl.h> 509b50d902SRodney W. Grimes #include <limits.h> 519b50d902SRodney W. Grimes #include <regex.h> 529b50d902SRodney W. Grimes #include <stdio.h> 539b50d902SRodney W. Grimes #include <stdlib.h> 549b50d902SRodney W. Grimes #include <string.h> 5581a8648aSTim J. Robbins #include <wchar.h> 569b50d902SRodney W. Grimes 579b50d902SRodney W. Grimes #include "defs.h" 589b50d902SRodney W. Grimes #include "extern.h" 599b50d902SRodney W. Grimes 609b50d902SRodney W. Grimes #define LHSZ 128 619b50d902SRodney W. Grimes #define LHMASK (LHSZ - 1) 629b50d902SRodney W. Grimes static struct labhash { 639b50d902SRodney W. Grimes struct labhash *lh_next; 649b50d902SRodney W. Grimes u_int lh_hash; 659b50d902SRodney W. Grimes struct s_command *lh_cmd; 669b50d902SRodney W. Grimes int lh_ref; 679b50d902SRodney W. Grimes } *labels[LHSZ]; 689b50d902SRodney W. Grimes 69249a8a80SPedro F. Giffuni static char *compile_addr(char *, struct s_addr *); 70249a8a80SPedro F. Giffuni static char *compile_ccl(char **, char *); 71249a8a80SPedro F. Giffuni static char *compile_delimited(char *, char *, int); 72249a8a80SPedro F. Giffuni static char *compile_flags(char *, struct s_subst *); 73249a8a80SPedro F. Giffuni static regex_t *compile_re(char *, int); 74249a8a80SPedro F. Giffuni static char *compile_subst(char *, struct s_subst *); 75249a8a80SPedro F. Giffuni static char *compile_text(void); 76249a8a80SPedro F. Giffuni static char *compile_tr(char *, struct s_tr **); 779b50d902SRodney W. Grimes static struct s_command 783f330d7dSWarner Losh **compile_stream(struct s_command **); 79249a8a80SPedro F. Giffuni static char *duptoeol(char *, const char *); 803f330d7dSWarner Losh static void enterlabel(struct s_command *); 819b50d902SRodney W. Grimes static struct s_command 82249a8a80SPedro F. Giffuni *findlabel(char *); 83249a8a80SPedro F. Giffuni static void fixuplabel(struct s_command *, struct s_command *); 843f330d7dSWarner Losh static void uselabel(void); 859b50d902SRodney W. Grimes 869b50d902SRodney W. Grimes /* 879b50d902SRodney W. Grimes * Command specification. This is used to drive the command parser. 889b50d902SRodney W. Grimes */ 899b50d902SRodney W. Grimes struct s_format { 909b50d902SRodney W. Grimes char code; /* Command code */ 919b50d902SRodney W. Grimes int naddr; /* Number of address args */ 929b50d902SRodney W. Grimes enum e_args args; /* Argument type */ 939b50d902SRodney W. Grimes }; 949b50d902SRodney W. Grimes 959b50d902SRodney W. Grimes static struct s_format cmd_fmts[] = { 969b50d902SRodney W. Grimes {'{', 2, GROUP}, 97ce19262dSJordan K. Hubbard {'}', 0, ENDGROUP}, 989b50d902SRodney W. Grimes {'a', 1, TEXT}, 999b50d902SRodney W. Grimes {'b', 2, BRANCH}, 1009b50d902SRodney W. Grimes {'c', 2, TEXT}, 1019b50d902SRodney W. Grimes {'d', 2, EMPTY}, 1029b50d902SRodney W. Grimes {'D', 2, EMPTY}, 1039b50d902SRodney W. Grimes {'g', 2, EMPTY}, 1049b50d902SRodney W. Grimes {'G', 2, EMPTY}, 1059b50d902SRodney W. Grimes {'h', 2, EMPTY}, 1069b50d902SRodney W. Grimes {'H', 2, EMPTY}, 1079b50d902SRodney W. Grimes {'i', 1, TEXT}, 1089b50d902SRodney W. Grimes {'l', 2, EMPTY}, 1099b50d902SRodney W. Grimes {'n', 2, EMPTY}, 1109b50d902SRodney W. Grimes {'N', 2, EMPTY}, 1119b50d902SRodney W. Grimes {'p', 2, EMPTY}, 1129b50d902SRodney W. Grimes {'P', 2, EMPTY}, 1139b50d902SRodney W. Grimes {'q', 1, EMPTY}, 1149b50d902SRodney W. Grimes {'r', 1, RFILE}, 1159b50d902SRodney W. Grimes {'s', 2, SUBST}, 1169b50d902SRodney W. Grimes {'t', 2, BRANCH}, 1179b50d902SRodney W. Grimes {'w', 2, WFILE}, 1189b50d902SRodney W. Grimes {'x', 2, EMPTY}, 1199b50d902SRodney W. Grimes {'y', 2, TR}, 1209b50d902SRodney W. Grimes {'!', 2, NONSEL}, 1219b50d902SRodney W. Grimes {':', 0, LABEL}, 1229b50d902SRodney W. Grimes {'#', 0, COMMENT}, 1239b50d902SRodney W. Grimes {'=', 1, EMPTY}, 1249b50d902SRodney W. Grimes {'\0', 0, COMMENT}, 1259b50d902SRodney W. Grimes }; 1269b50d902SRodney W. Grimes 1279b50d902SRodney W. Grimes /* The compiled program. */ 1289b50d902SRodney W. Grimes struct s_command *prog; 1299b50d902SRodney W. Grimes 1309b50d902SRodney W. Grimes /* 1319b50d902SRodney W. Grimes * Compile the program into prog. 1329b50d902SRodney W. Grimes * Initialise appends. 1339b50d902SRodney W. Grimes */ 1349b50d902SRodney W. Grimes void 135e6478125SDag-Erling Smørgrav compile(void) 1369b50d902SRodney W. Grimes { 137ce19262dSJordan K. Hubbard *compile_stream(&prog) = NULL; 1389b50d902SRodney W. Grimes fixuplabel(prog, NULL); 1399b50d902SRodney W. Grimes uselabel(); 1400ea56610SMike Heffner if (appendnum == 0) 1410ea56610SMike Heffner appends = NULL; 1420ea56610SMike Heffner else if ((appends = malloc(sizeof(struct s_appends) * appendnum)) == 1430ea56610SMike Heffner NULL) 1448e33c0a0SDavid E. O'Brien err(1, "malloc"); 1458e33c0a0SDavid E. O'Brien if ((match = malloc((maxnsub + 1) * sizeof(regmatch_t))) == NULL) 1468e33c0a0SDavid E. O'Brien err(1, "malloc"); 1479b50d902SRodney W. Grimes } 1489b50d902SRodney W. Grimes 1499b50d902SRodney W. Grimes #define EATSPACE() do { \ 150249a8a80SPedro F. Giffuni if (p) \ 151726aebe5SAndrey A. Chernov while (*p && isspace((unsigned char)*p)) \ 1529b50d902SRodney W. Grimes p++; \ 1539b50d902SRodney W. Grimes } while (0) 1549b50d902SRodney W. Grimes 1559b50d902SRodney W. Grimes static struct s_command ** 156e6478125SDag-Erling Smørgrav compile_stream(struct s_command **link) 1579b50d902SRodney W. Grimes { 158249a8a80SPedro F. Giffuni char *p; 159249a8a80SPedro F. Giffuni static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 160ce19262dSJordan K. Hubbard struct s_command *cmd, *cmd2, *stack; 1619b50d902SRodney W. Grimes struct s_format *fp; 162bdd72b70SSuleiman Souhlal char re[_POSIX2_LINE_MAX + 1]; 1639b50d902SRodney W. Grimes int naddr; /* Number of addresses */ 1649b50d902SRodney W. Grimes 165e55a6575SPedro F. Giffuni stack = NULL; 1669b50d902SRodney W. Grimes for (;;) { 167249a8a80SPedro F. Giffuni if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) { 168e55a6575SPedro F. Giffuni if (stack != NULL) 16973a08bb2SPhilippe Charnier errx(1, "%lu: %s: unexpected EOF (pending }'s)", 17073a08bb2SPhilippe Charnier linenum, fname); 1719b50d902SRodney W. Grimes return (link); 1729b50d902SRodney W. Grimes } 1739b50d902SRodney W. Grimes 174249a8a80SPedro F. Giffuni semicolon: EATSPACE(); 175249a8a80SPedro F. Giffuni if (p) { 176249a8a80SPedro F. Giffuni if (*p == '#' || *p == '\0') 177249a8a80SPedro F. Giffuni continue; 178249a8a80SPedro F. Giffuni else if (*p == ';') { 1790467aed3STim J. Robbins p++; 1800467aed3STim J. Robbins goto semicolon; 1810467aed3STim J. Robbins } 182249a8a80SPedro F. Giffuni } 1838e33c0a0SDavid E. O'Brien if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL) 1848e33c0a0SDavid E. O'Brien err(1, "malloc"); 1859b50d902SRodney W. Grimes link = &cmd->next; 186f879e8d9SBrian Somers cmd->startline = cmd->nonsel = 0; 1879b50d902SRodney W. Grimes /* First parse the addresses */ 1889b50d902SRodney W. Grimes naddr = 0; 1899b50d902SRodney W. Grimes 1909b50d902SRodney W. Grimes /* Valid characters to start an address */ 1919b50d902SRodney W. Grimes #define addrchar(c) (strchr("0123456789/\\$", (c))) 1929b50d902SRodney W. Grimes if (addrchar(*p)) { 1939b50d902SRodney W. Grimes naddr++; 1948e33c0a0SDavid E. O'Brien if ((cmd->a1 = malloc(sizeof(struct s_addr))) == NULL) 1958e33c0a0SDavid E. O'Brien err(1, "malloc"); 1969b50d902SRodney W. Grimes p = compile_addr(p, cmd->a1); 1979b50d902SRodney W. Grimes EATSPACE(); /* EXTENSION */ 1989b50d902SRodney W. Grimes if (*p == ',') { 1999b50d902SRodney W. Grimes p++; 2009b50d902SRodney W. Grimes EATSPACE(); /* EXTENSION */ 201ce19262dSJordan K. Hubbard naddr++; 2028e33c0a0SDavid E. O'Brien if ((cmd->a2 = malloc(sizeof(struct s_addr))) 2038e33c0a0SDavid E. O'Brien == NULL) 2048e33c0a0SDavid E. O'Brien err(1, "malloc"); 2059b50d902SRodney W. Grimes p = compile_addr(p, cmd->a2); 206ce19262dSJordan K. Hubbard EATSPACE(); 207ce19262dSJordan K. Hubbard } else 208e55a6575SPedro F. Giffuni cmd->a2 = NULL; 209ce19262dSJordan K. Hubbard } else 210e55a6575SPedro F. Giffuni cmd->a1 = cmd->a2 = NULL; 2119b50d902SRodney W. Grimes 2129b50d902SRodney W. Grimes nonsel: /* Now parse the command */ 213249a8a80SPedro F. Giffuni if (!*p) 21473a08bb2SPhilippe Charnier errx(1, "%lu: %s: command expected", linenum, fname); 2159b50d902SRodney W. Grimes cmd->code = *p; 2169b50d902SRodney W. Grimes for (fp = cmd_fmts; fp->code; fp++) 2179b50d902SRodney W. Grimes if (fp->code == *p) 2189b50d902SRodney W. Grimes break; 2199b50d902SRodney W. Grimes if (!fp->code) 220249a8a80SPedro F. Giffuni errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); 2219b50d902SRodney W. Grimes if (naddr > fp->naddr) 22273a08bb2SPhilippe Charnier errx(1, 22373a08bb2SPhilippe Charnier "%lu: %s: command %c expects up to %d address(es), found %d", 22473a08bb2SPhilippe Charnier linenum, fname, *p, fp->naddr, naddr); 2259b50d902SRodney W. Grimes switch (fp->args) { 2269b50d902SRodney W. Grimes case NONSEL: /* ! */ 2279b50d902SRodney W. Grimes p++; 228ce19262dSJordan K. Hubbard EATSPACE(); 22946da6c48SDiomidis Spinellis cmd->nonsel = 1; 2309b50d902SRodney W. Grimes goto nonsel; 2319b50d902SRodney W. Grimes case GROUP: /* { */ 2329b50d902SRodney W. Grimes p++; 233249a8a80SPedro F. Giffuni EATSPACE(); 234ce19262dSJordan K. Hubbard cmd->next = stack; 235ce19262dSJordan K. Hubbard stack = cmd; 236ce19262dSJordan K. Hubbard link = &cmd->u.c; 237249a8a80SPedro F. Giffuni if (*p) 238ce19262dSJordan K. Hubbard goto semicolon; 2399b50d902SRodney W. Grimes break; 240ce19262dSJordan K. Hubbard case ENDGROUP: 241ce19262dSJordan K. Hubbard /* 242ce19262dSJordan K. Hubbard * Short-circuit command processing, since end of 243ce19262dSJordan K. Hubbard * group is really just a noop. 244ce19262dSJordan K. Hubbard */ 245ce19262dSJordan K. Hubbard cmd->nonsel = 1; 246e55a6575SPedro F. Giffuni if (stack == NULL) 24773a08bb2SPhilippe Charnier errx(1, "%lu: %s: unexpected }", linenum, fname); 248ce19262dSJordan K. Hubbard cmd2 = stack; 249ce19262dSJordan K. Hubbard stack = cmd2->next; 250ce19262dSJordan K. Hubbard cmd2->next = cmd; 251ce19262dSJordan K. Hubbard /*FALLTHROUGH*/ 2529b50d902SRodney W. Grimes case EMPTY: /* d D g G h H l n N p P q x = \0 */ 2539b50d902SRodney W. Grimes p++; 254249a8a80SPedro F. Giffuni EATSPACE(); 2559b50d902SRodney W. Grimes if (*p == ';') { 2569b50d902SRodney W. Grimes p++; 2579b50d902SRodney W. Grimes link = &cmd->next; 2589b50d902SRodney W. Grimes goto semicolon; 2599b50d902SRodney W. Grimes } 260249a8a80SPedro F. Giffuni if (*p) 26173a08bb2SPhilippe Charnier errx(1, "%lu: %s: extra characters at the end of %c command", 26273a08bb2SPhilippe Charnier linenum, fname, cmd->code); 2639b50d902SRodney W. Grimes break; 2649b50d902SRodney W. Grimes case TEXT: /* a c i */ 2659b50d902SRodney W. Grimes p++; 2669b50d902SRodney W. Grimes EATSPACE(); 2679b50d902SRodney W. Grimes if (*p != '\\') 26873a08bb2SPhilippe Charnier errx(1, 26973a08bb2SPhilippe Charnier "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); 2709b50d902SRodney W. Grimes p++; 271249a8a80SPedro F. Giffuni EATSPACE(); 272249a8a80SPedro F. Giffuni if (*p) 27373a08bb2SPhilippe Charnier errx(1, 274249a8a80SPedro F. Giffuni "%lu: %s: extra characters after \\ at the end of %c command", 275249a8a80SPedro F. Giffuni linenum, fname, cmd->code); 276249a8a80SPedro F. Giffuni cmd->t = compile_text(); 2779b50d902SRodney W. Grimes break; 2789b50d902SRodney W. Grimes case COMMENT: /* \0 # */ 2799b50d902SRodney W. Grimes break; 2809b50d902SRodney W. Grimes case WFILE: /* w */ 2819b50d902SRodney W. Grimes p++; 2829b50d902SRodney W. Grimes EATSPACE(); 2839b50d902SRodney W. Grimes if (*p == '\0') 28473a08bb2SPhilippe Charnier errx(1, "%lu: %s: filename expected", linenum, fname); 285249a8a80SPedro F. Giffuni cmd->t = duptoeol(p, "w command"); 2869b50d902SRodney W. Grimes if (aflag) 2879b50d902SRodney W. Grimes cmd->u.fd = -1; 288249a8a80SPedro F. Giffuni else if ((cmd->u.fd = open(p, 2899b50d902SRodney W. Grimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 2909b50d902SRodney W. Grimes DEFFILEMODE)) == -1) 29173a08bb2SPhilippe Charnier err(1, "%s", p); 2929b50d902SRodney W. Grimes break; 2939b50d902SRodney W. Grimes case RFILE: /* r */ 2949b50d902SRodney W. Grimes p++; 2959b50d902SRodney W. Grimes EATSPACE(); 2969b50d902SRodney W. Grimes if (*p == '\0') 29773a08bb2SPhilippe Charnier errx(1, "%lu: %s: filename expected", linenum, fname); 2989b50d902SRodney W. Grimes else 299249a8a80SPedro F. Giffuni cmd->t = duptoeol(p, "read command"); 3009b50d902SRodney W. Grimes break; 3019b50d902SRodney W. Grimes case BRANCH: /* b t */ 3029b50d902SRodney W. Grimes p++; 303249a8a80SPedro F. Giffuni EATSPACE(); 304249a8a80SPedro F. Giffuni if (*p == '\0') 3059b50d902SRodney W. Grimes cmd->t = NULL; 3069b50d902SRodney W. Grimes else 307249a8a80SPedro F. Giffuni cmd->t = duptoeol(p, "branch"); 3089b50d902SRodney W. Grimes break; 3099b50d902SRodney W. Grimes case LABEL: /* : */ 3109b50d902SRodney W. Grimes p++; 3119b50d902SRodney W. Grimes EATSPACE(); 312249a8a80SPedro F. Giffuni cmd->t = duptoeol(p, "label"); 313249a8a80SPedro F. Giffuni if (strlen(p) == 0) 31473a08bb2SPhilippe Charnier errx(1, "%lu: %s: empty label", linenum, fname); 3159b50d902SRodney W. Grimes enterlabel(cmd); 3169b50d902SRodney W. Grimes break; 3179b50d902SRodney W. Grimes case SUBST: /* s */ 3189b50d902SRodney W. Grimes p++; 319249a8a80SPedro F. Giffuni if (*p == '\0' || *p == '\\') 32073a08bb2SPhilippe Charnier errx(1, 32173a08bb2SPhilippe Charnier "%lu: %s: substitute pattern can not be delimited by newline or backslash", 32273a08bb2SPhilippe Charnier linenum, fname); 323870945d8SXin LI if ((cmd->u.s = calloc(1, sizeof(struct s_subst))) == NULL) 3248e33c0a0SDavid E. O'Brien err(1, "malloc"); 32576570d0aSDiomidis Spinellis p = compile_delimited(p, re, 0); 3269b50d902SRodney W. Grimes if (p == NULL) 32773a08bb2SPhilippe Charnier errx(1, 32873a08bb2SPhilippe Charnier "%lu: %s: unterminated substitute pattern", linenum, fname); 329d3d0d3a3SHiroki Sato 330249a8a80SPedro F. Giffuni /* Compile RE with no case sensitivity temporarily */ 331249a8a80SPedro F. Giffuni if (*re == '\0') 332249a8a80SPedro F. Giffuni cmd->u.s->re = NULL; 333249a8a80SPedro F. Giffuni else 334249a8a80SPedro F. Giffuni cmd->u.s->re = compile_re(re, 0); 335870945d8SXin LI --p; 336870945d8SXin LI p = compile_subst(p, cmd->u.s); 337870945d8SXin LI p = compile_flags(p, cmd->u.s); 338d3d0d3a3SHiroki Sato 339249a8a80SPedro F. Giffuni /* Recompile RE with case sensitivity from "I" flag if any */ 340249a8a80SPedro F. Giffuni if (*re == '\0') 341249a8a80SPedro F. Giffuni cmd->u.s->re = NULL; 342249a8a80SPedro F. Giffuni else 343bdd72b70SSuleiman Souhlal cmd->u.s->re = compile_re(re, cmd->u.s->icase); 3449b50d902SRodney W. Grimes EATSPACE(); 3459b50d902SRodney W. Grimes if (*p == ';') { 3469b50d902SRodney W. Grimes p++; 3479b50d902SRodney W. Grimes link = &cmd->next; 3489b50d902SRodney W. Grimes goto semicolon; 3499b50d902SRodney W. Grimes } 3509b50d902SRodney W. Grimes break; 3519b50d902SRodney W. Grimes case TR: /* y */ 3529b50d902SRodney W. Grimes p++; 35381a8648aSTim J. Robbins p = compile_tr(p, &cmd->u.y); 3549b50d902SRodney W. Grimes EATSPACE(); 3559b50d902SRodney W. Grimes if (*p == ';') { 3569b50d902SRodney W. Grimes p++; 3579b50d902SRodney W. Grimes link = &cmd->next; 3589b50d902SRodney W. Grimes goto semicolon; 3599b50d902SRodney W. Grimes } 3609b50d902SRodney W. Grimes if (*p) 36173a08bb2SPhilippe Charnier errx(1, 36273a08bb2SPhilippe Charnier "%lu: %s: extra text at the end of a transform command", linenum, fname); 3639b50d902SRodney W. Grimes break; 3649b50d902SRodney W. Grimes } 3659b50d902SRodney W. Grimes } 3669b50d902SRodney W. Grimes } 3679b50d902SRodney W. Grimes 3689b50d902SRodney W. Grimes /* 369463a577bSEitan Adler * Get a delimited string. P points to the delimiter of the string; d points 3709b50d902SRodney W. Grimes * to a buffer area. Newline and delimiter escapes are processed; other 3719b50d902SRodney W. Grimes * escapes are ignored. 3729b50d902SRodney W. Grimes * 3739b50d902SRodney W. Grimes * Returns a pointer to the first character after the final delimiter or NULL 3749b50d902SRodney W. Grimes * in the case of a non-terminated string. The character array d is filled 3759b50d902SRodney W. Grimes * with the processed string. 3769b50d902SRodney W. Grimes */ 377249a8a80SPedro F. Giffuni static char * 378249a8a80SPedro F. Giffuni compile_delimited(char *p, char *d, int is_tr) 3799b50d902SRodney W. Grimes { 3809b50d902SRodney W. Grimes char c; 3819b50d902SRodney W. Grimes 3829b50d902SRodney W. Grimes c = *p++; 3839b50d902SRodney W. Grimes if (c == '\0') 3849b50d902SRodney W. Grimes return (NULL); 3859b50d902SRodney W. Grimes else if (c == '\\') 38673a08bb2SPhilippe Charnier errx(1, "%lu: %s: \\ can not be used as a string delimiter", 38773a08bb2SPhilippe Charnier linenum, fname); 3889b50d902SRodney W. Grimes else if (c == '\n') 38973a08bb2SPhilippe Charnier errx(1, "%lu: %s: newline can not be used as a string delimiter", 39073a08bb2SPhilippe Charnier linenum, fname); 3919b50d902SRodney W. Grimes while (*p) { 392128e6a12SDiomidis Spinellis if (*p == '[' && *p != c) { 393ce19262dSJordan K. Hubbard if ((d = compile_ccl(&p, d)) == NULL) 39473a08bb2SPhilippe Charnier errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname); 395ce19262dSJordan K. Hubbard continue; 396ce19262dSJordan K. Hubbard } else if (*p == '\\' && p[1] == '[') { 397ce19262dSJordan K. Hubbard *d++ = *p++; 398*43daed47SPedro F. Giffuni } else if (*p == '\\' && p[1] == c) 399*43daed47SPedro F. Giffuni p++; 400*43daed47SPedro F. Giffuni else if (*p == '\\' && p[1] == 'n') { 4019b50d902SRodney W. Grimes *d++ = '\n'; 4029b50d902SRodney W. Grimes p += 2; 4039b50d902SRodney W. Grimes continue; 40476570d0aSDiomidis Spinellis } else if (*p == '\\' && p[1] == '\\') { 40576570d0aSDiomidis Spinellis if (is_tr) 40676570d0aSDiomidis Spinellis p++; 40776570d0aSDiomidis Spinellis else 4089b50d902SRodney W. Grimes *d++ = *p++; 40976570d0aSDiomidis Spinellis } else if (*p == c) { 4109b50d902SRodney W. Grimes *d = '\0'; 4119b50d902SRodney W. Grimes return (p + 1); 4129b50d902SRodney W. Grimes } 4139b50d902SRodney W. Grimes *d++ = *p++; 4149b50d902SRodney W. Grimes } 4159b50d902SRodney W. Grimes return (NULL); 4169b50d902SRodney W. Grimes } 4179b50d902SRodney W. Grimes 418ce19262dSJordan K. Hubbard 419ce19262dSJordan K. Hubbard /* compile_ccl: expand a POSIX character class */ 420ce19262dSJordan K. Hubbard static char * 421249a8a80SPedro F. Giffuni compile_ccl(char **sp, char *t) 422ce19262dSJordan K. Hubbard { 423ce19262dSJordan K. Hubbard int c, d; 424249a8a80SPedro F. Giffuni char *s = *sp; 425ce19262dSJordan K. Hubbard 426ce19262dSJordan K. Hubbard *t++ = *s++; 427ce19262dSJordan K. Hubbard if (*s == '^') 428ce19262dSJordan K. Hubbard *t++ = *s++; 429ce19262dSJordan K. Hubbard if (*s == ']') 430ce19262dSJordan K. Hubbard *t++ = *s++; 431ce19262dSJordan K. Hubbard for (; *s && (*t = *s) != ']'; s++, t++) 432ce19262dSJordan K. Hubbard if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { 433ce19262dSJordan K. Hubbard *++t = *++s, t++, s++; 434ce19262dSJordan K. Hubbard for (c = *s; (*t = *s) != ']' || c != d; s++, t++) 435ce19262dSJordan K. Hubbard if ((c = *s) == '\0') 436ce19262dSJordan K. Hubbard return NULL; 437ac8f32ceSDiomidis Spinellis } 438ce19262dSJordan K. Hubbard return (*s == ']') ? *sp = ++s, ++t : NULL; 439ce19262dSJordan K. Hubbard } 440ce19262dSJordan K. Hubbard 4419b50d902SRodney W. Grimes /* 442bdd72b70SSuleiman Souhlal * Compiles the regular expression in RE and returns a pointer to the compiled 443bdd72b70SSuleiman Souhlal * regular expression. 4449b50d902SRodney W. Grimes * Cflags are passed to regcomp. 4459b50d902SRodney W. Grimes */ 446249a8a80SPedro F. Giffuni static regex_t * 447249a8a80SPedro F. Giffuni compile_re(char *re, int case_insensitive) 4489b50d902SRodney W. Grimes { 449bdd72b70SSuleiman Souhlal regex_t *rep; 450bdd72b70SSuleiman Souhlal int eval, flags; 4519b50d902SRodney W. Grimes 452bdd72b70SSuleiman Souhlal 453bdd72b70SSuleiman Souhlal flags = rflags; 454bdd72b70SSuleiman Souhlal if (case_insensitive) 455bdd72b70SSuleiman Souhlal flags |= REG_ICASE; 456bdd72b70SSuleiman Souhlal if ((rep = malloc(sizeof(regex_t))) == NULL) 4578e33c0a0SDavid E. O'Brien err(1, "malloc"); 458d3e5e11cSDavid Malone if ((eval = regcomp(rep, re, flags)) != 0) 45973a08bb2SPhilippe Charnier errx(1, "%lu: %s: RE error: %s", 460bdd72b70SSuleiman Souhlal linenum, fname, strregerror(eval, rep)); 461bdd72b70SSuleiman Souhlal if (maxnsub < rep->re_nsub) 462bdd72b70SSuleiman Souhlal maxnsub = rep->re_nsub; 463bdd72b70SSuleiman Souhlal return (rep); 4649b50d902SRodney W. Grimes } 4659b50d902SRodney W. Grimes 4669b50d902SRodney W. Grimes /* 4679b50d902SRodney W. Grimes * Compile the substitution string of a regular expression and set res to 4689b50d902SRodney W. Grimes * point to a saved copy of it. Nsub is the number of parenthesized regular 4699b50d902SRodney W. Grimes * expressions. 4709b50d902SRodney W. Grimes */ 471249a8a80SPedro F. Giffuni static char * 472249a8a80SPedro F. Giffuni compile_subst(char *p, struct s_subst *s) 4739b50d902SRodney W. Grimes { 474249a8a80SPedro F. Giffuni static char lbuf[_POSIX2_LINE_MAX + 1]; 475e74bf75fSMark Murray int asize, size; 476e74bf75fSMark Murray u_char ref; 4779b50d902SRodney W. Grimes char c, *text, *op, *sp; 478249a8a80SPedro F. Giffuni int more = 1, sawesc = 0; 4799b50d902SRodney W. Grimes 4809b50d902SRodney W. Grimes c = *p++; /* Terminator character */ 4819b50d902SRodney W. Grimes if (c == '\0') 4829b50d902SRodney W. Grimes return (NULL); 4839b50d902SRodney W. Grimes 4849b50d902SRodney W. Grimes s->maxbref = 0; 4859b50d902SRodney W. Grimes s->linenum = linenum; 4869b50d902SRodney W. Grimes asize = 2 * _POSIX2_LINE_MAX + 1; 4878e33c0a0SDavid E. O'Brien if ((text = malloc(asize)) == NULL) 4888e33c0a0SDavid E. O'Brien err(1, "malloc"); 4899b50d902SRodney W. Grimes size = 0; 4909b50d902SRodney W. Grimes do { 4919b50d902SRodney W. Grimes op = sp = text + size; 492249a8a80SPedro F. Giffuni for (; *p; p++) { 493f020c7faSBrian Feldman if (*p == '\\' || sawesc) { 494f020c7faSBrian Feldman /* 495f020c7faSBrian Feldman * If this is a continuation from the last 496f020c7faSBrian Feldman * buffer, we won't have a character to 497f020c7faSBrian Feldman * skip over. 498f020c7faSBrian Feldman */ 499f020c7faSBrian Feldman if (sawesc) 500f020c7faSBrian Feldman sawesc = 0; 501f020c7faSBrian Feldman else 5029b50d902SRodney W. Grimes p++; 503f020c7faSBrian Feldman 504f020c7faSBrian Feldman if (*p == '\0') { 505f020c7faSBrian Feldman /* 506f020c7faSBrian Feldman * This escaped character is continued 507f020c7faSBrian Feldman * in the next part of the line. Note 508f020c7faSBrian Feldman * this fact, then cause the loop to 509f020c7faSBrian Feldman * exit w/ normal EOL case and reenter 510f020c7faSBrian Feldman * above with the new buffer. 511f020c7faSBrian Feldman */ 512f020c7faSBrian Feldman sawesc = 1; 513f020c7faSBrian Feldman p--; 514249a8a80SPedro F. Giffuni continue; 515f020c7faSBrian Feldman } else if (strchr("123456789", *p) != NULL) { 5169b50d902SRodney W. Grimes *sp++ = '\\'; 5179b50d902SRodney W. Grimes ref = *p - '0'; 5189b50d902SRodney W. Grimes if (s->re != NULL && 5199b50d902SRodney W. Grimes ref > s->re->re_nsub) 52073a08bb2SPhilippe Charnier errx(1, "%lu: %s: \\%c not defined in the RE", 52173a08bb2SPhilippe Charnier linenum, fname, *p); 5229b50d902SRodney W. Grimes if (s->maxbref < ref) 5239b50d902SRodney W. Grimes s->maxbref = ref; 5249b50d902SRodney W. Grimes } else if (*p == '&' || *p == '\\') 5259b50d902SRodney W. Grimes *sp++ = '\\'; 5269b50d902SRodney W. Grimes } else if (*p == c) { 527c56690efSArchie Cobbs if (*++p == '\0' && more) { 528249a8a80SPedro F. Giffuni if (cu_fgets(lbuf, sizeof(lbuf), &more)) 529249a8a80SPedro F. Giffuni p = lbuf; 5301a6583daSArchie Cobbs } 5319b50d902SRodney W. Grimes *sp++ = '\0'; 5329b50d902SRodney W. Grimes size += sp - op; 5338e33c0a0SDavid E. O'Brien if ((s->new = realloc(text, size)) == NULL) 5348e33c0a0SDavid E. O'Brien err(1, "realloc"); 5359b50d902SRodney W. Grimes return (p); 5369b50d902SRodney W. Grimes } else if (*p == '\n') { 53773a08bb2SPhilippe Charnier errx(1, 53873a08bb2SPhilippe Charnier "%lu: %s: unescaped newline inside substitute pattern", linenum, fname); 5399b50d902SRodney W. Grimes /* NOTREACHED */ 5409b50d902SRodney W. Grimes } 5419b50d902SRodney W. Grimes *sp++ = *p; 5429b50d902SRodney W. Grimes } 5439b50d902SRodney W. Grimes size += sp - op; 5449b50d902SRodney W. Grimes if (asize - size < _POSIX2_LINE_MAX + 1) { 5459b50d902SRodney W. Grimes asize *= 2; 5468e33c0a0SDavid E. O'Brien if ((text = realloc(text, asize)) == NULL) 5478e33c0a0SDavid E. O'Brien err(1, "realloc"); 5489b50d902SRodney W. Grimes } 549249a8a80SPedro F. Giffuni } while (cu_fgets(p = lbuf, sizeof(lbuf), &more) != NULL); 55073a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated substitute in regular expression", 55173a08bb2SPhilippe Charnier linenum, fname); 5529b50d902SRodney W. Grimes /* NOTREACHED */ 5539b50d902SRodney W. Grimes } 5549b50d902SRodney W. Grimes 5559b50d902SRodney W. Grimes /* 5569b50d902SRodney W. Grimes * Compile the flags of the s command 5579b50d902SRodney W. Grimes */ 558249a8a80SPedro F. Giffuni static char * 559249a8a80SPedro F. Giffuni compile_flags(char *p, struct s_subst *s) 5609b50d902SRodney W. Grimes { 5619b50d902SRodney W. Grimes int gn; /* True if we have seen g or n */ 562d432588eSDiomidis Spinellis unsigned long nval; 563249a8a80SPedro F. Giffuni char wfile[_POSIX2_LINE_MAX + 1], *q, *eq; 5649b50d902SRodney W. Grimes 5659b50d902SRodney W. Grimes s->n = 1; /* Default */ 5669b50d902SRodney W. Grimes s->p = 0; 5679b50d902SRodney W. Grimes s->wfile = NULL; 5689b50d902SRodney W. Grimes s->wfd = -1; 569bdd72b70SSuleiman Souhlal s->icase = 0; 5709b50d902SRodney W. Grimes for (gn = 0;;) { 571249a8a80SPedro F. Giffuni EATSPACE(); /* EXTENSION */ 5729b50d902SRodney W. Grimes switch (*p) { 5739b50d902SRodney W. Grimes case 'g': 5749b50d902SRodney W. Grimes if (gn) 57573a08bb2SPhilippe Charnier errx(1, 57673a08bb2SPhilippe Charnier "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 5779b50d902SRodney W. Grimes gn = 1; 5789b50d902SRodney W. Grimes s->n = 0; 5799b50d902SRodney W. Grimes break; 5809b50d902SRodney W. Grimes case '\0': 5819b50d902SRodney W. Grimes case '\n': 5829b50d902SRodney W. Grimes case ';': 5839b50d902SRodney W. Grimes return (p); 5849b50d902SRodney W. Grimes case 'p': 5859b50d902SRodney W. Grimes s->p = 1; 5869b50d902SRodney W. Grimes break; 58749e89014SEitan Adler case 'i': 588bdd72b70SSuleiman Souhlal case 'I': 589bdd72b70SSuleiman Souhlal s->icase = 1; 590bdd72b70SSuleiman Souhlal break; 5919b50d902SRodney W. Grimes case '1': case '2': case '3': 5929b50d902SRodney W. Grimes case '4': case '5': case '6': 5939b50d902SRodney W. Grimes case '7': case '8': case '9': 5949b50d902SRodney W. Grimes if (gn) 59573a08bb2SPhilippe Charnier errx(1, 59673a08bb2SPhilippe Charnier "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 5979b50d902SRodney W. Grimes gn = 1; 598d432588eSDiomidis Spinellis errno = 0; 599249a8a80SPedro F. Giffuni nval = strtol(p, &p, 10); 600d432588eSDiomidis Spinellis if (errno == ERANGE || nval > INT_MAX) 601d432588eSDiomidis Spinellis errx(1, 602d432588eSDiomidis Spinellis "%lu: %s: overflow in the 'N' substitute flag", linenum, fname); 603d432588eSDiomidis Spinellis s->n = nval; 604249a8a80SPedro F. Giffuni p--; 605249a8a80SPedro F. Giffuni break; 6069b50d902SRodney W. Grimes case 'w': 6079b50d902SRodney W. Grimes p++; 6089b50d902SRodney W. Grimes #ifdef HISTORIC_PRACTICE 6099b50d902SRodney W. Grimes if (*p != ' ') { 61073a08bb2SPhilippe Charnier warnx("%lu: %s: space missing before w wfile", linenum, fname); 6119b50d902SRodney W. Grimes return (p); 6129b50d902SRodney W. Grimes } 6139b50d902SRodney W. Grimes #endif 6149b50d902SRodney W. Grimes EATSPACE(); 615249a8a80SPedro F. Giffuni q = wfile; 616249a8a80SPedro F. Giffuni eq = wfile + sizeof(wfile) - 1; 617249a8a80SPedro F. Giffuni while (*p) { 618249a8a80SPedro F. Giffuni if (*p == '\n') 619249a8a80SPedro F. Giffuni break; 620249a8a80SPedro F. Giffuni if (q >= eq) 621249a8a80SPedro F. Giffuni err(1, "wfile too long"); 622249a8a80SPedro F. Giffuni *q++ = *p++; 623249a8a80SPedro F. Giffuni } 624249a8a80SPedro F. Giffuni *q = '\0'; 625249a8a80SPedro F. Giffuni if (q == wfile) 626249a8a80SPedro F. Giffuni errx(1, "%lu: %s: no wfile specified", linenum, fname); 627249a8a80SPedro F. Giffuni s->wfile = strdup(wfile); 628249a8a80SPedro F. Giffuni if (!aflag && (s->wfd = open(wfile, 6299b50d902SRodney W. Grimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 6309b50d902SRodney W. Grimes DEFFILEMODE)) == -1) 631249a8a80SPedro F. Giffuni err(1, "%s", wfile); 6329b50d902SRodney W. Grimes return (p); 6339b50d902SRodney W. Grimes default: 634249a8a80SPedro F. Giffuni errx(1, "%lu: %s: bad flag in substitute command: '%c'", 635249a8a80SPedro F. Giffuni linenum, fname, *p); 6369b50d902SRodney W. Grimes break; 6379b50d902SRodney W. Grimes } 6389b50d902SRodney W. Grimes p++; 6399b50d902SRodney W. Grimes } 6409b50d902SRodney W. Grimes } 6419b50d902SRodney W. Grimes 6429b50d902SRodney W. Grimes /* 6439b50d902SRodney W. Grimes * Compile a translation set of strings into a lookup table. 6449b50d902SRodney W. Grimes */ 645249a8a80SPedro F. Giffuni static char * 646249a8a80SPedro F. Giffuni compile_tr(char *p, struct s_tr **py) 6479b50d902SRodney W. Grimes { 64881a8648aSTim J. Robbins struct s_tr *y; 6499b50d902SRodney W. Grimes int i; 65081a8648aSTim J. Robbins const char *op, *np; 6519b50d902SRodney W. Grimes char old[_POSIX2_LINE_MAX + 1]; 6529b50d902SRodney W. Grimes char new[_POSIX2_LINE_MAX + 1]; 65381a8648aSTim J. Robbins size_t oclen, oldlen, nclen, newlen; 65481a8648aSTim J. Robbins mbstate_t mbs1, mbs2; 65581a8648aSTim J. Robbins 65681a8648aSTim J. Robbins if ((*py = y = malloc(sizeof(*y))) == NULL) 657249a8a80SPedro F. Giffuni err(1, NULL); 65881a8648aSTim J. Robbins y->multis = NULL; 65981a8648aSTim J. Robbins y->nmultis = 0; 6609b50d902SRodney W. Grimes 6619b50d902SRodney W. Grimes if (*p == '\0' || *p == '\\') 66273a08bb2SPhilippe Charnier errx(1, 66373a08bb2SPhilippe Charnier "%lu: %s: transform pattern can not be delimited by newline or backslash", 66473a08bb2SPhilippe Charnier linenum, fname); 66576570d0aSDiomidis Spinellis p = compile_delimited(p, old, 1); 66673a08bb2SPhilippe Charnier if (p == NULL) 66773a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated transform source string", 66873a08bb2SPhilippe Charnier linenum, fname); 66976570d0aSDiomidis Spinellis p = compile_delimited(p - 1, new, 1); 67073a08bb2SPhilippe Charnier if (p == NULL) 67173a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated transform target string", 67273a08bb2SPhilippe Charnier linenum, fname); 6739b50d902SRodney W. Grimes EATSPACE(); 67481a8648aSTim J. Robbins op = old; 67581a8648aSTim J. Robbins oldlen = mbsrtowcs(NULL, &op, 0, NULL); 67681a8648aSTim J. Robbins if (oldlen == (size_t)-1) 677249a8a80SPedro F. Giffuni err(1, NULL); 67881a8648aSTim J. Robbins np = new; 67981a8648aSTim J. Robbins newlen = mbsrtowcs(NULL, &np, 0, NULL); 68081a8648aSTim J. Robbins if (newlen == (size_t)-1) 681249a8a80SPedro F. Giffuni err(1, NULL); 68281a8648aSTim J. Robbins if (newlen != oldlen) 68373a08bb2SPhilippe Charnier errx(1, "%lu: %s: transform strings are not the same length", 68473a08bb2SPhilippe Charnier linenum, fname); 68581a8648aSTim J. Robbins if (MB_CUR_MAX == 1) { 68681a8648aSTim J. Robbins /* 68781a8648aSTim J. Robbins * The single-byte encoding case is easy: generate a 68881a8648aSTim J. Robbins * lookup table. 68981a8648aSTim J. Robbins */ 6909b50d902SRodney W. Grimes for (i = 0; i <= UCHAR_MAX; i++) 69181a8648aSTim J. Robbins y->bytetab[i] = (char)i; 69281a8648aSTim J. Robbins for (; *op; op++, np++) 69381a8648aSTim J. Robbins y->bytetab[(u_char)*op] = *np; 69481a8648aSTim J. Robbins } else { 69581a8648aSTim J. Robbins /* 69681a8648aSTim J. Robbins * Multi-byte encoding case: generate a lookup table as 69781a8648aSTim J. Robbins * above, but only for single-byte characters. The first 69881a8648aSTim J. Robbins * bytes of multi-byte characters have their lookup table 69981a8648aSTim J. Robbins * entries set to 0, which causes do_tr() to search through 70081a8648aSTim J. Robbins * an auxiliary vector of multi-byte mappings. 70181a8648aSTim J. Robbins */ 70281a8648aSTim J. Robbins memset(&mbs1, 0, sizeof(mbs1)); 70381a8648aSTim J. Robbins memset(&mbs2, 0, sizeof(mbs2)); 70481a8648aSTim J. Robbins for (i = 0; i <= UCHAR_MAX; i++) 70581a8648aSTim J. Robbins y->bytetab[i] = (btowc(i) != WEOF) ? i : 0; 70681a8648aSTim J. Robbins while (*op != '\0') { 70781a8648aSTim J. Robbins oclen = mbrlen(op, MB_LEN_MAX, &mbs1); 70881a8648aSTim J. Robbins if (oclen == (size_t)-1 || oclen == (size_t)-2) 70981a8648aSTim J. Robbins errc(1, EILSEQ, NULL); 71081a8648aSTim J. Robbins nclen = mbrlen(np, MB_LEN_MAX, &mbs2); 71181a8648aSTim J. Robbins if (nclen == (size_t)-1 || nclen == (size_t)-2) 71281a8648aSTim J. Robbins errc(1, EILSEQ, NULL); 71381a8648aSTim J. Robbins if (oclen == 1 && nclen == 1) 71481a8648aSTim J. Robbins y->bytetab[(u_char)*op] = *np; 71581a8648aSTim J. Robbins else { 71681a8648aSTim J. Robbins y->bytetab[(u_char)*op] = 0; 71781a8648aSTim J. Robbins y->multis = realloc(y->multis, 71881a8648aSTim J. Robbins (y->nmultis + 1) * sizeof(*y->multis)); 71981a8648aSTim J. Robbins if (y->multis == NULL) 720249a8a80SPedro F. Giffuni err(1, NULL); 72181a8648aSTim J. Robbins i = y->nmultis++; 72281a8648aSTim J. Robbins y->multis[i].fromlen = oclen; 72381a8648aSTim J. Robbins memcpy(y->multis[i].from, op, oclen); 72481a8648aSTim J. Robbins y->multis[i].tolen = nclen; 72581a8648aSTim J. Robbins memcpy(y->multis[i].to, np, nclen); 72681a8648aSTim J. Robbins } 72781a8648aSTim J. Robbins op += oclen; 72881a8648aSTim J. Robbins np += nclen; 72981a8648aSTim J. Robbins } 73081a8648aSTim J. Robbins } 7319b50d902SRodney W. Grimes return (p); 7329b50d902SRodney W. Grimes } 7339b50d902SRodney W. Grimes 7349b50d902SRodney W. Grimes /* 735678fec50SPedro F. Giffuni * Compile the text following an a, c, or i command. 7369b50d902SRodney W. Grimes */ 7379b50d902SRodney W. Grimes static char * 738249a8a80SPedro F. Giffuni compile_text(void) 7399b50d902SRodney W. Grimes { 74049e65599SBruce Evans int asize, esc_nl, size; 741249a8a80SPedro F. Giffuni char *text, *p, *op, *s; 742249a8a80SPedro F. Giffuni char lbuf[_POSIX2_LINE_MAX + 1]; 7439b50d902SRodney W. Grimes 7449b50d902SRodney W. Grimes asize = 2 * _POSIX2_LINE_MAX + 1; 7458e33c0a0SDavid E. O'Brien if ((text = malloc(asize)) == NULL) 7468e33c0a0SDavid E. O'Brien err(1, "malloc"); 7479b50d902SRodney W. Grimes size = 0; 748249a8a80SPedro F. Giffuni while (cu_fgets(lbuf, sizeof(lbuf), NULL) != NULL) { 7499b50d902SRodney W. Grimes op = s = text + size; 750249a8a80SPedro F. Giffuni p = lbuf; 751b780b03cSPedro F. Giffuni #ifdef LEGACY_BSDSED_COMPAT 752b780b03cSPedro F. Giffuni EATSPACE(); 753b780b03cSPedro F. Giffuni #endif 75449e65599SBruce Evans for (esc_nl = 0; *p != '\0'; p++) { 75549e65599SBruce Evans if (*p == '\\' && p[1] != '\0' && *++p == '\n') 75649e65599SBruce Evans esc_nl = 1; 7579b50d902SRodney W. Grimes *s++ = *p; 7589b50d902SRodney W. Grimes } 7599b50d902SRodney W. Grimes size += s - op; 76049e65599SBruce Evans if (!esc_nl) { 7619b50d902SRodney W. Grimes *s = '\0'; 7629b50d902SRodney W. Grimes break; 7639b50d902SRodney W. Grimes } 7649b50d902SRodney W. Grimes if (asize - size < _POSIX2_LINE_MAX + 1) { 7659b50d902SRodney W. Grimes asize *= 2; 7668e33c0a0SDavid E. O'Brien if ((text = realloc(text, asize)) == NULL) 7678e33c0a0SDavid E. O'Brien err(1, "realloc"); 7689b50d902SRodney W. Grimes } 7699b50d902SRodney W. Grimes } 77013ede3c0SBrian Somers text[size] = '\0'; 771249a8a80SPedro F. Giffuni if ((p = realloc(text, size + 1)) == NULL) 7728e33c0a0SDavid E. O'Brien err(1, "realloc"); 773249a8a80SPedro F. Giffuni return (p); 7749b50d902SRodney W. Grimes } 7759b50d902SRodney W. Grimes 7769b50d902SRodney W. Grimes /* 7779b50d902SRodney W. Grimes * Get an address and return a pointer to the first character after 7789b50d902SRodney W. Grimes * it. Fill the structure pointed to according to the address. 7799b50d902SRodney W. Grimes */ 780249a8a80SPedro F. Giffuni static char * 781249a8a80SPedro F. Giffuni compile_addr(char *p, struct s_addr *a) 7829b50d902SRodney W. Grimes { 783bdd72b70SSuleiman Souhlal char *end, re[_POSIX2_LINE_MAX + 1]; 784bdd72b70SSuleiman Souhlal int icase; 785bdd72b70SSuleiman Souhlal 786bdd72b70SSuleiman Souhlal icase = 0; 7879b50d902SRodney W. Grimes 788f879e8d9SBrian Somers a->type = 0; 7899b50d902SRodney W. Grimes switch (*p) { 7909b50d902SRodney W. Grimes case '\\': /* Context address */ 7919b50d902SRodney W. Grimes ++p; 7929b50d902SRodney W. Grimes /* FALLTHROUGH */ 7939b50d902SRodney W. Grimes case '/': /* Context address */ 79476570d0aSDiomidis Spinellis p = compile_delimited(p, re, 0); 7959b50d902SRodney W. Grimes if (p == NULL) 79673a08bb2SPhilippe Charnier errx(1, "%lu: %s: unterminated regular expression", linenum, fname); 797bdd72b70SSuleiman Souhlal /* Check for case insensitive regexp flag */ 798bdd72b70SSuleiman Souhlal if (*p == 'I') { 799bdd72b70SSuleiman Souhlal icase = 1; 800bdd72b70SSuleiman Souhlal p++; 801bdd72b70SSuleiman Souhlal } 802bdd72b70SSuleiman Souhlal if (*re == '\0') 803bdd72b70SSuleiman Souhlal a->u.r = NULL; 804bdd72b70SSuleiman Souhlal else 805bdd72b70SSuleiman Souhlal a->u.r = compile_re(re, icase); 8069b50d902SRodney W. Grimes a->type = AT_RE; 8079b50d902SRodney W. Grimes return (p); 8089b50d902SRodney W. Grimes 8099b50d902SRodney W. Grimes case '$': /* Last line */ 8109b50d902SRodney W. Grimes a->type = AT_LAST; 8119b50d902SRodney W. Grimes return (p + 1); 812f879e8d9SBrian Somers 813f879e8d9SBrian Somers case '+': /* Relative line number */ 814f879e8d9SBrian Somers a->type = AT_RELLINE; 815f879e8d9SBrian Somers p++; 816f879e8d9SBrian Somers /* FALLTHROUGH */ 8179b50d902SRodney W. Grimes /* Line number */ 8189b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 8199b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 820f879e8d9SBrian Somers if (a->type == 0) 8219b50d902SRodney W. Grimes a->type = AT_LINE; 8229b50d902SRodney W. Grimes a->u.l = strtol(p, &end, 10); 8239b50d902SRodney W. Grimes return (end); 8249b50d902SRodney W. Grimes default: 82573a08bb2SPhilippe Charnier errx(1, "%lu: %s: expected context address", linenum, fname); 8269b50d902SRodney W. Grimes return (NULL); 8279b50d902SRodney W. Grimes } 8289b50d902SRodney W. Grimes } 8299b50d902SRodney W. Grimes 8309b50d902SRodney W. Grimes /* 8319b50d902SRodney W. Grimes * duptoeol -- 8329b50d902SRodney W. Grimes * Return a copy of all the characters up to \n or \0. 8339b50d902SRodney W. Grimes */ 8349b50d902SRodney W. Grimes static char * 835249a8a80SPedro F. Giffuni duptoeol(char *s, const char *ctype) 8369b50d902SRodney W. Grimes { 8379b50d902SRodney W. Grimes size_t len; 8389b50d902SRodney W. Grimes int ws; 839249a8a80SPedro F. Giffuni char *p, *start; 8409b50d902SRodney W. Grimes 8419b50d902SRodney W. Grimes ws = 0; 8429b50d902SRodney W. Grimes for (start = s; *s != '\0' && *s != '\n'; ++s) 843726aebe5SAndrey A. Chernov ws = isspace((unsigned char)*s); 844249a8a80SPedro F. Giffuni *s = '\0'; 8459b50d902SRodney W. Grimes if (ws) 84673a08bb2SPhilippe Charnier warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); 847249a8a80SPedro F. Giffuni len = s - start + 1; 848249a8a80SPedro F. Giffuni if ((p = malloc(len)) == NULL) 8498e33c0a0SDavid E. O'Brien err(1, "malloc"); 850249a8a80SPedro F. Giffuni return (memmove(p, start, len)); 8519b50d902SRodney W. Grimes } 8529b50d902SRodney W. Grimes 8539b50d902SRodney W. Grimes /* 8549b50d902SRodney W. Grimes * Convert goto label names to addresses, and count a and r commands, in 8559b50d902SRodney W. Grimes * the given subset of the script. Free the memory used by labels in b 8569b50d902SRodney W. Grimes * and t commands (but not by :). 8579b50d902SRodney W. Grimes * 8589b50d902SRodney W. Grimes * TODO: Remove } nodes 8599b50d902SRodney W. Grimes */ 8609b50d902SRodney W. Grimes static void 861249a8a80SPedro F. Giffuni fixuplabel(struct s_command *cp, struct s_command *end) 8629b50d902SRodney W. Grimes { 8639b50d902SRodney W. Grimes 8649b50d902SRodney W. Grimes for (; cp != end; cp = cp->next) 8659b50d902SRodney W. Grimes switch (cp->code) { 8669b50d902SRodney W. Grimes case 'a': 8679b50d902SRodney W. Grimes case 'r': 8689b50d902SRodney W. Grimes appendnum++; 8699b50d902SRodney W. Grimes break; 8709b50d902SRodney W. Grimes case 'b': 8719b50d902SRodney W. Grimes case 't': 8729b50d902SRodney W. Grimes /* Resolve branch target. */ 8739b50d902SRodney W. Grimes if (cp->t == NULL) { 8749b50d902SRodney W. Grimes cp->u.c = NULL; 8759b50d902SRodney W. Grimes break; 8769b50d902SRodney W. Grimes } 8779b50d902SRodney W. Grimes if ((cp->u.c = findlabel(cp->t)) == NULL) 878249a8a80SPedro F. Giffuni errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); 8799b50d902SRodney W. Grimes free(cp->t); 8809b50d902SRodney W. Grimes break; 8819b50d902SRodney W. Grimes case '{': 8829b50d902SRodney W. Grimes /* Do interior commands. */ 8839b50d902SRodney W. Grimes fixuplabel(cp->u.c, cp->next); 8849b50d902SRodney W. Grimes break; 8859b50d902SRodney W. Grimes } 8869b50d902SRodney W. Grimes } 8879b50d902SRodney W. Grimes 8889b50d902SRodney W. Grimes /* 8899b50d902SRodney W. Grimes * Associate the given command label for later lookup. 8909b50d902SRodney W. Grimes */ 8919b50d902SRodney W. Grimes static void 892e6478125SDag-Erling Smørgrav enterlabel(struct s_command *cp) 8939b50d902SRodney W. Grimes { 894e74bf75fSMark Murray struct labhash **lhp, *lh; 895e74bf75fSMark Murray u_char *p; 896e74bf75fSMark Murray u_int h, c; 8979b50d902SRodney W. Grimes 8989b50d902SRodney W. Grimes for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) 8999b50d902SRodney W. Grimes h = (h << 5) + h + c; 9009b50d902SRodney W. Grimes lhp = &labels[h & LHMASK]; 9019b50d902SRodney W. Grimes for (lh = *lhp; lh != NULL; lh = lh->lh_next) 9029b50d902SRodney W. Grimes if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) 90373a08bb2SPhilippe Charnier errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); 9048e33c0a0SDavid E. O'Brien if ((lh = malloc(sizeof *lh)) == NULL) 9058e33c0a0SDavid E. O'Brien err(1, "malloc"); 9069b50d902SRodney W. Grimes lh->lh_next = *lhp; 9079b50d902SRodney W. Grimes lh->lh_hash = h; 9089b50d902SRodney W. Grimes lh->lh_cmd = cp; 9099b50d902SRodney W. Grimes lh->lh_ref = 0; 9109b50d902SRodney W. Grimes *lhp = lh; 9119b50d902SRodney W. Grimes } 9129b50d902SRodney W. Grimes 9139b50d902SRodney W. Grimes /* 9149b50d902SRodney W. Grimes * Find the label contained in the command l in the command linked 9159b50d902SRodney W. Grimes * list cp. L is excluded from the search. Return NULL if not found. 9169b50d902SRodney W. Grimes */ 9179b50d902SRodney W. Grimes static struct s_command * 918249a8a80SPedro F. Giffuni findlabel(char *name) 9199b50d902SRodney W. Grimes { 920e74bf75fSMark Murray struct labhash *lh; 921249a8a80SPedro F. Giffuni u_char *p; 922e74bf75fSMark Murray u_int h, c; 9239b50d902SRodney W. Grimes 924249a8a80SPedro F. Giffuni for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) 9259b50d902SRodney W. Grimes h = (h << 5) + h + c; 9269b50d902SRodney W. Grimes for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { 9279b50d902SRodney W. Grimes if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { 9289b50d902SRodney W. Grimes lh->lh_ref = 1; 9299b50d902SRodney W. Grimes return (lh->lh_cmd); 9309b50d902SRodney W. Grimes } 9319b50d902SRodney W. Grimes } 9329b50d902SRodney W. Grimes return (NULL); 9339b50d902SRodney W. Grimes } 9349b50d902SRodney W. Grimes 9359b50d902SRodney W. Grimes /* 9369b50d902SRodney W. Grimes * Warn about any unused labels. As a side effect, release the label hash 9379b50d902SRodney W. Grimes * table space. 9389b50d902SRodney W. Grimes */ 9399b50d902SRodney W. Grimes static void 940e6478125SDag-Erling Smørgrav uselabel(void) 9419b50d902SRodney W. Grimes { 942e74bf75fSMark Murray struct labhash *lh, *next; 943e74bf75fSMark Murray int i; 9449b50d902SRodney W. Grimes 9459b50d902SRodney W. Grimes for (i = 0; i < LHSZ; i++) { 9469b50d902SRodney W. Grimes for (lh = labels[i]; lh != NULL; lh = next) { 9479b50d902SRodney W. Grimes next = lh->lh_next; 9489b50d902SRodney W. Grimes if (!lh->lh_ref) 94973a08bb2SPhilippe Charnier warnx("%lu: %s: unused label '%s'", 95073a08bb2SPhilippe Charnier linenum, fname, lh->lh_cmd->t); 9519b50d902SRodney W. Grimes free(lh); 9529b50d902SRodney W. Grimes } 9539b50d902SRodney W. Grimes } 9549b50d902SRodney W. Grimes } 955