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