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}