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