xref: /original-bsd/bin/sh/output.c (revision acda45c0)
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
12 static char sccsid[] = "@(#)output.c	5.1 (Berkeley) 03/07/91";
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 }
114 
115 
116 char out_junk[16];
117 
118 
119 void
120 emptyoutbuf(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 
150 void
151 flushall() {
152 	flushout(&output);
153 	flushout(&errout);
154 }
155 
156 
157 void
158 flushout(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 
171 void
172 freestdout() {
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__
184 void
185 outfmt(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 
194 void
195 out1fmt(char *fmt, ...) {
196 	va_list ap;
197 
198 	va_start(ap, fmt);
199 	doformat(out1, fmt, ap);
200 	va_end(ap);
201 }
202 
203 
204 void
205 fmtstr(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 
222 void
223 outfmt(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 
238 void
239 out1fmt(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 
252 void
253 fmtstr(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__
294 static const char digit[16] = "0123456789ABCDEF";
295 #else
296 static const char digit[17] = "0123456789ABCDEF";
297 #endif
298 
299 
300 void
301 doformat(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;
388 uns_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);
394 number:		  /* 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 
465 int
466 xwrite(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 
498 int
499 xioctl(fd, request, arg) {
500 	int i;
501 
502 	while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
503 	return i;
504 }
505