xref: /original-bsd/bin/sh/output.c (revision b3c06cab)
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
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
97 out1str(p)
98 	const char *p;
99 	{
100 	outstr(p, out1);
101 }
102 
103 
104 void
105 out2str(p)
106 	const char *p;
107 	{
108 	outstr(p, out2);
109 }
110 
111 
112 void
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
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
159 flushall() {
160 	flushout(&output);
161 	flushout(&errout);
162 }
163 
164 
165 void
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
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
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
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
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
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
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
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
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
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
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
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
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