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