xref: /csrg-svn/usr.bin/sed/process.c (revision 55997)
1*55997Sbostic /*-
2*55997Sbostic  * Copyright (c) 1992 Diomidis Spinellis.
3*55997Sbostic  * Copyright (c) 1992 The Regents of the University of California.
4*55997Sbostic  * All rights reserved.
5*55997Sbostic  *
6*55997Sbostic  * This code is derived from software contributed to Berkeley by
7*55997Sbostic  * Diomidis Spinellis of Imperial College, University of London.
8*55997Sbostic  *
9*55997Sbostic  * %sccs.include.redist.c%
10*55997Sbostic  */
11*55997Sbostic 
12*55997Sbostic #ifndef lint
13*55997Sbostic static char sccsid[] = "@(#)process.c	5.1 (Berkeley) 08/23/92";
14*55997Sbostic #endif /* not lint */
15*55997Sbostic 
16*55997Sbostic #include <sys/types.h>
17*55997Sbostic #include <sys/stat.h>
18*55997Sbostic #include <sys/ioctl.h>
19*55997Sbostic #include <sys/uio.h>
20*55997Sbostic 
21*55997Sbostic #include <ctype.h>
22*55997Sbostic #include <errno.h>
23*55997Sbostic #include <fcntl.h>
24*55997Sbostic #include <limits.h>
25*55997Sbostic #include <regex.h>
26*55997Sbostic #include <stdio.h>
27*55997Sbostic #include <stdlib.h>
28*55997Sbostic #include <string.h>
29*55997Sbostic #include <unistd.h>
30*55997Sbostic 
31*55997Sbostic #include "defs.h"
32*55997Sbostic #include "extern.h"
33*55997Sbostic 
34*55997Sbostic typedef struct {
35*55997Sbostic 	char *space;		/* Current space pointer. */
36*55997Sbostic 	size_t len;		/* Current length. */
37*55997Sbostic 	int deleted;		/* If deleted. */
38*55997Sbostic 	char *back;		/* Backing memory. */
39*55997Sbostic 	size_t blen;		/* Backing memory length. */
40*55997Sbostic } SPACE;
41*55997Sbostic static SPACE HS, PS, SS;
42*55997Sbostic #define	pd		PS.deleted
43*55997Sbostic #define	ps		PS.space
44*55997Sbostic #define	psl		PS.len
45*55997Sbostic #define	hs		HS.space
46*55997Sbostic #define	hsl		HS.len
47*55997Sbostic 
48*55997Sbostic static inline int	 applies __P((struct s_command *));
49*55997Sbostic static void		 cspace __P((SPACE *, char *, size_t, int));
50*55997Sbostic static void		 flush_appends __P((void));
51*55997Sbostic static void		 lputs __P((char *));
52*55997Sbostic static inline int	 match __P((struct s_addr *));
53*55997Sbostic static int		 regexec_check __P((regex_t *, const char *,
54*55997Sbostic 			    int, regmatch_t[], int));
55*55997Sbostic static void		 regsub __P((regmatch_t *, char *, char *, SPACE *));
56*55997Sbostic static int		 substitute __P((struct s_command *));
57*55997Sbostic 
58*55997Sbostic struct s_appends *appends;	/* Array of pointers to strings to append. */
59*55997Sbostic static int appendx;		/* Index into appends array. */
60*55997Sbostic int appendnum;			/* Size of appends array. */
61*55997Sbostic 
62*55997Sbostic static int lastaddr;		/* Set by applies if last address of a range. */
63*55997Sbostic static int sdone;		/* If any substitutes since last line input. */
64*55997Sbostic 				/* Iov structure for 'w' commands. */
65*55997Sbostic static struct iovec iov[2] = { NULL, 0, "\n", 1 };
66*55997Sbostic 
67*55997Sbostic void
68*55997Sbostic process()
69*55997Sbostic {
70*55997Sbostic 	struct s_command *cp;
71*55997Sbostic 	SPACE tspace;
72*55997Sbostic 	size_t len;
73*55997Sbostic 	char oldc, *p;
74*55997Sbostic 
75*55997Sbostic 	for (linenum = 0; ps = mf_fgets(&psl);) {
76*55997Sbostic 		pd = 0;
77*55997Sbostic 		cp = prog;
78*55997Sbostic redirect:
79*55997Sbostic 		while (cp != NULL) {
80*55997Sbostic 			if (!applies(cp)) {
81*55997Sbostic 				cp = cp->next;
82*55997Sbostic 				continue;
83*55997Sbostic 			}
84*55997Sbostic 			switch (cp->code) {
85*55997Sbostic 			case '{':
86*55997Sbostic 				cp = cp->u.c;
87*55997Sbostic 				goto redirect;
88*55997Sbostic 			case 'a':
89*55997Sbostic 				if (appendx >= appendnum)
90*55997Sbostic 					appends = xrealloc(appends,
91*55997Sbostic 					    sizeof(struct s_appends) *
92*55997Sbostic 					    (appendnum *= 2));
93*55997Sbostic 				appends[appendx].type = AP_STRING;
94*55997Sbostic 				appends[appendx].s = cp->t;
95*55997Sbostic 				appendx++;
96*55997Sbostic 				break;
97*55997Sbostic 			case 'b':
98*55997Sbostic 				cp = cp->u.c;
99*55997Sbostic 				goto redirect;
100*55997Sbostic 			case 'c':
101*55997Sbostic 				pd = 1;
102*55997Sbostic 				psl = 0;
103*55997Sbostic 				if (cp->a2 == NULL || lastaddr)
104*55997Sbostic 					(void)printf("%s", cp->t);
105*55997Sbostic 				break;
106*55997Sbostic 			case 'd':
107*55997Sbostic 				if (pd)
108*55997Sbostic 					goto new;
109*55997Sbostic 				psl = 0;
110*55997Sbostic 				ps[0] = '\0';
111*55997Sbostic 				goto new;
112*55997Sbostic 			case 'D':
113*55997Sbostic 				if (pd)
114*55997Sbostic 					goto new;
115*55997Sbostic 				if ((p = strchr(ps, '\n')) == NULL) {
116*55997Sbostic 					psl = 0;
117*55997Sbostic 					ps[0] = '\0';
118*55997Sbostic 				} else {
119*55997Sbostic 					psl -= (p - ps) - 1;
120*55997Sbostic 					memmove(ps, p + 1, psl);
121*55997Sbostic 				}
122*55997Sbostic 				goto new;
123*55997Sbostic 			case 'g':
124*55997Sbostic 				ps = hs;
125*55997Sbostic 				psl = hsl;
126*55997Sbostic 				break;
127*55997Sbostic 			case 'G':
128*55997Sbostic 				cspace(&PS, hs, hsl, 1);
129*55997Sbostic 				break;
130*55997Sbostic 			case 'h':
131*55997Sbostic 				cspace(&HS, ps, psl, 0);
132*55997Sbostic 				break;
133*55997Sbostic 			case 'H':
134*55997Sbostic 				cspace(&HS, ps, psl, 1);
135*55997Sbostic 				break;
136*55997Sbostic 			case 'i':
137*55997Sbostic 				(void)printf("%s", cp->t);
138*55997Sbostic 				break;
139*55997Sbostic 			case 'l':
140*55997Sbostic 				lputs(ps);
141*55997Sbostic 				break;
142*55997Sbostic 			case 'n':
143*55997Sbostic 				if (!nflag && !pd)
144*55997Sbostic 					(void)printf("%s\n", ps);
145*55997Sbostic 				flush_appends();
146*55997Sbostic 				ps = mf_fgets(&psl);
147*55997Sbostic #ifdef HISTORIC_PRACTICE
148*55997Sbostic 				if (ps == NULL)
149*55997Sbostic 					exit(0);
150*55997Sbostic #endif
151*55997Sbostic 				pd = 0;
152*55997Sbostic 				break;
153*55997Sbostic 			case 'N':
154*55997Sbostic 				flush_appends();
155*55997Sbostic 				if (ps != PS.back)
156*55997Sbostic 					cspace(&PS, NULL, 0, 0);
157*55997Sbostic 				if ((p = mf_fgets(&len)) == NULL) {
158*55997Sbostic 					if (!nflag && !pd)
159*55997Sbostic 						(void)printf("%s\n", ps);
160*55997Sbostic 					exit(0);
161*55997Sbostic 				}
162*55997Sbostic 				cspace(&PS, p, len, 1);
163*55997Sbostic 				break;
164*55997Sbostic 			case 'p':
165*55997Sbostic 				if (pd)
166*55997Sbostic 					break;
167*55997Sbostic 				(void)printf("%s\n", ps);
168*55997Sbostic 				break;
169*55997Sbostic 			case 'P':
170*55997Sbostic 				if (pd)
171*55997Sbostic 					break;
172*55997Sbostic 				if ((p = strchr(ps, '\n')) != NULL) {
173*55997Sbostic 					oldc = *p;
174*55997Sbostic 					*p = '\0';
175*55997Sbostic 				}
176*55997Sbostic 				(void)printf("%s\n", ps);
177*55997Sbostic 				if (p != NULL)
178*55997Sbostic 					*p = oldc;
179*55997Sbostic 				break;
180*55997Sbostic 			case 'q':
181*55997Sbostic 				if (!nflag && !pd)
182*55997Sbostic 					(void)printf("%s\n", ps);
183*55997Sbostic 				flush_appends();
184*55997Sbostic 				exit(0);
185*55997Sbostic 			case 'r':
186*55997Sbostic 				if (appendx >= appendnum)
187*55997Sbostic 					appends = xrealloc(appends,
188*55997Sbostic 					    sizeof(struct s_appends) *
189*55997Sbostic 					    (appendnum *= 2));
190*55997Sbostic 				appends[appendx].type = AP_FILE;
191*55997Sbostic 				appends[appendx].s = cp->t;
192*55997Sbostic 				appendx++;
193*55997Sbostic 				break;
194*55997Sbostic 			case 's':
195*55997Sbostic 				sdone = substitute(cp);
196*55997Sbostic 				break;
197*55997Sbostic 			case 't':
198*55997Sbostic 				if (sdone) {
199*55997Sbostic 					sdone = 0;
200*55997Sbostic 					cp = cp->u.c;
201*55997Sbostic 					goto redirect;
202*55997Sbostic 				}
203*55997Sbostic 				break;
204*55997Sbostic 			case 'w':
205*55997Sbostic 				if (pd)
206*55997Sbostic 					break;
207*55997Sbostic 				if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
208*55997Sbostic 				    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
209*55997Sbostic 				    DEFFILEMODE)) == -1)
210*55997Sbostic 					err(FATAL, "%s: %s\n",
211*55997Sbostic 					    cp->t, strerror(errno));
212*55997Sbostic 				iov[0].iov_base = ps;
213*55997Sbostic 				iov[0].iov_len = psl;
214*55997Sbostic 				if (writev(cp->u.fd, iov, 2) != psl + 1)
215*55997Sbostic 					err(FATAL, "%s: %s\n",
216*55997Sbostic 					    cp->t, strerror(errno));
217*55997Sbostic 				break;
218*55997Sbostic 			case 'x':
219*55997Sbostic 				tspace = PS;
220*55997Sbostic 				PS = HS;
221*55997Sbostic 				HS = tspace;
222*55997Sbostic 				break;
223*55997Sbostic 			case 'y':
224*55997Sbostic 				if (pd)
225*55997Sbostic 					break;
226*55997Sbostic 				for (p = ps, len = psl; len--; ++p)
227*55997Sbostic 					*p = cp->u.y[*p];
228*55997Sbostic 				break;
229*55997Sbostic 			case ':':
230*55997Sbostic 			case '}':
231*55997Sbostic 				break;
232*55997Sbostic 			case '=':
233*55997Sbostic 				(void)printf("%lu\n", linenum);
234*55997Sbostic 			}
235*55997Sbostic 			cp = cp->next;
236*55997Sbostic 		} /* for all cp */
237*55997Sbostic 
238*55997Sbostic new:		if (!nflag && !pd)
239*55997Sbostic 			(void)printf("%s\n", ps);
240*55997Sbostic 		flush_appends();
241*55997Sbostic 	} /* for all lines */
242*55997Sbostic }
243*55997Sbostic 
244*55997Sbostic /*
245*55997Sbostic  * Return TRUE if the command applies to the current line.  Sets the inrange
246*55997Sbostic  * flag to process ranges.  Interprets the non-select (``!'') flag.
247*55997Sbostic  */
248*55997Sbostic static inline int
249*55997Sbostic applies(cp)
250*55997Sbostic 	struct s_command *cp;
251*55997Sbostic {
252*55997Sbostic 	int r;
253*55997Sbostic 
254*55997Sbostic 	lastaddr = 0;
255*55997Sbostic 	if (cp->a1 == NULL && cp->a2 == NULL)
256*55997Sbostic 		r = 1;
257*55997Sbostic 	else if (cp->a2)
258*55997Sbostic 		if (cp->inrange) {
259*55997Sbostic 			if (match(cp->a2)) {
260*55997Sbostic 				cp->inrange = 0;
261*55997Sbostic 				lastaddr = 1;
262*55997Sbostic 			}
263*55997Sbostic 			r = 1;
264*55997Sbostic 		} else if (match(cp->a1)) {
265*55997Sbostic 			/*
266*55997Sbostic 			 * If the second address is a number less than or
267*55997Sbostic 			 * equal to the line number first selected, only
268*55997Sbostic 			 * one line shall be selected.
269*55997Sbostic 			 *	-- POSIX 1003.2
270*55997Sbostic 			 */
271*55997Sbostic 			if (cp->a2->type == AT_LINE &&
272*55997Sbostic 			    linenum >= cp->a2->u.l)
273*55997Sbostic 				lastaddr = 1;
274*55997Sbostic 			else
275*55997Sbostic 				cp->inrange = 1;
276*55997Sbostic 			r = 1;
277*55997Sbostic 		} else
278*55997Sbostic 			r = 0;
279*55997Sbostic 	else
280*55997Sbostic 		r = match(cp->a1);
281*55997Sbostic 	return (cp->nonsel ? ! r : r);
282*55997Sbostic }
283*55997Sbostic 
284*55997Sbostic /*
285*55997Sbostic  * Return TRUE if the address passed matches the current program
286*55997Sbostic  * state (linenumber, ps, lastline)
287*55997Sbostic  */
288*55997Sbostic static int inline
289*55997Sbostic match(a)
290*55997Sbostic 	struct s_addr *a;
291*55997Sbostic {
292*55997Sbostic 	int eval;
293*55997Sbostic 
294*55997Sbostic 	switch (a->type) {
295*55997Sbostic 	case AT_RE:
296*55997Sbostic 		switch (eval = regexec(a->u.r, ps, 0, NULL, 0)) {
297*55997Sbostic 		case 0:
298*55997Sbostic 			return (1);
299*55997Sbostic 		case REG_NOMATCH:
300*55997Sbostic 			return (0);
301*55997Sbostic 		default:
302*55997Sbostic 			err(FATAL, "RE error: %s", strregerror(eval, a->u.r));
303*55997Sbostic 		}
304*55997Sbostic 	case AT_LINE:
305*55997Sbostic 		return (linenum == a->u.l);
306*55997Sbostic 	case AT_LAST:
307*55997Sbostic 		return (lastline);
308*55997Sbostic 	}
309*55997Sbostic 	/* NOTREACHED */
310*55997Sbostic }
311*55997Sbostic 
312*55997Sbostic /*
313*55997Sbostic  * substitute --
314*55997Sbostic  *	Do substitutions in the pattern space.  Currently, we build a
315*55997Sbostic  *	copy of the new pattern space in the substitute space structure
316*55997Sbostic  *	and then swap them.
317*55997Sbostic  */
318*55997Sbostic static int
319*55997Sbostic substitute(cp)
320*55997Sbostic 	struct s_command *cp;
321*55997Sbostic {
322*55997Sbostic 	SPACE tspace;
323*55997Sbostic 	static regex_t *re;
324*55997Sbostic 	int n, re_off;
325*55997Sbostic 	char *endp, *s;
326*55997Sbostic 
327*55997Sbostic 	s = ps;
328*55997Sbostic 	re = &cp->u.s->re;
329*55997Sbostic 	if (regexec_check(re,
330*55997Sbostic 	    s, re->re_nsub + 1, cp->u.s->pmatch, 0) == REG_NOMATCH)
331*55997Sbostic 		return (0);
332*55997Sbostic 
333*55997Sbostic 	SS.len = 0;				/* Clean substitute space. */
334*55997Sbostic 	n = cp->u.s->n;
335*55997Sbostic 	switch (n) {
336*55997Sbostic 	case 0:					/* Global */
337*55997Sbostic 		do {
338*55997Sbostic 			/* Locate start of replaced string. */
339*55997Sbostic 			re_off = cp->u.s->pmatch[0].rm_so;
340*55997Sbostic 			/* Locate end of replaced string + 1. */
341*55997Sbostic 			endp = s + cp->u.s->pmatch[0].rm_eo;
342*55997Sbostic 			/* Copy leading retained string. */
343*55997Sbostic 			cspace(&SS, s, re_off, 0);
344*55997Sbostic 			/* Add in regular expression. */
345*55997Sbostic 			regsub(cp->u.s->pmatch, s, cp->u.s->new, &SS);
346*55997Sbostic 			/* Move past this match. */
347*55997Sbostic 			s += cp->u.s->pmatch[0].rm_eo;
348*55997Sbostic 		} while(regexec_check(re, s, re->re_nsub + 1,
349*55997Sbostic 		    cp->u.s->pmatch, REG_NOTBOL) != REG_NOMATCH);
350*55997Sbostic 		/* Copy trailing retained string. */
351*55997Sbostic 		cspace(&SS, s, strlen(s), 0);
352*55997Sbostic 		break;
353*55997Sbostic 	default:				/* Nth occurrence */
354*55997Sbostic 		while (--n) {
355*55997Sbostic 			s += cp->u.s->pmatch[0].rm_eo;
356*55997Sbostic 			if (regexec_check(re, s, re->re_nsub + 1,
357*55997Sbostic 			    cp->u.s->pmatch, REG_NOTBOL) == REG_NOMATCH)
358*55997Sbostic 				return (0);
359*55997Sbostic 		}
360*55997Sbostic 		/* FALLTHROUGH */
361*55997Sbostic 	case 1:					/* 1st occurrence */
362*55997Sbostic 		/* Locate start of replaced string. */
363*55997Sbostic 		re_off = cp->u.s->pmatch[0].rm_so + s - ps;
364*55997Sbostic 		/* Copy leading retained string. */
365*55997Sbostic 		cspace(&SS, ps, re_off, 0);
366*55997Sbostic 		/* Add in regular expression. */
367*55997Sbostic 		regsub(cp->u.s->pmatch, s, cp->u.s->new, &SS);
368*55997Sbostic 		/* Copy trailing retained string. */
369*55997Sbostic 		s += cp->u.s->pmatch[0].rm_eo;
370*55997Sbostic 		cspace(&SS, s, strlen(s), 0);
371*55997Sbostic 		break;
372*55997Sbostic 	}
373*55997Sbostic 
374*55997Sbostic 	/*
375*55997Sbostic 	 * Swap the substitute space and the pattern space, and make sure
376*55997Sbostic 	 * that any leftover pointers into stdio memory get lost.
377*55997Sbostic 	 */
378*55997Sbostic 	tspace = PS;
379*55997Sbostic 	PS = SS;
380*55997Sbostic 	SS = tspace;
381*55997Sbostic 	SS.space = SS.back;
382*55997Sbostic 
383*55997Sbostic 	/* Handle the 'p' flag. */
384*55997Sbostic 	if (cp->u.s->p)
385*55997Sbostic 		(void)printf("%s\n", ps);
386*55997Sbostic 
387*55997Sbostic 	/* Handle the 'w' flag. */
388*55997Sbostic 	if (cp->u.s->wfile && !pd) {
389*55997Sbostic 		if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
390*55997Sbostic 		    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
391*55997Sbostic 			err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno));
392*55997Sbostic 		iov[0].iov_base = ps;
393*55997Sbostic 		iov[0].iov_len = psl;
394*55997Sbostic 		if (writev(cp->u.s->wfd, iov, 2) != psl + 1)
395*55997Sbostic 			err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno));
396*55997Sbostic 	}
397*55997Sbostic 	return (1);
398*55997Sbostic }
399*55997Sbostic 
400*55997Sbostic /*
401*55997Sbostic  * Flush append requests.  Always called before reading a line,
402*55997Sbostic  * therefore it also resets the substitution done (sdone) flag.
403*55997Sbostic  */
404*55997Sbostic static void
405*55997Sbostic flush_appends()
406*55997Sbostic {
407*55997Sbostic 	FILE *f;
408*55997Sbostic 	int count, i;
409*55997Sbostic 	char buf[8 * 1024];
410*55997Sbostic 
411*55997Sbostic 	for (i = 0; i < appendx; i++)
412*55997Sbostic 		switch (appends[i].type) {
413*55997Sbostic 		case AP_STRING:
414*55997Sbostic 			(void)printf("%s", appends[i].s);
415*55997Sbostic 			break;
416*55997Sbostic 		case AP_FILE:
417*55997Sbostic 			/*
418*55997Sbostic 			 * Read files probably shouldn't be cached.  Since
419*55997Sbostic 			 * it's not an error to read a non-existent file,
420*55997Sbostic 			 * it's possible that another program is interacting
421*55997Sbostic 			 * with the sed script through the file system.  It
422*55997Sbostic 			 * would be truly bizarre, but possible.  It's probably
423*55997Sbostic 			 * not that big a performance win, anyhow.
424*55997Sbostic 			 */
425*55997Sbostic 			if ((f = fopen(appends[i].s, "r")) == NULL)
426*55997Sbostic 				break;
427*55997Sbostic 			while (count = fread(buf, 1, sizeof(buf), f))
428*55997Sbostic 				(void)fwrite(buf, 1, count, stdout);
429*55997Sbostic 			(void)fclose(f);
430*55997Sbostic 			break;
431*55997Sbostic 		}
432*55997Sbostic 	if (ferror(stdout))
433*55997Sbostic 		err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
434*55997Sbostic 	appendx = 0;
435*55997Sbostic 	sdone = 0;
436*55997Sbostic }
437*55997Sbostic 
438*55997Sbostic static void
439*55997Sbostic lputs(s)
440*55997Sbostic 	register char *s;
441*55997Sbostic {
442*55997Sbostic 	register int count;
443*55997Sbostic 	register char *escapes, *p;
444*55997Sbostic 	struct winsize win;
445*55997Sbostic 	static int termwidth = -1;
446*55997Sbostic 
447*55997Sbostic 	if (termwidth == -1)
448*55997Sbostic 		if (p = getenv("COLUMNS"))
449*55997Sbostic 			termwidth = atoi(p);
450*55997Sbostic 		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
451*55997Sbostic 		    win.ws_col > 0)
452*55997Sbostic 			termwidth = win.ws_col;
453*55997Sbostic 		else
454*55997Sbostic 			termwidth = 60;
455*55997Sbostic 
456*55997Sbostic 	for (count = 0; *s; ++s) {
457*55997Sbostic 		if (count >= termwidth) {
458*55997Sbostic 			(void)printf("\\\n");
459*55997Sbostic 			count = 0;
460*55997Sbostic 		}
461*55997Sbostic 		if (isascii(*s) && isprint(*s) && *s != '\\') {
462*55997Sbostic 			(void)putchar(*s);
463*55997Sbostic 			count++;
464*55997Sbostic 		} else {
465*55997Sbostic 			escapes = "\\\a\b\f\n\r\t\v";
466*55997Sbostic 			(void)putchar('\\');
467*55997Sbostic 			if (p = strchr(escapes, *s)) {
468*55997Sbostic 				(void)putchar("\\abfnrtv"[p - escapes]);
469*55997Sbostic 				count += 2;
470*55997Sbostic 			} else {
471*55997Sbostic 				(void)printf("%03o", (u_char)*s);
472*55997Sbostic 				count += 4;
473*55997Sbostic 			}
474*55997Sbostic 		}
475*55997Sbostic 	}
476*55997Sbostic 	(void)putchar('$');
477*55997Sbostic 	(void)putchar('\n');
478*55997Sbostic 	if (ferror(stdout))
479*55997Sbostic 		err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
480*55997Sbostic }
481*55997Sbostic 
482*55997Sbostic /*
483*55997Sbostic  * Regexec with checking for errors
484*55997Sbostic  */
485*55997Sbostic static int
486*55997Sbostic regexec_check(preg, string, nmatch, pmatch, eflags)
487*55997Sbostic 	regex_t *preg;
488*55997Sbostic 	const char *string;
489*55997Sbostic 	int nmatch;
490*55997Sbostic 	regmatch_t pmatch[];
491*55997Sbostic 	int eflags;
492*55997Sbostic {
493*55997Sbostic 	int eval;
494*55997Sbostic 
495*55997Sbostic 	switch (eval = regexec(preg, string, nmatch, pmatch, eflags)) {
496*55997Sbostic 	case 0:
497*55997Sbostic 		return (0);
498*55997Sbostic 	case REG_NOMATCH:
499*55997Sbostic 		return (REG_NOMATCH);
500*55997Sbostic 	default:
501*55997Sbostic 		err(FATAL, "RE error: %s", strregerror(eval, preg));
502*55997Sbostic 	}
503*55997Sbostic 	/* NOTREACHED */
504*55997Sbostic }
505*55997Sbostic 
506*55997Sbostic /*
507*55997Sbostic  * regsub - perform substitutions after a regexp match
508*55997Sbostic  * Based on a routine by Henry Spencer
509*55997Sbostic  */
510*55997Sbostic static void
511*55997Sbostic regsub(pmatch, string, src, sp)
512*55997Sbostic 	regmatch_t *pmatch;
513*55997Sbostic 	char *string, *src;
514*55997Sbostic 	SPACE *sp;
515*55997Sbostic {
516*55997Sbostic 	register int len, no;
517*55997Sbostic 	register char c, *dst;
518*55997Sbostic 
519*55997Sbostic #define	NEEDSP(reqlen)							\
520*55997Sbostic 	if (sp->len >= sp->blen - (reqlen) - 1) {			\
521*55997Sbostic 		sp->blen += (reqlen) + 1024;				\
522*55997Sbostic 		sp->space = sp->back = xrealloc(sp->back, sp->blen);	\
523*55997Sbostic 		dst = sp->space + sp->len;				\
524*55997Sbostic 	}
525*55997Sbostic 
526*55997Sbostic 	dst = sp->space + sp->len;
527*55997Sbostic 	while ((c = *src++) != '\0') {
528*55997Sbostic 		if (c == '&')
529*55997Sbostic 			no = 0;
530*55997Sbostic 		else if (c == '\\' && isdigit(*src))
531*55997Sbostic 			no = *src++ - '0';
532*55997Sbostic 		else
533*55997Sbostic 			no = -1;
534*55997Sbostic 		if (no < 0) {		/* Ordinary character. */
535*55997Sbostic  			if (c == '\\' && (*src == '\\' || *src == '&'))
536*55997Sbostic  				c = *src++;
537*55997Sbostic 			NEEDSP(1);
538*55997Sbostic  			*dst++ = c;
539*55997Sbostic 			++sp->len;
540*55997Sbostic  		} else if (pmatch[no].rm_so != -1 && pmatch[no].rm_eo != -1) {
541*55997Sbostic 			len = pmatch[no].rm_eo - pmatch[no].rm_so;
542*55997Sbostic 			NEEDSP(len);
543*55997Sbostic 			memmove(dst, string + pmatch[no].rm_so, len);
544*55997Sbostic 			dst += len;
545*55997Sbostic 			sp->len += len;
546*55997Sbostic 		}
547*55997Sbostic 	}
548*55997Sbostic 	NEEDSP(1);
549*55997Sbostic 	*dst = '\0';
550*55997Sbostic }
551*55997Sbostic 
552*55997Sbostic /*
553*55997Sbostic  * aspace --
554*55997Sbostic  *	Append the source space to the destination space, allocating new
555*55997Sbostic  *	space as necessary.
556*55997Sbostic  */
557*55997Sbostic static void
558*55997Sbostic cspace(sp, p, len, append)
559*55997Sbostic 	SPACE *sp;
560*55997Sbostic 	char *p;
561*55997Sbostic 	size_t len;
562*55997Sbostic 	int append;
563*55997Sbostic {
564*55997Sbostic 	size_t tlen;
565*55997Sbostic 	int needcopy;
566*55997Sbostic 
567*55997Sbostic 	/* Current pointer may point to something else at the moment. */
568*55997Sbostic 	needcopy = sp->space != sp->back;
569*55997Sbostic 
570*55997Sbostic 	/*
571*55997Sbostic 	 * Make sure SPACE has enough memory and ramp up quickly.
572*55997Sbostic 	 * Add in two extra bytes, one for the newline, one for a
573*55997Sbostic 	 * terminating NULL.
574*55997Sbostic 	 */
575*55997Sbostic 	tlen = sp->len + len + 2;
576*55997Sbostic 	if (tlen > sp->blen) {
577*55997Sbostic 		sp->blen = tlen + 1024;
578*55997Sbostic 		sp->back = xrealloc(sp->back, sp->blen);
579*55997Sbostic 	}
580*55997Sbostic 
581*55997Sbostic 	if (needcopy)
582*55997Sbostic 		memmove(sp->back, sp->space, sp->len + 1);
583*55997Sbostic 	sp->space = sp->back;
584*55997Sbostic 
585*55997Sbostic 	/* May just be copying out of a stdio buffer. */
586*55997Sbostic 	if (len == NULL)
587*55997Sbostic 		return;
588*55997Sbostic 
589*55997Sbostic 	/* Append a separating newline. */
590*55997Sbostic 	if (append)
591*55997Sbostic 		sp->space[sp->len++] = '\n';
592*55997Sbostic 
593*55997Sbostic 	/* Append the new stuff, plus its terminating NULL. */
594*55997Sbostic 	memmove(sp->space + sp->len, p, len + 1);
595*55997Sbostic 	sp->len += len;
596*55997Sbostic }
597*55997Sbostic 
598*55997Sbostic /*
599*55997Sbostic  * Close all cached opened files and report any errors
600*55997Sbostic  */
601*55997Sbostic void
602*55997Sbostic cfclose(cp)
603*55997Sbostic 	register struct s_command *cp;
604*55997Sbostic {
605*55997Sbostic 
606*55997Sbostic 	for (; cp != NULL; cp = cp->next)
607*55997Sbostic 		switch(cp->code) {
608*55997Sbostic 		case 's':
609*55997Sbostic 			if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
610*55997Sbostic 				err(FATAL,
611*55997Sbostic 				    "%s: %s", cp->u.s->wfile, strerror(errno));
612*55997Sbostic 			break;
613*55997Sbostic 		case 'w':
614*55997Sbostic 			if (cp->u.fd != -1 && close(cp->u.fd))
615*55997Sbostic 				err(FATAL, "%s: %s", cp->t, strerror(errno));
616*55997Sbostic 			break;
617*55997Sbostic 		case '{':
618*55997Sbostic 			cfclose(cp->u.c);
619*55997Sbostic 			break;
620*55997Sbostic 		}
621*55997Sbostic }
622