master xplshn/aruu / cmd / posix / sh / output.c
  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/*
 36 * Shell output routines.  We use our own output routines because:
 37 *	When a builtin command is interrupted we have to discard
 38 *		any pending output.
 39 *	When a builtin command appears in back quotes, we want to
 40 *		save the output of the command in a region obtained
 41 *		via malloc, rather than doing a fork and reading the
 42 *		output of the command via a pipe.
 43 */
 44
 45#include <stdio.h>	/* defines BUFSIZ */
 46#include <string.h>
 47#include <stdarg.h>
 48#include <errno.h>
 49#include <unistd.h>
 50#include <stdlib.h>
 51#include <wchar.h>
 52#include <wctype.h>
 53
 54#if defined(__linux__) || defined(__CYGWIN__)
 55struct fwopen_cookie {
 56	void *cookie;
 57	int (*writefn)(void *, const char *, int);
 58};
 59
 60static ssize_t
 61compat_fwopen_write(void *cookie, const char *buf, size_t size)
 62{
 63	struct fwopen_cookie *c = cookie;
 64	return c->writefn(c->cookie, buf, (int)size);
 65}
 66
 67static int
 68compat_fwopen_close(void *cookie)
 69{
 70	free(cookie);
 71	return 0;
 72}
 73
 74static FILE *
 75fwopen(void *cookie, int (*writefn)(void *, const char *, int))
 76{
 77	struct fwopen_cookie *c;
 78	cookie_io_functions_t io_funcs = {
 79		.read = NULL,
 80		.write = compat_fwopen_write,
 81		.seek = NULL,
 82		.close = compat_fwopen_close
 83	};
 84	FILE *fp;
 85
 86	c = malloc(sizeof(struct fwopen_cookie));
 87	if (c == NULL)
 88		return NULL;
 89	c->cookie = cookie;
 90	c->writefn = writefn;
 91
 92	fp = fopencookie(c, "w", io_funcs);
 93	if (fp == NULL) {
 94		free(c);
 95		return NULL;
 96	}
 97	return fp;
 98}
 99#endif
100
101#include "shell.h"
102#include "syntax.h"
103#include "output.h"
104#include "memalloc.h"
105#include "error.h"
106#include "var.h"
107
108
109#define OUTBUFSIZ BUFSIZ
110#define MEM_OUT -2		/* output to dynamically allocated memory */
111#define OUTPUT_ERR 01		/* error occurred on output */
112
113static int doformat_wr(void *, const char *, int);
114
115struct output output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0};
116struct output errout = {NULL, NULL, NULL, 256, 2, 0};
117struct output memout = {NULL, NULL, NULL, 64, MEM_OUT, 0};
118struct output *out1 = &output;
119struct output *out2 = &errout;
120
121void
122outcslow(int c, struct output *file)
123{
124	outc(c, file);
125}
126
127void
128out1str(const char *p)
129{
130	outstr(p, out1);
131}
132
133void
134out1qstr(const char *p)
135{
136	outqstr(p, out1);
137}
138
139void
140out2str(const char *p)
141{
142	outstr(p, out2);
143}
144
145void
146out2qstr(const char *p)
147{
148	outqstr(p, out2);
149}
150
151void
152outstr(const char *p, struct output *file)
153{
154	outbin(p, strlen(p), file);
155}
156
157static void
158byteseq(int ch, struct output *file)
159{
160	char seq[4];
161
162	seq[0] = '\\';
163	seq[1] = (ch >> 6 & 0x3) + '0';
164	seq[2] = (ch >> 3 & 0x7) + '0';
165	seq[3] = (ch & 0x7) + '0';
166	outbin(seq, 4, file);
167}
168
169static void
170outdqstr(const char *p, struct output *file)
171{
172	const char *end;
173	mbstate_t mbs;
174	size_t clen;
175	wchar_t wc;
176
177	memset(&mbs, '\0', sizeof(mbs));
178	end = p + strlen(p);
179	outstr("$'", file);
180	while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
181		if (clen == (size_t)-2) {
182			while (p < end)
183				byteseq(*p++, file);
184			break;
185		}
186		if (clen == (size_t)-1) {
187			memset(&mbs, '\0', sizeof(mbs));
188			byteseq(*p++, file);
189			continue;
190		}
191		if (wc == L'\n')
192			outcslow('\n', file), p++;
193		else if (wc == L'\r')
194			outstr("\\r", file), p++;
195		else if (wc == L'\t')
196			outstr("\\t", file), p++;
197		else if (!iswprint(wc)) {
198			for (; clen > 0; clen--)
199				byteseq(*p++, file);
200		} else {
201			if (wc == L'\'' || wc == L'\\')
202				outcslow('\\', file);
203			outbin(p, clen, file);
204			p += clen;
205		}
206	}
207	outcslow('\'', file);
208}
209
210/* Like outstr(), but quote for re-input into the shell. */
211void
212outqstr(const char *p, struct output *file)
213{
214	int i;
215
216	if (p[0] == '\0') {
217		outstr("''", file);
218		return;
219	}
220	for (i = 0; p[i] != '\0'; i++) {
221		if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
222		    (p[i] & 0x80) != 0 || p[i] == '\'') {
223			outdqstr(p, file);
224			return;
225		}
226	}
227
228	if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
229			strcmp(p, "[") == 0) {
230		outstr(p, file);
231		return;
232	}
233
234	outcslow('\'', file);
235	outstr(p, file);
236	outcslow('\'', file);
237}
238
239void
240outbin(const void *data, size_t len, struct output *file)
241{
242	const char *p;
243
244	p = data;
245	while (len-- > 0)
246		outc(*p++, file);
247}
248
249void
250emptyoutbuf(struct output *dest)
251{
252	int offset, newsize;
253
254	if (dest->buf == NULL) {
255		INTOFF;
256		dest->buf = ckmalloc(dest->bufsize);
257		dest->nextc = dest->buf;
258		dest->bufend = dest->buf + dest->bufsize;
259		INTON;
260	} else if (dest->fd == MEM_OUT) {
261		offset = dest->nextc - dest->buf;
262		newsize = dest->bufsize << 1;
263		INTOFF;
264		dest->buf = ckrealloc(dest->buf, newsize);
265		dest->bufsize = newsize;
266		dest->bufend = dest->buf + newsize;
267		dest->nextc = dest->buf + offset;
268		INTON;
269	} else {
270		flushout(dest);
271	}
272}
273
274
275void
276flushall(void)
277{
278	flushout(&output);
279	flushout(&errout);
280}
281
282
283void
284flushout(struct output *dest)
285{
286
287	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
288		return;
289	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
290		dest->flags |= OUTPUT_ERR;
291	dest->nextc = dest->buf;
292}
293
294
295void
296freestdout(void)
297{
298	output.nextc = output.buf;
299}
300
301
302int
303outiserror(struct output *file)
304{
305	return (file->flags & OUTPUT_ERR);
306}
307
308
309void
310outclearerror(struct output *file)
311{
312	file->flags &= ~OUTPUT_ERR;
313}
314
315
316void
317outfmt(struct output *file, const char *fmt, ...)
318{
319	va_list ap;
320
321	va_start(ap, fmt);
322	doformat(file, fmt, ap);
323	va_end(ap);
324}
325
326
327void
328out1fmt(const char *fmt, ...)
329{
330	va_list ap;
331
332	va_start(ap, fmt);
333	doformat(out1, fmt, ap);
334	va_end(ap);
335}
336
337void
338out2fmt_flush(const char *fmt, ...)
339{
340	va_list ap;
341
342	va_start(ap, fmt);
343	doformat(out2, fmt, ap);
344	va_end(ap);
345	flushout(out2);
346}
347
348void
349fmtstr(char *outbuf, int length, const char *fmt, ...)
350{
351	va_list ap;
352
353	INTOFF;
354	va_start(ap, fmt);
355	vsnprintf(outbuf, length, fmt, ap);
356	va_end(ap);
357	INTON;
358}
359
360static int
361doformat_wr(void *cookie, const char *buf, int len)
362{
363	struct output *o;
364
365	o = (struct output *)cookie;
366	outbin(buf, len, o);
367
368	return (len);
369}
370
371void
372doformat(struct output *dest, const char *f, va_list ap)
373{
374	FILE *fp;
375
376	if ((fp = fwopen(dest, doformat_wr)) != NULL) {
377		vfprintf(fp, f, ap);
378		fclose(fp);
379	}
380}
381
382FILE *
383out1fp(void)
384{
385	return fwopen(out1, doformat_wr);
386}
387
388/*
389 * Version of write which resumes after a signal is caught.
390 */
391
392int
393xwrite(int fd, const char *buf, int nbytes)
394{
395	int ntry;
396	int i;
397	int n;
398
399	n = nbytes;
400	ntry = 0;
401	for (;;) {
402		i = write(fd, buf, n);
403		if (i > 0) {
404			if ((n -= i) <= 0)
405				return nbytes;
406			buf += i;
407			ntry = 0;
408		} else if (i == 0) {
409			if (++ntry > 10)
410				return nbytes - n;
411		} else if (errno != EINTR) {
412			return -1;
413		}
414	}
415}