11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1992 Diomidis Spinellis.
31590Srgrimes * Copyright (c) 1992, 1993, 1994
41590Srgrimes *	The Regents of the University of California.  All rights reserved.
51590Srgrimes *
61590Srgrimes * This code is derived from software contributed to Berkeley by
71590Srgrimes * Diomidis Spinellis of Imperial College, University of London.
81590Srgrimes *
91590Srgrimes * Redistribution and use in source and binary forms, with or without
101590Srgrimes * modification, are permitted provided that the following conditions
111590Srgrimes * are met:
121590Srgrimes * 1. Redistributions of source code must retain the above copyright
131590Srgrimes *    notice, this list of conditions and the following disclaimer.
141590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer in the
161590Srgrimes *    documentation and/or other materials provided with the distribution.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3487766Smarkm#include <sys/cdefs.h>
3587766Smarkm__FBSDID("$FreeBSD: stable/10/usr.bin/sed/process.c 337367 2018-08-06 02:10:52Z pfg $");
3687766Smarkm
371590Srgrimes#ifndef lint
3887766Smarkmstatic const char sccsid[] = "@(#)process.c	8.6 (Berkeley) 4/20/94";
3928066Scharnier#endif
401590Srgrimes
411590Srgrimes#include <sys/types.h>
421590Srgrimes#include <sys/stat.h>
431590Srgrimes#include <sys/ioctl.h>
441590Srgrimes#include <sys/uio.h>
451590Srgrimes
461590Srgrimes#include <ctype.h>
4728066Scharnier#include <err.h>
481590Srgrimes#include <errno.h>
491590Srgrimes#include <fcntl.h>
501590Srgrimes#include <limits.h>
511590Srgrimes#include <regex.h>
521590Srgrimes#include <stdio.h>
531590Srgrimes#include <stdlib.h>
541590Srgrimes#include <string.h>
551590Srgrimes#include <unistd.h>
56132083Stjr#include <wchar.h>
57132083Stjr#include <wctype.h>
581590Srgrimes
591590Srgrimes#include "defs.h"
601590Srgrimes#include "extern.h"
611590Srgrimes
62132145Stjrstatic SPACE HS, PS, SS, YS;
631590Srgrimes#define	pd		PS.deleted
641590Srgrimes#define	ps		PS.space
651590Srgrimes#define	psl		PS.len
661590Srgrimes#define	hs		HS.space
671590Srgrimes#define	hsl		HS.len
681590Srgrimes
69277538Spfgstatic inline int	 applies(struct s_command *);
70132145Stjrstatic void		 do_tr(struct s_tr *);
7192922Simpstatic void		 flush_appends(void);
72132083Stjrstatic void		 lputs(char *, size_t);
73302228Spfgstatic int		 regexec_e(regex_t *, const char *, int, int, size_t,
74302228Spfg			     size_t);
7592922Simpstatic void		 regsub(SPACE *, char *, char *);
7692922Simpstatic int		 substitute(struct s_command *);
771590Srgrimes
781590Srgrimesstruct s_appends *appends;	/* Array of pointers to strings to append. */
79337367Spfgstatic unsigned int appendx;	/* Index into appends array. */
80337367Spfgunsigned int appendnum;		/* Size of appends array. */
811590Srgrimes
821590Srgrimesstatic int lastaddr;		/* Set by applies if last address of a range. */
831590Srgrimesstatic int sdone;		/* If any substitutes since last line input. */
841590Srgrimes				/* Iov structure for 'w' commands. */
851590Srgrimesstatic regex_t *defpreg;
861590Srgrimessize_t maxnsub;
871590Srgrimesregmatch_t *match;
881590Srgrimes
89170609Syar#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0)
901590Srgrimes
911590Srgrimesvoid
92122044Sdesprocess(void)
931590Srgrimes{
941590Srgrimes	struct s_command *cp;
951590Srgrimes	SPACE tspace;
96144840Sstefanf	size_t oldpsl = 0;
9710075Sjkh	char *p;
981590Srgrimes
99103715Seric	p = NULL;
100103715Seric
1011590Srgrimes	for (linenum = 0; mf_fgets(&PS, REPLACE);) {
1021590Srgrimes		pd = 0;
10310075Sjkhtop:
1041590Srgrimes		cp = prog;
1051590Srgrimesredirect:
1061590Srgrimes		while (cp != NULL) {
1071590Srgrimes			if (!applies(cp)) {
1081590Srgrimes				cp = cp->next;
1091590Srgrimes				continue;
1101590Srgrimes			}
1111590Srgrimes			switch (cp->code) {
1121590Srgrimes			case '{':
1131590Srgrimes				cp = cp->u.c;
1141590Srgrimes				goto redirect;
1151590Srgrimes			case 'a':
1161590Srgrimes				if (appendx >= appendnum)
11780286Sobrien					if ((appends = realloc(appends,
1181590Srgrimes					    sizeof(struct s_appends) *
11980286Sobrien					    (appendnum *= 2))) == NULL)
12080286Sobrien						err(1, "realloc");
1211590Srgrimes				appends[appendx].type = AP_STRING;
1221590Srgrimes				appends[appendx].s = cp->t;
1231590Srgrimes				appends[appendx].len = strlen(cp->t);
1241590Srgrimes				appendx++;
1251590Srgrimes				break;
1261590Srgrimes			case 'b':
1271590Srgrimes				cp = cp->u.c;
1281590Srgrimes				goto redirect;
1291590Srgrimes			case 'c':
1301590Srgrimes				pd = 1;
1311590Srgrimes				psl = 0;
132168211Syar				if (cp->a2 == NULL || lastaddr || lastline())
133122049Sdes					(void)fprintf(outfile, "%s", cp->t);
1341590Srgrimes				break;
1351590Srgrimes			case 'd':
1361590Srgrimes				pd = 1;
1371590Srgrimes				goto new;
1381590Srgrimes			case 'D':
1391590Srgrimes				if (pd)
1401590Srgrimes					goto new;
14194012Sjmallett				if (psl == 0 ||
142101668Stjr				    (p = memchr(ps, '\n', psl)) == NULL) {
1431590Srgrimes					pd = 1;
14410075Sjkh					goto new;
14510075Sjkh				} else {
14610075Sjkh					psl -= (p + 1) - ps;
1471590Srgrimes					memmove(ps, p + 1, psl);
14810075Sjkh					goto top;
1491590Srgrimes				}
1501590Srgrimes			case 'g':
1511590Srgrimes				cspace(&PS, hs, hsl, REPLACE);
1521590Srgrimes				break;
1531590Srgrimes			case 'G':
154170605Syar				cspace(&PS, "\n", 1, APPEND);
155170605Syar				cspace(&PS, hs, hsl, APPEND);
1561590Srgrimes				break;
1571590Srgrimes			case 'h':
1581590Srgrimes				cspace(&HS, ps, psl, REPLACE);
1591590Srgrimes				break;
1601590Srgrimes			case 'H':
161170605Syar				cspace(&HS, "\n", 1, APPEND);
162170605Syar				cspace(&HS, ps, psl, APPEND);
1631590Srgrimes				break;
1641590Srgrimes			case 'i':
165122049Sdes				(void)fprintf(outfile, "%s", cp->t);
1661590Srgrimes				break;
1671590Srgrimes			case 'l':
168132083Stjr				lputs(ps, psl);
1691590Srgrimes				break;
1701590Srgrimes			case 'n':
1711590Srgrimes				if (!nflag && !pd)
172170609Syar					OUT();
1731590Srgrimes				flush_appends();
1741590Srgrimes				if (!mf_fgets(&PS, REPLACE))
1751590Srgrimes					exit(0);
1761590Srgrimes				pd = 0;
1771590Srgrimes				break;
1781590Srgrimes			case 'N':
1791590Srgrimes				flush_appends();
180170605Syar				cspace(&PS, "\n", 1, APPEND);
181170605Syar				if (!mf_fgets(&PS, APPEND))
1821590Srgrimes					exit(0);
1831590Srgrimes				break;
1841590Srgrimes			case 'p':
1851590Srgrimes				if (pd)
1861590Srgrimes					break;
187170609Syar				OUT();
1881590Srgrimes				break;
1891590Srgrimes			case 'P':
1901590Srgrimes				if (pd)
1911590Srgrimes					break;
192158989Skrion				if ((p = memchr(ps, '\n', psl)) != NULL) {
19310075Sjkh					oldpsl = psl;
19498601Stjr					psl = p - ps;
1951590Srgrimes				}
196170609Syar				OUT();
1971590Srgrimes				if (p != NULL)
19810075Sjkh					psl = oldpsl;
1991590Srgrimes				break;
2001590Srgrimes			case 'q':
2011590Srgrimes				if (!nflag && !pd)
202170609Syar					OUT();
2031590Srgrimes				flush_appends();
2041590Srgrimes				exit(0);
2051590Srgrimes			case 'r':
2061590Srgrimes				if (appendx >= appendnum)
20780286Sobrien					if ((appends = realloc(appends,
2081590Srgrimes					    sizeof(struct s_appends) *
20980286Sobrien					    (appendnum *= 2))) == NULL)
21080286Sobrien						err(1, "realloc");
2111590Srgrimes				appends[appendx].type = AP_FILE;
2121590Srgrimes				appends[appendx].s = cp->t;
2131590Srgrimes				appends[appendx].len = strlen(cp->t);
2141590Srgrimes				appendx++;
2151590Srgrimes				break;
2161590Srgrimes			case 's':
2171590Srgrimes				sdone |= substitute(cp);
2181590Srgrimes				break;
2191590Srgrimes			case 't':
2201590Srgrimes				if (sdone) {
2211590Srgrimes					sdone = 0;
2221590Srgrimes					cp = cp->u.c;
2231590Srgrimes					goto redirect;
2241590Srgrimes				}
2251590Srgrimes				break;
2261590Srgrimes			case 'w':
2271590Srgrimes				if (pd)
2281590Srgrimes					break;
2291590Srgrimes				if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
2301590Srgrimes				    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
2311590Srgrimes				    DEFFILEMODE)) == -1)
23228066Scharnier					err(1, "%s", cp->t);
233176126Sdwmalone				if (write(cp->u.fd, ps, psl) != (ssize_t)psl ||
23498601Stjr				    write(cp->u.fd, "\n", 1) != 1)
23528066Scharnier					err(1, "%s", cp->t);
2361590Srgrimes				break;
2371590Srgrimes			case 'x':
238170608Syar				/*
239170608Syar				 * If the hold space is null, make it empty
240170608Syar				 * but not null.  Otherwise the pattern space
241170608Syar				 * will become null after the swap, which is
242170608Syar				 * an abnormal condition.
243170608Syar				 */
24499351Stjr				if (hs == NULL)
24599351Stjr					cspace(&HS, "", 0, REPLACE);
2461590Srgrimes				tspace = PS;
2471590Srgrimes				PS = HS;
2481590Srgrimes				HS = tspace;
2491590Srgrimes				break;
2501590Srgrimes			case 'y':
25194012Sjmallett				if (pd || psl == 0)
2521590Srgrimes					break;
253132145Stjr				do_tr(cp->u.y);
2541590Srgrimes				break;
2551590Srgrimes			case ':':
2561590Srgrimes			case '}':
2571590Srgrimes				break;
2581590Srgrimes			case '=':
259122049Sdes				(void)fprintf(outfile, "%lu\n", linenum);
2601590Srgrimes			}
2611590Srgrimes			cp = cp->next;
2621590Srgrimes		} /* for all cp */
2631590Srgrimes
2641590Srgrimesnew:		if (!nflag && !pd)
265170609Syar			OUT();
2661590Srgrimes		flush_appends();
2671590Srgrimes	} /* for all lines */
2681590Srgrimes}
2691590Srgrimes
2701590Srgrimes/*
2711590Srgrimes * TRUE if the address passed matches the current program state
2721590Srgrimes * (lastline, linenumber, ps).
2731590Srgrimes */
274168255Syar#define	MATCH(a)							\
275302228Spfg	((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, 0, psl) :	\
276168255Syar	    (a)->type == AT_LINE ? linenum == (a)->u.l : lastline())
2771590Srgrimes
2781590Srgrimes/*
279192732Sbrian * Return TRUE if the command applies to the current line.  Sets the start
280192732Sbrian * line for process ranges.  Interprets the non-select (``!'') flag.
2811590Srgrimes */
282277538Spfgstatic inline int
283122044Sdesapplies(struct s_command *cp)
2841590Srgrimes{
2851590Srgrimes	int r;
2861590Srgrimes
2871590Srgrimes	lastaddr = 0;
2881590Srgrimes	if (cp->a1 == NULL && cp->a2 == NULL)
2891590Srgrimes		r = 1;
2901590Srgrimes	else if (cp->a2)
291192732Sbrian		if (cp->startline > 0) {
292269837Sjlh                        switch (cp->a2->type) {
293269837Sjlh                        case AT_RELLINE:
294269837Sjlh                                if (linenum - cp->startline <= cp->a2->u.l)
295269837Sjlh                                        r = 1;
296269837Sjlh                                else {
297269837Sjlh				        cp->startline = 0;
298269837Sjlh				        r = 0;
299269837Sjlh                                }
300269837Sjlh                                break;
301269837Sjlh                        default:
302269837Sjlh                                if (MATCH(cp->a2)) {
303269837Sjlh                                        cp->startline = 0;
304269837Sjlh                                        lastaddr = 1;
305269837Sjlh                                        r = 1;
306269837Sjlh                                } else if (cp->a2->type == AT_LINE &&
307269837Sjlh                                            linenum > cp->a2->u.l) {
308269837Sjlh                                        /*
309269837Sjlh                                         * We missed the 2nd address due to a
310269837Sjlh                                         * branch, so just close the range and
311269837Sjlh                                         * return false.
312269837Sjlh                                         */
313269837Sjlh                                        cp->startline = 0;
314269837Sjlh                                        r = 0;
315269837Sjlh                                } else
316269837Sjlh                                        r = 1;
317269837Sjlh                        }
318277933Spfg		} else if (cp->a1 && MATCH(cp->a1)) {
3191590Srgrimes			/*
3201590Srgrimes			 * If the second address is a number less than or
3211590Srgrimes			 * equal to the line number first selected, only
3221590Srgrimes			 * one line shall be selected.
3231590Srgrimes			 *	-- POSIX 1003.2
324192732Sbrian			 * Likewise if the relative second line address is zero.
3251590Srgrimes			 */
326192732Sbrian			if ((cp->a2->type == AT_LINE &&
327192732Sbrian			    linenum >= cp->a2->u.l) ||
328192732Sbrian			    (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0))
3291590Srgrimes				lastaddr = 1;
330192732Sbrian			else {
331192732Sbrian				cp->startline = linenum;
332192732Sbrian			}
3331590Srgrimes			r = 1;
3341590Srgrimes		} else
3351590Srgrimes			r = 0;
3361590Srgrimes	else
3371590Srgrimes		r = MATCH(cp->a1);
3381590Srgrimes	return (cp->nonsel ? ! r : r);
3391590Srgrimes}
3401590Srgrimes
3411590Srgrimes/*
342170608Syar * Reset the sed processor to its initial state.
343168921Syar */
344168921Syarvoid
345170608Syarresetstate(void)
346168921Syar{
347168921Syar	struct s_command *cp;
348168921Syar
349170608Syar	/*
350192732Sbrian	 * Reset all in-range markers.
351170608Syar	 */
352168921Syar	for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
353168921Syar		if (cp->a2)
354192732Sbrian			cp->startline = 0;
355170608Syar
356170608Syar	/*
357170608Syar	 * Clear out the hold space.
358170608Syar	 */
359170608Syar	cspace(&HS, "", 0, REPLACE);
360168921Syar}
361168921Syar
362168921Syar/*
3631590Srgrimes * substitute --
3641590Srgrimes *	Do substitutions in the pattern space.  Currently, we build a
3651590Srgrimes *	copy of the new pattern space in the substitute space structure
3661590Srgrimes *	and then swap them.
3671590Srgrimes */
3681590Srgrimesstatic int
369122044Sdessubstitute(struct s_command *cp)
3701590Srgrimes{
3711590Srgrimes	SPACE tspace;
3721590Srgrimes	regex_t *re;
373300555Spfg	regoff_t slen;
3741590Srgrimes	int lastempty, n;
375337367Spfg	regoff_t le = 0;
3761590Srgrimes	char *s;
3771590Srgrimes
3781590Srgrimes	s = ps;
3791590Srgrimes	re = cp->u.s->re;
3801590Srgrimes	if (re == NULL) {
3811590Srgrimes		if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
3821590Srgrimes			linenum = cp->u.s->linenum;
383176126Sdwmalone			errx(1, "%lu: %s: \\%u not defined in the RE",
38428066Scharnier					linenum, fname, cp->u.s->maxbref);
3851590Srgrimes		}
3861590Srgrimes	}
387302244Spfg	if (!regexec_e(re, ps, 0, 0, 0, psl))
3881590Srgrimes		return (0);
3891590Srgrimes
390122045Sdes	SS.len = 0;				/* Clean substitute space. */
391122045Sdes	slen = psl;
392122045Sdes	n = cp->u.s->n;
3931590Srgrimes	lastempty = 1;
3941590Srgrimes
395300555Spfg	do {
396300555Spfg		/* Copy the leading retained string. */
397302244Spfg		if (n <= 1 && (match[0].rm_so > le))
398302228Spfg			cspace(&SS, s, match[0].rm_so - le, APPEND);
399300555Spfg
400300555Spfg		/* Skip zero-length matches right after other matches. */
401302228Spfg		if (lastempty || (match[0].rm_so - le) ||
402300555Spfg		    match[0].rm_so != match[0].rm_eo) {
403300555Spfg			if (n <= 1) {
404300555Spfg				/* Want this match: append replacement. */
405302228Spfg				regsub(&SS, ps, cp->u.s->new);
406300555Spfg				if (n == 1)
407300555Spfg					n = -1;
4081590Srgrimes			} else {
409300555Spfg				/* Want a later match: append original. */
410302228Spfg				if (match[0].rm_eo - le)
411302228Spfg					cspace(&SS, s, match[0].rm_eo - le,
412302228Spfg					    APPEND);
413300555Spfg				n--;
4141590Srgrimes			}
4151590Srgrimes		}
416300555Spfg
417300555Spfg		/* Move past this match. */
418302244Spfg		s = ps + match[0].rm_eo;
419302244Spfg		slen = psl - match[0].rm_eo;
420302228Spfg		le = match[0].rm_eo;
421300555Spfg
422300555Spfg		/*
423300555Spfg		 * After a zero-length match, advance one byte,
424300555Spfg		 * and at the end of the line, terminate.
425300555Spfg		 */
426300555Spfg		if (match[0].rm_so == match[0].rm_eo) {
427300555Spfg			if (*s == '\0' || *s == '\n')
428300555Spfg				slen = -1;
429300555Spfg			else
430300555Spfg				slen--;
431302228Spfg			if (*s != '\0') {
432300555Spfg			 	cspace(&SS, s++, 1, APPEND);
433302228Spfg				le++;
434302228Spfg			}
435300555Spfg			lastempty = 1;
436300555Spfg		} else
437300555Spfg			lastempty = 0;
438300555Spfg
439302244Spfg	} while (n >= 0 && slen >= 0 &&
440302244Spfg	    regexec_e(re, ps, REG_NOTBOL, 0, le, psl));
441300555Spfg
442300555Spfg	/* Did not find the requested number of matches. */
443303065Spfg	if (n > 0)
444300555Spfg		return (0);
445300555Spfg
446300555Spfg	/* Copy the trailing retained string. */
447300555Spfg	if (slen > 0)
4481590Srgrimes		cspace(&SS, s, slen, APPEND);
4491590Srgrimes
4501590Srgrimes	/*
4511590Srgrimes	 * Swap the substitute space and the pattern space, and make sure
4521590Srgrimes	 * that any leftover pointers into stdio memory get lost.
4531590Srgrimes	 */
4541590Srgrimes	tspace = PS;
4551590Srgrimes	PS = SS;
4561590Srgrimes	SS = tspace;
4571590Srgrimes	SS.space = SS.back;
4581590Srgrimes
4591590Srgrimes	/* Handle the 'p' flag. */
4601590Srgrimes	if (cp->u.s->p)
461170609Syar		OUT();
4621590Srgrimes
4631590Srgrimes	/* Handle the 'w' flag. */
4641590Srgrimes	if (cp->u.s->wfile && !pd) {
4651590Srgrimes		if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
4661590Srgrimes		    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
46728066Scharnier			err(1, "%s", cp->u.s->wfile);
468176126Sdwmalone		if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl ||
46998601Stjr		    write(cp->u.s->wfd, "\n", 1) != 1)
47028066Scharnier			err(1, "%s", cp->u.s->wfile);
4711590Srgrimes	}
4721590Srgrimes	return (1);
4731590Srgrimes}
4741590Srgrimes
4751590Srgrimes/*
476132145Stjr * do_tr --
477132145Stjr *	Perform translation ('y' command) in the pattern space.
478132145Stjr */
479132145Stjrstatic void
480132145Stjrdo_tr(struct s_tr *y)
481132145Stjr{
482132145Stjr	SPACE tmp;
483132145Stjr	char c, *p;
484132145Stjr	size_t clen, left;
485132145Stjr	int i;
486132145Stjr
487132145Stjr	if (MB_CUR_MAX == 1) {
488132145Stjr		/*
489132145Stjr		 * Single-byte encoding: perform in-place translation
490132145Stjr		 * of the pattern space.
491132145Stjr		 */
492132145Stjr		for (p = ps; p < &ps[psl]; p++)
493132145Stjr			*p = y->bytetab[(u_char)*p];
494132145Stjr	} else {
495132145Stjr		/*
496132145Stjr		 * Multi-byte encoding: perform translation into the
497132145Stjr		 * translation space, then swap the translation and
498132145Stjr		 * pattern spaces.
499132145Stjr		 */
500132145Stjr		/* Clean translation space. */
501132145Stjr		YS.len = 0;
502132145Stjr		for (p = ps, left = psl; left > 0; p += clen, left -= clen) {
503132145Stjr			if ((c = y->bytetab[(u_char)*p]) != '\0') {
504132145Stjr				cspace(&YS, &c, 1, APPEND);
505132145Stjr				clen = 1;
506132145Stjr				continue;
507132145Stjr			}
508132145Stjr			for (i = 0; i < y->nmultis; i++)
509132145Stjr				if (left >= y->multis[i].fromlen &&
510132145Stjr				    memcmp(p, y->multis[i].from,
511132145Stjr				    y->multis[i].fromlen) == 0)
512132145Stjr					break;
513132145Stjr			if (i < y->nmultis) {
514132145Stjr				cspace(&YS, y->multis[i].to,
515132145Stjr				    y->multis[i].tolen, APPEND);
516132145Stjr				clen = y->multis[i].fromlen;
517132145Stjr			} else {
518132145Stjr				cspace(&YS, p, 1, APPEND);
519132145Stjr				clen = 1;
520132145Stjr			}
521132145Stjr		}
522132145Stjr		/* Swap the translation space and the pattern space. */
523132145Stjr		tmp = PS;
524132145Stjr		PS = YS;
525132145Stjr		YS = tmp;
526132145Stjr		YS.space = YS.back;
527132145Stjr	}
528132145Stjr}
529132145Stjr
530132145Stjr/*
5311590Srgrimes * Flush append requests.  Always called before reading a line,
5321590Srgrimes * therefore it also resets the substitution done (sdone) flag.
5331590Srgrimes */
5341590Srgrimesstatic void
535122044Sdesflush_appends(void)
5361590Srgrimes{
5371590Srgrimes	FILE *f;
538337367Spfg	unsigned int count, idx;
5391590Srgrimes	char buf[8 * 1024];
5401590Srgrimes
541337367Spfg	for (idx = 0; idx < appendx; idx++)
542337367Spfg		switch (appends[idx].type) {
5431590Srgrimes		case AP_STRING:
544337367Spfg			fwrite(appends[idx].s, sizeof(char), appends[idx].len,
545122049Sdes			    outfile);
5461590Srgrimes			break;
5471590Srgrimes		case AP_FILE:
5481590Srgrimes			/*
5491590Srgrimes			 * Read files probably shouldn't be cached.  Since
5501590Srgrimes			 * it's not an error to read a non-existent file,
5511590Srgrimes			 * it's possible that another program is interacting
55296704Strhodes			 * with the sed script through the filesystem.  It
5531590Srgrimes			 * would be truly bizarre, but possible.  It's probably
5541590Srgrimes			 * not that big a performance win, anyhow.
5551590Srgrimes			 */
556337367Spfg			if ((f = fopen(appends[idx].s, "r")) == NULL)
5571590Srgrimes				break;
55828066Scharnier			while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
559122049Sdes				(void)fwrite(buf, sizeof(char), count, outfile);
5601590Srgrimes			(void)fclose(f);
5611590Srgrimes			break;
5621590Srgrimes		}
563122049Sdes	if (ferror(outfile))
564122049Sdes		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
5651590Srgrimes	appendx = sdone = 0;
5661590Srgrimes}
5671590Srgrimes
5681590Srgrimesstatic void
569132083Stjrlputs(char *s, size_t len)
5701590Srgrimes{
571132083Stjr	static const char escapes[] = "\\\a\b\f\r\t\v";
572132083Stjr	int c, col, width;
573176126Sdwmalone	const char *p;
5741590Srgrimes	struct winsize win;
5751590Srgrimes	static int termwidth = -1;
576132083Stjr	size_t clen, i;
577132083Stjr	wchar_t wc;
578132083Stjr	mbstate_t mbs;
5791590Srgrimes
580122049Sdes	if (outfile != stdout)
581122049Sdes		termwidth = 60;
58246081Simp	if (termwidth == -1) {
58397801Stjr		if ((p = getenv("COLUMNS")) && *p != '\0')
5841590Srgrimes			termwidth = atoi(p);
5851590Srgrimes		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
5861590Srgrimes		    win.ws_col > 0)
5871590Srgrimes			termwidth = win.ws_col;
5881590Srgrimes		else
5891590Srgrimes			termwidth = 60;
59046081Simp	}
591176126Sdwmalone	if (termwidth <= 0)
592176126Sdwmalone		termwidth = 1;
5931590Srgrimes
594132083Stjr	memset(&mbs, 0, sizeof(mbs));
595132083Stjr	col = 0;
596132083Stjr	while (len != 0) {
597132083Stjr		clen = mbrtowc(&wc, s, len, &mbs);
598132083Stjr		if (clen == 0)
599132083Stjr			clen = 1;
600132083Stjr		if (clen == (size_t)-1 || clen == (size_t)-2) {
601132083Stjr			wc = (unsigned char)*s;
602132083Stjr			clen = 1;
603132083Stjr			memset(&mbs, 0, sizeof(mbs));
6041590Srgrimes		}
605132083Stjr		if (wc == '\n') {
606132083Stjr			if (col + 1 >= termwidth)
607132083Stjr				fprintf(outfile, "\\\n");
608132083Stjr			fputc('$', outfile);
609132083Stjr			fputc('\n', outfile);
610132083Stjr			col = 0;
611132083Stjr		} else if (iswprint(wc)) {
612132083Stjr			width = wcwidth(wc);
613132083Stjr			if (col + width >= termwidth) {
614132083Stjr				fprintf(outfile, "\\\n");
615132083Stjr				col = 0;
616132083Stjr			}
617132083Stjr			fwrite(s, 1, clen, outfile);
618132083Stjr			col += width;
619132083Stjr		} else if (wc != L'\0' && (c = wctob(wc)) != EOF &&
620132083Stjr		    (p = strchr(escapes, c)) != NULL) {
621132083Stjr			if (col + 2 >= termwidth) {
622132083Stjr				fprintf(outfile, "\\\n");
623132083Stjr				col = 0;
624132083Stjr			}
625132083Stjr			fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]);
626132083Stjr			col += 2;
62798603Stjr		} else {
628176126Sdwmalone			if (col + 4 * clen >= (unsigned)termwidth) {
629132083Stjr				fprintf(outfile, "\\\n");
630132083Stjr				col = 0;
6311590Srgrimes			}
632132083Stjr			for (i = 0; i < clen; i++)
633132083Stjr				fprintf(outfile, "\\%03o",
634132083Stjr				    (int)(unsigned char)s[i]);
635132083Stjr			col += 4 * clen;
6361590Srgrimes		}
637132083Stjr		s += clen;
638132083Stjr		len -= clen;
6391590Srgrimes	}
640132083Stjr	if (col + 1 >= termwidth)
641132083Stjr		fprintf(outfile, "\\\n");
642122049Sdes	(void)fputc('$', outfile);
643122049Sdes	(void)fputc('\n', outfile);
644122049Sdes	if (ferror(outfile))
645122049Sdes		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
6461590Srgrimes}
6471590Srgrimes
648277538Spfgstatic int
649122044Sdesregexec_e(regex_t *preg, const char *string, int eflags, int nomatch,
650302228Spfg	size_t start, size_t stop)
6511590Srgrimes{
6521590Srgrimes	int eval;
6538874Srgrimes
6541590Srgrimes	if (preg == NULL) {
6551590Srgrimes		if (defpreg == NULL)
65628066Scharnier			errx(1, "first RE may not be empty");
6571590Srgrimes	} else
6581590Srgrimes		defpreg = preg;
6591590Srgrimes
66098601Stjr	/* Set anchors */
661302228Spfg	match[0].rm_so = start;
662302228Spfg	match[0].rm_eo = stop;
6638874Srgrimes
6641590Srgrimes	eval = regexec(defpreg, string,
6651590Srgrimes	    nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
6661590Srgrimes	switch(eval) {
6671590Srgrimes	case 0:
6681590Srgrimes		return (1);
6691590Srgrimes	case REG_NOMATCH:
6701590Srgrimes		return (0);
6711590Srgrimes	}
67228066Scharnier	errx(1, "RE error: %s", strregerror(eval, defpreg));
6731590Srgrimes	/* NOTREACHED */
6741590Srgrimes}
6751590Srgrimes
6761590Srgrimes/*
6771590Srgrimes * regsub - perform substitutions after a regexp match
6781590Srgrimes * Based on a routine by Henry Spencer
6791590Srgrimes */
6801590Srgrimesstatic void
681122044Sdesregsub(SPACE *sp, char *string, char *src)
6821590Srgrimes{
68387766Smarkm	int len, no;
68487766Smarkm	char c, *dst;
6851590Srgrimes
6861590Srgrimes#define	NEEDSP(reqlen)							\
687121915Stjr	/* XXX What is the +1 for? */					\
688121915Stjr	if (sp->len + (reqlen) + 1 >= sp->blen) {			\
6891590Srgrimes		sp->blen += (reqlen) + 1024;				\
69080286Sobrien		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \
69180286Sobrien		    == NULL)						\
69280286Sobrien			err(1, "realloc");				\
6931590Srgrimes		dst = sp->space + sp->len;				\
6941590Srgrimes	}
6951590Srgrimes
6961590Srgrimes	dst = sp->space + sp->len;
6971590Srgrimes	while ((c = *src++) != '\0') {
6981590Srgrimes		if (c == '&')
6991590Srgrimes			no = 0;
70017522Sache		else if (c == '\\' && isdigit((unsigned char)*src))
7011590Srgrimes			no = *src++ - '0';
7021590Srgrimes		else
7031590Srgrimes			no = -1;
7041590Srgrimes		if (no < 0) {		/* Ordinary character. */
705122045Sdes			if (c == '\\' && (*src == '\\' || *src == '&'))
706122045Sdes				c = *src++;
7071590Srgrimes			NEEDSP(1);
708122045Sdes			*dst++ = c;
7091590Srgrimes			++sp->len;
710122045Sdes		} else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
7111590Srgrimes			len = match[no].rm_eo - match[no].rm_so;
7121590Srgrimes			NEEDSP(len);
7131590Srgrimes			memmove(dst, string + match[no].rm_so, len);
7141590Srgrimes			dst += len;
7151590Srgrimes			sp->len += len;
7161590Srgrimes		}
7171590Srgrimes	}
7181590Srgrimes	NEEDSP(1);
7191590Srgrimes	*dst = '\0';
7201590Srgrimes}
7211590Srgrimes
7221590Srgrimes/*
723168120Syar * cspace --
724168120Syar *	Concatenate space: append the source space to the destination space,
725168120Syar *	allocating new space as necessary.
7261590Srgrimes */
7271590Srgrimesvoid
728122044Sdescspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
7291590Srgrimes{
7301590Srgrimes	size_t tlen;
7311590Srgrimes
7321590Srgrimes	/* Make sure SPACE has enough memory and ramp up quickly. */
7331590Srgrimes	tlen = sp->len + len + 1;
7341590Srgrimes	if (tlen > sp->blen) {
7351590Srgrimes		sp->blen = tlen + 1024;
73680286Sobrien		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
73780286Sobrien		    NULL)
73880286Sobrien			err(1, "realloc");
7391590Srgrimes	}
7401590Srgrimes
7411590Srgrimes	if (spflag == REPLACE)
7421590Srgrimes		sp->len = 0;
7431590Srgrimes
7441590Srgrimes	memmove(sp->space + sp->len, p, len);
7451590Srgrimes
7461590Srgrimes	sp->space[sp->len += len] = '\0';
7471590Srgrimes}
7481590Srgrimes
7491590Srgrimes/*
7501590Srgrimes * Close all cached opened files and report any errors
7511590Srgrimes */
7521590Srgrimesvoid
753122044Sdescfclose(struct s_command *cp, struct s_command *end)
7541590Srgrimes{
7551590Srgrimes
7561590Srgrimes	for (; cp != end; cp = cp->next)
7571590Srgrimes		switch(cp->code) {
7581590Srgrimes		case 's':
7591590Srgrimes			if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
76028066Scharnier				err(1, "%s", cp->u.s->wfile);
7611590Srgrimes			cp->u.s->wfd = -1;
7621590Srgrimes			break;
7631590Srgrimes		case 'w':
7641590Srgrimes			if (cp->u.fd != -1 && close(cp->u.fd))
76528066Scharnier				err(1, "%s", cp->t);
7661590Srgrimes			cp->u.fd = -1;
7671590Srgrimes			break;
7681590Srgrimes		case '{':
7691590Srgrimes			cfclose(cp->u.c, cp->next);
7701590Srgrimes			break;
7711590Srgrimes		}
7721590Srgrimes}
773