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