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