process.c revision 302244
1238106Sdes/*-
2238106Sdes * Copyright (c) 1992 Diomidis Spinellis.
3238106Sdes * Copyright (c) 1992, 1993, 1994
4238106Sdes *	The Regents of the University of California.  All rights reserved.
5238106Sdes *
6238106Sdes * This code is derived from software contributed to Berkeley by
7238106Sdes * Diomidis Spinellis of Imperial College, University of London.
8238106Sdes *
9238106Sdes * Redistribution and use in source and binary forms, with or without
10238106Sdes * modification, are permitted provided that the following conditions
11238106Sdes * are met:
12238106Sdes * 1. Redistributions of source code must retain the above copyright
13238106Sdes *    notice, this list of conditions and the following disclaimer.
14238106Sdes * 2. Redistributions in binary form must reproduce the above copyright
15238106Sdes *    notice, this list of conditions and the following disclaimer in the
16238106Sdes *    documentation and/or other materials provided with the distribution.
17238106Sdes * 4. Neither the name of the University nor the names of its contributors
18238106Sdes *    may be used to endorse or promote products derived from this software
19238106Sdes *    without specific prior written permission.
20238106Sdes *
21238106Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22238106Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23238106Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24269257Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25269257Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26269257Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27269257Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28269257Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29269257Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30269257Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31269257Sdes * SUCH DAMAGE.
32269257Sdes */
33269257Sdes
34238106Sdes#include <sys/cdefs.h>
35238106Sdes__FBSDID("$FreeBSD: stable/10/usr.bin/sed/process.c 302244 2016-06-28 03:11:07Z pfg $");
36238106Sdes
37238106Sdes#ifndef lint
38238106Sdesstatic const char sccsid[] = "@(#)process.c	8.6 (Berkeley) 4/20/94";
39238106Sdes#endif
40238106Sdes
41238106Sdes#include <sys/types.h>
42238106Sdes#include <sys/stat.h>
43238106Sdes#include <sys/ioctl.h>
44255585Sdes#include <sys/uio.h>
45255585Sdes
46255585Sdes#include <ctype.h>
47238106Sdes#include <err.h>
48238106Sdes#include <errno.h>
49238106Sdes#include <fcntl.h>
50238106Sdes#include <limits.h>
51238106Sdes#include <regex.h>
52238106Sdes#include <stdio.h>
53238106Sdes#include <stdlib.h>
54238106Sdes#include <string.h>
55238106Sdes#include <unistd.h>
56238106Sdes#include <wchar.h>
57238106Sdes#include <wctype.h>
58238106Sdes
59238106Sdes#include "defs.h"
60238106Sdes#include "extern.h"
61238106Sdes
62238106Sdesstatic SPACE HS, PS, SS, YS;
63238106Sdes#define	pd		PS.deleted
64238106Sdes#define	ps		PS.space
65238106Sdes#define	psl		PS.len
66238106Sdes#define	hs		HS.space
67238106Sdes#define	hsl		HS.len
68238106Sdes
69238106Sdesstatic inline int	 applies(struct s_command *);
70238106Sdesstatic void		 do_tr(struct s_tr *);
71238106Sdesstatic void		 flush_appends(void);
72238106Sdesstatic void		 lputs(char *, size_t);
73238106Sdesstatic int		 regexec_e(regex_t *, const char *, int, int, size_t,
74238106Sdes			     size_t);
75238106Sdesstatic void		 regsub(SPACE *, char *, char *);
76238106Sdesstatic int		 substitute(struct s_command *);
77238106Sdes
78238106Sdesstruct s_appends *appends;	/* Array of pointers to strings to append. */
79238106Sdesstatic int appendx;		/* Index into appends array. */
80238106Sdesint appendnum;			/* Size of appends array. */
81238106Sdes
82238106Sdesstatic int lastaddr;		/* Set by applies if last address of a range. */
83238106Sdesstatic int sdone;		/* If any substitutes since last line input. */
84238106Sdes				/* Iov structure for 'w' commands. */
85238106Sdesstatic regex_t *defpreg;
86238106Sdessize_t maxnsub;
87238106Sdesregmatch_t *match;
88238106Sdes
89238106Sdes#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0)
90238106Sdes
91238106Sdesvoid
92238106Sdesprocess(void)
93238106Sdes{
94238106Sdes	struct s_command *cp;
95238106Sdes	SPACE tspace;
96238106Sdes	size_t oldpsl = 0;
97238106Sdes	char *p;
98238106Sdes
99238106Sdes	p = NULL;
100238106Sdes
101238106Sdes	for (linenum = 0; mf_fgets(&PS, REPLACE);) {
102238106Sdes		pd = 0;
103238106Sdestop:
104238106Sdes		cp = prog;
105255585Sdesredirect:
106285206Sdes		while (cp != NULL) {
107255585Sdes			if (!applies(cp)) {
108255585Sdes				cp = cp->next;
109238106Sdes				continue;
110238106Sdes			}
111238106Sdes			switch (cp->code) {
112238106Sdes			case '{':
113238106Sdes				cp = cp->u.c;
114238106Sdes				goto redirect;
115238106Sdes			case 'a':
116238106Sdes				if (appendx >= appendnum)
117238106Sdes					if ((appends = realloc(appends,
118238106Sdes					    sizeof(struct s_appends) *
119238106Sdes					    (appendnum *= 2))) == NULL)
120238106Sdes						err(1, "realloc");
121238106Sdes				appends[appendx].type = AP_STRING;
122238106Sdes				appends[appendx].s = cp->t;
123238106Sdes				appends[appendx].len = strlen(cp->t);
124238106Sdes				appendx++;
125238106Sdes				break;
126238106Sdes			case 'b':
127238106Sdes				cp = cp->u.c;
128238106Sdes				goto redirect;
129238106Sdes			case 'c':
130238106Sdes				pd = 1;
131238106Sdes				psl = 0;
132238106Sdes				if (cp->a2 == NULL || lastaddr || lastline())
133238106Sdes					(void)fprintf(outfile, "%s", cp->t);
134238106Sdes				break;
135238106Sdes			case 'd':
136238106Sdes				pd = 1;
137238106Sdes				goto new;
138285206Sdes			case 'D':
139255585Sdes				if (pd)
140255585Sdes					goto new;
141238106Sdes				if (psl == 0 ||
142238106Sdes				    (p = memchr(ps, '\n', psl)) == NULL) {
143238106Sdes					pd = 1;
144238106Sdes					goto new;
145238106Sdes				} else {
146238106Sdes					psl -= (p + 1) - ps;
147238106Sdes					memmove(ps, p + 1, psl);
148238106Sdes					goto top;
149238106Sdes				}
150238106Sdes			case 'g':
151238106Sdes				cspace(&PS, hs, hsl, REPLACE);
152238106Sdes				break;
153238106Sdes			case 'G':
154238106Sdes				cspace(&PS, "\n", 1, APPEND);
155238106Sdes				cspace(&PS, hs, hsl, APPEND);
156238106Sdes				break;
157238106Sdes			case 'h':
158238106Sdes				cspace(&HS, ps, psl, REPLACE);
159238106Sdes				break;
160238106Sdes			case 'H':
161238106Sdes				cspace(&HS, "\n", 1, APPEND);
162238106Sdes				cspace(&HS, ps, psl, APPEND);
163238106Sdes				break;
164238106Sdes			case 'i':
165238106Sdes				(void)fprintf(outfile, "%s", cp->t);
166238106Sdes				break;
167238106Sdes			case 'l':
168238106Sdes				lputs(ps, psl);
169238106Sdes				break;
170269257Sdes			case 'n':
171238106Sdes				if (!nflag && !pd)
172238106Sdes					OUT();
173238106Sdes				flush_appends();
174238106Sdes				if (!mf_fgets(&PS, REPLACE))
175238106Sdes					exit(0);
176238106Sdes				pd = 0;
177269257Sdes				break;
178238106Sdes			case 'N':
179238106Sdes				flush_appends();
180238106Sdes				cspace(&PS, "\n", 1, APPEND);
181238106Sdes				if (!mf_fgets(&PS, APPEND))
182238106Sdes					exit(0);
183269257Sdes				break;
184269257Sdes			case 'p':
185269257Sdes				if (pd)
186269257Sdes					break;
187269257Sdes				OUT();
188269257Sdes				break;
189269257Sdes			case 'P':
190238106Sdes				if (pd)
191238106Sdes					break;
192238106Sdes				if ((p = memchr(ps, '\n', psl)) != NULL) {
193238106Sdes					oldpsl = psl;
194238106Sdes					psl = p - ps;
195238106Sdes				}
196238106Sdes				OUT();
197238106Sdes				if (p != NULL)
198238106Sdes					psl = oldpsl;
199238106Sdes				break;
200238106Sdes			case 'q':
201238106Sdes				if (!nflag && !pd)
202238106Sdes					OUT();
203238106Sdes				flush_appends();
204238106Sdes				exit(0);
205238106Sdes			case 'r':
206238106Sdes				if (appendx >= appendnum)
207238106Sdes					if ((appends = realloc(appends,
208238106Sdes					    sizeof(struct s_appends) *
209238106Sdes					    (appendnum *= 2))) == NULL)
210238106Sdes						err(1, "realloc");
211238106Sdes				appends[appendx].type = AP_FILE;
212238106Sdes				appends[appendx].s = cp->t;
213238106Sdes				appends[appendx].len = strlen(cp->t);
214238106Sdes				appendx++;
215238106Sdes				break;
216238106Sdes			case 's':
217238106Sdes				sdone |= substitute(cp);
218238106Sdes				break;
219238106Sdes			case 't':
220238106Sdes				if (sdone) {
221238106Sdes					sdone = 0;
222238106Sdes					cp = cp->u.c;
223238106Sdes					goto redirect;
224238106Sdes				}
225238106Sdes				break;
226238106Sdes			case 'w':
227238106Sdes				if (pd)
228238106Sdes					break;
229238106Sdes				if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
230238106Sdes				    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
231238106Sdes				    DEFFILEMODE)) == -1)
232238106Sdes					err(1, "%s", cp->t);
233238106Sdes				if (write(cp->u.fd, ps, psl) != (ssize_t)psl ||
234238106Sdes				    write(cp->u.fd, "\n", 1) != 1)
235238106Sdes					err(1, "%s", cp->t);
236238106Sdes				break;
237238106Sdes			case 'x':
238238106Sdes				/*
239238106Sdes				 * If the hold space is null, make it empty
240238106Sdes				 * but not null.  Otherwise the pattern space
241238106Sdes				 * will become null after the swap, which is
242238106Sdes				 * an abnormal condition.
243238106Sdes				 */
244238106Sdes				if (hs == NULL)
245238106Sdes					cspace(&HS, "", 0, REPLACE);
246238106Sdes				tspace = PS;
247238106Sdes				PS = HS;
248238106Sdes				HS = tspace;
249238106Sdes				break;
250238106Sdes			case 'y':
251				if (pd || psl == 0)
252					break;
253				do_tr(cp->u.y);
254				break;
255			case ':':
256			case '}':
257				break;
258			case '=':
259				(void)fprintf(outfile, "%lu\n", linenum);
260			}
261			cp = cp->next;
262		} /* for all cp */
263
264new:		if (!nflag && !pd)
265			OUT();
266		flush_appends();
267	} /* for all lines */
268}
269
270/*
271 * TRUE if the address passed matches the current program state
272 * (lastline, linenumber, ps).
273 */
274#define	MATCH(a)							\
275	((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, 0, psl) :	\
276	    (a)->type == AT_LINE ? linenum == (a)->u.l : lastline())
277
278/*
279 * Return TRUE if the command applies to the current line.  Sets the start
280 * line for process ranges.  Interprets the non-select (``!'') flag.
281 */
282static inline int
283applies(struct s_command *cp)
284{
285	int r;
286
287	lastaddr = 0;
288	if (cp->a1 == NULL && cp->a2 == NULL)
289		r = 1;
290	else if (cp->a2)
291		if (cp->startline > 0) {
292                        switch (cp->a2->type) {
293                        case AT_RELLINE:
294                                if (linenum - cp->startline <= cp->a2->u.l)
295                                        r = 1;
296                                else {
297				        cp->startline = 0;
298				        r = 0;
299                                }
300                                break;
301                        default:
302                                if (MATCH(cp->a2)) {
303                                        cp->startline = 0;
304                                        lastaddr = 1;
305                                        r = 1;
306                                } else if (cp->a2->type == AT_LINE &&
307                                            linenum > cp->a2->u.l) {
308                                        /*
309                                         * We missed the 2nd address due to a
310                                         * branch, so just close the range and
311                                         * return false.
312                                         */
313                                        cp->startline = 0;
314                                        r = 0;
315                                } else
316                                        r = 1;
317                        }
318		} else if (cp->a1 && MATCH(cp->a1)) {
319			/*
320			 * If the second address is a number less than or
321			 * equal to the line number first selected, only
322			 * one line shall be selected.
323			 *	-- POSIX 1003.2
324			 * Likewise if the relative second line address is zero.
325			 */
326			if ((cp->a2->type == AT_LINE &&
327			    linenum >= cp->a2->u.l) ||
328			    (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0))
329				lastaddr = 1;
330			else {
331				cp->startline = linenum;
332			}
333			r = 1;
334		} else
335			r = 0;
336	else
337		r = MATCH(cp->a1);
338	return (cp->nonsel ? ! r : r);
339}
340
341/*
342 * Reset the sed processor to its initial state.
343 */
344void
345resetstate(void)
346{
347	struct s_command *cp;
348
349	/*
350	 * Reset all in-range markers.
351	 */
352	for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
353		if (cp->a2)
354			cp->startline = 0;
355
356	/*
357	 * Clear out the hold space.
358	 */
359	cspace(&HS, "", 0, REPLACE);
360}
361
362/*
363 * substitute --
364 *	Do substitutions in the pattern space.  Currently, we build a
365 *	copy of the new pattern space in the substitute space structure
366 *	and then swap them.
367 */
368static int
369substitute(struct s_command *cp)
370{
371	SPACE tspace;
372	regex_t *re;
373	regoff_t slen;
374	int lastempty, n;
375	size_t le = 0;
376	char *s;
377
378	s = ps;
379	re = cp->u.s->re;
380	if (re == NULL) {
381		if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
382			linenum = cp->u.s->linenum;
383			errx(1, "%lu: %s: \\%u not defined in the RE",
384					linenum, fname, cp->u.s->maxbref);
385		}
386	}
387	if (!regexec_e(re, ps, 0, 0, 0, psl))
388		return (0);
389
390	SS.len = 0;				/* Clean substitute space. */
391	slen = psl;
392	n = cp->u.s->n;
393	lastempty = 1;
394
395	do {
396		/* Copy the leading retained string. */
397		if (n <= 1 && (match[0].rm_so > le))
398			cspace(&SS, s, match[0].rm_so - le, APPEND);
399
400		/* Skip zero-length matches right after other matches. */
401		if (lastempty || (match[0].rm_so - le) ||
402		    match[0].rm_so != match[0].rm_eo) {
403			if (n <= 1) {
404				/* Want this match: append replacement. */
405				regsub(&SS, ps, cp->u.s->new);
406				if (n == 1)
407					n = -1;
408			} else {
409				/* Want a later match: append original. */
410				if (match[0].rm_eo - le)
411					cspace(&SS, s, match[0].rm_eo - le,
412					    APPEND);
413				n--;
414			}
415		}
416
417		/* Move past this match. */
418		s = ps + match[0].rm_eo;
419		slen = psl - match[0].rm_eo;
420		le = match[0].rm_eo;
421
422		/*
423		 * After a zero-length match, advance one byte,
424		 * and at the end of the line, terminate.
425		 */
426		if (match[0].rm_so == match[0].rm_eo) {
427			if (*s == '\0' || *s == '\n')
428				slen = -1;
429			else
430				slen--;
431			if (*s != '\0') {
432			 	cspace(&SS, s++, 1, APPEND);
433				le++;
434			}
435			lastempty = 1;
436		} else
437			lastempty = 0;
438
439	} while (n >= 0 && slen >= 0 &&
440	    regexec_e(re, ps, REG_NOTBOL, 0, le, psl));
441
442	/* Did not find the requested number of matches. */
443	if (n > 1)
444		return (0);
445
446	/* Copy the trailing retained string. */
447	if (slen > 0)
448		cspace(&SS, s, slen, APPEND);
449
450	/*
451	 * Swap the substitute space and the pattern space, and make sure
452	 * that any leftover pointers into stdio memory get lost.
453	 */
454	tspace = PS;
455	PS = SS;
456	SS = tspace;
457	SS.space = SS.back;
458
459	/* Handle the 'p' flag. */
460	if (cp->u.s->p)
461		OUT();
462
463	/* Handle the 'w' flag. */
464	if (cp->u.s->wfile && !pd) {
465		if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
466		    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
467			err(1, "%s", cp->u.s->wfile);
468		if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl ||
469		    write(cp->u.s->wfd, "\n", 1) != 1)
470			err(1, "%s", cp->u.s->wfile);
471	}
472	return (1);
473}
474
475/*
476 * do_tr --
477 *	Perform translation ('y' command) in the pattern space.
478 */
479static void
480do_tr(struct s_tr *y)
481{
482	SPACE tmp;
483	char c, *p;
484	size_t clen, left;
485	int i;
486
487	if (MB_CUR_MAX == 1) {
488		/*
489		 * Single-byte encoding: perform in-place translation
490		 * of the pattern space.
491		 */
492		for (p = ps; p < &ps[psl]; p++)
493			*p = y->bytetab[(u_char)*p];
494	} else {
495		/*
496		 * Multi-byte encoding: perform translation into the
497		 * translation space, then swap the translation and
498		 * pattern spaces.
499		 */
500		/* Clean translation space. */
501		YS.len = 0;
502		for (p = ps, left = psl; left > 0; p += clen, left -= clen) {
503			if ((c = y->bytetab[(u_char)*p]) != '\0') {
504				cspace(&YS, &c, 1, APPEND);
505				clen = 1;
506				continue;
507			}
508			for (i = 0; i < y->nmultis; i++)
509				if (left >= y->multis[i].fromlen &&
510				    memcmp(p, y->multis[i].from,
511				    y->multis[i].fromlen) == 0)
512					break;
513			if (i < y->nmultis) {
514				cspace(&YS, y->multis[i].to,
515				    y->multis[i].tolen, APPEND);
516				clen = y->multis[i].fromlen;
517			} else {
518				cspace(&YS, p, 1, APPEND);
519				clen = 1;
520			}
521		}
522		/* Swap the translation space and the pattern space. */
523		tmp = PS;
524		PS = YS;
525		YS = tmp;
526		YS.space = YS.back;
527	}
528}
529
530/*
531 * Flush append requests.  Always called before reading a line,
532 * therefore it also resets the substitution done (sdone) flag.
533 */
534static void
535flush_appends(void)
536{
537	FILE *f;
538	int count, i;
539	char buf[8 * 1024];
540
541	for (i = 0; i < appendx; i++)
542		switch (appends[i].type) {
543		case AP_STRING:
544			fwrite(appends[i].s, sizeof(char), appends[i].len,
545			    outfile);
546			break;
547		case AP_FILE:
548			/*
549			 * Read files probably shouldn't be cached.  Since
550			 * it's not an error to read a non-existent file,
551			 * it's possible that another program is interacting
552			 * with the sed script through the filesystem.  It
553			 * would be truly bizarre, but possible.  It's probably
554			 * not that big a performance win, anyhow.
555			 */
556			if ((f = fopen(appends[i].s, "r")) == NULL)
557				break;
558			while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
559				(void)fwrite(buf, sizeof(char), count, outfile);
560			(void)fclose(f);
561			break;
562		}
563	if (ferror(outfile))
564		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
565	appendx = sdone = 0;
566}
567
568static void
569lputs(char *s, size_t len)
570{
571	static const char escapes[] = "\\\a\b\f\r\t\v";
572	int c, col, width;
573	const char *p;
574	struct winsize win;
575	static int termwidth = -1;
576	size_t clen, i;
577	wchar_t wc;
578	mbstate_t mbs;
579
580	if (outfile != stdout)
581		termwidth = 60;
582	if (termwidth == -1) {
583		if ((p = getenv("COLUMNS")) && *p != '\0')
584			termwidth = atoi(p);
585		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
586		    win.ws_col > 0)
587			termwidth = win.ws_col;
588		else
589			termwidth = 60;
590	}
591	if (termwidth <= 0)
592		termwidth = 1;
593
594	memset(&mbs, 0, sizeof(mbs));
595	col = 0;
596	while (len != 0) {
597		clen = mbrtowc(&wc, s, len, &mbs);
598		if (clen == 0)
599			clen = 1;
600		if (clen == (size_t)-1 || clen == (size_t)-2) {
601			wc = (unsigned char)*s;
602			clen = 1;
603			memset(&mbs, 0, sizeof(mbs));
604		}
605		if (wc == '\n') {
606			if (col + 1 >= termwidth)
607				fprintf(outfile, "\\\n");
608			fputc('$', outfile);
609			fputc('\n', outfile);
610			col = 0;
611		} else if (iswprint(wc)) {
612			width = wcwidth(wc);
613			if (col + width >= termwidth) {
614				fprintf(outfile, "\\\n");
615				col = 0;
616			}
617			fwrite(s, 1, clen, outfile);
618			col += width;
619		} else if (wc != L'\0' && (c = wctob(wc)) != EOF &&
620		    (p = strchr(escapes, c)) != NULL) {
621			if (col + 2 >= termwidth) {
622				fprintf(outfile, "\\\n");
623				col = 0;
624			}
625			fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]);
626			col += 2;
627		} else {
628			if (col + 4 * clen >= (unsigned)termwidth) {
629				fprintf(outfile, "\\\n");
630				col = 0;
631			}
632			for (i = 0; i < clen; i++)
633				fprintf(outfile, "\\%03o",
634				    (int)(unsigned char)s[i]);
635			col += 4 * clen;
636		}
637		s += clen;
638		len -= clen;
639	}
640	if (col + 1 >= termwidth)
641		fprintf(outfile, "\\\n");
642	(void)fputc('$', outfile);
643	(void)fputc('\n', outfile);
644	if (ferror(outfile))
645		errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
646}
647
648static int
649regexec_e(regex_t *preg, const char *string, int eflags, int nomatch,
650	size_t start, size_t stop)
651{
652	int eval;
653
654	if (preg == NULL) {
655		if (defpreg == NULL)
656			errx(1, "first RE may not be empty");
657	} else
658		defpreg = preg;
659
660	/* Set anchors */
661	match[0].rm_so = start;
662	match[0].rm_eo = stop;
663
664	eval = regexec(defpreg, string,
665	    nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
666	switch(eval) {
667	case 0:
668		return (1);
669	case REG_NOMATCH:
670		return (0);
671	}
672	errx(1, "RE error: %s", strregerror(eval, defpreg));
673	/* NOTREACHED */
674}
675
676/*
677 * regsub - perform substitutions after a regexp match
678 * Based on a routine by Henry Spencer
679 */
680static void
681regsub(SPACE *sp, char *string, char *src)
682{
683	int len, no;
684	char c, *dst;
685
686#define	NEEDSP(reqlen)							\
687	/* XXX What is the +1 for? */					\
688	if (sp->len + (reqlen) + 1 >= sp->blen) {			\
689		sp->blen += (reqlen) + 1024;				\
690		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \
691		    == NULL)						\
692			err(1, "realloc");				\
693		dst = sp->space + sp->len;				\
694	}
695
696	dst = sp->space + sp->len;
697	while ((c = *src++) != '\0') {
698		if (c == '&')
699			no = 0;
700		else if (c == '\\' && isdigit((unsigned char)*src))
701			no = *src++ - '0';
702		else
703			no = -1;
704		if (no < 0) {		/* Ordinary character. */
705			if (c == '\\' && (*src == '\\' || *src == '&'))
706				c = *src++;
707			NEEDSP(1);
708			*dst++ = c;
709			++sp->len;
710		} else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
711			len = match[no].rm_eo - match[no].rm_so;
712			NEEDSP(len);
713			memmove(dst, string + match[no].rm_so, len);
714			dst += len;
715			sp->len += len;
716		}
717	}
718	NEEDSP(1);
719	*dst = '\0';
720}
721
722/*
723 * cspace --
724 *	Concatenate space: append the source space to the destination space,
725 *	allocating new space as necessary.
726 */
727void
728cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
729{
730	size_t tlen;
731
732	/* Make sure SPACE has enough memory and ramp up quickly. */
733	tlen = sp->len + len + 1;
734	if (tlen > sp->blen) {
735		sp->blen = tlen + 1024;
736		if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
737		    NULL)
738			err(1, "realloc");
739	}
740
741	if (spflag == REPLACE)
742		sp->len = 0;
743
744	memmove(sp->space + sp->len, p, len);
745
746	sp->space[sp->len += len] = '\0';
747}
748
749/*
750 * Close all cached opened files and report any errors
751 */
752void
753cfclose(struct s_command *cp, struct s_command *end)
754{
755
756	for (; cp != end; cp = cp->next)
757		switch(cp->code) {
758		case 's':
759			if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
760				err(1, "%s", cp->u.s->wfile);
761			cp->u.s->wfd = -1;
762			break;
763		case 'w':
764			if (cp->u.fd != -1 && close(cp->u.fd))
765				err(1, "%s", cp->t);
766			cp->u.fd = -1;
767			break;
768		case '{':
769			cfclose(cp->u.c, cp->next);
770			break;
771		}
772}
773