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$");
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
69100359Smarkmstatic __inline int	 applies(struct s_command *);
70132145Stjrstatic void		 do_tr(struct s_tr *);
7192922Simpstatic void		 flush_appends(void);
72132083Stjrstatic void		 lputs(char *, size_t);
73100359Smarkmstatic __inline int	 regexec_e(regex_t *, const char *, int, int, size_t);
7492922Simpstatic void		 regsub(SPACE *, char *, char *);
7592922Simpstatic int		 substitute(struct s_command *);
761590Srgrimes
771590Srgrimesstruct s_appends *appends;	/* Array of pointers to strings to append. */
781590Srgrimesstatic int appendx;		/* Index into appends array. */
791590Srgrimesint appendnum;			/* Size of appends array. */
801590Srgrimes
811590Srgrimesstatic int lastaddr;		/* Set by applies if last address of a range. */
821590Srgrimesstatic int sdone;		/* If any substitutes since last line input. */
831590Srgrimes				/* Iov structure for 'w' commands. */
841590Srgrimesstatic regex_t *defpreg;
851590Srgrimessize_t maxnsub;
861590Srgrimesregmatch_t *match;
871590Srgrimes
88170609Syar#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0)
891590Srgrimes
901590Srgrimesvoid
91122044Sdesprocess(void)
921590Srgrimes{
931590Srgrimes	struct s_command *cp;
941590Srgrimes	SPACE tspace;
95144840Sstefanf	size_t oldpsl = 0;
9610075Sjkh	char *p;
971590Srgrimes
98103715Seric	p = NULL;
99103715Seric
1001590Srgrimes	for (linenum = 0; mf_fgets(&PS, REPLACE);) {
1011590Srgrimes		pd = 0;
10210075Sjkhtop:
1031590Srgrimes		cp = prog;
1041590Srgrimesredirect:
1051590Srgrimes		while (cp != NULL) {
1061590Srgrimes			if (!applies(cp)) {
1071590Srgrimes				cp = cp->next;
1081590Srgrimes				continue;
1091590Srgrimes			}
1101590Srgrimes			switch (cp->code) {
1111590Srgrimes			case '{':
1121590Srgrimes				cp = cp->u.c;
1131590Srgrimes				goto redirect;
1141590Srgrimes			case 'a':
1151590Srgrimes				if (appendx >= appendnum)
11680286Sobrien					if ((appends = realloc(appends,
1171590Srgrimes					    sizeof(struct s_appends) *
11880286Sobrien					    (appendnum *= 2))) == NULL)
11980286Sobrien						err(1, "realloc");
1201590Srgrimes				appends[appendx].type = AP_STRING;
1211590Srgrimes				appends[appendx].s = cp->t;
1221590Srgrimes				appends[appendx].len = strlen(cp->t);
1231590Srgrimes				appendx++;
1241590Srgrimes				break;
1251590Srgrimes			case 'b':
1261590Srgrimes				cp = cp->u.c;
1271590Srgrimes				goto redirect;
1281590Srgrimes			case 'c':
1291590Srgrimes				pd = 1;
1301590Srgrimes				psl = 0;
131168211Syar				if (cp->a2 == NULL || lastaddr || lastline())
132122049Sdes					(void)fprintf(outfile, "%s", cp->t);
1331590Srgrimes				break;
1341590Srgrimes			case 'd':
1351590Srgrimes				pd = 1;
1361590Srgrimes				goto new;
1371590Srgrimes			case 'D':
1381590Srgrimes				if (pd)
1391590Srgrimes					goto new;
14094012Sjmallett				if (psl == 0 ||
141101668Stjr				    (p = memchr(ps, '\n', psl)) == NULL) {
1421590Srgrimes					pd = 1;
14310075Sjkh					goto new;
14410075Sjkh				} else {
14510075Sjkh					psl -= (p + 1) - ps;
1461590Srgrimes					memmove(ps, p + 1, psl);
14710075Sjkh					goto top;
1481590Srgrimes				}
1491590Srgrimes			case 'g':
1501590Srgrimes				cspace(&PS, hs, hsl, REPLACE);
1511590Srgrimes				break;
1521590Srgrimes			case 'G':
153170605Syar				cspace(&PS, "\n", 1, APPEND);
154170605Syar				cspace(&PS, hs, hsl, APPEND);
1551590Srgrimes				break;
1561590Srgrimes			case 'h':
1571590Srgrimes				cspace(&HS, ps, psl, REPLACE);
1581590Srgrimes				break;
1591590Srgrimes			case 'H':
160170605Syar				cspace(&HS, "\n", 1, APPEND);
161170605Syar				cspace(&HS, ps, psl, APPEND);
1621590Srgrimes				break;
1631590Srgrimes			case 'i':
164122049Sdes				(void)fprintf(outfile, "%s", cp->t);
1651590Srgrimes				break;
1661590Srgrimes			case 'l':
167132083Stjr				lputs(ps, psl);
1681590Srgrimes				break;
1691590Srgrimes			case 'n':
1701590Srgrimes				if (!nflag && !pd)
171170609Syar					OUT();
1721590Srgrimes				flush_appends();
1731590Srgrimes				if (!mf_fgets(&PS, REPLACE))
1741590Srgrimes					exit(0);
1751590Srgrimes				pd = 0;
1761590Srgrimes				break;
1771590Srgrimes			case 'N':
1781590Srgrimes				flush_appends();
179170605Syar				cspace(&PS, "\n", 1, APPEND);
180170605Syar				if (!mf_fgets(&PS, APPEND))
1811590Srgrimes					exit(0);
1821590Srgrimes				break;
1831590Srgrimes			case 'p':
1841590Srgrimes				if (pd)
1851590Srgrimes					break;
186170609Syar				OUT();
1871590Srgrimes				break;
1881590Srgrimes			case 'P':
1891590Srgrimes				if (pd)
1901590Srgrimes					break;
191158989Skrion				if ((p = memchr(ps, '\n', psl)) != NULL) {
19210075Sjkh					oldpsl = psl;
19398601Stjr					psl = p - ps;
1941590Srgrimes				}
195170609Syar				OUT();
1961590Srgrimes				if (p != NULL)
19710075Sjkh					psl = oldpsl;
1981590Srgrimes				break;
1991590Srgrimes			case 'q':
2001590Srgrimes				if (!nflag && !pd)
201170609Syar					OUT();
2021590Srgrimes				flush_appends();
2031590Srgrimes				exit(0);
2041590Srgrimes			case 'r':
2051590Srgrimes				if (appendx >= appendnum)
20680286Sobrien					if ((appends = realloc(appends,
2071590Srgrimes					    sizeof(struct s_appends) *
20880286Sobrien					    (appendnum *= 2))) == NULL)
20980286Sobrien						err(1, "realloc");
2101590Srgrimes				appends[appendx].type = AP_FILE;
2111590Srgrimes				appends[appendx].s = cp->t;
2121590Srgrimes				appends[appendx].len = strlen(cp->t);
2131590Srgrimes				appendx++;
2141590Srgrimes				break;
2151590Srgrimes			case 's':
2161590Srgrimes				sdone |= substitute(cp);
2171590Srgrimes				break;
2181590Srgrimes			case 't':
2191590Srgrimes				if (sdone) {
2201590Srgrimes					sdone = 0;
2211590Srgrimes					cp = cp->u.c;
2221590Srgrimes					goto redirect;
2231590Srgrimes				}
2241590Srgrimes				break;
2251590Srgrimes			case 'w':
2261590Srgrimes				if (pd)
2271590Srgrimes					break;
2281590Srgrimes				if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
2291590Srgrimes				    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
2301590Srgrimes				    DEFFILEMODE)) == -1)
23128066Scharnier					err(1, "%s", cp->t);
232176126Sdwmalone				if (write(cp->u.fd, ps, psl) != (ssize_t)psl ||
23398601Stjr				    write(cp->u.fd, "\n", 1) != 1)
23428066Scharnier					err(1, "%s", cp->t);
2351590Srgrimes				break;
2361590Srgrimes			case 'x':
237170608Syar				/*
238170608Syar				 * If the hold space is null, make it empty
239170608Syar				 * but not null.  Otherwise the pattern space
240170608Syar				 * will become null after the swap, which is
241170608Syar				 * an abnormal condition.
242170608Syar				 */
24399351Stjr				if (hs == NULL)
24499351Stjr					cspace(&HS, "", 0, REPLACE);
2451590Srgrimes				tspace = PS;
2461590Srgrimes				PS = HS;
2471590Srgrimes				HS = tspace;
2481590Srgrimes				break;
2491590Srgrimes			case 'y':
25094012Sjmallett				if (pd || psl == 0)
2511590Srgrimes					break;
252132145Stjr				do_tr(cp->u.y);
2531590Srgrimes				break;
2541590Srgrimes			case ':':
2551590Srgrimes			case '}':
2561590Srgrimes				break;
2571590Srgrimes			case '=':
258122049Sdes				(void)fprintf(outfile, "%lu\n", linenum);
2591590Srgrimes			}
2601590Srgrimes			cp = cp->next;
2611590Srgrimes		} /* for all cp */
2621590Srgrimes
2631590Srgrimesnew:		if (!nflag && !pd)
264170609Syar			OUT();
2651590Srgrimes		flush_appends();
2661590Srgrimes	} /* for all lines */
2671590Srgrimes}
2681590Srgrimes
2691590Srgrimes/*
2701590Srgrimes * TRUE if the address passed matches the current program state
2711590Srgrimes * (lastline, linenumber, ps).
2721590Srgrimes */
273168255Syar#define	MATCH(a)							\
274168255Syar	((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) :	\
275168255Syar	    (a)->type == AT_LINE ? linenum == (a)->u.l : lastline())
2761590Srgrimes
2771590Srgrimes/*
278192732Sbrian * Return TRUE if the command applies to the current line.  Sets the start
279192732Sbrian * line for process ranges.  Interprets the non-select (``!'') flag.
2801590Srgrimes */
281100359Smarkmstatic __inline int
282122044Sdesapplies(struct s_command *cp)
2831590Srgrimes{
2841590Srgrimes	int r;
2851590Srgrimes
2861590Srgrimes	lastaddr = 0;
2871590Srgrimes	if (cp->a1 == NULL && cp->a2 == NULL)
2881590Srgrimes		r = 1;
2891590Srgrimes	else if (cp->a2)
290192732Sbrian		if (cp->startline > 0) {
291269837Sjlh                        switch (cp->a2->type) {
292269837Sjlh                        case AT_RELLINE:
293269837Sjlh                                if (linenum - cp->startline <= cp->a2->u.l)
294269837Sjlh                                        r = 1;
295269837Sjlh                                else {
296269837Sjlh				        cp->startline = 0;
297269837Sjlh				        r = 0;
298269837Sjlh                                }
299269837Sjlh                                break;
300269837Sjlh                        default:
301269837Sjlh                                if (MATCH(cp->a2)) {
302269837Sjlh                                        cp->startline = 0;
303269837Sjlh                                        lastaddr = 1;
304269837Sjlh                                        r = 1;
305269837Sjlh                                } else if (cp->a2->type == AT_LINE &&
306269837Sjlh                                            linenum > cp->a2->u.l) {
307269837Sjlh                                        /*
308269837Sjlh                                         * We missed the 2nd address due to a
309269837Sjlh                                         * branch, so just close the range and
310269837Sjlh                                         * return false.
311269837Sjlh                                         */
312269837Sjlh                                        cp->startline = 0;
313269837Sjlh                                        r = 0;
314269837Sjlh                                } else
315269837Sjlh                                        r = 1;
316269837Sjlh                        }
3171590Srgrimes		} else if (MATCH(cp->a1)) {
3181590Srgrimes			/*
3191590Srgrimes			 * If the second address is a number less than or
3201590Srgrimes			 * equal to the line number first selected, only
3211590Srgrimes			 * one line shall be selected.
3221590Srgrimes			 *	-- POSIX 1003.2
323192732Sbrian			 * Likewise if the relative second line address is zero.
3241590Srgrimes			 */
325192732Sbrian			if ((cp->a2->type == AT_LINE &&
326192732Sbrian			    linenum >= cp->a2->u.l) ||
327192732Sbrian			    (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0))
3281590Srgrimes				lastaddr = 1;
329192732Sbrian			else {
330192732Sbrian				cp->startline = linenum;
331192732Sbrian			}
3321590Srgrimes			r = 1;
3331590Srgrimes		} else
3341590Srgrimes			r = 0;
3351590Srgrimes	else
3361590Srgrimes		r = MATCH(cp->a1);
3371590Srgrimes	return (cp->nonsel ? ! r : r);
3381590Srgrimes}
3391590Srgrimes
3401590Srgrimes/*
341170608Syar * Reset the sed processor to its initial state.
342168921Syar */
343168921Syarvoid
344170608Syarresetstate(void)
345168921Syar{
346168921Syar	struct s_command *cp;
347168921Syar
348170608Syar	/*
349192732Sbrian	 * Reset all in-range markers.
350170608Syar	 */
351168921Syar	for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
352168921Syar		if (cp->a2)
353192732Sbrian			cp->startline = 0;
354170608Syar
355170608Syar	/*
356170608Syar	 * Clear out the hold space.
357170608Syar	 */
358170608Syar	cspace(&HS, "", 0, REPLACE);
359168921Syar}
360168921Syar
361168921Syar/*
3621590Srgrimes * substitute --
3631590Srgrimes *	Do substitutions in the pattern space.  Currently, we build a
3641590Srgrimes *	copy of the new pattern space in the substitute space structure
3651590Srgrimes *	and then swap them.
3661590Srgrimes */
3671590Srgrimesstatic int
368122044Sdessubstitute(struct s_command *cp)
3691590Srgrimes{
3701590Srgrimes	SPACE tspace;
3711590Srgrimes	regex_t *re;
372115831Sfanf	regoff_t re_off, slen;
3731590Srgrimes	int lastempty, n;
3741590Srgrimes	char *s;
3751590Srgrimes
3761590Srgrimes	s = ps;
3771590Srgrimes	re = cp->u.s->re;
3781590Srgrimes	if (re == NULL) {
3791590Srgrimes		if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
3801590Srgrimes			linenum = cp->u.s->linenum;
381176126Sdwmalone			errx(1, "%lu: %s: \\%u not defined in the RE",
38228066Scharnier					linenum, fname, cp->u.s->maxbref);
3831590Srgrimes		}
3841590Srgrimes	}
3851590Srgrimes	if (!regexec_e(re, s, 0, 0, psl))
3861590Srgrimes		return (0);
3871590Srgrimes
388122045Sdes	SS.len = 0;				/* Clean substitute space. */
389122045Sdes	slen = psl;
390122045Sdes	n = cp->u.s->n;
3911590Srgrimes	lastempty = 1;
3921590Srgrimes
393122045Sdes	switch (n) {
394122045Sdes	case 0:					/* Global */
395122045Sdes		do {
3961590Srgrimes			if (lastempty || match[0].rm_so != match[0].rm_eo) {
3971590Srgrimes				/* Locate start of replaced string. */
3981590Srgrimes				re_off = match[0].rm_so;
3991590Srgrimes				/* Copy leading retained string. */
4001590Srgrimes				cspace(&SS, s, re_off, APPEND);
4011590Srgrimes				/* Add in regular expression. */
4021590Srgrimes				regsub(&SS, s, cp->u.s->new);
4031590Srgrimes			}
4041590Srgrimes
40510075Sjkh			/* Move past this match. */
4061590Srgrimes			if (match[0].rm_so != match[0].rm_eo) {
4071590Srgrimes				s += match[0].rm_eo;
4081590Srgrimes				slen -= match[0].rm_eo;
4091590Srgrimes				lastempty = 0;
4101590Srgrimes			} else {
411115871Sfanf				if (match[0].rm_so < slen)
41210075Sjkh					cspace(&SS, s + match[0].rm_so, 1,
41310075Sjkh					    APPEND);
4141590Srgrimes				s += match[0].rm_so + 1;
4151590Srgrimes				slen -= match[0].rm_so + 1;
4161590Srgrimes				lastempty = 1;
4171590Srgrimes			}
418115831Sfanf		} while (slen >= 0 && regexec_e(re, s, REG_NOTBOL, 0, slen));
4191590Srgrimes		/* Copy trailing retained string. */
4201590Srgrimes		if (slen > 0)
4211590Srgrimes			cspace(&SS, s, slen, APPEND);
422122045Sdes		break;
4231590Srgrimes	default:				/* Nth occurrence */
4241590Srgrimes		while (--n) {
425115831Sfanf			if (match[0].rm_eo == match[0].rm_so)
426115831Sfanf				match[0].rm_eo = match[0].rm_so + 1;
4271590Srgrimes			s += match[0].rm_eo;
4281590Srgrimes			slen -= match[0].rm_eo;
429115831Sfanf			if (slen < 0)
430115831Sfanf				return (0);
4311590Srgrimes			if (!regexec_e(re, s, REG_NOTBOL, 0, slen))
4321590Srgrimes				return (0);
4331590Srgrimes		}
4341590Srgrimes		/* FALLTHROUGH */
4351590Srgrimes	case 1:					/* 1st occurrence */
4361590Srgrimes		/* Locate start of replaced string. */
4371590Srgrimes		re_off = match[0].rm_so + (s - ps);
4381590Srgrimes		/* Copy leading retained string. */
4391590Srgrimes		cspace(&SS, ps, re_off, APPEND);
4401590Srgrimes		/* Add in regular expression. */
4411590Srgrimes		regsub(&SS, s, cp->u.s->new);
4421590Srgrimes		/* Copy trailing retained string. */
4431590Srgrimes		s += match[0].rm_eo;
4441590Srgrimes		slen -= match[0].rm_eo;
4451590Srgrimes		cspace(&SS, s, slen, APPEND);
4461590Srgrimes		break;
4471590Srgrimes	}
4481590Srgrimes
4491590Srgrimes	/*
4501590Srgrimes	 * Swap the substitute space and the pattern space, and make sure
4511590Srgrimes	 * that any leftover pointers into stdio memory get lost.
4521590Srgrimes	 */
4531590Srgrimes	tspace = PS;
4541590Srgrimes	PS = SS;
4551590Srgrimes	SS = tspace;
4561590Srgrimes	SS.space = SS.back;
4571590Srgrimes
4581590Srgrimes	/* Handle the 'p' flag. */
4591590Srgrimes	if (cp->u.s->p)
460170609Syar		OUT();
4611590Srgrimes
4621590Srgrimes	/* Handle the 'w' flag. */
4631590Srgrimes	if (cp->u.s->wfile && !pd) {
4641590Srgrimes		if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
4651590Srgrimes		    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
46628066Scharnier			err(1, "%s", cp->u.s->wfile);
467176126Sdwmalone		if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl ||
46898601Stjr		    write(cp->u.s->wfd, "\n", 1) != 1)
46928066Scharnier			err(1, "%s", cp->u.s->wfile);
4701590Srgrimes	}
4711590Srgrimes	return (1);
4721590Srgrimes}
4731590Srgrimes
4741590Srgrimes/*
475132145Stjr * do_tr --
476132145Stjr *	Perform translation ('y' command) in the pattern space.
477132145Stjr */
478132145Stjrstatic void
479132145Stjrdo_tr(struct s_tr *y)
480132145Stjr{
481132145Stjr	SPACE tmp;
482132145Stjr	char c, *p;
483132145Stjr	size_t clen, left;
484132145Stjr	int i;
485132145Stjr
486132145Stjr	if (MB_CUR_MAX == 1) {
487132145Stjr		/*
488132145Stjr		 * Single-byte encoding: perform in-place translation
489132145Stjr		 * of the pattern space.
490132145Stjr		 */
491132145Stjr		for (p = ps; p < &ps[psl]; p++)
492132145Stjr			*p = y->bytetab[(u_char)*p];
493132145Stjr	} else {
494132145Stjr		/*
495132145Stjr		 * Multi-byte encoding: perform translation into the
496132145Stjr		 * translation space, then swap the translation and
497132145Stjr		 * pattern spaces.
498132145Stjr		 */
499132145Stjr		/* Clean translation space. */
500132145Stjr		YS.len = 0;
501132145Stjr		for (p = ps, left = psl; left > 0; p += clen, left -= clen) {
502132145Stjr			if ((c = y->bytetab[(u_char)*p]) != '\0') {
503132145Stjr				cspace(&YS, &c, 1, APPEND);
504132145Stjr				clen = 1;
505132145Stjr				continue;
506132145Stjr			}
507132145Stjr			for (i = 0; i < y->nmultis; i++)
508132145Stjr				if (left >= y->multis[i].fromlen &&
509132145Stjr				    memcmp(p, y->multis[i].from,
510132145Stjr				    y->multis[i].fromlen) == 0)
511132145Stjr					break;
512132145Stjr			if (i < y->nmultis) {
513132145Stjr				cspace(&YS, y->multis[i].to,
514132145Stjr				    y->multis[i].tolen, APPEND);
515132145Stjr				clen = y->multis[i].fromlen;
516132145Stjr			} else {
517132145Stjr				cspace(&YS, p, 1, APPEND);
518132145Stjr				clen = 1;
519132145Stjr			}
520132145Stjr		}
521132145Stjr		/* Swap the translation space and the pattern space. */
522132145Stjr		tmp = PS;
523132145Stjr		PS = YS;
524132145Stjr		YS = tmp;
525132145Stjr		YS.space = YS.back;
526132145Stjr	}
527132145Stjr}
528132145Stjr
529132145Stjr/*
5301590Srgrimes * Flush append requests.  Always called before reading a line,
5311590Srgrimes * therefore it also resets the substitution done (sdone) flag.
5321590Srgrimes */
5331590Srgrimesstatic void
534122044Sdesflush_appends(void)
5351590Srgrimes{
5361590Srgrimes	FILE *f;
5371590Srgrimes	int count, i;
5381590Srgrimes	char buf[8 * 1024];
5391590Srgrimes
5408874Srgrimes	for (i = 0; i < appendx; i++)
5411590Srgrimes		switch (appends[i].type) {
5421590Srgrimes		case AP_STRING:
5438874Srgrimes			fwrite(appends[i].s, sizeof(char), appends[i].len,
544122049Sdes			    outfile);
5451590Srgrimes			break;
5461590Srgrimes		case AP_FILE:
5471590Srgrimes			/*
5481590Srgrimes			 * Read files probably shouldn't be cached.  Since
5491590Srgrimes			 * it's not an error to read a non-existent file,
5501590Srgrimes			 * it's possible that another program is interacting
55196704Strhodes			 * with the sed script through the filesystem.  It
5521590Srgrimes			 * would be truly bizarre, but possible.  It's probably
5531590Srgrimes			 * not that big a performance win, anyhow.
5541590Srgrimes			 */
5551590Srgrimes			if ((f = fopen(appends[i].s, "r")) == NULL)
5561590Srgrimes				break;
55728066Scharnier			while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
558122049Sdes				(void)fwrite(buf, sizeof(char), count, outfile);
5591590Srgrimes			(void)fclose(f);
5601590Srgrimes			break;
5611590Srgrimes		}
562122049Sdes	if (ferror(outfile))
563122049Sdes		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
5641590Srgrimes	appendx = sdone = 0;
5651590Srgrimes}
5661590Srgrimes
5671590Srgrimesstatic void
568132083Stjrlputs(char *s, size_t len)
5691590Srgrimes{
570132083Stjr	static const char escapes[] = "\\\a\b\f\r\t\v";
571132083Stjr	int c, col, width;
572176126Sdwmalone	const char *p;
5731590Srgrimes	struct winsize win;
5741590Srgrimes	static int termwidth = -1;
575132083Stjr	size_t clen, i;
576132083Stjr	wchar_t wc;
577132083Stjr	mbstate_t mbs;
5781590Srgrimes
579122049Sdes	if (outfile != stdout)
580122049Sdes		termwidth = 60;
58146081Simp	if (termwidth == -1) {
58297801Stjr		if ((p = getenv("COLUMNS")) && *p != '\0')
5831590Srgrimes			termwidth = atoi(p);
5841590Srgrimes		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
5851590Srgrimes		    win.ws_col > 0)
5861590Srgrimes			termwidth = win.ws_col;
5871590Srgrimes		else
5881590Srgrimes			termwidth = 60;
58946081Simp	}
590176126Sdwmalone	if (termwidth <= 0)
591176126Sdwmalone		termwidth = 1;
5921590Srgrimes
593132083Stjr	memset(&mbs, 0, sizeof(mbs));
594132083Stjr	col = 0;
595132083Stjr	while (len != 0) {
596132083Stjr		clen = mbrtowc(&wc, s, len, &mbs);
597132083Stjr		if (clen == 0)
598132083Stjr			clen = 1;
599132083Stjr		if (clen == (size_t)-1 || clen == (size_t)-2) {
600132083Stjr			wc = (unsigned char)*s;
601132083Stjr			clen = 1;
602132083Stjr			memset(&mbs, 0, sizeof(mbs));
6031590Srgrimes		}
604132083Stjr		if (wc == '\n') {
605132083Stjr			if (col + 1 >= termwidth)
606132083Stjr				fprintf(outfile, "\\\n");
607132083Stjr			fputc('$', outfile);
608132083Stjr			fputc('\n', outfile);
609132083Stjr			col = 0;
610132083Stjr		} else if (iswprint(wc)) {
611132083Stjr			width = wcwidth(wc);
612132083Stjr			if (col + width >= termwidth) {
613132083Stjr				fprintf(outfile, "\\\n");
614132083Stjr				col = 0;
615132083Stjr			}
616132083Stjr			fwrite(s, 1, clen, outfile);
617132083Stjr			col += width;
618132083Stjr		} else if (wc != L'\0' && (c = wctob(wc)) != EOF &&
619132083Stjr		    (p = strchr(escapes, c)) != NULL) {
620132083Stjr			if (col + 2 >= termwidth) {
621132083Stjr				fprintf(outfile, "\\\n");
622132083Stjr				col = 0;
623132083Stjr			}
624132083Stjr			fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]);
625132083Stjr			col += 2;
62698603Stjr		} else {
627176126Sdwmalone			if (col + 4 * clen >= (unsigned)termwidth) {
628132083Stjr				fprintf(outfile, "\\\n");
629132083Stjr				col = 0;
6301590Srgrimes			}
631132083Stjr			for (i = 0; i < clen; i++)
632132083Stjr				fprintf(outfile, "\\%03o",
633132083Stjr				    (int)(unsigned char)s[i]);
634132083Stjr			col += 4 * clen;
6351590Srgrimes		}
636132083Stjr		s += clen;
637132083Stjr		len -= clen;
6381590Srgrimes	}
639132083Stjr	if (col + 1 >= termwidth)
640132083Stjr		fprintf(outfile, "\\\n");
641122049Sdes	(void)fputc('$', outfile);
642122049Sdes	(void)fputc('\n', outfile);
643122049Sdes	if (ferror(outfile))
644122049Sdes		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
6451590Srgrimes}
6461590Srgrimes
647100359Smarkmstatic __inline int
648122044Sdesregexec_e(regex_t *preg, const char *string, int eflags, int nomatch,
649122044Sdes	size_t slen)
6501590Srgrimes{
6511590Srgrimes	int eval;
6528874Srgrimes
6531590Srgrimes	if (preg == NULL) {
6541590Srgrimes		if (defpreg == NULL)
65528066Scharnier			errx(1, "first RE may not be empty");
6561590Srgrimes	} else
6571590Srgrimes		defpreg = preg;
6581590Srgrimes
65998601Stjr	/* Set anchors */
6601590Srgrimes	match[0].rm_so = 0;
6611590Srgrimes	match[0].rm_eo = slen;
6628874Srgrimes
6631590Srgrimes	eval = regexec(defpreg, string,
6641590Srgrimes	    nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
6651590Srgrimes	switch(eval) {
6661590Srgrimes	case 0:
6671590Srgrimes		return (1);
6681590Srgrimes	case REG_NOMATCH:
6691590Srgrimes		return (0);
6701590Srgrimes	}
67128066Scharnier	errx(1, "RE error: %s", strregerror(eval, defpreg));
6721590Srgrimes	/* NOTREACHED */
6731590Srgrimes}
6741590Srgrimes
6751590Srgrimes/*
6761590Srgrimes * regsub - perform substitutions after a regexp match
6771590Srgrimes * Based on a routine by Henry Spencer
6781590Srgrimes */
6791590Srgrimesstatic void
680122044Sdesregsub(SPACE *sp, char *string, char *src)
6811590Srgrimes{
68287766Smarkm	int len, no;
68387766Smarkm	char c, *dst;
6841590Srgrimes
6851590Srgrimes#define	NEEDSP(reqlen)							\
686121915Stjr	/* XXX What is the +1 for? */					\
687121915Stjr	if (sp->len + (reqlen) + 1 >= sp->blen) {			\
6881590Srgrimes		sp->blen += (reqlen) + 1024;				\
68980286Sobrien		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \
69080286Sobrien		    == NULL)						\
69180286Sobrien			err(1, "realloc");				\
6921590Srgrimes		dst = sp->space + sp->len;				\
6931590Srgrimes	}
6941590Srgrimes
6951590Srgrimes	dst = sp->space + sp->len;
6961590Srgrimes	while ((c = *src++) != '\0') {
6971590Srgrimes		if (c == '&')
6981590Srgrimes			no = 0;
69917522Sache		else if (c == '\\' && isdigit((unsigned char)*src))
7001590Srgrimes			no = *src++ - '0';
7011590Srgrimes		else
7021590Srgrimes			no = -1;
7031590Srgrimes		if (no < 0) {		/* Ordinary character. */
704122045Sdes			if (c == '\\' && (*src == '\\' || *src == '&'))
705122045Sdes				c = *src++;
7061590Srgrimes			NEEDSP(1);
707122045Sdes			*dst++ = c;
7081590Srgrimes			++sp->len;
709122045Sdes		} else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
7101590Srgrimes			len = match[no].rm_eo - match[no].rm_so;
7111590Srgrimes			NEEDSP(len);
7121590Srgrimes			memmove(dst, string + match[no].rm_so, len);
7131590Srgrimes			dst += len;
7141590Srgrimes			sp->len += len;
7151590Srgrimes		}
7161590Srgrimes	}
7171590Srgrimes	NEEDSP(1);
7181590Srgrimes	*dst = '\0';
7191590Srgrimes}
7201590Srgrimes
7211590Srgrimes/*
722168120Syar * cspace --
723168120Syar *	Concatenate space: append the source space to the destination space,
724168120Syar *	allocating new space as necessary.
7251590Srgrimes */
7261590Srgrimesvoid
727122044Sdescspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
7281590Srgrimes{
7291590Srgrimes	size_t tlen;
7301590Srgrimes
7311590Srgrimes	/* Make sure SPACE has enough memory and ramp up quickly. */
7321590Srgrimes	tlen = sp->len + len + 1;
7331590Srgrimes	if (tlen > sp->blen) {
7341590Srgrimes		sp->blen = tlen + 1024;
73580286Sobrien		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
73680286Sobrien		    NULL)
73780286Sobrien			err(1, "realloc");
7381590Srgrimes	}
7391590Srgrimes
7401590Srgrimes	if (spflag == REPLACE)
7411590Srgrimes		sp->len = 0;
7421590Srgrimes
7431590Srgrimes	memmove(sp->space + sp->len, p, len);
7441590Srgrimes
7451590Srgrimes	sp->space[sp->len += len] = '\0';
7461590Srgrimes}
7471590Srgrimes
7481590Srgrimes/*
7491590Srgrimes * Close all cached opened files and report any errors
7501590Srgrimes */
7511590Srgrimesvoid
752122044Sdescfclose(struct s_command *cp, struct s_command *end)
7531590Srgrimes{
7541590Srgrimes
7551590Srgrimes	for (; cp != end; cp = cp->next)
7561590Srgrimes		switch(cp->code) {
7571590Srgrimes		case 's':
7581590Srgrimes			if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
75928066Scharnier				err(1, "%s", cp->u.s->wfile);
7601590Srgrimes			cp->u.s->wfd = -1;
7611590Srgrimes			break;
7621590Srgrimes		case 'w':
7631590Srgrimes			if (cp->u.fd != -1 && close(cp->u.fd))
76428066Scharnier				err(1, "%s", cp->t);
7651590Srgrimes			cp->u.fd = -1;
7661590Srgrimes			break;
7671590Srgrimes		case '{':
7681590Srgrimes			cfclose(cp->u.c, cp->next);
7691590Srgrimes			break;
7701590Srgrimes		}
7711590Srgrimes}
772