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