xref: /freebsd/bin/sh/output.c (revision fbbd9655e5107c68e4e0146ff22b73d7350475bc)
14b88c807SRodney W. Grimes /*-
24b88c807SRodney W. Grimes  * Copyright (c) 1991, 1993
34b88c807SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
44b88c807SRodney W. Grimes  *
54b88c807SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
64b88c807SRodney W. Grimes  * Kenneth Almquist.
74b88c807SRodney W. Grimes  *
84b88c807SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
94b88c807SRodney W. Grimes  * modification, are permitted provided that the following conditions
104b88c807SRodney W. Grimes  * are met:
114b88c807SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
124b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
134b88c807SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
144b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
154b88c807SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
16*fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
174b88c807SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
184b88c807SRodney W. Grimes  *    without specific prior written permission.
194b88c807SRodney W. Grimes  *
204b88c807SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
214b88c807SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224b88c807SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234b88c807SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
244b88c807SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254b88c807SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264b88c807SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274b88c807SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284b88c807SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294b88c807SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304b88c807SRodney W. Grimes  * SUCH DAMAGE.
314b88c807SRodney W. Grimes  */
324b88c807SRodney W. Grimes 
334b88c807SRodney W. Grimes #ifndef lint
343d7b5b93SPhilippe Charnier #if 0
353d7b5b93SPhilippe Charnier static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
363d7b5b93SPhilippe Charnier #endif
374b88c807SRodney W. Grimes #endif /* not lint */
382749b141SDavid E. O'Brien #include <sys/cdefs.h>
392749b141SDavid E. O'Brien __FBSDID("$FreeBSD$");
404b88c807SRodney W. Grimes 
414b88c807SRodney W. Grimes /*
424b88c807SRodney W. Grimes  * Shell output routines.  We use our own output routines because:
434b88c807SRodney W. Grimes  *	When a builtin command is interrupted we have to discard
444b88c807SRodney W. Grimes  *		any pending output.
454b88c807SRodney W. Grimes  *	When a builtin command appears in back quotes, we want to
464b88c807SRodney W. Grimes  *		save the output of the command in a region obtained
474b88c807SRodney W. Grimes  *		via malloc, rather than doing a fork and reading the
484b88c807SRodney W. Grimes  *		output of the command via a pipe.
494b88c807SRodney W. Grimes  */
504b88c807SRodney W. Grimes 
514b88c807SRodney W. Grimes #include <stdio.h>	/* defines BUFSIZ */
52aa9caaf6SPeter Wemm #include <string.h>
53aa9caaf6SPeter Wemm #include <stdarg.h>
54aa9caaf6SPeter Wemm #include <errno.h>
55aa9caaf6SPeter Wemm #include <unistd.h>
56aa9caaf6SPeter Wemm #include <stdlib.h>
5788ef06f3SJilles Tjoelker #include <wchar.h>
5888ef06f3SJilles Tjoelker #include <wctype.h>
59aa9caaf6SPeter Wemm 
604b88c807SRodney W. Grimes #include "shell.h"
614b88c807SRodney W. Grimes #include "syntax.h"
624b88c807SRodney W. Grimes #include "output.h"
634b88c807SRodney W. Grimes #include "memalloc.h"
644b88c807SRodney W. Grimes #include "error.h"
65dee75cf7STim J. Robbins #include "var.h"
664b88c807SRodney W. Grimes 
674b88c807SRodney W. Grimes 
684b88c807SRodney W. Grimes #define OUTBUFSIZ BUFSIZ
696903c683SJilles Tjoelker #define MEM_OUT -2		/* output to dynamically allocated memory */
704b88c807SRodney W. Grimes #define OUTPUT_ERR 01		/* error occurred on output */
714b88c807SRodney W. Grimes 
7288328642SDavid E. O'Brien static int doformat_wr(void *, const char *, int);
734b88c807SRodney W. Grimes 
744b88c807SRodney W. Grimes struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
75c6204d4aSJilles Tjoelker struct output errout = {NULL, 0, NULL, 256, 2, 0};
764b88c807SRodney W. Grimes struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
774b88c807SRodney W. Grimes struct output *out1 = &output;
784b88c807SRodney W. Grimes struct output *out2 = &errout;
794b88c807SRodney W. Grimes 
804b88c807SRodney W. Grimes void
81aeb5d065SJilles Tjoelker outcslow(int c, struct output *file)
82aeb5d065SJilles Tjoelker {
83aeb5d065SJilles Tjoelker 	outc(c, file);
84aeb5d065SJilles Tjoelker }
85aeb5d065SJilles Tjoelker 
86aeb5d065SJilles Tjoelker void
875134c3f7SWarner Losh out1str(const char *p)
884b88c807SRodney W. Grimes {
894b88c807SRodney W. Grimes 	outstr(p, out1);
904b88c807SRodney W. Grimes }
914b88c807SRodney W. Grimes 
92e5341cbbSTim J. Robbins void
93e5341cbbSTim J. Robbins out1qstr(const char *p)
94e5341cbbSTim J. Robbins {
95e5341cbbSTim J. Robbins 	outqstr(p, out1);
96e5341cbbSTim J. Robbins }
974b88c807SRodney W. Grimes 
984b88c807SRodney W. Grimes void
995134c3f7SWarner Losh out2str(const char *p)
1004b88c807SRodney W. Grimes {
1014b88c807SRodney W. Grimes 	outstr(p, out2);
1024b88c807SRodney W. Grimes }
1034b88c807SRodney W. Grimes 
104e5341cbbSTim J. Robbins void
105e5341cbbSTim J. Robbins out2qstr(const char *p)
106e5341cbbSTim J. Robbins {
107e5341cbbSTim J. Robbins 	outqstr(p, out2);
108e5341cbbSTim J. Robbins }
1094b88c807SRodney W. Grimes 
1104b88c807SRodney W. Grimes void
1115134c3f7SWarner Losh outstr(const char *p, struct output *file)
1124b88c807SRodney W. Grimes {
113c3f57269SJilles Tjoelker 	outbin(p, strlen(p), file);
1144b88c807SRodney W. Grimes }
1154b88c807SRodney W. Grimes 
11688ef06f3SJilles Tjoelker static void
11788ef06f3SJilles Tjoelker byteseq(int ch, struct output *file)
11888ef06f3SJilles Tjoelker {
11988ef06f3SJilles Tjoelker 	char seq[4];
12088ef06f3SJilles Tjoelker 
12188ef06f3SJilles Tjoelker 	seq[0] = '\\';
12288ef06f3SJilles Tjoelker 	seq[1] = (ch >> 6 & 0x3) + '0';
12388ef06f3SJilles Tjoelker 	seq[2] = (ch >> 3 & 0x7) + '0';
12488ef06f3SJilles Tjoelker 	seq[3] = (ch & 0x7) + '0';
12588ef06f3SJilles Tjoelker 	outbin(seq, 4, file);
12688ef06f3SJilles Tjoelker }
12788ef06f3SJilles Tjoelker 
12888ef06f3SJilles Tjoelker static void
12988ef06f3SJilles Tjoelker outdqstr(const char *p, struct output *file)
13088ef06f3SJilles Tjoelker {
13188ef06f3SJilles Tjoelker 	const char *end;
13288ef06f3SJilles Tjoelker 	mbstate_t mbs;
13388ef06f3SJilles Tjoelker 	size_t clen;
13488ef06f3SJilles Tjoelker 	wchar_t wc;
13588ef06f3SJilles Tjoelker 
13688ef06f3SJilles Tjoelker 	memset(&mbs, '\0', sizeof(mbs));
13788ef06f3SJilles Tjoelker 	end = p + strlen(p);
13888ef06f3SJilles Tjoelker 	outstr("$'", file);
13988ef06f3SJilles Tjoelker 	while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
14088ef06f3SJilles Tjoelker 		if (clen == (size_t)-2) {
14188ef06f3SJilles Tjoelker 			while (p < end)
14288ef06f3SJilles Tjoelker 				byteseq(*p++, file);
14388ef06f3SJilles Tjoelker 			break;
14488ef06f3SJilles Tjoelker 		}
14588ef06f3SJilles Tjoelker 		if (clen == (size_t)-1) {
14688ef06f3SJilles Tjoelker 			memset(&mbs, '\0', sizeof(mbs));
14788ef06f3SJilles Tjoelker 			byteseq(*p++, file);
14888ef06f3SJilles Tjoelker 			continue;
14988ef06f3SJilles Tjoelker 		}
15088ef06f3SJilles Tjoelker 		if (wc == L'\n')
15188ef06f3SJilles Tjoelker 			outcslow('\n', file), p++;
15288ef06f3SJilles Tjoelker 		else if (wc == L'\r')
15388ef06f3SJilles Tjoelker 			outstr("\\r", file), p++;
15488ef06f3SJilles Tjoelker 		else if (wc == L'\t')
15588ef06f3SJilles Tjoelker 			outstr("\\t", file), p++;
15688ef06f3SJilles Tjoelker 		else if (!iswprint(wc)) {
15788ef06f3SJilles Tjoelker 			for (; clen > 0; clen--)
15888ef06f3SJilles Tjoelker 				byteseq(*p++, file);
15988ef06f3SJilles Tjoelker 		} else {
16088ef06f3SJilles Tjoelker 			if (wc == L'\'' || wc == L'\\')
16188ef06f3SJilles Tjoelker 				outcslow('\\', file);
16288ef06f3SJilles Tjoelker 			outbin(p, clen, file);
16388ef06f3SJilles Tjoelker 			p += clen;
16488ef06f3SJilles Tjoelker 		}
16588ef06f3SJilles Tjoelker 	}
16688ef06f3SJilles Tjoelker 	outcslow('\'', file);
16788ef06f3SJilles Tjoelker }
16888ef06f3SJilles Tjoelker 
169e5341cbbSTim J. Robbins /* Like outstr(), but quote for re-input into the shell. */
170e5341cbbSTim J. Robbins void
171e5341cbbSTim J. Robbins outqstr(const char *p, struct output *file)
172e5341cbbSTim J. Robbins {
17388ef06f3SJilles Tjoelker 	int i;
174e5341cbbSTim J. Robbins 
1753f0131f6SStefan Farfeleder 	if (p[0] == '\0') {
1763f0131f6SStefan Farfeleder 		outstr("''", file);
1773f0131f6SStefan Farfeleder 		return;
1783f0131f6SStefan Farfeleder 	}
17988ef06f3SJilles Tjoelker 	for (i = 0; p[i] != '\0'; i++) {
18088ef06f3SJilles Tjoelker 		if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
18188ef06f3SJilles Tjoelker 		    (p[i] & 0x80) != 0 || p[i] == '\'') {
18288ef06f3SJilles Tjoelker 			outdqstr(p, file);
18388ef06f3SJilles Tjoelker 			return;
18488ef06f3SJilles Tjoelker 		}
18588ef06f3SJilles Tjoelker 	}
18688ef06f3SJilles Tjoelker 
18788ef06f3SJilles Tjoelker 	if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
188e68165a6SJilles Tjoelker 			strcmp(p, "[") == 0) {
189dee75cf7STim J. Robbins 		outstr(p, file);
190dee75cf7STim J. Robbins 		return;
191dee75cf7STim J. Robbins 	}
192dee75cf7STim J. Robbins 
193aeb5d065SJilles Tjoelker 	outcslow('\'', file);
19488ef06f3SJilles Tjoelker 	outstr(p, file);
195aeb5d065SJilles Tjoelker 	outcslow('\'', file);
196e5341cbbSTim J. Robbins }
1974b88c807SRodney W. Grimes 
198c3f57269SJilles Tjoelker void
199c3f57269SJilles Tjoelker outbin(const void *data, size_t len, struct output *file)
200c3f57269SJilles Tjoelker {
201c3f57269SJilles Tjoelker 	const char *p;
202c3f57269SJilles Tjoelker 
203c3f57269SJilles Tjoelker 	p = data;
204c3f57269SJilles Tjoelker 	while (len-- > 0)
205c3f57269SJilles Tjoelker 		outc(*p++, file);
206c3f57269SJilles Tjoelker }
207c3f57269SJilles Tjoelker 
2084b88c807SRodney W. Grimes void
2095134c3f7SWarner Losh emptyoutbuf(struct output *dest)
2104b88c807SRodney W. Grimes {
2114b88c807SRodney W. Grimes 	int offset;
2124b88c807SRodney W. Grimes 
2136903c683SJilles Tjoelker 	if (dest->buf == NULL) {
2144b88c807SRodney W. Grimes 		INTOFF;
2154b88c807SRodney W. Grimes 		dest->buf = ckmalloc(dest->bufsize);
2164b88c807SRodney W. Grimes 		dest->nextc = dest->buf;
2174b88c807SRodney W. Grimes 		dest->nleft = dest->bufsize;
2184b88c807SRodney W. Grimes 		INTON;
2194b88c807SRodney W. Grimes 	} else if (dest->fd == MEM_OUT) {
2204b88c807SRodney W. Grimes 		offset = dest->bufsize;
2214b88c807SRodney W. Grimes 		INTOFF;
2224b88c807SRodney W. Grimes 		dest->bufsize <<= 1;
2234b88c807SRodney W. Grimes 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
2244b88c807SRodney W. Grimes 		dest->nleft = dest->bufsize - offset;
2254b88c807SRodney W. Grimes 		dest->nextc = dest->buf + offset;
2264b88c807SRodney W. Grimes 		INTON;
2274b88c807SRodney W. Grimes 	} else {
2284b88c807SRodney W. Grimes 		flushout(dest);
2294b88c807SRodney W. Grimes 	}
2304b88c807SRodney W. Grimes 	dest->nleft--;
2314b88c807SRodney W. Grimes }
2324b88c807SRodney W. Grimes 
2334b88c807SRodney W. Grimes 
2344b88c807SRodney W. Grimes void
2355134c3f7SWarner Losh flushall(void)
2365134c3f7SWarner Losh {
2374b88c807SRodney W. Grimes 	flushout(&output);
2384b88c807SRodney W. Grimes 	flushout(&errout);
2394b88c807SRodney W. Grimes }
2404b88c807SRodney W. Grimes 
2414b88c807SRodney W. Grimes 
2424b88c807SRodney W. Grimes void
2435134c3f7SWarner Losh flushout(struct output *dest)
2444b88c807SRodney W. Grimes {
2454b88c807SRodney W. Grimes 
2464b88c807SRodney W. Grimes 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
2474b88c807SRodney W. Grimes 		return;
2484b88c807SRodney W. Grimes 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
2494b88c807SRodney W. Grimes 		dest->flags |= OUTPUT_ERR;
2504b88c807SRodney W. Grimes 	dest->nextc = dest->buf;
2514b88c807SRodney W. Grimes 	dest->nleft = dest->bufsize;
2524b88c807SRodney W. Grimes }
2534b88c807SRodney W. Grimes 
2544b88c807SRodney W. Grimes 
2554b88c807SRodney W. Grimes void
2565134c3f7SWarner Losh freestdout(void)
2575134c3f7SWarner Losh {
2584b88c807SRodney W. Grimes 	INTOFF;
2594b88c807SRodney W. Grimes 	if (output.buf) {
2604b88c807SRodney W. Grimes 		ckfree(output.buf);
2614b88c807SRodney W. Grimes 		output.buf = NULL;
2624b88c807SRodney W. Grimes 		output.nleft = 0;
2634b88c807SRodney W. Grimes 	}
2644b88c807SRodney W. Grimes 	INTON;
2654b88c807SRodney W. Grimes }
2664b88c807SRodney W. Grimes 
2674b88c807SRodney W. Grimes 
268d6d66cfcSJilles Tjoelker int
269d6d66cfcSJilles Tjoelker outiserror(struct output *file)
270d6d66cfcSJilles Tjoelker {
271d6d66cfcSJilles Tjoelker 	return (file->flags & OUTPUT_ERR);
272d6d66cfcSJilles Tjoelker }
273d6d66cfcSJilles Tjoelker 
274d6d66cfcSJilles Tjoelker 
275d6d66cfcSJilles Tjoelker void
276d6d66cfcSJilles Tjoelker outclearerror(struct output *file)
277d6d66cfcSJilles Tjoelker {
278d6d66cfcSJilles Tjoelker 	file->flags &= ~OUTPUT_ERR;
279d6d66cfcSJilles Tjoelker }
280d6d66cfcSJilles Tjoelker 
281d6d66cfcSJilles Tjoelker 
2824b88c807SRodney W. Grimes void
2835134c3f7SWarner Losh outfmt(struct output *file, const char *fmt, ...)
2845134c3f7SWarner Losh {
2854b88c807SRodney W. Grimes 	va_list ap;
2864b88c807SRodney W. Grimes 
2874b88c807SRodney W. Grimes 	va_start(ap, fmt);
2884b88c807SRodney W. Grimes 	doformat(file, fmt, ap);
2894b88c807SRodney W. Grimes 	va_end(ap);
2904b88c807SRodney W. Grimes }
2914b88c807SRodney W. Grimes 
2924b88c807SRodney W. Grimes 
2934b88c807SRodney W. Grimes void
2945134c3f7SWarner Losh out1fmt(const char *fmt, ...)
2955134c3f7SWarner Losh {
2964b88c807SRodney W. Grimes 	va_list ap;
2974b88c807SRodney W. Grimes 
2984b88c807SRodney W. Grimes 	va_start(ap, fmt);
2994b88c807SRodney W. Grimes 	doformat(out1, fmt, ap);
3004b88c807SRodney W. Grimes 	va_end(ap);
3014b88c807SRodney W. Grimes }
3024b88c807SRodney W. Grimes 
3034b88c807SRodney W. Grimes void
304c6204d4aSJilles Tjoelker out2fmt_flush(const char *fmt, ...)
3055134c3f7SWarner Losh {
3064b88c807SRodney W. Grimes 	va_list ap;
3074b88c807SRodney W. Grimes 
3084b88c807SRodney W. Grimes 	va_start(ap, fmt);
3094b88c807SRodney W. Grimes 	doformat(out2, fmt, ap);
3104b88c807SRodney W. Grimes 	va_end(ap);
3114b88c807SRodney W. Grimes 	flushout(out2);
3124b88c807SRodney W. Grimes }
3134b88c807SRodney W. Grimes 
3144b88c807SRodney W. Grimes void
3155134c3f7SWarner Losh fmtstr(char *outbuf, int length, const char *fmt, ...)
3165134c3f7SWarner Losh {
3174b88c807SRodney W. Grimes 	va_list ap;
3184b88c807SRodney W. Grimes 
3196903c683SJilles Tjoelker 	INTOFF;
3204b88c807SRodney W. Grimes 	va_start(ap, fmt);
3216903c683SJilles Tjoelker 	vsnprintf(outbuf, length, fmt, ap);
3227e73d40eSTim J. Robbins 	va_end(ap);
3236903c683SJilles Tjoelker 	INTON;
3244b88c807SRodney W. Grimes }
3254b88c807SRodney W. Grimes 
32688328642SDavid E. O'Brien static int
3277e73d40eSTim J. Robbins doformat_wr(void *cookie, const char *buf, int len)
3287e73d40eSTim J. Robbins {
3297e73d40eSTim J. Robbins 	struct output *o;
3304b88c807SRodney W. Grimes 
3317e73d40eSTim J. Robbins 	o = (struct output *)cookie;
332c3f57269SJilles Tjoelker 	outbin(buf, len, o);
3334b88c807SRodney W. Grimes 
334c3f57269SJilles Tjoelker 	return (len);
3357e73d40eSTim J. Robbins }
3364b88c807SRodney W. Grimes 
3374b88c807SRodney W. Grimes void
3385134c3f7SWarner Losh doformat(struct output *dest, const char *f, va_list ap)
3394b88c807SRodney W. Grimes {
3407e73d40eSTim J. Robbins 	FILE *fp;
3414b88c807SRodney W. Grimes 
3427e73d40eSTim J. Robbins 	if ((fp = fwopen(dest, doformat_wr)) != NULL) {
3437e73d40eSTim J. Robbins 		vfprintf(fp, f, ap);
3447e73d40eSTim J. Robbins 		fclose(fp);
3454b88c807SRodney W. Grimes 	}
3464b88c807SRodney W. Grimes }
3474b88c807SRodney W. Grimes 
3484b88c807SRodney W. Grimes /*
3494b88c807SRodney W. Grimes  * Version of write which resumes after a signal is caught.
3504b88c807SRodney W. Grimes  */
3514b88c807SRodney W. Grimes 
3524b88c807SRodney W. Grimes int
3532cac6e36SJilles Tjoelker xwrite(int fd, const char *buf, int nbytes)
3544b88c807SRodney W. Grimes {
3554b88c807SRodney W. Grimes 	int ntry;
3564b88c807SRodney W. Grimes 	int i;
3574b88c807SRodney W. Grimes 	int n;
3584b88c807SRodney W. Grimes 
3594b88c807SRodney W. Grimes 	n = nbytes;
3604b88c807SRodney W. Grimes 	ntry = 0;
3614b88c807SRodney W. Grimes 	for (;;) {
3624b88c807SRodney W. Grimes 		i = write(fd, buf, n);
3634b88c807SRodney W. Grimes 		if (i > 0) {
3644b88c807SRodney W. Grimes 			if ((n -= i) <= 0)
3654b88c807SRodney W. Grimes 				return nbytes;
3664b88c807SRodney W. Grimes 			buf += i;
3674b88c807SRodney W. Grimes 			ntry = 0;
3684b88c807SRodney W. Grimes 		} else if (i == 0) {
3694b88c807SRodney W. Grimes 			if (++ntry > 10)
3704b88c807SRodney W. Grimes 				return nbytes - n;
3714b88c807SRodney W. Grimes 		} else if (errno != EINTR) {
3724b88c807SRodney W. Grimes 			return -1;
3734b88c807SRodney W. Grimes 		}
3744b88c807SRodney W. Grimes 	}
3754b88c807SRodney W. Grimes }
376