Commit | Line | Data |
---|---|---|
3636ea79 | 1 | /*- |
d1b73048 KB |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
3636ea79 KB |
4 | * |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Kenneth Almquist. | |
7 | * | |
ad787160 C |
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. | |
3636ea79 KB |
35 | */ |
36 | ||
37 | #ifndef lint | |
ad787160 | 38 | static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 5/31/93"; |
3636ea79 KB |
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); | |
f776ece4 MT |
139 | if (file == out2) |
140 | flushout(file); | |
3636ea79 KB |
141 | } |
142 | ||
143 | ||
144 | char out_junk[16]; | |
145 | ||
146 | ||
147 | void | |
148 | emptyoutbuf(dest) | |
149 | struct output *dest; | |
150 | { | |
151 | int offset; | |
152 | ||
153 | if (dest->fd == BLOCK_OUT) { | |
154 | dest->nextc = out_junk; | |
155 | dest->nleft = sizeof out_junk; | |
156 | dest->flags |= OUTPUT_ERR; | |
157 | } else if (dest->buf == NULL) { | |
158 | INTOFF; | |
159 | dest->buf = ckmalloc(dest->bufsize); | |
160 | dest->nextc = dest->buf; | |
161 | dest->nleft = dest->bufsize; | |
162 | INTON; | |
163 | } else if (dest->fd == MEM_OUT) { | |
164 | offset = dest->bufsize; | |
165 | INTOFF; | |
166 | dest->bufsize <<= 1; | |
167 | dest->buf = ckrealloc(dest->buf, dest->bufsize); | |
168 | dest->nleft = dest->bufsize - offset; | |
169 | dest->nextc = dest->buf + offset; | |
170 | INTON; | |
171 | } else { | |
172 | flushout(dest); | |
173 | } | |
174 | dest->nleft--; | |
175 | } | |
176 | ||
177 | ||
178 | void | |
179 | flushall() { | |
180 | flushout(&output); | |
181 | flushout(&errout); | |
182 | } | |
183 | ||
184 | ||
185 | void | |
186 | flushout(dest) | |
187 | struct output *dest; | |
188 | { | |
189 | ||
190 | if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) | |
191 | return; | |
192 | if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) | |
193 | dest->flags |= OUTPUT_ERR; | |
194 | dest->nextc = dest->buf; | |
195 | dest->nleft = dest->bufsize; | |
196 | } | |
197 | ||
198 | ||
199 | void | |
200 | freestdout() { | |
201 | INTOFF; | |
202 | if (output.buf) { | |
203 | ckfree(output.buf); | |
204 | output.buf = NULL; | |
205 | output.nleft = 0; | |
206 | } | |
207 | INTON; | |
208 | } | |
209 | ||
210 | ||
211 | #ifdef __STDC__ | |
212 | void | |
213 | outfmt(struct output *file, char *fmt, ...) { | |
214 | va_list ap; | |
215 | ||
216 | va_start(ap, fmt); | |
217 | doformat(file, fmt, ap); | |
218 | va_end(ap); | |
219 | } | |
220 | ||
221 | ||
222 | void | |
223 | out1fmt(char *fmt, ...) { | |
224 | va_list ap; | |
225 | ||
226 | va_start(ap, fmt); | |
227 | doformat(out1, fmt, ap); | |
228 | va_end(ap); | |
229 | } | |
230 | ||
e6b17676 MT |
231 | void |
232 | dprintf(char *fmt, ...) { | |
233 | va_list ap; | |
234 | ||
235 | va_start(ap, fmt); | |
236 | doformat(out2, fmt, ap); | |
237 | va_end(ap); | |
238 | flushout(out2); | |
239 | } | |
3636ea79 KB |
240 | |
241 | void | |
242 | fmtstr(char *outbuf, int length, char *fmt, ...) { | |
243 | va_list ap; | |
244 | struct output strout; | |
245 | ||
246 | va_start(ap, fmt); | |
247 | strout.nextc = outbuf; | |
248 | strout.nleft = length; | |
249 | strout.fd = BLOCK_OUT; | |
250 | strout.flags = 0; | |
251 | doformat(&strout, fmt, ap); | |
252 | outc('\0', &strout); | |
253 | if (strout.flags & OUTPUT_ERR) | |
254 | outbuf[length - 1] = '\0'; | |
255 | } | |
256 | ||
257 | #else /* not __STDC__ */ | |
258 | ||
259 | void | |
260 | outfmt(va_alist) | |
261 | va_dcl | |
262 | { | |
263 | va_list ap; | |
264 | struct output *file; | |
265 | char *fmt; | |
266 | ||
267 | va_start(ap); | |
268 | file = va_arg(ap, struct output *); | |
269 | fmt = va_arg(ap, char *); | |
270 | doformat(file, fmt, ap); | |
271 | va_end(ap); | |
272 | } | |
273 | ||
274 | ||
275 | void | |
276 | out1fmt(va_alist) | |
277 | va_dcl | |
278 | { | |
279 | va_list ap; | |
280 | char *fmt; | |
281 | ||
282 | va_start(ap); | |
283 | fmt = va_arg(ap, char *); | |
284 | doformat(out1, fmt, ap); | |
285 | va_end(ap); | |
286 | } | |
287 | ||
e6b17676 MT |
288 | void |
289 | dprintf(va_alist) | |
290 | va_dcl | |
291 | { | |
292 | va_list ap; | |
293 | char *fmt; | |
294 | ||
295 | va_start(ap); | |
296 | fmt = va_arg(ap, char *); | |
297 | doformat(out2, fmt, ap); | |
298 | va_end(ap); | |
299 | flushout(out2); | |
300 | } | |
3636ea79 KB |
301 | |
302 | void | |
303 | fmtstr(va_alist) | |
304 | va_dcl | |
305 | { | |
306 | va_list ap; | |
307 | struct output strout; | |
308 | char *outbuf; | |
309 | int length; | |
310 | char *fmt; | |
311 | ||
312 | va_start(ap); | |
313 | outbuf = va_arg(ap, char *); | |
314 | length = va_arg(ap, int); | |
315 | fmt = va_arg(ap, char *); | |
316 | strout.nextc = outbuf; | |
317 | strout.nleft = length; | |
318 | strout.fd = BLOCK_OUT; | |
319 | strout.flags = 0; | |
320 | doformat(&strout, fmt, ap); | |
321 | outc('\0', &strout); | |
322 | if (strout.flags & OUTPUT_ERR) | |
323 | outbuf[length - 1] = '\0'; | |
324 | } | |
325 | #endif /* __STDC__ */ | |
326 | ||
327 | ||
328 | /* | |
329 | * Formatted output. This routine handles a subset of the printf formats: | |
330 | * - Formats supported: d, u, o, X, s, and c. | |
331 | * - The x format is also accepted but is treated like X. | |
332 | * - The l modifier is accepted. | |
333 | * - The - and # flags are accepted; # only works with the o format. | |
334 | * - Width and precision may be specified with any format except c. | |
335 | * - An * may be given for the width or precision. | |
336 | * - The obsolete practice of preceding the width with a zero to get | |
337 | * zero padding is not supported; use the precision field. | |
338 | * - A % may be printed by writing %% in the format string. | |
339 | */ | |
340 | ||
341 | #define TEMPSIZE 24 | |
342 | ||
343 | #ifdef __STDC__ | |
344 | static const char digit[16] = "0123456789ABCDEF"; | |
345 | #else | |
346 | static const char digit[17] = "0123456789ABCDEF"; | |
347 | #endif | |
348 | ||
349 | ||
350 | void | |
351 | doformat(dest, f, ap) | |
352 | register struct output *dest; | |
353 | register char *f; /* format string */ | |
354 | va_list ap; | |
355 | { | |
356 | register char c; | |
357 | char temp[TEMPSIZE]; | |
358 | int flushleft; | |
359 | int sharp; | |
360 | int width; | |
361 | int prec; | |
362 | int islong; | |
363 | char *p; | |
364 | int sign; | |
365 | long l; | |
366 | unsigned long num; | |
367 | unsigned base; | |
368 | int len; | |
369 | int size; | |
370 | int pad; | |
371 | ||
372 | while ((c = *f++) != '\0') { | |
373 | if (c != '%') { | |
374 | outc(c, dest); | |
375 | continue; | |
376 | } | |
377 | flushleft = 0; | |
378 | sharp = 0; | |
379 | width = 0; | |
380 | prec = -1; | |
381 | islong = 0; | |
382 | for (;;) { | |
383 | if (*f == '-') | |
384 | flushleft++; | |
385 | else if (*f == '#') | |
386 | sharp++; | |
387 | else | |
388 | break; | |
389 | f++; | |
390 | } | |
391 | if (*f == '*') { | |
392 | width = va_arg(ap, int); | |
393 | f++; | |
394 | } else { | |
395 | while (is_digit(*f)) { | |
396 | width = 10 * width + digit_val(*f++); | |
397 | } | |
398 | } | |
399 | if (*f == '.') { | |
400 | if (*++f == '*') { | |
401 | prec = va_arg(ap, int); | |
402 | f++; | |
403 | } else { | |
404 | prec = 0; | |
405 | while (is_digit(*f)) { | |
406 | prec = 10 * prec + digit_val(*f++); | |
407 | } | |
408 | } | |
409 | } | |
410 | if (*f == 'l') { | |
411 | islong++; | |
412 | f++; | |
413 | } | |
414 | switch (*f) { | |
415 | case 'd': | |
416 | if (islong) | |
417 | l = va_arg(ap, long); | |
418 | else | |
419 | l = va_arg(ap, int); | |
420 | sign = 0; | |
421 | num = l; | |
422 | if (l < 0) { | |
423 | num = -l; | |
424 | sign = 1; | |
425 | } | |
426 | base = 10; | |
427 | goto number; | |
428 | case 'u': | |
429 | base = 10; | |
430 | goto uns_number; | |
431 | case 'o': | |
432 | base = 8; | |
433 | goto uns_number; | |
434 | case 'x': | |
435 | /* we don't implement 'x'; treat like 'X' */ | |
436 | case 'X': | |
437 | base = 16; | |
438 | uns_number: /* an unsigned number */ | |
439 | sign = 0; | |
440 | if (islong) | |
441 | num = va_arg(ap, unsigned long); | |
442 | else | |
443 | num = va_arg(ap, unsigned int); | |
444 | number: /* process a number */ | |
445 | p = temp + TEMPSIZE - 1; | |
446 | *p = '\0'; | |
447 | while (num) { | |
448 | *--p = digit[num % base]; | |
449 | num /= base; | |
450 | } | |
451 | len = (temp + TEMPSIZE - 1) - p; | |
452 | if (prec < 0) | |
453 | prec = 1; | |
454 | if (sharp && *f == 'o' && prec <= len) | |
455 | prec = len + 1; | |
456 | pad = 0; | |
457 | if (width) { | |
458 | size = len; | |
459 | if (size < prec) | |
460 | size = prec; | |
461 | size += sign; | |
462 | pad = width - size; | |
463 | if (flushleft == 0) { | |
464 | while (--pad >= 0) | |
465 | outc(' ', dest); | |
466 | } | |
467 | } | |
468 | if (sign) | |
469 | outc('-', dest); | |
470 | prec -= len; | |
471 | while (--prec >= 0) | |
472 | outc('0', dest); | |
473 | while (*p) | |
474 | outc(*p++, dest); | |
475 | while (--pad >= 0) | |
476 | outc(' ', dest); | |
477 | break; | |
478 | case 's': | |
479 | p = va_arg(ap, char *); | |
480 | pad = 0; | |
481 | if (width) { | |
482 | len = strlen(p); | |
483 | if (prec >= 0 && len > prec) | |
484 | len = prec; | |
485 | pad = width - len; | |
486 | if (flushleft == 0) { | |
487 | while (--pad >= 0) | |
488 | outc(' ', dest); | |
489 | } | |
490 | } | |
491 | prec++; | |
492 | while (--prec != 0 && *p) | |
493 | outc(*p++, dest); | |
494 | while (--pad >= 0) | |
495 | outc(' ', dest); | |
496 | break; | |
497 | case 'c': | |
498 | c = va_arg(ap, int); | |
499 | outc(c, dest); | |
500 | break; | |
501 | default: | |
502 | outc(*f, dest); | |
503 | break; | |
504 | } | |
505 | f++; | |
506 | } | |
507 | } | |
508 | ||
509 | ||
510 | ||
511 | /* | |
512 | * Version of write which resumes after a signal is caught. | |
513 | */ | |
514 | ||
515 | int | |
516 | xwrite(fd, buf, nbytes) | |
517 | int fd; | |
518 | char *buf; | |
519 | int nbytes; | |
520 | { | |
521 | int ntry; | |
522 | int i; | |
523 | int n; | |
524 | ||
525 | n = nbytes; | |
526 | ntry = 0; | |
527 | for (;;) { | |
528 | i = write(fd, buf, n); | |
529 | if (i > 0) { | |
530 | if ((n -= i) <= 0) | |
531 | return nbytes; | |
532 | buf += i; | |
533 | ntry = 0; | |
534 | } else if (i == 0) { | |
535 | if (++ntry > 10) | |
536 | return nbytes - n; | |
537 | } else if (errno != EINTR) { | |
538 | return -1; | |
539 | } | |
540 | } | |
541 | } | |
542 | ||
543 | ||
544 | /* | |
545 | * Version of ioctl that retries after a signal is caught. | |
546 | */ | |
547 | ||
548 | int | |
549 | xioctl(fd, request, arg) { | |
550 | int i; | |
551 | ||
552 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); | |
553 | return i; | |
554 | } |