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