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