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