Home | History | Annotate | Line # | Download | only in sed
      1 /*	$NetBSD: compile.c,v 1.56 2026/03/12 19:13:11 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992 Diomidis Spinellis.
      5  * Copyright (c) 1992, 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * Diomidis Spinellis of Imperial College, University of London.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #if HAVE_NBTOOL_CONFIG_H
     37 #include "nbtool_config.h"
     38 #endif
     39 
     40 #include <sys/cdefs.h>
     41 __RCSID("$NetBSD: compile.c,v 1.56 2026/03/12 19:13:11 christos Exp $");
     42 #ifdef __FBSDID
     43 __FBSDID("$FreeBSD: head/usr.bin/sed/compile.c 259132 2013-12-09 18:57:20Z eadler $");
     44 #endif
     45 
     46 #if 0
     47 static const char sccsid[] = "@(#)compile.c	8.1 (Berkeley) 6/6/93";
     48 #endif
     49 
     50 #include <sys/types.h>
     51 #include <sys/stat.h>
     52 
     53 #include <ctype.h>
     54 #include <err.h>
     55 #include <errno.h>
     56 #include <fcntl.h>
     57 #include <limits.h>
     58 #include <regex.h>
     59 #include <stdio.h>
     60 #include <stdlib.h>
     61 #include <string.h>
     62 #include <wchar.h>
     63 #include <util.h>
     64 
     65 #include "defs.h"
     66 #include "extern.h"
     67 
     68 #define LHSZ	128
     69 #define	LHMASK	(LHSZ - 1)
     70 static struct labhash {
     71 	struct	labhash *lh_next;
     72 	u_int	lh_hash;
     73 	struct	s_command *lh_cmd;
     74 	int	lh_ref;
     75 } *labels[LHSZ];
     76 
     77 static char	 *compile_addr(char *, struct s_addr *);
     78 static char	 *compile_ccl(char **, char *);
     79 static char	 *compile_delimited(char *, char *, int);
     80 static char	 *compile_flags(char *, struct s_subst *);
     81 static regex_t	 *compile_re(char *, int);
     82 static char	 *compile_subst(char *, struct s_subst *);
     83 static char	 *compile_text(void);
     84 static char	 *compile_tr(char *, struct s_tr **);
     85 static struct s_command
     86 		**compile_stream(struct s_command **);
     87 static char	 *duptoeol(char *, const char *);
     88 static void	  enterlabel(struct s_command *);
     89 static struct s_command
     90 		 *findlabel(char *);
     91 static void	  fixuplabel(struct s_command *, struct s_command *);
     92 static void	  uselabel(void);
     93 static void	  parse_escapes(char *);
     94 
     95 /*
     96  * Command specification.  This is used to drive the command parser.
     97  */
     98 struct s_format {
     99 	char code;				/* Command code */
    100 	int naddr;				/* Number of address args */
    101 	enum e_args args;			/* Argument type */
    102 };
    103 
    104 static struct s_format cmd_fmts[] = {
    105 	{'{', 2, GROUP},
    106 	{'}', 0, ENDGROUP},
    107 	{'a', 1, TEXT},
    108 	{'b', 2, BRANCH},
    109 	{'c', 2, TEXT},
    110 	{'d', 2, EMPTY},
    111 	{'D', 2, EMPTY},
    112 	{'g', 2, EMPTY},
    113 	{'G', 2, EMPTY},
    114 	{'h', 2, EMPTY},
    115 	{'H', 2, EMPTY},
    116 	{'i', 1, TEXT},
    117 	{'l', 2, EMPTY},
    118 	{'n', 2, EMPTY},
    119 	{'N', 2, EMPTY},
    120 	{'p', 2, EMPTY},
    121 	{'P', 2, EMPTY},
    122 	{'q', 1, EMPTY},
    123 	{'r', 1, RFILE},
    124 	{'s', 2, SUBST},
    125 	{'t', 2, BRANCH},
    126 	{'w', 2, WFILE},
    127 	{'x', 2, EMPTY},
    128 	{'y', 2, TR},
    129 	{'!', 2, NONSEL},
    130 	{':', 0, LABEL},
    131 	{'#', 0, COMMENT},
    132 	{'=', 1, EMPTY},
    133 	{'\0', 0, COMMENT},
    134 };
    135 
    136 /* The compiled program. */
    137 struct s_command *prog;
    138 
    139 /*
    140  * Compile the program into prog.
    141  * Initialise appends.
    142  */
    143 void
    144 compile(void)
    145 {
    146 	*compile_stream(&prog) = NULL;
    147 	fixuplabel(prog, NULL);
    148 	uselabel();
    149 	if (appendnum > 0)
    150 		appends = emalloc(sizeof(struct s_appends) * appendnum);
    151 	match = emalloc((maxnsub + 1) * sizeof(regmatch_t));
    152 }
    153 
    154 #define EATSPACE() do {							\
    155 	if (p)								\
    156 		while (*p && isspace((unsigned char)*p))                \
    157 			p++;						\
    158 	} while (0)
    159 
    160 static struct s_command **
    161 compile_stream(struct s_command **link)
    162 {
    163 	char *p;
    164 	static char lbuf[_POSIX2_LINE_MAX + 1];	/* To save stack */
    165 	struct s_command *cmd, *cmd2, *stack;
    166 	struct s_format *fp;
    167 	char re[_POSIX2_LINE_MAX + 1];
    168 	int naddr;				/* Number of addresses */
    169 
    170 	stack = 0;
    171 	for (;;) {
    172 		if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) {
    173 			if (stack != 0)
    174 				sederr("unexpected EOF (pending }'s)");
    175 			return (link);
    176 		}
    177 
    178 semicolon:	EATSPACE();
    179 		if (p) {
    180 			if (*p == '#' || *p == '\0')
    181 				continue;
    182 			else if (*p == ';') {
    183 				p++;
    184 				goto semicolon;
    185 			}
    186 		}
    187 		*link = cmd = emalloc(sizeof(struct s_command));
    188 		link = &cmd->next;
    189 		cmd->startline = cmd->nonsel = 0;
    190 		/* First parse the addresses */
    191 		naddr = 0;
    192 
    193 /* Valid characters to start an address */
    194 #define	addrchar(c)	(strchr("0123456789/\\$", (c)))
    195 		if (addrchar(*p)) {
    196 			naddr++;
    197 			cmd->a1 = emalloc(sizeof(struct s_addr));
    198 			p = compile_addr(p, cmd->a1);
    199 			EATSPACE();				/* EXTENSION */
    200 			if (*p == ',') {
    201 				p++;
    202 				EATSPACE();			/* EXTENSION */
    203 				naddr++;
    204 				cmd->a2 = emalloc(sizeof(struct s_addr));
    205 				p = compile_addr(p, cmd->a2);
    206 				EATSPACE();
    207 			} else
    208 				cmd->a2 = 0;
    209 		} else
    210 			cmd->a1 = cmd->a2 = 0;
    211 
    212 nonsel:		/* Now parse the command */
    213 		if (!*p)
    214 			sederr("command expected");
    215 		cmd->code = *p;
    216 		for (fp = cmd_fmts; fp->code; fp++)
    217 			if (fp->code == *p)
    218 				break;
    219 		if (!fp->code)
    220 			sederr("invalid command code %c", *p);
    221 		if (naddr > fp->naddr)
    222 			sederr(
    223 			    "command %c expects up to %d address(es), found %d",
    224 				*p, fp->naddr, naddr);
    225 		switch (fp->args) {
    226 		case NONSEL:			/* ! */
    227 			p++;
    228 			EATSPACE();
    229 			cmd->nonsel = ! cmd->nonsel;
    230 			goto nonsel;
    231 		case GROUP:			/* { */
    232 			p++;
    233 			EATSPACE();
    234 			cmd->next = stack;
    235 			stack = cmd;
    236 			link = &cmd->u.c;
    237 			if (*p)
    238 				goto semicolon;
    239 			break;
    240 		case ENDGROUP:
    241 			/*
    242 			 * Short-circuit command processing, since end of
    243 			 * group is really just a noop.
    244 			 */
    245 			cmd->nonsel = 1;
    246 			if (stack == 0)
    247 				sederr("unexpected }");
    248 			cmd2 = stack;
    249 			stack = cmd2->next;
    250 			cmd2->next = cmd;
    251 			/*FALLTHROUGH*/
    252 		case EMPTY:		/* d D g G h H l n N p P q x = \0 */
    253 			p++;
    254 			EATSPACE();
    255 			switch (*p) {
    256 			case ';':
    257 				p++;
    258 				link = &cmd->next;
    259 				goto semicolon;
    260 			case '}':
    261 				goto semicolon;
    262 			case '\0':
    263 				break;
    264 			default:
    265 				sederr(
    266 				    "extra characters at the end of %c command",
    267 						cmd->code);
    268 			}
    269 			break;
    270 		case TEXT:			/* a c i */
    271 			p++;
    272 			EATSPACE();
    273 			if (*p != '\\')
    274 				sederr("command %c expects \\ followed by text", cmd->code);
    275 			p++;
    276 			EATSPACE();
    277 			if (*p)
    278 				sederr(
    279 "extra characters after \\ at the end of %c command",
    280 				cmd->code);
    281 			cmd->t = compile_text();
    282 			break;
    283 		case COMMENT:			/* \0 # */
    284 			break;
    285 		case WFILE:			/* w */
    286 			p++;
    287 			EATSPACE();
    288 			if (*p == '\0')
    289 				sederr("filename expected");
    290 			cmd->t = duptoeol(p, "w command");
    291 			if (aflag)
    292 				cmd->u.fd = -1;
    293 			else if ((cmd->u.fd = open(p,
    294 			    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
    295 			    DEFFILEMODE)) == -1)
    296 				err(1, "%s", p);
    297 			break;
    298 		case RFILE:			/* r */
    299 			p++;
    300 			EATSPACE();
    301 			if (*p == '\0')
    302 				sederr("filename expected");
    303 			else
    304 				cmd->t = duptoeol(p, "read command");
    305 			break;
    306 		case BRANCH:			/* b t */
    307 			p++;
    308 			EATSPACE();
    309 			if (*p == '\0')
    310 				cmd->t = NULL;
    311 			else
    312 				cmd->t = duptoeol(p, "branch");
    313 			break;
    314 		case LABEL:			/* : */
    315 			p++;
    316 			EATSPACE();
    317 			cmd->t = duptoeol(p, "label");
    318 			if (strlen(p) == 0)
    319 				sederr("empty label");
    320 			enterlabel(cmd);
    321 			break;
    322 		case SUBST:			/* s */
    323 			p++;
    324 			if (*p == '\0' || *p == '\\')
    325 				sederr(
    326 "substitute pattern can not be delimited by newline or backslash");
    327 			cmd->u.s = ecalloc(1, sizeof(struct s_subst));
    328 			p = compile_delimited(p, re, 0);
    329 			if (p == NULL)
    330 				sederr("unterminated substitute pattern");
    331 
    332 			/* Compile RE with no case sensitivity temporarily */
    333 			if (*re == '\0')
    334 				cmd->u.s->re = NULL;
    335 			else
    336 				cmd->u.s->re = compile_re(re, 0);
    337 			--p;
    338 			p = compile_subst(p, cmd->u.s);
    339 			p = compile_flags(p, cmd->u.s);
    340 
    341 			/* Recompile RE with case sensitivity from "I" flag if any */
    342 			if (*re == '\0')
    343 				cmd->u.s->re = NULL;
    344 			else
    345 				cmd->u.s->re = compile_re(re, cmd->u.s->icase);
    346 			EATSPACE();
    347 			if (*p == ';') {
    348 				p++;
    349 				link = &cmd->next;
    350 				goto semicolon;
    351 			}
    352 			break;
    353 		case TR:			/* y */
    354 			p++;
    355 			p = compile_tr(p, &cmd->u.y);
    356 			EATSPACE();
    357 			switch (*p) {
    358 			case ';':
    359 				p++;
    360 				link = &cmd->next;
    361 				goto semicolon;
    362 			case '}':
    363 				goto semicolon;
    364 			case '\0':
    365 				break;
    366 			default:
    367 				sederr(
    368 "extra text at the end of a transform command");
    369 			}
    370 			if (*p)
    371 			break;
    372 		}
    373 	}
    374 }
    375 
    376 /*
    377  * Get a delimited string.  P points to the delimiter of the string; d points
    378  * to a buffer area.  Newline and delimiter escapes are processed; other
    379  * escapes are ignored.
    380  *
    381  * Returns a pointer to the first character after the final delimiter or NULL
    382  * in the case of a non-terminated string.  The character array d is filled
    383  * with the processed string.
    384  */
    385 static char *
    386 compile_delimited(char *p, char *d, int is_tr)
    387 {
    388 	char c;
    389 
    390 	c = *p++;
    391 	if (c == '\0')
    392 		return (NULL);
    393 	else if (c == '\\')
    394 		sederr("\\ can not be used as a string delimiter");
    395 	else if (c == '\n')
    396 		sederr("newline can not be used as a string delimiter");
    397 	while (*p) {
    398 		if (*p == '[' && *p != c) {
    399 			if ((d = compile_ccl(&p, d)) == NULL)
    400 				sederr("unbalanced brackets ([])");
    401 			continue;
    402 		} else if (*p == '\\' && p[1] == '[') {
    403 			*d++ = *p++;
    404 		} else if (*p == '\\' && p[1] == c)
    405 			p++;
    406 		else if (*p == '\\' && p[1] == 'n') {
    407 			*d++ = '\n';
    408 			p += 2;
    409 			continue;
    410 		} else if (*p == '\\' && p[1] == '\\') {
    411 			if (is_tr)
    412 				p++;
    413 			else
    414 				*d++ = *p++;
    415 		} else if (*p == c) {
    416 			*d = '\0';
    417 			return (p + 1);
    418 		}
    419 		*d++ = *p++;
    420 	}
    421 	return (NULL);
    422 }
    423 
    424 
    425 /* compile_ccl: expand a POSIX character class */
    426 static char *
    427 compile_ccl(char **sp, char *t)
    428 {
    429 	int c, d;
    430 	char *s = *sp;
    431 
    432 	*t++ = *s++;
    433 	if (*s == '^')
    434 		*t++ = *s++;
    435 	if (*s == ']')
    436 		*t++ = *s++;
    437 	for (; *s && (*t = *s) != ']'; s++, t++)
    438 		if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) {
    439 			*++t = *++s, t++, s++;
    440 			for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
    441 				if ((c = *s) == '\0')
    442 					return NULL;
    443 		}
    444 	return (*s == ']') ? *sp = ++s, ++t : NULL;
    445 }
    446 
    447 /*
    448  * Compiles the regular expression in RE and returns a pointer to the compiled
    449  * regular expression.
    450  * Cflags are passed to regcomp.
    451  */
    452 static regex_t *
    453 compile_re(char *re, int case_insensitive)
    454 {
    455 	regex_t *rep;
    456 	int eval, flags;
    457 
    458 
    459 	flags = rflags;
    460 	if (case_insensitive)
    461 		flags |= REG_ICASE;
    462 	rep = emalloc(sizeof(regex_t));
    463 	parse_escapes(re);
    464 	if ((eval = regcomp(rep, re, flags)) != 0)
    465 		sederr("RE error: %s",
    466 				strregerror(eval, rep));
    467 	if (maxnsub < rep->re_nsub)
    468 		maxnsub = rep->re_nsub;
    469 	return (rep);
    470 }
    471 
    472 static char
    473 cton(char c, int base)
    474 {
    475 	switch (c) {
    476 	case '0': case '1': case '2': case '3': case '4':
    477 	case '5': case '6': case '7':
    478 		return (char)(c - '0');
    479 	case '8': case '9':
    480 		return base == 8 ? '?' : (char)(c - '0');
    481 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
    482 		return base == 16 ? (char)(c - 'a' + 10) : '?';
    483 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    484 		return base == 16 ? (char)(c - 'A' + 10) : '?';
    485 	default:
    486 		return '?';
    487 	}
    488 }
    489 
    490 static int
    491 ston(char **pp, char *sp, int base)
    492 {
    493 	char *p = *pp, n;
    494 	int r = cton(p[1], base);
    495 	char *ep = p + (base == 16 ? 2 : 3);
    496 
    497 	if (r == '?')
    498 		return 0;
    499 	for (p++; p < ep && (n = cton(p[1], base)) != '?'; p++) {
    500 		int nr = r * base + n;
    501 		if (nr > 255)
    502 			break;
    503 		r = nr;
    504 	}
    505 	*sp = (char)r;
    506 	*pp = p;
    507 	return 1;
    508 }
    509 
    510 static int
    511 unescape(char **pp, char **spp)
    512 {
    513 	char *p = *pp;
    514 	char *sp = *spp;
    515 
    516 	switch (*p) {
    517 	case 'o':
    518 		if (!ston(&p, sp, 8))
    519 			return 0;
    520 		break;
    521 	case 'd':
    522 		if (!ston(&p, sp, 10))
    523 			return 0;
    524 		break;
    525 	case 'x':
    526 		if (!ston(&p, sp, 16))
    527 			return 0;
    528 		break;
    529 	case 'a':
    530 		*sp = '\a';
    531 		break;
    532 #if 0
    533 	// No, \b RE
    534 	case 'b':
    535 		*sp = '\b';
    536 		break;
    537 #endif
    538 	case 'f':
    539 		*sp = '\f';
    540 		break;
    541 	case 'n':
    542 		*sp = '\n';
    543 		break;
    544 	case 'r':
    545 		*sp = '\r';
    546 		break;
    547 	case 't':
    548 		*sp = '\t';
    549 		break;
    550 	case 'v':
    551 		*sp = '\v';
    552 		break;
    553 	default:
    554 		return 0;
    555 	}
    556 	*spp = sp + 1;
    557 	*pp = p;
    558 	return 1;
    559 }
    560 
    561 static void
    562 parse_escapes(char *buf)
    563 {
    564 	char bracket = '\0';
    565 	char *p, *q;
    566 
    567 	p = q = buf;
    568 
    569 	for (p = q = buf; *p; p++) {
    570 		if (*p == '\\' && p[1] && !bracket) {
    571 			p++;
    572 			if (unescape(&p, &q))
    573 				continue;
    574 			*q++ = '\\';
    575 		}
    576 		switch (*p) {
    577 		case '[':
    578 			if (!bracket)
    579 				bracket = *p;
    580 			break;
    581 		case '.':
    582 		case ':':
    583 		case '=':
    584 			if (bracket == '[' && p[-1] == '[')
    585 				bracket = *p;
    586 			break;
    587 		case ']':
    588 			if (!bracket)
    589 			    break;
    590 			if (bracket == '[')
    591 				bracket = '\0';
    592 			else if (p[-2] != bracket && p[-1] == bracket)
    593 				bracket = '[';
    594 			break;
    595 		default:
    596 			break;
    597 		}
    598 		*q++ = *p;
    599 	}
    600 	*q = '\0';
    601 }
    602 
    603 /*
    604  * Compile the substitution string of a regular expression and set res to
    605  * point to a saved copy of it.  Nsub is the number of parenthesized regular
    606  * expressions.
    607  */
    608 static char *
    609 compile_subst(char *p, struct s_subst *s)
    610 {
    611 	static char lbuf[_POSIX2_LINE_MAX + 1];
    612 	size_t asize, size;
    613 	u_char ref;
    614 	char c, *text, *op, *sp;
    615 	int more = 1, sawesc = 0;
    616 
    617 	c = *p++;			/* Terminator character */
    618 	if (c == '\0')
    619 		return (NULL);
    620 
    621 	s->maxbref = 0;
    622 	s->linenum = linenum;
    623 	asize = 2 * _POSIX2_LINE_MAX + 1;
    624 	text = emalloc(asize);
    625 	size = 0;
    626 	do {
    627 		op = sp = text + size;
    628 		for (; *p; p++) {
    629 			if (*p == '\\' || sawesc) {
    630 				/*
    631 				 * If this is a continuation from the last
    632 				 * buffer, we won't have a character to
    633 				 * skip over.
    634 				 */
    635 				if (sawesc)
    636 					sawesc = 0;
    637 				else
    638 					p++;
    639 
    640 				switch (*p) {
    641 				case '\0':
    642 					/*
    643 					 * This escaped character is continued
    644 					 * in the next part of the line.  Note
    645 					 * this fact, then cause the loop to
    646 					 * exit w/ normal EOL case and reenter
    647 					 * above with the new buffer.
    648 					 */
    649 					sawesc = 1;
    650 					p--;
    651 					continue;
    652 				case '0': case '1': case '2': case '3':
    653 				case '4': case '5': case '6': case '7':
    654 				case '8': case '9':
    655 					*sp++ = '\\';
    656 					ref = (u_char)(*p - '0');
    657 					if (s->re != NULL &&
    658 					    ref > s->re->re_nsub)
    659 						sederr("\\%c not defined in the RE",
    660 								*p);
    661 					if (s->maxbref < ref)
    662 						s->maxbref = ref;
    663 					break;
    664 				case '&':
    665 				case '\\':
    666 					*sp++ = '\\';
    667 					break;
    668 				default:
    669 					if (unescape(&p, &sp))
    670 						continue;
    671 					break;
    672 				}
    673 			} else if (*p == c) {
    674 				if (*++p == '\0' && more) {
    675 					if (cu_fgets(lbuf, sizeof(lbuf), &more))
    676 						p = lbuf;
    677 				}
    678 				*sp++ = '\0';
    679 				size += (size_t)(sp - op);
    680 				s->new = erealloc(text, size);
    681 				return (p);
    682 			} else if (*p == '\n') {
    683 				sederr(
    684 "unescaped newline inside substitute pattern");
    685 				/* NOTREACHED */
    686 			}
    687 			*sp++ = *p;
    688 		}
    689 		size += (size_t)(sp - op);
    690 		if (asize - size < _POSIX2_LINE_MAX + 1) {
    691 			asize *= 2;
    692 			text = erealloc(text, asize);
    693 		}
    694 	} while (cu_fgets(p = lbuf, sizeof(lbuf), &more));
    695 	sederr("unterminated substitute in regular expression");
    696 	/* NOTREACHED */
    697 }
    698 
    699 /*
    700  * Compile the flags of the s command
    701  */
    702 static char *
    703 compile_flags(char *p, struct s_subst *s)
    704 {
    705 	int gn;			/* True if we have seen g or n */
    706 	unsigned long nval;
    707 	char wfile[_POSIX2_LINE_MAX + 1], *q;
    708 
    709 	s->n = 1;				/* Default */
    710 	s->p = 0;
    711 	s->wfile = NULL;
    712 	s->wfd = -1;
    713 	s->icase = 0;
    714 	for (gn = 0;;) {
    715 		EATSPACE();			/* EXTENSION */
    716 		switch (*p) {
    717 		case 'g':
    718 			if (gn)
    719 				sederr(
    720 "more than one number or 'g' in substitute flags");
    721 			gn = 1;
    722 			s->n = 0;
    723 			break;
    724 		case '\0':
    725 		case '\n':
    726 		case ';':
    727 			return (p);
    728 		case 'p':
    729 			s->p = 1;
    730 			break;
    731 		case 'i':
    732 		case 'I':
    733 			s->icase = 1;
    734 			break;
    735 		case '1': case '2': case '3':
    736 		case '4': case '5': case '6':
    737 		case '7': case '8': case '9':
    738 			if (gn)
    739 				sederr(
    740 "more than one number or 'g' in substitute flags");
    741 			gn = 1;
    742 			errno = 0;
    743 			nval = strtoul(p, &p, 10);
    744 			if (errno == ERANGE || nval > INT_MAX)
    745 				sederr(
    746 "overflow in the 'N' substitute flag");
    747 			s->n = (int)nval;
    748 			p--;
    749 			break;
    750 		case 'w':
    751 			p++;
    752 #ifdef HISTORIC_PRACTICE
    753 			if (*p != ' ') {
    754 				sedwarn("space missing before w wfile");
    755 				return (p);
    756 			}
    757 #endif
    758 			EATSPACE();
    759 			q = wfile;
    760 			while (*p) {
    761 				if (*p == '\n')
    762 					break;
    763 				*q++ = *p++;
    764 			}
    765 			*q = '\0';
    766 			if (q == wfile)
    767 				sederr("no wfile specified");
    768 			s->wfile = strdup(wfile);
    769 			if (!aflag && (s->wfd = open(wfile,
    770 			    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
    771 			    DEFFILEMODE)) == -1)
    772 				err(1, "%s", wfile);
    773 			return (p);
    774 		default:
    775 			sederr("bad flag in substitute command: '%c'",
    776 					*p);
    777 		}
    778 		p++;
    779 	}
    780 }
    781 
    782 /*
    783  * Compile a translation set of strings into a lookup table.
    784  */
    785 static char *
    786 compile_tr(char *p, struct s_tr **py)
    787 {
    788 	struct s_tr *y;
    789 	size_t i;
    790 	const char *op, *np;
    791 	char old[_POSIX2_LINE_MAX + 1];
    792 	char new[_POSIX2_LINE_MAX + 1];
    793 	size_t oclen, oldlen, nclen, newlen;
    794 	mbstate_t mbs1, mbs2;
    795 
    796 	*py = y = emalloc(sizeof(*y));
    797 	y->multis = NULL;
    798 	y->nmultis = 0;
    799 
    800 	if (*p == '\0' || *p == '\\')
    801 		sederr(
    802 	"transform pattern can not be delimited by newline or backslash");
    803 	p = compile_delimited(p, old, 1);
    804 	if (p == NULL)
    805 		sederr("unterminated transform source string");
    806 	p = compile_delimited(p - 1, new, 1);
    807 	if (p == NULL)
    808 		sederr("unterminated transform target string");
    809 	EATSPACE();
    810 	op = old;
    811 	oldlen = mbsrtowcs(NULL, &op, 0, NULL);
    812 	if (oldlen == (size_t)-1)
    813 		err(1, NULL);
    814 	np = new;
    815 	newlen = mbsrtowcs(NULL, &np, 0, NULL);
    816 	if (newlen == (size_t)-1)
    817 		err(1, NULL);
    818 	if (newlen != oldlen)
    819 		sederr("transform strings are not the same length");
    820 	if (MB_CUR_MAX == 1) {
    821 		/*
    822 		 * The single-byte encoding case is easy: generate a
    823 		 * lookup table.
    824 		 */
    825 		for (i = 0; i <= UCHAR_MAX; i++)
    826 			y->bytetab[i] = (u_char)i;
    827 		for (; *op; op++, np++)
    828 			y->bytetab[(u_char)*op] = (u_char)*np;
    829 	} else {
    830 		/*
    831 		 * Multi-byte encoding case: generate a lookup table as
    832 		 * above, but only for single-byte characters. The first
    833 		 * bytes of multi-byte characters have their lookup table
    834 		 * entries set to 0, which causes do_tr() to search through
    835 		 * an auxiliary vector of multi-byte mappings.
    836 		 */
    837 		memset(&mbs1, 0, sizeof(mbs1));
    838 		memset(&mbs2, 0, sizeof(mbs2));
    839 		for (i = 0; i <= UCHAR_MAX; i++)
    840 			y->bytetab[i] = (u_char)((btowc((int)i) != WEOF) ? i : 0);
    841 		while (*op != '\0') {
    842 			oclen = mbrlen(op, MB_LEN_MAX, &mbs1);
    843 			if (oclen == (size_t)-1 || oclen == (size_t)-2)
    844 				errc(1, EILSEQ, NULL);
    845 			nclen = mbrlen(np, MB_LEN_MAX, &mbs2);
    846 			if (nclen == (size_t)-1 || nclen == (size_t)-2)
    847 				errc(1, EILSEQ, NULL);
    848 			if (oclen == 1 && nclen == 1)
    849 				y->bytetab[(u_char)*op] = (u_char)*np;
    850 			else {
    851 				y->bytetab[(u_char)*op] = 0;
    852 				y->multis = erealloc(y->multis,
    853 				    (y->nmultis + 1) * sizeof(*y->multis));
    854 				i = y->nmultis++;
    855 				y->multis[i].fromlen = oclen;
    856 				memcpy(y->multis[i].from, op, oclen);
    857 				y->multis[i].tolen = nclen;
    858 				memcpy(y->multis[i].to, np, nclen);
    859 			}
    860 			op += oclen;
    861 			np += nclen;
    862 		}
    863 	}
    864 	return (p);
    865 }
    866 
    867 /*
    868  * Compile the text following an a or i command.
    869  */
    870 static char *
    871 compile_text(void)
    872 {
    873 	size_t asize, size;
    874 	int esc_nl;
    875 	char *text, *p, *op, *s;
    876 	char lbuf[_POSIX2_LINE_MAX + 1];
    877 
    878 	asize = 2 * _POSIX2_LINE_MAX + 1;
    879 	text = emalloc(asize);
    880 	size = 0;
    881 	while (cu_fgets(lbuf, sizeof(lbuf), NULL)) {
    882 		op = s = text + size;
    883 		p = lbuf;
    884 		for (esc_nl = 0; *p != '\0'; p++) {
    885 			if (*p == '\\' && p[1] != '\0' && *++p == '\n')
    886 				esc_nl = 1;
    887 			*s++ = *p;
    888 		}
    889 		size += (size_t)(s - op);
    890 		if (!esc_nl) {
    891 			*s = '\0';
    892 			break;
    893 		}
    894 		if (asize - size < _POSIX2_LINE_MAX + 1) {
    895 			asize *= 2;
    896 			text = erealloc(text, asize);
    897 		}
    898 	}
    899 	text[size] = '\0';
    900 	p = erealloc(text, size + 1);
    901 	return (p);
    902 }
    903 
    904 /*
    905  * Get an address and return a pointer to the first character after
    906  * it.  Fill the structure pointed to according to the address.
    907  */
    908 static char *
    909 compile_addr(char *p, struct s_addr *a)
    910 {
    911 	char *end, re[_POSIX2_LINE_MAX + 1];
    912 	int icase;
    913 
    914 	icase = 0;
    915 
    916 	a->type = 0;
    917 	switch (*p) {
    918 	case '\\':				/* Context address */
    919 		++p;
    920 		/* FALLTHROUGH */
    921 	case '/':				/* Context address */
    922 		p = compile_delimited(p, re, 0);
    923 		if (p == NULL)
    924 			sederr("unterminated regular expression");
    925 		/* Check for case insensitive regexp flag */
    926 		if (*p == 'I') {
    927 			icase = 1;
    928 			p++;
    929 		}
    930 		if (*re == '\0')
    931 			a->u.r = NULL;
    932 		else
    933 			a->u.r = compile_re(re, icase);
    934 		a->type = AT_RE;
    935 		return (p);
    936 
    937 	case '$':				/* Last line */
    938 		a->type = AT_LAST;
    939 		return (p + 1);
    940 
    941 	case '+':				/* Relative line number */
    942 		a->type = AT_RELLINE;
    943 		p++;
    944 		/* FALLTHROUGH */
    945 						/* Line number */
    946 	case '0': case '1': case '2': case '3': case '4':
    947 	case '5': case '6': case '7': case '8': case '9':
    948 		if (a->type == 0)
    949 			a->type = AT_LINE;
    950 		a->u.l = strtoul(p, &end, 10);
    951 		return (end);
    952 	default:
    953 		sederr("expected context address");
    954 	}
    955 }
    956 
    957 /*
    958  * duptoeol --
    959  *	Return a copy of all the characters up to \n or \0.
    960  */
    961 static char *
    962 duptoeol(char *s, const char *ctype)
    963 {
    964 	size_t len;
    965 	int ws;
    966 	char *p, *start;
    967 
    968 	ws = 0;
    969 	for (start = s; *s != '\0' && *s != '\n'; ++s)
    970 		ws = isspace((unsigned char)*s);
    971 	*s = '\0';
    972 	if (ws)
    973 		sedwarn("whitespace after %s", ctype);
    974 	len = (size_t)(s - start + 1);
    975 	p = emalloc(len);
    976 	return (memmove(p, start, len));
    977 }
    978 
    979 /*
    980  * Convert goto label names to addresses, and count a and r commands, in
    981  * the given subset of the script.  Free the memory used by labels in b
    982  * and t commands (but not by :).
    983  *
    984  * TODO: Remove } nodes
    985  */
    986 static void
    987 fixuplabel(struct s_command *cp, struct s_command *end)
    988 {
    989 
    990 	for (; cp != end; cp = cp->next)
    991 		switch (cp->code) {
    992 		case 'a':
    993 		case 'r':
    994 			appendnum++;
    995 			break;
    996 		case 'b':
    997 		case 't':
    998 			/* Resolve branch target. */
    999 			if (cp->t == NULL) {
   1000 				cp->u.c = NULL;
   1001 				break;
   1002 			}
   1003 			if ((cp->u.c = findlabel(cp->t)) == NULL)
   1004 				sederr("undefined label '%s'", cp->t);
   1005 			free(cp->t);
   1006 			break;
   1007 		case '{':
   1008 			/* Do interior commands. */
   1009 			fixuplabel(cp->u.c, cp->next);
   1010 			break;
   1011 		}
   1012 }
   1013 
   1014 /*
   1015  * Associate the given command label for later lookup.
   1016  */
   1017 static void
   1018 enterlabel(struct s_command *cp)
   1019 {
   1020 	struct labhash **lhp, *lh;
   1021 	u_char *p;
   1022 	u_int h, c;
   1023 
   1024 	for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)
   1025 		h = (h << 5) + h + c;
   1026 	lhp = &labels[h & LHMASK];
   1027 	for (lh = *lhp; lh != NULL; lh = lh->lh_next)
   1028 		if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)
   1029 			sederr("duplicate label '%s'", cp->t);
   1030 	lh = emalloc(sizeof *lh);
   1031 	lh->lh_next = *lhp;
   1032 	lh->lh_hash = h;
   1033 	lh->lh_cmd = cp;
   1034 	lh->lh_ref = 0;
   1035 	*lhp = lh;
   1036 }
   1037 
   1038 /*
   1039  * Find the label contained in the command l in the command linked
   1040  * list cp.  L is excluded from the search.  Return NULL if not found.
   1041  */
   1042 static struct s_command *
   1043 findlabel(char *name)
   1044 {
   1045 	struct labhash *lh;
   1046 	u_char *p;
   1047 	u_int h, c;
   1048 
   1049 	for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)
   1050 		h = (h << 5) + h + c;
   1051 	for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) {
   1052 		if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) {
   1053 			lh->lh_ref = 1;
   1054 			return (lh->lh_cmd);
   1055 		}
   1056 	}
   1057 	return (NULL);
   1058 }
   1059 
   1060 /*
   1061  * Warn about any unused labels.  As a side effect, release the label hash
   1062  * table space.
   1063  */
   1064 static void
   1065 uselabel(void)
   1066 {
   1067 	struct labhash *lh, *next;
   1068 	int i;
   1069 
   1070 	for (i = 0; i < LHSZ; i++) {
   1071 		for (lh = labels[i]; lh != NULL; lh = next) {
   1072 			next = lh->lh_next;
   1073 			if (!lh->lh_ref)
   1074 				sedwarn("unused label '%s'", lh->lh_cmd->t);
   1075 			free(lh);
   1076 		}
   1077 	}
   1078 }
   1079