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