output.c revision 1.38 1 /* $NetBSD: output.c,v 1.38 2017/11/19 03:22:55 kre Exp $ */
2
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 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: output.c,v 1.38 2017/11/19 03:22:55 kre Exp $");
41 #endif
42 #endif /* not lint */
43
44 /*
45 * Shell output routines. We use our own output routines because:
46 * When a builtin command is interrupted we have to discard
47 * any pending output.
48 * When a builtin command appears in back quotes, we want to
49 * save the output of the command in a region obtained
50 * via malloc, rather than doing a fork and reading the
51 * output of the command via a pipe.
52 * Our output routines may be smaller than the stdio routines.
53 */
54
55 #include <sys/types.h> /* quad_t */
56 #include <sys/param.h> /* BSD4_4 */
57 #include <sys/ioctl.h>
58
59 #include <stdio.h> /* defines BUFSIZ */
60 #include <string.h>
61 #include <errno.h>
62 #include <unistd.h>
63 #include <stdlib.h>
64
65 #include "shell.h"
66 #include "syntax.h"
67 #include "output.h"
68 #include "memalloc.h"
69 #include "error.h"
70
71
72 #define OUTBUFSIZ BUFSIZ
73 #define BLOCK_OUT -2 /* output to a fixed block of memory */
74 #define MEM_OUT -3 /* output to dynamically allocated memory */
75
76
77 struct output output = {NULL, 0, OUTBUFSIZ, NULL, 1, 0};
78 struct output errout = {NULL, 0, 100, NULL, 2, 0};
79 struct output memout = {NULL, 0, 0, NULL, MEM_OUT, 0};
80 struct output *out1 = &output;
81 struct output *out2 = &errout;
82
83
84
85 #ifdef mkinit
86
87 INCLUDE "output.h"
88 INCLUDE "memalloc.h"
89
90 RESET {
91 out1 = &output;
92 out2 = &errout;
93 if (memout.buf != NULL) {
94 ckfree(memout.buf);
95 memout.buf = NULL;
96 }
97 }
98
99 #endif
100
101
102 #ifdef notdef /* no longer used */
103 /*
104 * Set up an output file to write to memory rather than a file.
105 */
106
107 void
108 open_mem(char *block, int length, struct output *file)
109 {
110 file->nextc = block;
111 file->nleft = --length;
112 file->fd = BLOCK_OUT;
113 file->flags = 0;
114 }
115 #endif
116
117
118 void
119 out1str(const char *p)
120 {
121 outstr(p, out1);
122 }
123
124
125 void
126 out2str(const char *p)
127 {
128 outstr(p, out2);
129 }
130
131
132 void
133 outstr(const char *p, struct output *file)
134 {
135 while (*p)
136 outc(*p++, file);
137 if (file == out2)
138 flushout(file);
139 }
140
141
142 void
143 out2shstr(const char *p)
144 {
145 outshstr(p, out2);
146 }
147
148
149 /*
150 * ' is in this list, not because it does not require quoting
151 * (which applies to all the others) but because '' quoting cannot
152 * be used to quote it.
153 */
154 static const char norm_chars [] = \
155 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+-=_,.'";
156
157 static int
158 inquote(const char *p)
159 {
160 size_t l = strspn(p, norm_chars);
161 char *s = strchr(p, '\'');
162
163 return s == NULL ? p[l] != '\0' : s - p > (off_t)l;
164 }
165
166 void
167 outshstr(const char *p, struct output *file)
168 {
169 int need_q;
170 int inq;
171 char c;
172
173 if (strchr(p, '\'') != NULL && p[1] != '\0') {
174 /*
175 * string contains ' in it, and is not only the '
176 * see if " quoting will work
177 */
178 size_t i = strcspn(p, "\\\"$`");
179
180 if (p[i] == '\0') {
181 /*
182 * string contains no $ ` \ or " chars, perfect...
183 *
184 * We know it contains ' so needs quoting, so
185 * this is easy...
186 */
187 outc('"', file);
188 outstr(p, file);
189 outc('"', file);
190 return;
191 }
192 }
193
194 need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0;
195
196 /*
197 * Don't emit ' unless something needs quoting before closing '
198 */
199 if (need_q && (p[0] == 0 || inquote(p) != 0)) {
200 outc('\'', file);
201 inq = 1;
202 } else
203 inq = 0;
204
205 while ((c = *p++) != '\0') {
206 if (c != '\'') {
207 outc(c, file);
208 continue;
209 }
210
211 if (inq)
212 outc('\'', file); /* inq = 0, implicit */
213 outc('\\', file);
214 outc(c, file);
215 if (need_q && *p != '\0') {
216 if ((inq = inquote(p)) != 0)
217 outc('\'', file);
218 } else
219 inq = 0;
220 }
221
222 if (inq)
223 outc('\'', file);
224
225 if (file == out2)
226 flushout(file);
227 }
228
229
230 char out_junk[16];
231
232
233 void
234 emptyoutbuf(struct output *dest)
235 {
236 int offset;
237
238 if (dest->fd == BLOCK_OUT) {
239 dest->nextc = out_junk;
240 dest->nleft = sizeof out_junk;
241 dest->flags |= OUTPUT_ERR;
242 } else if (dest->buf == NULL) {
243 INTOFF;
244 dest->buf = ckmalloc(dest->bufsize);
245 dest->nextc = dest->buf;
246 dest->nleft = dest->bufsize;
247 INTON;
248 } else if (dest->fd == MEM_OUT) {
249 offset = dest->bufsize;
250 INTOFF;
251 dest->bufsize <<= 1;
252 dest->buf = ckrealloc(dest->buf, dest->bufsize);
253 dest->nleft = dest->bufsize - offset;
254 dest->nextc = dest->buf + offset;
255 INTON;
256 } else {
257 flushout(dest);
258 }
259 dest->nleft--;
260 }
261
262
263 void
264 flushall(void)
265 {
266 flushout(&output);
267 flushout(&errout);
268 }
269
270
271 void
272 flushout(struct output *dest)
273 {
274
275 if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
276 return;
277 if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
278 dest->flags |= OUTPUT_ERR;
279 dest->nextc = dest->buf;
280 dest->nleft = dest->bufsize;
281 }
282
283
284 void
285 freestdout(void)
286 {
287 INTOFF;
288 if (output.buf) {
289 ckfree(output.buf);
290 output.buf = NULL;
291 output.nleft = 0;
292 }
293 INTON;
294 }
295
296
297 void
298 outfmt(struct output *file, const char *fmt, ...)
299 {
300 va_list ap;
301
302 va_start(ap, fmt);
303 doformat(file, fmt, ap);
304 va_end(ap);
305 }
306
307
308 void
309 out1fmt(const char *fmt, ...)
310 {
311 va_list ap;
312
313 va_start(ap, fmt);
314 doformat(out1, fmt, ap);
315 va_end(ap);
316 }
317
318 #ifdef DEBUG
319 void
320 debugprintf(const char *fmt, ...)
321 {
322 va_list ap;
323
324 va_start(ap, fmt);
325 doformat(out2, fmt, ap);
326 va_end(ap);
327 flushout(out2);
328 }
329 #endif
330
331 void
332 fmtstr(char *outbuf, size_t length, const char *fmt, ...)
333 {
334 va_list ap;
335 struct output strout;
336
337 va_start(ap, fmt);
338 strout.nextc = outbuf;
339 strout.nleft = length;
340 strout.fd = BLOCK_OUT;
341 strout.flags = 0;
342 doformat(&strout, fmt, ap);
343 outc('\0', &strout);
344 if (strout.flags & OUTPUT_ERR)
345 outbuf[length - 1] = '\0';
346 va_end(ap);
347 }
348
349 /*
350 * Formatted output. This routine handles a subset of the printf formats:
351 * - Formats supported: d, u, o, p, X, s, and c.
352 * - The x format is also accepted but is treated like X.
353 * - The l, ll and q modifiers are accepted.
354 * - The - and # flags are accepted; # only works with the o format.
355 * - Width and precision may be specified with any format except c.
356 * - An * may be given for the width or precision.
357 * - The obsolete practice of preceding the width with a zero to get
358 * zero padding is not supported; use the precision field.
359 * - A % may be printed by writing %% in the format string.
360 */
361
362 #define TEMPSIZE 24
363
364 #ifdef BSD4_4
365 #define HAVE_VASPRINTF 1
366 #endif
367
368 void
369 doformat(struct output *dest, const char *f, va_list ap)
370 {
371 #if HAVE_VASPRINTF
372 char *s;
373
374 vasprintf(&s, f, ap);
375 if (s == NULL)
376 error("Could not allocate formatted output buffer");
377 outstr(s, dest);
378 free(s);
379 #else /* !HAVE_VASPRINTF */
380 static const char digit[] = "0123456789ABCDEF";
381 char c;
382 char temp[TEMPSIZE];
383 int flushleft;
384 int sharp;
385 int width;
386 int prec;
387 int islong;
388 int isquad;
389 char *p;
390 int sign;
391 #ifdef BSD4_4
392 quad_t l;
393 u_quad_t num;
394 #else
395 long l;
396 u_long num;
397 #endif
398 unsigned base;
399 int len;
400 int size;
401 int pad;
402
403 while ((c = *f++) != '\0') {
404 if (c != '%') {
405 outc(c, dest);
406 continue;
407 }
408 flushleft = 0;
409 sharp = 0;
410 width = 0;
411 prec = -1;
412 islong = 0;
413 isquad = 0;
414 for (;;) {
415 if (*f == '-')
416 flushleft++;
417 else if (*f == '#')
418 sharp++;
419 else
420 break;
421 f++;
422 }
423 if (*f == '*') {
424 width = va_arg(ap, int);
425 f++;
426 } else {
427 while (is_digit(*f)) {
428 width = 10 * width + digit_val(*f++);
429 }
430 }
431 if (*f == '.') {
432 if (*++f == '*') {
433 prec = va_arg(ap, int);
434 f++;
435 } else {
436 prec = 0;
437 while (is_digit(*f)) {
438 prec = 10 * prec + digit_val(*f++);
439 }
440 }
441 }
442 if (*f == 'l') {
443 f++;
444 if (*f == 'l') {
445 isquad++;
446 f++;
447 } else
448 islong++;
449 } else if (*f == 'q') {
450 isquad++;
451 f++;
452 }
453 switch (*f) {
454 case 'd':
455 #ifdef BSD4_4
456 if (isquad)
457 l = va_arg(ap, quad_t);
458 else
459 #endif
460 if (islong)
461 l = va_arg(ap, long);
462 else
463 l = va_arg(ap, int);
464 sign = 0;
465 num = l;
466 if (l < 0) {
467 num = -l;
468 sign = 1;
469 }
470 base = 10;
471 goto number;
472 case 'u':
473 base = 10;
474 goto uns_number;
475 case 'o':
476 base = 8;
477 goto uns_number;
478 case 'p':
479 outc('0', dest);
480 outc('x', dest);
481 /*FALLTHROUGH*/
482 case 'x':
483 /* we don't implement 'x'; treat like 'X' */
484 case 'X':
485 base = 16;
486 uns_number: /* an unsigned number */
487 sign = 0;
488 #ifdef BSD4_4
489 if (isquad)
490 num = va_arg(ap, u_quad_t);
491 else
492 #endif
493 if (islong)
494 num = va_arg(ap, unsigned long);
495 else
496 num = va_arg(ap, unsigned int);
497 number: /* process a number */
498 p = temp + TEMPSIZE - 1;
499 *p = '\0';
500 while (num) {
501 *--p = digit[num % base];
502 num /= base;
503 }
504 len = (temp + TEMPSIZE - 1) - p;
505 if (prec < 0)
506 prec = 1;
507 if (sharp && *f == 'o' && prec <= len)
508 prec = len + 1;
509 pad = 0;
510 if (width) {
511 size = len;
512 if (size < prec)
513 size = prec;
514 size += sign;
515 pad = width - size;
516 if (flushleft == 0) {
517 while (--pad >= 0)
518 outc(' ', dest);
519 }
520 }
521 if (sign)
522 outc('-', dest);
523 prec -= len;
524 while (--prec >= 0)
525 outc('0', dest);
526 while (*p)
527 outc(*p++, dest);
528 while (--pad >= 0)
529 outc(' ', dest);
530 break;
531 case 's':
532 p = va_arg(ap, char *);
533 pad = 0;
534 if (width) {
535 len = strlen(p);
536 if (prec >= 0 && len > prec)
537 len = prec;
538 pad = width - len;
539 if (flushleft == 0) {
540 while (--pad >= 0)
541 outc(' ', dest);
542 }
543 }
544 prec++;
545 while (--prec != 0 && *p)
546 outc(*p++, dest);
547 while (--pad >= 0)
548 outc(' ', dest);
549 break;
550 case 'c':
551 c = va_arg(ap, int);
552 outc(c, dest);
553 break;
554 default:
555 outc(*f, dest);
556 break;
557 }
558 f++;
559 }
560 #endif /* !HAVE_VASPRINTF */
561 }
562
563
564
565 /*
566 * Version of write which resumes after a signal is caught.
567 */
568
569 int
570 xwrite(int fd, char *buf, int nbytes)
571 {
572 int ntry;
573 int i;
574 int n;
575
576 n = nbytes;
577 ntry = 0;
578 while (n > 0) {
579 i = write(fd, buf, n);
580 if (i > 0) {
581 if ((n -= i) <= 0)
582 return nbytes;
583 buf += i;
584 ntry = 0;
585 } else if (i == 0) {
586 if (++ntry > 10)
587 return nbytes - n;
588 } else if (errno != EINTR) {
589 return -1;
590 }
591 }
592 return nbytes;
593 }
594
595
596 /*
597 * Version of ioctl that retries after a signal is caught.
598 * XXX unused function
599 */
600
601 int
602 xioctl(int fd, unsigned long request, char *arg)
603 {
604 int i;
605
606 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
607 return i;
608 }
609