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