1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1997-2005
5 * Herbert Xu <herbert@gondor.apana.org.au>. 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 * Our output routines may be smaller than the stdio routines.
44 */
45
46 #include <sys/types.h> /* quad_t */
47 #include <sys/param.h> /* BSD4_4 */
48 #include <sys/ioctl.h>
49
50 #include <stdio.h> /* defines BUFSIZ */
51 #include <string.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #ifdef USE_GLIBC_STDIO
55 #include <fcntl.h>
56 #endif
57 #include <limits.h>
58
59 #include "shell.h"
60 #include "syntax.h"
61 #include "options.h"
62 #include "output.h"
63 #include "memalloc.h"
64 #include "error.h"
65 #include "main.h"
66 #include "system.h"
67
68
69 #define OUTBUFSIZ BUFSIZ
70 #define MEM_OUT -3 /* output to dynamically allocated memory */
71
72
73 #ifdef USE_GLIBC_STDIO
74 struct output output = {
75 stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 1, flags: 0
76 };
77 struct output errout = {
78 stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
79 }
80 #ifdef notyet
81 struct output memout = {
82 stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
83 };
84 #endif
85 #else
86 struct output output = {
87 nextc: 0, end: 0, buf: 0, bufsize: OUTBUFSIZ, fd: 1, flags: 0
88 };
89 struct output errout = {
90 nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
91 };
92 struct output preverrout;
93 #ifdef notyet
94 struct output memout = {
95 nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
96 };
97 #endif
98 #endif
99 struct output *out1 = &output;
100 struct output *out2 = &errout;
101
102
103 static int xvsnprintf(char *, size_t, const char *, va_list);
104
105
106 #ifdef mkinit
107
108 INCLUDE "output.h"
109 INCLUDE "memalloc.h"
110
111 INIT {
112 #ifdef USE_GLIBC_STDIO
113 initstreams();
114 #endif
115 }
116
117 RESET {
118 #ifdef notyet
119 out1 = &output;
120 out2 = &errout;
121 #ifdef USE_GLIBC_STDIO
122 if (memout.stream != NULL)
123 __closememout();
124 #endif
125 if (memout.buf != NULL) {
126 ckfree(memout.buf);
127 memout.buf = NULL;
128 }
129 #endif
130 }
131
132 #endif
133
134
135 void
outmem(const char * p,size_t len,struct output * dest)136 outmem(const char *p, size_t len, struct output *dest)
137 {
138 #ifdef USE_GLIBC_STDIO
139 INTOFF;
140 fwrite(p, 1, len, dest->stream);
141 INTON;
142 #else
143 size_t bufsize;
144 size_t offset;
145 size_t nleft;
146
147 nleft = dest->end - dest->nextc;
148 if (likely(nleft >= len)) {
149 buffered:
150 dest->nextc = mempcpy(dest->nextc, p, len);
151 return;
152 }
153
154 bufsize = dest->bufsize;
155 if (!bufsize) {
156 ;
157 } else if (dest->buf == NULL) {
158 #ifdef notyet
159 if (dest->fd == MEM_OUT && len > bufsize) {
160 bufsize = len;
161 }
162 #endif
163 offset = 0;
164 #ifdef notyet
165 goto alloc;
166 } else if (dest->fd == MEM_OUT) {
167 offset = bufsize;
168 if (bufsize >= len) {
169 bufsize <<= 1;
170 } else {
171 bufsize += len;
172 }
173 if (bufsize < offset)
174 goto err;
175 alloc:
176 #endif
177 INTOFF;
178 dest->buf = ckrealloc(dest->buf, bufsize);
179 dest->bufsize = bufsize;
180 dest->end = dest->buf + bufsize;
181 dest->nextc = dest->buf + offset;
182 INTON;
183 } else {
184 flushout(dest);
185 }
186
187 nleft = dest->end - dest->nextc;
188 if (nleft > len)
189 goto buffered;
190
191 if ((xwrite(dest->fd, p, len))) {
192 #ifdef notyet
193 err:
194 #endif
195 dest->flags |= OUTPUT_ERR;
196 }
197 #endif
198 }
199
200
201 void
outstr(const char * p,struct output * file)202 outstr(const char *p, struct output *file)
203 {
204 #ifdef USE_GLIBC_STDIO
205 INTOFF;
206 fputs(p, file->stream);
207 INTON;
208 #else
209 size_t len;
210
211 len = strlen(p);
212 outmem(p, len, file);
213 #endif
214 }
215
216
217 #ifndef USE_GLIBC_STDIO
218
219
220 void
outcslow(int c,struct output * dest)221 outcslow(int c, struct output *dest)
222 {
223 char buf = c;
224 outmem(&buf, 1, dest);
225 }
226 #endif
227
228
229 void
flushall(void)230 flushall(void)
231 {
232 flushout(&output);
233 #ifdef FLUSHERR
234 flushout(&errout);
235 #endif
236 }
237
238
239 void
flushout(struct output * dest)240 flushout(struct output *dest)
241 {
242 #ifdef USE_GLIBC_STDIO
243 INTOFF;
244 fflush(dest->stream);
245 INTON;
246 #else
247 size_t len;
248
249 len = dest->nextc - dest->buf;
250 if (!len || dest->fd < 0)
251 return;
252 dest->nextc = dest->buf;
253 if ((xwrite(dest->fd, dest->buf, len)))
254 dest->flags |= OUTPUT_ERR;
255 #endif
256 }
257
258
259 void
outfmt(struct output * file,const char * fmt,...)260 outfmt(struct output *file, const char *fmt, ...)
261 {
262 va_list ap;
263
264 va_start(ap, fmt);
265 doformat(file, fmt, ap);
266 va_end(ap);
267 }
268
269
270 void
out1fmt(const char * fmt,...)271 out1fmt(const char *fmt, ...)
272 {
273 va_list ap;
274
275 va_start(ap, fmt);
276 doformat(out1, fmt, ap);
277 va_end(ap);
278 }
279
280
281 int
fmtstr(char * outbuf,size_t length,const char * fmt,...)282 fmtstr(char *outbuf, size_t length, const char *fmt, ...)
283 {
284 va_list ap;
285 int ret;
286
287 va_start(ap, fmt);
288 ret = xvsnprintf(outbuf, length, fmt, ap);
289 va_end(ap);
290 return ret;
291 }
292
293
xvasprintf(char ** sp,size_t size,const char * f,va_list ap)294 static int xvasprintf(char **sp, size_t size, const char *f, va_list ap)
295 {
296 char *s;
297 int len;
298 va_list ap2;
299
300 va_copy(ap2, ap);
301 len = xvsnprintf(*sp, size, f, ap2);
302 va_end(ap2);
303 if (len < 0)
304 sh_error("xvsnprintf failed");
305 if (len < size)
306 return len;
307
308 s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1);
309 *sp = s;
310 len = xvsnprintf(s, len + 1, f, ap);
311 return len;
312 }
313
314
xasprintf(char ** sp,const char * f,...)315 int xasprintf(char **sp, const char *f, ...)
316 {
317 va_list ap;
318 int ret;
319
320 va_start(ap, f);
321 ret = xvasprintf(sp, 0, f, ap);
322 va_end(ap);
323 return ret;
324 }
325
326
327 #ifndef USE_GLIBC_STDIO
328 void
doformat(struct output * dest,const char * f,va_list ap)329 doformat(struct output *dest, const char *f, va_list ap)
330 {
331 struct stackmark smark;
332 char *s;
333 int len;
334 int olen;
335
336 setstackmark(&smark);
337 s = dest->nextc;
338 olen = dest->end - dest->nextc;
339 len = xvasprintf(&s, olen, f, ap);
340 if (likely(olen > len)) {
341 dest->nextc += len;
342 goto out;
343 }
344 outmem(s, len, dest);
345 out:
346 popstackmark(&smark);
347 }
348 #endif
349
350
settitle(const char * title)351 void settitle(const char* title) {
352 if (!iflag || !isatty(0))
353 return;
354 char *term = getenv("TERM");
355 if (term == NULL || strcmp(term, "dumb") == 0 || strcmp(term, "uart") == 0) {
356 return;
357 }
358 char str[16];
359 int n = snprintf(str, sizeof(str) - 1, "\033]2;%s", title);
360 if (n < 0) {
361 return; // error
362 } else if ((size_t)n >= sizeof(str) - 1) {
363 n = sizeof(str) - 2; // truncated
364 }
365 str[n] = '\007';
366 str[n+1] = '\0';
367 out2str(str);
368 }
369
370
371 /*
372 * Version of write which resumes after a signal is caught.
373 */
374
375 int
xwrite(int fd,const void * p,size_t n)376 xwrite(int fd, const void *p, size_t n)
377 {
378 const char *buf = p;
379
380 while (n) {
381 ssize_t i;
382 size_t m;
383
384 m = n;
385 if (m > SSIZE_MAX)
386 m = SSIZE_MAX;
387 do {
388 i = write(fd, buf, m);
389 } while (i < 0 && errno == EINTR);
390 if (i < 0)
391 return -1;
392 buf += i;
393 n -= i;
394 }
395 return 0;
396 }
397
398
399 #ifdef notyet
400 #ifdef USE_GLIBC_STDIO
initstreams()401 void initstreams() {
402 output.stream = stdout;
403 errout.stream = stderr;
404 }
405
406
407 void
openmemout(void)408 openmemout(void) {
409 INTOFF;
410 memout.stream = open_memstream(&memout.buf, &memout.bufsize);
411 INTON;
412 }
413
414
415 int
__closememout(void)416 __closememout(void) {
417 int error;
418 error = fclose(memout.stream);
419 memout.stream = NULL;
420 return error;
421 }
422 #endif
423 #endif
424
425
426 static int
xvsnprintf(char * outbuf,size_t length,const char * fmt,va_list ap)427 xvsnprintf(char *outbuf, size_t length, const char *fmt, va_list ap)
428 {
429 int ret;
430
431 #ifdef __sun
432 /*
433 * vsnprintf() on older versions of Solaris returns -1 when
434 * passed a length of 0. To avoid this, use a dummy
435 * 1-character buffer instead.
436 */
437 char dummy[1];
438
439 if (length == 0) {
440 outbuf = dummy;
441 length = sizeof(dummy);
442 }
443 #endif
444
445 INTOFF;
446 ret = vsnprintf(outbuf, length, fmt, ap);
447 INTON;
448 return ret;
449 }
450