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