xref: /illumos-gate/usr/src/cmd/sed/process.c (revision 4e81fcfe)
184441f85SGarrett D'Amore /*
284441f85SGarrett D'Amore  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
3*4e81fcfeSAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
484441f85SGarrett D'Amore  * Copyright (c) 1992 Diomidis Spinellis.
584441f85SGarrett D'Amore  * Copyright (c) 1992, 1993, 1994
684441f85SGarrett D'Amore  *	The Regents of the University of California.  All rights reserved.
784441f85SGarrett D'Amore  *
884441f85SGarrett D'Amore  * This code is derived from software contributed to Berkeley by
984441f85SGarrett D'Amore  * Diomidis Spinellis of Imperial College, University of London.
1084441f85SGarrett D'Amore  *
1184441f85SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
1284441f85SGarrett D'Amore  * modification, are permitted provided that the following conditions
1384441f85SGarrett D'Amore  * are met:
1484441f85SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright
1584441f85SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer.
1684441f85SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright
1784441f85SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer in the
1884441f85SGarrett D'Amore  *    documentation and/or other materials provided with the distribution.
1984441f85SGarrett D'Amore  * 4. Neither the name of the University nor the names of its contributors
2084441f85SGarrett D'Amore  *    may be used to endorse or promote products derived from this software
2184441f85SGarrett D'Amore  *    without specific prior written permission.
2284441f85SGarrett D'Amore  *
2384441f85SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2484441f85SGarrett D'Amore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2584441f85SGarrett D'Amore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2684441f85SGarrett D'Amore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2784441f85SGarrett D'Amore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2884441f85SGarrett D'Amore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2984441f85SGarrett D'Amore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3084441f85SGarrett D'Amore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3184441f85SGarrett D'Amore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3284441f85SGarrett D'Amore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3384441f85SGarrett D'Amore  * SUCH DAMAGE.
3484441f85SGarrett D'Amore  */
3584441f85SGarrett D'Amore 
3684441f85SGarrett D'Amore #include <sys/types.h>
3784441f85SGarrett D'Amore #include <sys/stat.h>
38*4e81fcfeSAndy Fiddaman #include <sys/ccompile.h>
3984441f85SGarrett D'Amore 
4084441f85SGarrett D'Amore #include <ctype.h>
4184441f85SGarrett D'Amore #include <err.h>
4284441f85SGarrett D'Amore #include <errno.h>
4384441f85SGarrett D'Amore #include <fcntl.h>
4484441f85SGarrett D'Amore #include <limits.h>
4584441f85SGarrett D'Amore #include <regex.h>
4684441f85SGarrett D'Amore #include <stdio.h>
4784441f85SGarrett D'Amore #include <stdlib.h>
4884441f85SGarrett D'Amore #include <string.h>
4984441f85SGarrett D'Amore #include <unistd.h>
5084441f85SGarrett D'Amore #include <wchar.h>
5184441f85SGarrett D'Amore #include <wctype.h>
5284441f85SGarrett D'Amore #include <termio.h>
5384441f85SGarrett D'Amore #include <libintl.h>
5484441f85SGarrett D'Amore #include <note.h>
5584441f85SGarrett D'Amore 
5684441f85SGarrett D'Amore #include "defs.h"
5784441f85SGarrett D'Amore #include "extern.h"
5884441f85SGarrett D'Amore 
5984441f85SGarrett D'Amore static SPACE HS, PS, SS, YS;
6084441f85SGarrett D'Amore #define	pd		PS.deleted
6184441f85SGarrett D'Amore #define	ps		PS.space
6284441f85SGarrett D'Amore #define	psl		PS.len
6384441f85SGarrett D'Amore #define	hs		HS.space
6484441f85SGarrett D'Amore #define	hsl		HS.len
6584441f85SGarrett D'Amore 
6684441f85SGarrett D'Amore static int	applies(struct s_command *);
6784441f85SGarrett D'Amore static void	do_tr(struct s_tr *);
6884441f85SGarrett D'Amore static void	flush_appends(void);
6984441f85SGarrett D'Amore static void	lputs(char *, size_t);
7084441f85SGarrett D'Amore static int	 regexec_e(regex_t *, const char *, int, int, size_t);
7184441f85SGarrett D'Amore static void	regsub(SPACE *, char *, char *);
7284441f85SGarrett D'Amore static int	substitute(struct s_command *);
7384441f85SGarrett D'Amore 
7484441f85SGarrett D'Amore struct s_appends *appends;	/* Array of pointers to strings to append. */
7584441f85SGarrett D'Amore static int appendx;		/* Index into appends array. */
7684441f85SGarrett D'Amore int appendnum;			/* Size of appends array. */
7784441f85SGarrett D'Amore 
7884441f85SGarrett D'Amore static int lastaddr;		/* Set by applies if last address of a range. */
7984441f85SGarrett D'Amore static int sdone;		/* If any substitutes since last line input. */
8084441f85SGarrett D'Amore 				/* Iov structure for 'w' commands. */
8184441f85SGarrett D'Amore static regex_t *defpreg;
8284441f85SGarrett D'Amore size_t maxnsub;
8384441f85SGarrett D'Amore regmatch_t *match;
8484441f85SGarrett D'Amore 
8584441f85SGarrett D'Amore #define	OUT() do {					\
8684441f85SGarrett D'Amore 	(void) fwrite(ps, 1, psl, outfile);		\
8784441f85SGarrett D'Amore 	(void) fputc('\n', outfile);			\
8884441f85SGarrett D'Amore 	_NOTE(CONSTCOND)				\
8984441f85SGarrett D'Amore } while (0)
9084441f85SGarrett D'Amore 
9184441f85SGarrett D'Amore void
process(void)9284441f85SGarrett D'Amore process(void)
9384441f85SGarrett D'Amore {
9484441f85SGarrett D'Amore 	struct s_command *cp;
9584441f85SGarrett D'Amore 	SPACE tspace;
9684441f85SGarrett D'Amore 	size_t oldpsl = 0;
9784441f85SGarrett D'Amore 	char *p;
9884441f85SGarrett D'Amore 
9984441f85SGarrett D'Amore 	p = NULL;
10084441f85SGarrett D'Amore 
10184441f85SGarrett D'Amore 	for (linenum = 0; mf_fgets(&PS, REPLACE); /* NOP */) {
10284441f85SGarrett D'Amore 		pd = 0;
10384441f85SGarrett D'Amore top:
10484441f85SGarrett D'Amore 		cp = prog;
10584441f85SGarrett D'Amore redirect:
10684441f85SGarrett D'Amore 		while (cp != NULL) {
10784441f85SGarrett D'Amore 			if (!applies(cp)) {
10884441f85SGarrett D'Amore 				cp = cp->next;
10984441f85SGarrett D'Amore 				continue;
11084441f85SGarrett D'Amore 			}
11184441f85SGarrett D'Amore 			switch (cp->code) {
11284441f85SGarrett D'Amore 			case '{':
11384441f85SGarrett D'Amore 				cp = cp->u.c;
11484441f85SGarrett D'Amore 				goto redirect;
11584441f85SGarrett D'Amore 			case 'a':
11684441f85SGarrett D'Amore 				if (appendx >= appendnum)
11784441f85SGarrett D'Amore 					if ((appends = realloc(appends,
11884441f85SGarrett D'Amore 					    sizeof (struct s_appends) *
11984441f85SGarrett D'Amore 					    (appendnum *= 2))) == NULL)
12084441f85SGarrett D'Amore 						err(1, "realloc");
12184441f85SGarrett D'Amore 				appends[appendx].type = AP_STRING;
12284441f85SGarrett D'Amore 				appends[appendx].s = cp->t;
12384441f85SGarrett D'Amore 				appends[appendx].len = strlen(cp->t);
12484441f85SGarrett D'Amore 				appendx++;
12584441f85SGarrett D'Amore 				break;
12684441f85SGarrett D'Amore 			case 'b':
12784441f85SGarrett D'Amore 				cp = cp->u.c;
12884441f85SGarrett D'Amore 				goto redirect;
12984441f85SGarrett D'Amore 			case 'c':
13084441f85SGarrett D'Amore 				pd = 1;
13184441f85SGarrett D'Amore 				psl = 0;
13284441f85SGarrett D'Amore 				if (cp->a2 == NULL || lastaddr || lastline())
13384441f85SGarrett D'Amore 					(void) fprintf(outfile, "%s", cp->t);
13484441f85SGarrett D'Amore 				break;
13584441f85SGarrett D'Amore 			case 'd':
13684441f85SGarrett D'Amore 				pd = 1;
13784441f85SGarrett D'Amore 				goto new;
13884441f85SGarrett D'Amore 			case 'D':
13984441f85SGarrett D'Amore 				if (pd)
14084441f85SGarrett D'Amore 					goto new;
14184441f85SGarrett D'Amore 				if (psl == 0 ||
14284441f85SGarrett D'Amore 				    (p = memchr(ps, '\n', psl)) == NULL) {
14384441f85SGarrett D'Amore 					pd = 1;
14484441f85SGarrett D'Amore 					goto new;
14584441f85SGarrett D'Amore 				} else {
14684441f85SGarrett D'Amore 					psl -=
14784441f85SGarrett D'Amore 					    (uintptr_t)(p + 1) - (uintptr_t)ps;
14884441f85SGarrett D'Amore 					(void) memmove(ps, p + 1, psl);
14984441f85SGarrett D'Amore 					goto top;
15084441f85SGarrett D'Amore 				}
15184441f85SGarrett D'Amore 			case 'g':
15284441f85SGarrett D'Amore 				cspace(&PS, hs, hsl, REPLACE);
15384441f85SGarrett D'Amore 				break;
15484441f85SGarrett D'Amore 			case 'G':
15584441f85SGarrett D'Amore 				cspace(&PS, "\n", 1, APPEND);
15684441f85SGarrett D'Amore 				cspace(&PS, hs, hsl, APPEND);
15784441f85SGarrett D'Amore 				break;
15884441f85SGarrett D'Amore 			case 'h':
15984441f85SGarrett D'Amore 				cspace(&HS, ps, psl, REPLACE);
16084441f85SGarrett D'Amore 				break;
16184441f85SGarrett D'Amore 			case 'H':
16284441f85SGarrett D'Amore 				cspace(&HS, "\n", 1, APPEND);
16384441f85SGarrett D'Amore 				cspace(&HS, ps, psl, APPEND);
16484441f85SGarrett D'Amore 				break;
16584441f85SGarrett D'Amore 			case 'i':
16684441f85SGarrett D'Amore 				(void) fprintf(outfile, "%s", cp->t);
16784441f85SGarrett D'Amore 				break;
16884441f85SGarrett D'Amore 			case 'l':
16984441f85SGarrett D'Amore 				lputs(ps, psl);
17084441f85SGarrett D'Amore 				break;
17184441f85SGarrett D'Amore 			case 'n':
17284441f85SGarrett D'Amore 				if (!nflag && !pd)
17384441f85SGarrett D'Amore 					OUT();
17484441f85SGarrett D'Amore 				flush_appends();
17584441f85SGarrett D'Amore 				if (!mf_fgets(&PS, REPLACE))
17684441f85SGarrett D'Amore 					exit(0);
17784441f85SGarrett D'Amore 				pd = 0;
17884441f85SGarrett D'Amore 				break;
17984441f85SGarrett D'Amore 			case 'N':
18084441f85SGarrett D'Amore 				flush_appends();
18184441f85SGarrett D'Amore 				cspace(&PS, "\n", 1, APPEND);
18284441f85SGarrett D'Amore 				if (!mf_fgets(&PS, APPEND))
18384441f85SGarrett D'Amore 					exit(0);
18484441f85SGarrett D'Amore 				break;
18584441f85SGarrett D'Amore 			case 'p':
18684441f85SGarrett D'Amore 				if (pd)
18784441f85SGarrett D'Amore 					break;
18884441f85SGarrett D'Amore 				OUT();
18984441f85SGarrett D'Amore 				break;
19084441f85SGarrett D'Amore 			case 'P':
19184441f85SGarrett D'Amore 				if (pd)
19284441f85SGarrett D'Amore 					break;
19384441f85SGarrett D'Amore 				if ((p = memchr(ps, '\n', psl)) != NULL) {
19484441f85SGarrett D'Amore 					oldpsl = psl;
19584441f85SGarrett D'Amore 					psl = (uintptr_t)p - (uintptr_t)ps;
19684441f85SGarrett D'Amore 				}
19784441f85SGarrett D'Amore 				OUT();
19884441f85SGarrett D'Amore 				if (p != NULL)
19984441f85SGarrett D'Amore 					psl = oldpsl;
20084441f85SGarrett D'Amore 				break;
20184441f85SGarrett D'Amore 			case 'q':
20284441f85SGarrett D'Amore 				if (!nflag && !pd)
20384441f85SGarrett D'Amore 					OUT();
20484441f85SGarrett D'Amore 				flush_appends();
20584441f85SGarrett D'Amore 				exit(0);
20684441f85SGarrett D'Amore 				/*NOTREACHED*/
20784441f85SGarrett D'Amore 			case 'r':
20884441f85SGarrett D'Amore 				if (appendx >= appendnum)
20984441f85SGarrett D'Amore 					if ((appends = realloc(appends,
21084441f85SGarrett D'Amore 					    sizeof (struct s_appends) *
21184441f85SGarrett D'Amore 					    (appendnum *= 2))) == NULL)
21284441f85SGarrett D'Amore 						err(1, "realloc");
21384441f85SGarrett D'Amore 				appends[appendx].type = AP_FILE;
21484441f85SGarrett D'Amore 				appends[appendx].s = cp->t;
21584441f85SGarrett D'Amore 				appends[appendx].len = strlen(cp->t);
21684441f85SGarrett D'Amore 				appendx++;
21784441f85SGarrett D'Amore 				break;
21884441f85SGarrett D'Amore 			case 's':
21984441f85SGarrett D'Amore 				sdone |= substitute(cp);
22084441f85SGarrett D'Amore 				break;
22184441f85SGarrett D'Amore 			case 't':
22284441f85SGarrett D'Amore 				if (sdone) {
22384441f85SGarrett D'Amore 					sdone = 0;
22484441f85SGarrett D'Amore 					cp = cp->u.c;
22584441f85SGarrett D'Amore 					goto redirect;
22684441f85SGarrett D'Amore 				}
22784441f85SGarrett D'Amore 				break;
22884441f85SGarrett D'Amore 			case 'w':
22984441f85SGarrett D'Amore 				if (pd)
23084441f85SGarrett D'Amore 					break;
23184441f85SGarrett D'Amore 				if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
23284441f85SGarrett D'Amore 				    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 0666))
23384441f85SGarrett D'Amore 				    == -1)
23484441f85SGarrett D'Amore 					err(1, "%s", cp->t);
23584441f85SGarrett D'Amore 				if (write(cp->u.fd, ps, psl) != (ssize_t)psl ||
23684441f85SGarrett D'Amore 				    write(cp->u.fd, "\n", 1) != 1)
23784441f85SGarrett D'Amore 					err(1, "%s", cp->t);
23884441f85SGarrett D'Amore 				break;
23984441f85SGarrett D'Amore 			case 'x':
24084441f85SGarrett D'Amore 				/*
24184441f85SGarrett D'Amore 				 * If the hold space is null, make it empty
24284441f85SGarrett D'Amore 				 * but not null.  Otherwise the pattern space
24384441f85SGarrett D'Amore 				 * will become null after the swap, which is
24484441f85SGarrett D'Amore 				 * an abnormal condition.
24584441f85SGarrett D'Amore 				 */
24684441f85SGarrett D'Amore 				if (hs == NULL)
24784441f85SGarrett D'Amore 					cspace(&HS, "", 0, REPLACE);
24884441f85SGarrett D'Amore 				tspace = PS;
24984441f85SGarrett D'Amore 				PS = HS;
25084441f85SGarrett D'Amore 				HS = tspace;
25184441f85SGarrett D'Amore 				break;
25284441f85SGarrett D'Amore 			case 'y':
25384441f85SGarrett D'Amore 				if (pd || psl == 0)
25484441f85SGarrett D'Amore 					break;
25584441f85SGarrett D'Amore 				do_tr(cp->u.y);
25684441f85SGarrett D'Amore 				break;
25784441f85SGarrett D'Amore 			case ':':
25884441f85SGarrett D'Amore 			case '}':
25984441f85SGarrett D'Amore 				break;
26084441f85SGarrett D'Amore 			case '=':
26184441f85SGarrett D'Amore 				(void) fprintf(outfile, "%lu\n", linenum);
26284441f85SGarrett D'Amore 			}
26384441f85SGarrett D'Amore 			cp = cp->next;
26484441f85SGarrett D'Amore 		} /* for all cp */
26584441f85SGarrett D'Amore 
26684441f85SGarrett D'Amore new:		if (!nflag && !pd)
26784441f85SGarrett D'Amore 			OUT();
26884441f85SGarrett D'Amore 		flush_appends();
26984441f85SGarrett D'Amore 	} /* for all lines */
27084441f85SGarrett D'Amore }
27184441f85SGarrett D'Amore 
27284441f85SGarrett D'Amore /*
27384441f85SGarrett D'Amore  * TRUE if the address passed matches the current program state
27484441f85SGarrett D'Amore  * (lastline, linenumber, ps).
27584441f85SGarrett D'Amore  */
276*4e81fcfeSAndy Fiddaman static __GNU_INLINE int
MATCH(struct s_command * cp,struct s_addr * a)277*4e81fcfeSAndy Fiddaman MATCH(struct s_command *cp, struct s_addr *a)
278*4e81fcfeSAndy Fiddaman {
279*4e81fcfeSAndy Fiddaman 	switch (a->type) {
280*4e81fcfeSAndy Fiddaman 	case AT_RE:
281*4e81fcfeSAndy Fiddaman 		return (regexec_e(a->u.r, ps, 0, 1, psl));
282*4e81fcfeSAndy Fiddaman 	case AT_LINE:
283*4e81fcfeSAndy Fiddaman 		return (linenum == a->u.l);
284*4e81fcfeSAndy Fiddaman 	case AT_RELLINE:
285*4e81fcfeSAndy Fiddaman 		return (linenum - cp->startline == a->u.l);
286*4e81fcfeSAndy Fiddaman 	case AT_LAST:
287*4e81fcfeSAndy Fiddaman 		return (lastline());
288*4e81fcfeSAndy Fiddaman 	}
289*4e81fcfeSAndy Fiddaman 	fatal(_("Unhandled match type"));
290*4e81fcfeSAndy Fiddaman 	return (0);
291*4e81fcfeSAndy Fiddaman }
29284441f85SGarrett D'Amore 
29384441f85SGarrett D'Amore /*
29484441f85SGarrett D'Amore  * Return TRUE if the command applies to the current line.  Sets the start
29584441f85SGarrett D'Amore  * line for process ranges.  Interprets the non-select (``!'') flag.
29684441f85SGarrett D'Amore  */
29784441f85SGarrett D'Amore static int
applies(struct s_command * cp)29884441f85SGarrett D'Amore applies(struct s_command *cp)
29984441f85SGarrett D'Amore {
30084441f85SGarrett D'Amore 	int r;
30184441f85SGarrett D'Amore 
30284441f85SGarrett D'Amore 	lastaddr = 0;
30384441f85SGarrett D'Amore 	if (cp->a1 == NULL && cp->a2 == NULL)
30484441f85SGarrett D'Amore 		r = 1;
30584441f85SGarrett D'Amore 	else if (cp->a2)
30684441f85SGarrett D'Amore 		if (cp->startline > 0) {
307*4e81fcfeSAndy Fiddaman 			if (MATCH(cp, cp->a2)) {
30884441f85SGarrett D'Amore 				cp->startline = 0;
30984441f85SGarrett D'Amore 				lastaddr = 1;
31084441f85SGarrett D'Amore 				r = 1;
311*4e81fcfeSAndy Fiddaman 			} else if ((cp->a2->type == AT_LINE &&
31284441f85SGarrett D'Amore 			    linenum > cp->a2->u.l) ||
31384441f85SGarrett D'Amore 			    (cp->a2->type == AT_RELLINE &&
31484441f85SGarrett D'Amore 			    linenum - cp->startline > cp->a2->u.l)) {
31584441f85SGarrett D'Amore 				/*
31684441f85SGarrett D'Amore 				 * We missed the 2nd address due to a branch,
31784441f85SGarrett D'Amore 				 * so just close the range and return false.
31884441f85SGarrett D'Amore 				 */
31984441f85SGarrett D'Amore 				cp->startline = 0;
32084441f85SGarrett D'Amore 				r = 0;
321*4e81fcfeSAndy Fiddaman 			} else {
32284441f85SGarrett D'Amore 				r = 1;
323*4e81fcfeSAndy Fiddaman 			}
324*4e81fcfeSAndy Fiddaman 		} else if (MATCH(cp, cp->a1)) {
32584441f85SGarrett D'Amore 			/*
32684441f85SGarrett D'Amore 			 * If the second address is a number less than or
32784441f85SGarrett D'Amore 			 * equal to the line number first selected, only
32884441f85SGarrett D'Amore 			 * one line shall be selected.
32984441f85SGarrett D'Amore 			 *	-- POSIX 1003.2
33084441f85SGarrett D'Amore 			 * Likewise if the relative second line address is zero.
33184441f85SGarrett D'Amore 			 */
33284441f85SGarrett D'Amore 			if ((cp->a2->type == AT_LINE &&
33384441f85SGarrett D'Amore 			    linenum >= cp->a2->u.l) ||
33484441f85SGarrett D'Amore 			    (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0))
33584441f85SGarrett D'Amore 				lastaddr = 1;
33684441f85SGarrett D'Amore 			else {
33784441f85SGarrett D'Amore 				cp->startline = linenum;
33884441f85SGarrett D'Amore 			}
33984441f85SGarrett D'Amore 			r = 1;
34084441f85SGarrett D'Amore 		} else
34184441f85SGarrett D'Amore 			r = 0;
34284441f85SGarrett D'Amore 	else
343*4e81fcfeSAndy Fiddaman 		r = MATCH(cp, cp->a1);
34484441f85SGarrett D'Amore 	return (cp->nonsel ? ! r : r);
34584441f85SGarrett D'Amore }
34684441f85SGarrett D'Amore 
34784441f85SGarrett D'Amore /*
34884441f85SGarrett D'Amore  * Reset the sed processor to its initial state.
34984441f85SGarrett D'Amore  */
35084441f85SGarrett D'Amore void
resetstate(void)35184441f85SGarrett D'Amore resetstate(void)
35284441f85SGarrett D'Amore {
35384441f85SGarrett D'Amore 	struct s_command *cp;
35484441f85SGarrett D'Amore 
35584441f85SGarrett D'Amore 	/*
35684441f85SGarrett D'Amore 	 * Reset all in-range markers.
35784441f85SGarrett D'Amore 	 */
35884441f85SGarrett D'Amore 	for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
35984441f85SGarrett D'Amore 		if (cp->a2)
36084441f85SGarrett D'Amore 			cp->startline = 0;
36184441f85SGarrett D'Amore 
36284441f85SGarrett D'Amore 	/*
36384441f85SGarrett D'Amore 	 * Clear out the hold space.
36484441f85SGarrett D'Amore 	 */
36584441f85SGarrett D'Amore 	cspace(&HS, "", 0, REPLACE);
36684441f85SGarrett D'Amore }
36784441f85SGarrett D'Amore 
36884441f85SGarrett D'Amore /*
36984441f85SGarrett D'Amore  * substitute --
37084441f85SGarrett D'Amore  *	Do substitutions in the pattern space.  Currently, we build a
37184441f85SGarrett D'Amore  *	copy of the new pattern space in the substitute space structure
37284441f85SGarrett D'Amore  *	and then swap them.
37384441f85SGarrett D'Amore  */
37484441f85SGarrett D'Amore static int
substitute(struct s_command * cp)37584441f85SGarrett D'Amore substitute(struct s_command *cp)
37684441f85SGarrett D'Amore {
37784441f85SGarrett D'Amore 	SPACE tspace;
37884441f85SGarrett D'Amore 	regex_t *re;
37984441f85SGarrett D'Amore 	regoff_t re_off, slen;
38084441f85SGarrett D'Amore 	int lastempty, n;
38184441f85SGarrett D'Amore 	char *s;
38284441f85SGarrett D'Amore 
38384441f85SGarrett D'Amore 	s = ps;
38484441f85SGarrett D'Amore 	re = cp->u.s->re;
38584441f85SGarrett D'Amore 	if (re == NULL) {
38684441f85SGarrett D'Amore 		if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
38784441f85SGarrett D'Amore 			linenum = cp->u.s->linenum;
38884441f85SGarrett D'Amore 			fatal(_("\\%u not defined in the RE"),
38984441f85SGarrett D'Amore 			    cp->u.s->maxbref);
39084441f85SGarrett D'Amore 		}
39184441f85SGarrett D'Amore 	}
39284441f85SGarrett D'Amore 	if (!regexec_e(re, s, 0, 0, psl))
39384441f85SGarrett D'Amore 		return (0);
39484441f85SGarrett D'Amore 
39584441f85SGarrett D'Amore 	SS.len = 0;				/* Clean substitute space. */
39684441f85SGarrett D'Amore 	slen = psl;
39784441f85SGarrett D'Amore 	n = cp->u.s->n;
39884441f85SGarrett D'Amore 	lastempty = 1;
39984441f85SGarrett D'Amore 
40084441f85SGarrett D'Amore 	switch (n) {
40184441f85SGarrett D'Amore 	case 0:					/* Global */
40284441f85SGarrett D'Amore 		do {
40384441f85SGarrett D'Amore 			if (lastempty || match[0].rm_so != match[0].rm_eo) {
40484441f85SGarrett D'Amore 				/* Locate start of replaced string. */
40584441f85SGarrett D'Amore 				re_off = match[0].rm_so;
40684441f85SGarrett D'Amore 				/* Copy leading retained string. */
40784441f85SGarrett D'Amore 				cspace(&SS, s, re_off, APPEND);
40884441f85SGarrett D'Amore 				/* Add in regular expression. */
40984441f85SGarrett D'Amore 				regsub(&SS, s, cp->u.s->new);
41084441f85SGarrett D'Amore 			}
41184441f85SGarrett D'Amore 
41284441f85SGarrett D'Amore 			/* Move past this match. */
41384441f85SGarrett D'Amore 			if (match[0].rm_so != match[0].rm_eo) {
41484441f85SGarrett D'Amore 				s += match[0].rm_eo;
41584441f85SGarrett D'Amore 				slen -= match[0].rm_eo;
41684441f85SGarrett D'Amore 				lastempty = 0;
41784441f85SGarrett D'Amore 			} else {
41884441f85SGarrett D'Amore 				if (match[0].rm_so < slen)
41984441f85SGarrett D'Amore 					cspace(&SS, s + match[0].rm_so, 1,
42084441f85SGarrett D'Amore 					    APPEND);
42184441f85SGarrett D'Amore 				s += match[0].rm_so + 1;
42284441f85SGarrett D'Amore 				slen -= match[0].rm_so + 1;
42384441f85SGarrett D'Amore 				lastempty = 1;
42484441f85SGarrett D'Amore 			}
42584441f85SGarrett D'Amore 		} while (slen >= 0 && regexec_e(re, s, REG_NOTBOL, 0, slen));
42684441f85SGarrett D'Amore 		/* Copy trailing retained string. */
42784441f85SGarrett D'Amore 		if (slen > 0)
42884441f85SGarrett D'Amore 			cspace(&SS, s, slen, APPEND);
42984441f85SGarrett D'Amore 		break;
43084441f85SGarrett D'Amore 	default:				/* Nth occurrence */
43184441f85SGarrett D'Amore 		while (--n) {
43284441f85SGarrett D'Amore 			if (match[0].rm_eo == match[0].rm_so)
43384441f85SGarrett D'Amore 				match[0].rm_eo = match[0].rm_so + 1;
43484441f85SGarrett D'Amore 			s += match[0].rm_eo;
43584441f85SGarrett D'Amore 			slen -= match[0].rm_eo;
43684441f85SGarrett D'Amore 			if (slen < 0)
43784441f85SGarrett D'Amore 				return (0);
43884441f85SGarrett D'Amore 			if (!regexec_e(re, s, REG_NOTBOL, 0, slen))
43984441f85SGarrett D'Amore 				return (0);
44084441f85SGarrett D'Amore 		}
44184441f85SGarrett D'Amore 		/* FALLTHROUGH */
44284441f85SGarrett D'Amore 	case 1:					/* 1st occurrence */
44384441f85SGarrett D'Amore 		/* Locate start of replaced string. */
44484441f85SGarrett D'Amore 		re_off = match[0].rm_so + ((uintptr_t)s - (uintptr_t)ps);
44584441f85SGarrett D'Amore 		/* Copy leading retained string. */
44684441f85SGarrett D'Amore 		cspace(&SS, ps, re_off, APPEND);
44784441f85SGarrett D'Amore 		/* Add in regular expression. */
44884441f85SGarrett D'Amore 		regsub(&SS, s, cp->u.s->new);
44984441f85SGarrett D'Amore 		/* Copy trailing retained string. */
45084441f85SGarrett D'Amore 		s += match[0].rm_eo;
45184441f85SGarrett D'Amore 		slen -= match[0].rm_eo;
45284441f85SGarrett D'Amore 		cspace(&SS, s, slen, APPEND);
45384441f85SGarrett D'Amore 		break;
45484441f85SGarrett D'Amore 	}
45584441f85SGarrett D'Amore 
45684441f85SGarrett D'Amore 	/*
45784441f85SGarrett D'Amore 	 * Swap the substitute space and the pattern space, and make sure
45884441f85SGarrett D'Amore 	 * that any leftover pointers into stdio memory get lost.
45984441f85SGarrett D'Amore 	 */
46084441f85SGarrett D'Amore 	tspace = PS;
46184441f85SGarrett D'Amore 	PS = SS;
46284441f85SGarrett D'Amore 	SS = tspace;
46384441f85SGarrett D'Amore 	SS.space = SS.back;
46484441f85SGarrett D'Amore 
46584441f85SGarrett D'Amore 	/* Handle the 'p' flag. */
46684441f85SGarrett D'Amore 	if (cp->u.s->p)
46784441f85SGarrett D'Amore 		OUT();
46884441f85SGarrett D'Amore 
46984441f85SGarrett D'Amore 	/* Handle the 'w' flag. */
47084441f85SGarrett D'Amore 	if (cp->u.s->wfile && !pd) {
47184441f85SGarrett D'Amore 		if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
47284441f85SGarrett D'Amore 		    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 0666)) == -1)
47384441f85SGarrett D'Amore 			err(1, "%s", cp->u.s->wfile);
47484441f85SGarrett D'Amore 		if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl ||
47584441f85SGarrett D'Amore 		    write(cp->u.s->wfd, "\n", 1) != 1)
47684441f85SGarrett D'Amore 			err(1, "%s", cp->u.s->wfile);
47784441f85SGarrett D'Amore 	}
47884441f85SGarrett D'Amore 	return (1);
47984441f85SGarrett D'Amore }
48084441f85SGarrett D'Amore 
48184441f85SGarrett D'Amore /*
48284441f85SGarrett D'Amore  * do_tr --
48384441f85SGarrett D'Amore  *	Perform translation ('y' command) in the pattern space.
48484441f85SGarrett D'Amore  */
48584441f85SGarrett D'Amore static void
do_tr(struct s_tr * y)48684441f85SGarrett D'Amore do_tr(struct s_tr *y)
48784441f85SGarrett D'Amore {
48884441f85SGarrett D'Amore 	SPACE tmp;
48984441f85SGarrett D'Amore 	char c, *p;
49084441f85SGarrett D'Amore 	size_t clen, left;
49184441f85SGarrett D'Amore 	int i;
49284441f85SGarrett D'Amore 
49384441f85SGarrett D'Amore 	if (MB_CUR_MAX == 1) {
49484441f85SGarrett D'Amore 		/*
49584441f85SGarrett D'Amore 		 * Single-byte encoding: perform in-place translation
49684441f85SGarrett D'Amore 		 * of the pattern space.
49784441f85SGarrett D'Amore 		 */
49884441f85SGarrett D'Amore 		for (p = ps; p < &ps[psl]; p++)
49984441f85SGarrett D'Amore 			*p = y->bytetab[(uchar_t)*p];
50084441f85SGarrett D'Amore 	} else {
50184441f85SGarrett D'Amore 		/*
50284441f85SGarrett D'Amore 		 * Multi-byte encoding: perform translation into the
50384441f85SGarrett D'Amore 		 * translation space, then swap the translation and
50484441f85SGarrett D'Amore 		 * pattern spaces.
50584441f85SGarrett D'Amore 		 */
50684441f85SGarrett D'Amore 		/* Clean translation space. */
50784441f85SGarrett D'Amore 		YS.len = 0;
50884441f85SGarrett D'Amore 		for (p = ps, left = psl; left > 0; p += clen, left -= clen) {
50984441f85SGarrett D'Amore 			if ((c = y->bytetab[(uchar_t)*p]) != '\0') {
51084441f85SGarrett D'Amore 				cspace(&YS, &c, 1, APPEND);
51184441f85SGarrett D'Amore 				clen = 1;
51284441f85SGarrett D'Amore 				continue;
51384441f85SGarrett D'Amore 			}
51484441f85SGarrett D'Amore 			for (i = 0; i < y->nmultis; i++)
51584441f85SGarrett D'Amore 				if (left >= y->multis[i].fromlen &&
51684441f85SGarrett D'Amore 				    memcmp(p, y->multis[i].from,
51784441f85SGarrett D'Amore 				    y->multis[i].fromlen) == 0)
51884441f85SGarrett D'Amore 					break;
51984441f85SGarrett D'Amore 			if (i < y->nmultis) {
52084441f85SGarrett D'Amore 				cspace(&YS, y->multis[i].to,
52184441f85SGarrett D'Amore 				    y->multis[i].tolen, APPEND);
52284441f85SGarrett D'Amore 				clen = y->multis[i].fromlen;
52384441f85SGarrett D'Amore 			} else {
52484441f85SGarrett D'Amore 				cspace(&YS, p, 1, APPEND);
52584441f85SGarrett D'Amore 				clen = 1;
52684441f85SGarrett D'Amore 			}
52784441f85SGarrett D'Amore 		}
52884441f85SGarrett D'Amore 		/* Swap the translation space and the pattern space. */
52984441f85SGarrett D'Amore 		tmp = PS;
53084441f85SGarrett D'Amore 		PS = YS;
53184441f85SGarrett D'Amore 		YS = tmp;
53284441f85SGarrett D'Amore 		YS.space = YS.back;
53384441f85SGarrett D'Amore 	}
53484441f85SGarrett D'Amore }
53584441f85SGarrett D'Amore 
53684441f85SGarrett D'Amore /*
53784441f85SGarrett D'Amore  * Flush append requests.  Always called before reading a line,
53884441f85SGarrett D'Amore  * therefore it also resets the substitution done (sdone) flag.
53984441f85SGarrett D'Amore  */
54084441f85SGarrett D'Amore static void
flush_appends(void)54184441f85SGarrett D'Amore flush_appends(void)
54284441f85SGarrett D'Amore {
54384441f85SGarrett D'Amore 	FILE *f;
54484441f85SGarrett D'Amore 	int count, i;
54584441f85SGarrett D'Amore 	char buf[8 * 1024];
54684441f85SGarrett D'Amore 
54784441f85SGarrett D'Amore 	for (i = 0; i < appendx; i++)
54884441f85SGarrett D'Amore 		switch (appends[i].type) {
54984441f85SGarrett D'Amore 		case AP_STRING:
55084441f85SGarrett D'Amore 			(void) fwrite(appends[i].s, sizeof (char),
55184441f85SGarrett D'Amore 			    appends[i].len, outfile);
55284441f85SGarrett D'Amore 			break;
55384441f85SGarrett D'Amore 		case AP_FILE:
55484441f85SGarrett D'Amore 			/*
55584441f85SGarrett D'Amore 			 * Read files probably shouldn't be cached.  Since
55684441f85SGarrett D'Amore 			 * it's not an error to read a non-existent file,
55784441f85SGarrett D'Amore 			 * it's possible that another program is interacting
55884441f85SGarrett D'Amore 			 * with the sed script through the filesystem.  It
55984441f85SGarrett D'Amore 			 * would be truly bizarre, but possible.  It's probably
56084441f85SGarrett D'Amore 			 * not that big a performance win, anyhow.
56184441f85SGarrett D'Amore 			 */
56284441f85SGarrett D'Amore 			if ((f = fopen(appends[i].s, "r")) == NULL)
56384441f85SGarrett D'Amore 				break;
56484441f85SGarrett D'Amore 			while ((count =
56584441f85SGarrett D'Amore 			    fread(buf, sizeof (char), sizeof (buf), f)))
56684441f85SGarrett D'Amore 				(void) fwrite(buf, sizeof (char), count,
56784441f85SGarrett D'Amore 				    outfile);
56884441f85SGarrett D'Amore 			(void) fclose(f);
56984441f85SGarrett D'Amore 			break;
57084441f85SGarrett D'Amore 		}
57184441f85SGarrett D'Amore 	if (ferror(outfile))
57284441f85SGarrett D'Amore 		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
57384441f85SGarrett D'Amore 	appendx = sdone = 0;
57484441f85SGarrett D'Amore }
57584441f85SGarrett D'Amore 
57684441f85SGarrett D'Amore static void
lputs(char * s,size_t len)57784441f85SGarrett D'Amore lputs(char *s, size_t len)
57884441f85SGarrett D'Amore {
57984441f85SGarrett D'Amore 	static const char escapes[] = "\\\a\b\f\r\t\v";
58084441f85SGarrett D'Amore 	int c, col, width;
58184441f85SGarrett D'Amore 	const char *p;
58284441f85SGarrett D'Amore 	struct winsize win;
58384441f85SGarrett D'Amore 	static int termwidth = -1;
58484441f85SGarrett D'Amore 	size_t clen, i;
58584441f85SGarrett D'Amore 	wchar_t wc;
58684441f85SGarrett D'Amore 	mbstate_t mbs;
58784441f85SGarrett D'Amore 
58884441f85SGarrett D'Amore 	if (outfile != stdout)
58984441f85SGarrett D'Amore 		termwidth = 60;
59084441f85SGarrett D'Amore 	if (termwidth == -1) {
59184441f85SGarrett D'Amore 		if (((p = getenv("COLUMNS")) != NULL) && (*p != '\0'))
59284441f85SGarrett D'Amore 			termwidth = atoi(p);
59384441f85SGarrett D'Amore 		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
59484441f85SGarrett D'Amore 		    win.ws_col > 0)
59584441f85SGarrett D'Amore 			termwidth = win.ws_col;
59684441f85SGarrett D'Amore 		else
59784441f85SGarrett D'Amore 			termwidth = 60;
59884441f85SGarrett D'Amore 	}
59984441f85SGarrett D'Amore 	if (termwidth <= 0)
60084441f85SGarrett D'Amore 		termwidth = 1;
60184441f85SGarrett D'Amore 
60284441f85SGarrett D'Amore 	(void) memset(&mbs, 0, sizeof (mbs));
60384441f85SGarrett D'Amore 	col = 0;
60484441f85SGarrett D'Amore 	while (len != 0) {
60584441f85SGarrett D'Amore 		clen = mbrtowc(&wc, s, len, &mbs);
60684441f85SGarrett D'Amore 		if (clen == 0)
60784441f85SGarrett D'Amore 			clen = 1;
60884441f85SGarrett D'Amore 		if (clen == (size_t)-1 || clen == (size_t)-2) {
60984441f85SGarrett D'Amore 			wc = (unsigned char)*s;
61084441f85SGarrett D'Amore 			clen = 1;
61184441f85SGarrett D'Amore 			(void) memset(&mbs, 0, sizeof (mbs));
61284441f85SGarrett D'Amore 		}
61384441f85SGarrett D'Amore 		if (wc == '\n') {
61484441f85SGarrett D'Amore 			if (col + 1 >= termwidth)
61584441f85SGarrett D'Amore 				(void) fprintf(outfile, "\\\n");
61684441f85SGarrett D'Amore 			(void) fputc('$', outfile);
61784441f85SGarrett D'Amore 			(void) fputc('\n', outfile);
61884441f85SGarrett D'Amore 			col = 0;
61984441f85SGarrett D'Amore 		} else if (iswprint(wc)) {
62084441f85SGarrett D'Amore 			width = wcwidth(wc);
62184441f85SGarrett D'Amore 			if (col + width >= termwidth) {
62284441f85SGarrett D'Amore 				(void) fprintf(outfile, "\\\n");
62384441f85SGarrett D'Amore 				col = 0;
62484441f85SGarrett D'Amore 			}
62584441f85SGarrett D'Amore 			(void) fwrite(s, 1, clen, outfile);
62684441f85SGarrett D'Amore 			col += width;
62784441f85SGarrett D'Amore 		} else if (wc != L'\0' && (c = wctob(wc)) != EOF &&
62884441f85SGarrett D'Amore 		    (p = strchr(escapes, c)) != NULL) {
62984441f85SGarrett D'Amore 			if (col + 2 >= termwidth) {
63084441f85SGarrett D'Amore 				(void) fprintf(outfile, "\\\n");
63184441f85SGarrett D'Amore 				col = 0;
63284441f85SGarrett D'Amore 			}
63384441f85SGarrett D'Amore 			(void) fprintf(outfile, "\\%c",
63484441f85SGarrett D'Amore 			    "\\abfrtv"[(uintptr_t)p - (uintptr_t)escapes]);
63584441f85SGarrett D'Amore 			col += 2;
63684441f85SGarrett D'Amore 		} else {
63784441f85SGarrett D'Amore 			if (col + 4 * clen >= (unsigned)termwidth) {
63884441f85SGarrett D'Amore 				(void) fprintf(outfile, "\\\n");
63984441f85SGarrett D'Amore 				col = 0;
64084441f85SGarrett D'Amore 			}
64184441f85SGarrett D'Amore 			for (i = 0; i < clen; i++)
64284441f85SGarrett D'Amore 				(void) fprintf(outfile, "\\%03o",
64384441f85SGarrett D'Amore 				    (int)(unsigned char)s[i]);
64484441f85SGarrett D'Amore 			col += 4 * clen;
64584441f85SGarrett D'Amore 		}
64684441f85SGarrett D'Amore 		s += clen;
64784441f85SGarrett D'Amore 		len -= clen;
64884441f85SGarrett D'Amore 	}
64984441f85SGarrett D'Amore 	if (col + 1 >= termwidth)
65084441f85SGarrett D'Amore 		(void) fprintf(outfile, "\\\n");
65184441f85SGarrett D'Amore 	(void) fputc('$', outfile);
65284441f85SGarrett D'Amore 	(void) fputc('\n', outfile);
65384441f85SGarrett D'Amore 	if (ferror(outfile))
65484441f85SGarrett D'Amore 		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
65584441f85SGarrett D'Amore }
65684441f85SGarrett D'Amore 
65784441f85SGarrett D'Amore static int
regexec_e(regex_t * preg,const char * string,int eflags,int nomatch,size_t slen)65884441f85SGarrett D'Amore regexec_e(regex_t *preg, const char *string, int eflags, int nomatch,
659*4e81fcfeSAndy Fiddaman     size_t slen)
66084441f85SGarrett D'Amore {
66184441f85SGarrett D'Amore 	int eval;
66284441f85SGarrett D'Amore 
66384441f85SGarrett D'Amore 	if (preg == NULL) {
66484441f85SGarrett D'Amore 		if (defpreg == NULL)
66584441f85SGarrett D'Amore 			fatal(_("first RE may not be empty"));
66684441f85SGarrett D'Amore 	} else
66784441f85SGarrett D'Amore 		defpreg = preg;
66884441f85SGarrett D'Amore 
66984441f85SGarrett D'Amore 	/* Set anchors */
67084441f85SGarrett D'Amore 	match[0].rm_so = 0;
67184441f85SGarrett D'Amore 	match[0].rm_eo = slen;
67284441f85SGarrett D'Amore 
67384441f85SGarrett D'Amore 	eval = regexec(defpreg, string,
67484441f85SGarrett D'Amore 	    nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
67584441f85SGarrett D'Amore 	switch (eval) {
67684441f85SGarrett D'Amore 	case 0:
67784441f85SGarrett D'Amore 		return (1);
67884441f85SGarrett D'Amore 	case REG_NOMATCH:
67984441f85SGarrett D'Amore 		return (0);
68084441f85SGarrett D'Amore 	}
68184441f85SGarrett D'Amore 	fatal(_("RE error: %s"), strregerror(eval, defpreg));
68284441f85SGarrett D'Amore 	return (0);
68384441f85SGarrett D'Amore }
68484441f85SGarrett D'Amore 
68584441f85SGarrett D'Amore /*
68684441f85SGarrett D'Amore  * regsub - perform substitutions after a regexp match
68784441f85SGarrett D'Amore  * Based on a routine by Henry Spencer
68884441f85SGarrett D'Amore  */
68984441f85SGarrett D'Amore static void
regsub(SPACE * sp,char * string,char * src)69084441f85SGarrett D'Amore regsub(SPACE *sp, char *string, char *src)
69184441f85SGarrett D'Amore {
69284441f85SGarrett D'Amore 	int len, no;
69384441f85SGarrett D'Amore 	char c, *dst;
69484441f85SGarrett D'Amore 
69584441f85SGarrett D'Amore #define	NEEDSP(reqlen)							\
69684441f85SGarrett D'Amore 	/* XXX What is the +1 for? */					\
69784441f85SGarrett D'Amore 	if (sp->len + (reqlen) + 1 >= sp->blen) {			\
69884441f85SGarrett D'Amore 		sp->blen += (reqlen) + 1024;				\
69984441f85SGarrett D'Amore 		if ((sp->back = realloc(sp->back, sp->blen)) == NULL)	\
70084441f85SGarrett D'Amore 			err(1, "realloc");				\
70184441f85SGarrett D'Amore 		sp->space = sp->back;					\
70284441f85SGarrett D'Amore 		dst = sp->space + sp->len;				\
70384441f85SGarrett D'Amore 	}
70484441f85SGarrett D'Amore 
70584441f85SGarrett D'Amore 	dst = sp->space + sp->len;
70684441f85SGarrett D'Amore 	while ((c = *src++) != '\0') {
70784441f85SGarrett D'Amore 		if (c == '&')
70884441f85SGarrett D'Amore 			no = 0;
70984441f85SGarrett D'Amore 		else if (c == '\\' && isdigit((unsigned char)*src))
71084441f85SGarrett D'Amore 			no = *src++ - '0';
71184441f85SGarrett D'Amore 		else
71284441f85SGarrett D'Amore 			no = -1;
71384441f85SGarrett D'Amore 		if (no < 0) {		/* Ordinary character. */
71484441f85SGarrett D'Amore 			if (c == '\\' && (*src == '\\' || *src == '&'))
71584441f85SGarrett D'Amore 				c = *src++;
71684441f85SGarrett D'Amore 			NEEDSP(1);
71784441f85SGarrett D'Amore 			*dst++ = c;
71884441f85SGarrett D'Amore 			++sp->len;
71984441f85SGarrett D'Amore 		} else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
72084441f85SGarrett D'Amore 			len = match[no].rm_eo - match[no].rm_so;
72184441f85SGarrett D'Amore 			NEEDSP(len);
72284441f85SGarrett D'Amore 			(void) memmove(dst, string + match[no].rm_so, len);
72384441f85SGarrett D'Amore 			dst += len;
72484441f85SGarrett D'Amore 			sp->len += len;
72584441f85SGarrett D'Amore 		}
72684441f85SGarrett D'Amore 	}
72784441f85SGarrett D'Amore 	NEEDSP(1);
72884441f85SGarrett D'Amore 	*dst = '\0';
72984441f85SGarrett D'Amore }
73084441f85SGarrett D'Amore 
73184441f85SGarrett D'Amore /*
73284441f85SGarrett D'Amore  * cspace --
73384441f85SGarrett D'Amore  *	Concatenate space: append the source space to the destination space,
73484441f85SGarrett D'Amore  *	allocating new space as necessary.
73584441f85SGarrett D'Amore  */
73684441f85SGarrett D'Amore void
cspace(SPACE * sp,const char * p,size_t len,enum e_spflag spflag)73784441f85SGarrett D'Amore cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
73884441f85SGarrett D'Amore {
73984441f85SGarrett D'Amore 	size_t tlen;
74084441f85SGarrett D'Amore 
74184441f85SGarrett D'Amore 	/* Make sure SPACE has enough memory and ramp up quickly. */
74284441f85SGarrett D'Amore 	tlen = sp->len + len + 1;
74384441f85SGarrett D'Amore 	if (tlen > sp->blen) {
74484441f85SGarrett D'Amore 		sp->blen = tlen + 1024;
74584441f85SGarrett D'Amore 		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
74684441f85SGarrett D'Amore 		    NULL)
74784441f85SGarrett D'Amore 			err(1, "realloc");
74884441f85SGarrett D'Amore 	}
74984441f85SGarrett D'Amore 
75084441f85SGarrett D'Amore 	if (spflag == REPLACE)
75184441f85SGarrett D'Amore 		sp->len = 0;
75284441f85SGarrett D'Amore 
75384441f85SGarrett D'Amore 	(void) memmove(sp->space + sp->len, p, len);
75484441f85SGarrett D'Amore 
75584441f85SGarrett D'Amore 	sp->space[sp->len += len] = '\0';
75684441f85SGarrett D'Amore }
75784441f85SGarrett D'Amore 
75884441f85SGarrett D'Amore /*
75984441f85SGarrett D'Amore  * Close all cached opened files and report any errors
76084441f85SGarrett D'Amore  */
76184441f85SGarrett D'Amore void
cfclose(struct s_command * cp,struct s_command * end)76284441f85SGarrett D'Amore cfclose(struct s_command *cp, struct s_command *end)
76384441f85SGarrett D'Amore {
76484441f85SGarrett D'Amore 
76584441f85SGarrett D'Amore 	for (; cp != end; cp = cp->next)
76684441f85SGarrett D'Amore 		switch (cp->code) {
76784441f85SGarrett D'Amore 		case 's':
76884441f85SGarrett D'Amore 			if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
76984441f85SGarrett D'Amore 				err(1, "%s", cp->u.s->wfile);
77084441f85SGarrett D'Amore 			cp->u.s->wfd = -1;
77184441f85SGarrett D'Amore 			break;
77284441f85SGarrett D'Amore 		case 'w':
77384441f85SGarrett D'Amore 			if (cp->u.fd != -1 && close(cp->u.fd))
77484441f85SGarrett D'Amore 				err(1, "%s", cp->t);
77584441f85SGarrett D'Amore 			cp->u.fd = -1;
77684441f85SGarrett D'Amore 			break;
77784441f85SGarrett D'Amore 		case '{':
77884441f85SGarrett D'Amore 			cfclose(cp->u.c, cp->next);
77984441f85SGarrett D'Amore 			break;
78084441f85SGarrett D'Amore 		}
78184441f85SGarrett D'Amore }
782