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