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