output.c revision 331722
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: stable/11/bin/sh/output.c 331722 2018-03-29 02:50:57Z eadler $");
40
41/*
42 * Shell output routines.  We use our own output routines because:
43 *	When a builtin command is interrupted we have to discard
44 *		any pending output.
45 *	When a builtin command appears in back quotes, we want to
46 *		save the output of the command in a region obtained
47 *		via malloc, rather than doing a fork and reading the
48 *		output of the command via a pipe.
49 */
50
51#include <stdio.h>	/* defines BUFSIZ */
52#include <string.h>
53#include <stdarg.h>
54#include <errno.h>
55#include <unistd.h>
56#include <stdlib.h>
57#include <wchar.h>
58#include <wctype.h>
59
60#include "shell.h"
61#include "syntax.h"
62#include "output.h"
63#include "memalloc.h"
64#include "error.h"
65#include "var.h"
66
67
68#define OUTBUFSIZ BUFSIZ
69#define MEM_OUT -2		/* output to dynamically allocated memory */
70#define OUTPUT_ERR 01		/* error occurred on output */
71
72static int doformat_wr(void *, const char *, int);
73
74struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
75struct output errout = {NULL, 0, NULL, 256, 2, 0};
76struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
77struct output *out1 = &output;
78struct output *out2 = &errout;
79
80void
81outcslow(int c, struct output *file)
82{
83	outc(c, file);
84}
85
86void
87out1str(const char *p)
88{
89	outstr(p, out1);
90}
91
92void
93out1qstr(const char *p)
94{
95	outqstr(p, out1);
96}
97
98void
99out2str(const char *p)
100{
101	outstr(p, out2);
102}
103
104void
105out2qstr(const char *p)
106{
107	outqstr(p, out2);
108}
109
110void
111outstr(const char *p, struct output *file)
112{
113	outbin(p, strlen(p), file);
114}
115
116static void
117byteseq(int ch, struct output *file)
118{
119	char seq[4];
120
121	seq[0] = '\\';
122	seq[1] = (ch >> 6 & 0x3) + '0';
123	seq[2] = (ch >> 3 & 0x7) + '0';
124	seq[3] = (ch & 0x7) + '0';
125	outbin(seq, 4, file);
126}
127
128static void
129outdqstr(const char *p, struct output *file)
130{
131	const char *end;
132	mbstate_t mbs;
133	size_t clen;
134	wchar_t wc;
135
136	memset(&mbs, '\0', sizeof(mbs));
137	end = p + strlen(p);
138	outstr("$'", file);
139	while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
140		if (clen == (size_t)-2) {
141			while (p < end)
142				byteseq(*p++, file);
143			break;
144		}
145		if (clen == (size_t)-1) {
146			memset(&mbs, '\0', sizeof(mbs));
147			byteseq(*p++, file);
148			continue;
149		}
150		if (wc == L'\n')
151			outcslow('\n', file), p++;
152		else if (wc == L'\r')
153			outstr("\\r", file), p++;
154		else if (wc == L'\t')
155			outstr("\\t", file), p++;
156		else if (!iswprint(wc)) {
157			for (; clen > 0; clen--)
158				byteseq(*p++, file);
159		} else {
160			if (wc == L'\'' || wc == L'\\')
161				outcslow('\\', file);
162			outbin(p, clen, file);
163			p += clen;
164		}
165	}
166	outcslow('\'', file);
167}
168
169/* Like outstr(), but quote for re-input into the shell. */
170void
171outqstr(const char *p, struct output *file)
172{
173	int i;
174
175	if (p[0] == '\0') {
176		outstr("''", file);
177		return;
178	}
179	for (i = 0; p[i] != '\0'; i++) {
180		if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
181		    (p[i] & 0x80) != 0 || p[i] == '\'') {
182			outdqstr(p, file);
183			return;
184		}
185	}
186
187	if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
188			strcmp(p, "[") == 0) {
189		outstr(p, file);
190		return;
191	}
192
193	outcslow('\'', file);
194	outstr(p, file);
195	outcslow('\'', file);
196}
197
198void
199outbin(const void *data, size_t len, struct output *file)
200{
201	const char *p;
202
203	p = data;
204	while (len-- > 0)
205		outc(*p++, file);
206}
207
208void
209emptyoutbuf(struct output *dest)
210{
211	int offset;
212
213	if (dest->buf == NULL) {
214		INTOFF;
215		dest->buf = ckmalloc(dest->bufsize);
216		dest->nextc = dest->buf;
217		dest->nleft = dest->bufsize;
218		INTON;
219	} else if (dest->fd == MEM_OUT) {
220		offset = dest->bufsize;
221		INTOFF;
222		dest->bufsize <<= 1;
223		dest->buf = ckrealloc(dest->buf, dest->bufsize);
224		dest->nleft = dest->bufsize - offset;
225		dest->nextc = dest->buf + offset;
226		INTON;
227	} else {
228		flushout(dest);
229	}
230	dest->nleft--;
231}
232
233
234void
235flushall(void)
236{
237	flushout(&output);
238	flushout(&errout);
239}
240
241
242void
243flushout(struct output *dest)
244{
245
246	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
247		return;
248	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
249		dest->flags |= OUTPUT_ERR;
250	dest->nextc = dest->buf;
251	dest->nleft = dest->bufsize;
252}
253
254
255void
256freestdout(void)
257{
258	INTOFF;
259	if (output.buf) {
260		ckfree(output.buf);
261		output.buf = NULL;
262		output.nleft = 0;
263	}
264	INTON;
265}
266
267
268int
269outiserror(struct output *file)
270{
271	return (file->flags & OUTPUT_ERR);
272}
273
274
275void
276outclearerror(struct output *file)
277{
278	file->flags &= ~OUTPUT_ERR;
279}
280
281
282void
283outfmt(struct output *file, const char *fmt, ...)
284{
285	va_list ap;
286
287	va_start(ap, fmt);
288	doformat(file, fmt, ap);
289	va_end(ap);
290}
291
292
293void
294out1fmt(const char *fmt, ...)
295{
296	va_list ap;
297
298	va_start(ap, fmt);
299	doformat(out1, fmt, ap);
300	va_end(ap);
301}
302
303void
304out2fmt_flush(const char *fmt, ...)
305{
306	va_list ap;
307
308	va_start(ap, fmt);
309	doformat(out2, fmt, ap);
310	va_end(ap);
311	flushout(out2);
312}
313
314void
315fmtstr(char *outbuf, int length, const char *fmt, ...)
316{
317	va_list ap;
318
319	INTOFF;
320	va_start(ap, fmt);
321	vsnprintf(outbuf, length, fmt, ap);
322	va_end(ap);
323	INTON;
324}
325
326static int
327doformat_wr(void *cookie, const char *buf, int len)
328{
329	struct output *o;
330
331	o = (struct output *)cookie;
332	outbin(buf, len, o);
333
334	return (len);
335}
336
337void
338doformat(struct output *dest, const char *f, va_list ap)
339{
340	FILE *fp;
341
342	if ((fp = fwopen(dest, doformat_wr)) != NULL) {
343		vfprintf(fp, f, ap);
344		fclose(fp);
345	}
346}
347
348/*
349 * Version of write which resumes after a signal is caught.
350 */
351
352int
353xwrite(int fd, const char *buf, int nbytes)
354{
355	int ntry;
356	int i;
357	int n;
358
359	n = nbytes;
360	ntry = 0;
361	for (;;) {
362		i = write(fd, buf, n);
363		if (i > 0) {
364			if ((n -= i) <= 0)
365				return nbytes;
366			buf += i;
367			ntry = 0;
368		} else if (i == 0) {
369			if (++ntry > 10)
370				return nbytes - n;
371		} else if (errno != EINTR) {
372			return -1;
373		}
374	}
375}
376