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