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