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