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