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