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