xref: /netbsd-src/bin/sh/output.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: output.c,v 1.19 1997/07/04 21:02:18 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
43 #else
44 __RCSID("$NetBSD: output.c,v 1.19 1997/07/04 21:02:18 christos Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * Shell output routines.  We use our own output routines because:
50  *	When a builtin command is interrupted we have to discard
51  *		any pending output.
52  *	When a builtin command appears in back quotes, we want to
53  *		save the output of the command in a region obtained
54  *		via malloc, rather than doing a fork and reading the
55  *		output of the command via a pipe.
56  *	Our output routines may be smaller than the stdio routines.
57  */
58 
59 #include <sys/types.h>        /* quad_t */
60 #include <sys/ioctl.h>
61 
62 #include <stdio.h>	/* defines BUFSIZ */
63 #include <string.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <stdlib.h>
67 
68 #include "shell.h"
69 #include "syntax.h"
70 #include "output.h"
71 #include "memalloc.h"
72 #include "error.h"
73 
74 
75 #define OUTBUFSIZ BUFSIZ
76 #define BLOCK_OUT -2		/* output to a fixed block of memory */
77 #define MEM_OUT -3		/* output to dynamically allocated memory */
78 #define OUTPUT_ERR 01		/* error occurred on output */
79 
80 
81 struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
82 struct output errout = {NULL, 0, NULL, 100, 2, 0};
83 struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
84 struct output *out1 = &output;
85 struct output *out2 = &errout;
86 
87 
88 
89 #ifdef mkinit
90 
91 INCLUDE "output.h"
92 INCLUDE "memalloc.h"
93 
94 RESET {
95 	out1 = &output;
96 	out2 = &errout;
97 	if (memout.buf != NULL) {
98 		ckfree(memout.buf);
99 		memout.buf = NULL;
100 	}
101 }
102 
103 #endif
104 
105 
106 #ifdef notdef	/* no longer used */
107 /*
108  * Set up an output file to write to memory rather than a file.
109  */
110 
111 void
112 open_mem(block, length, file)
113 	char *block;
114 	int length;
115 	struct output *file;
116 	{
117 	file->nextc = block;
118 	file->nleft = --length;
119 	file->fd = BLOCK_OUT;
120 	file->flags = 0;
121 }
122 #endif
123 
124 
125 void
126 out1str(p)
127 	const char *p;
128 	{
129 	outstr(p, out1);
130 }
131 
132 
133 void
134 out2str(p)
135 	const char *p;
136 	{
137 	outstr(p, out2);
138 }
139 
140 
141 void
142 outstr(p, file)
143 	const char *p;
144 	struct output *file;
145 	{
146 	while (*p)
147 		outc(*p++, file);
148 	if (file == out2)
149 		flushout(file);
150 }
151 
152 
153 char out_junk[16];
154 
155 
156 void
157 emptyoutbuf(dest)
158 	struct output *dest;
159 	{
160 	int offset;
161 
162 	if (dest->fd == BLOCK_OUT) {
163 		dest->nextc = out_junk;
164 		dest->nleft = sizeof out_junk;
165 		dest->flags |= OUTPUT_ERR;
166 	} else if (dest->buf == NULL) {
167 		INTOFF;
168 		dest->buf = ckmalloc(dest->bufsize);
169 		dest->nextc = dest->buf;
170 		dest->nleft = dest->bufsize;
171 		INTON;
172 	} else if (dest->fd == MEM_OUT) {
173 		offset = dest->bufsize;
174 		INTOFF;
175 		dest->bufsize <<= 1;
176 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
177 		dest->nleft = dest->bufsize - offset;
178 		dest->nextc = dest->buf + offset;
179 		INTON;
180 	} else {
181 		flushout(dest);
182 	}
183 	dest->nleft--;
184 }
185 
186 
187 void
188 flushall() {
189 	flushout(&output);
190 	flushout(&errout);
191 }
192 
193 
194 void
195 flushout(dest)
196 	struct output *dest;
197 	{
198 
199 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
200 		return;
201 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
202 		dest->flags |= OUTPUT_ERR;
203 	dest->nextc = dest->buf;
204 	dest->nleft = dest->bufsize;
205 }
206 
207 
208 void
209 freestdout() {
210 	INTOFF;
211 	if (output.buf) {
212 		ckfree(output.buf);
213 		output.buf = NULL;
214 		output.nleft = 0;
215 	}
216 	INTON;
217 }
218 
219 
220 #ifdef __STDC__
221 void
222 outfmt(struct output *file, char *fmt, ...) {
223 	va_list ap;
224 
225 	va_start(ap, fmt);
226 	doformat(file, fmt, ap);
227 	va_end(ap);
228 }
229 
230 
231 void
232 out1fmt(char *fmt, ...) {
233 	va_list ap;
234 
235 	va_start(ap, fmt);
236 	doformat(out1, fmt, ap);
237 	va_end(ap);
238 }
239 
240 void
241 dprintf(char *fmt, ...) {
242 	va_list ap;
243 
244 	va_start(ap, fmt);
245 	doformat(out2, fmt, ap);
246 	va_end(ap);
247 	flushout(out2);
248 }
249 
250 void
251 fmtstr(char *outbuf, int length, char *fmt, ...) {
252 	va_list ap;
253 	struct output strout;
254 
255 	va_start(ap, fmt);
256 	strout.nextc = outbuf;
257 	strout.nleft = length;
258 	strout.fd = BLOCK_OUT;
259 	strout.flags = 0;
260 	doformat(&strout, fmt, ap);
261 	outc('\0', &strout);
262 	if (strout.flags & OUTPUT_ERR)
263 		outbuf[length - 1] = '\0';
264 }
265 
266 #else /* not __STDC__ */
267 
268 void
269 outfmt(va_alist)
270 	va_dcl
271 	{
272 	va_list ap;
273 	struct output *file;
274 	char *fmt;
275 
276 	va_start(ap);
277 	file = va_arg(ap, struct output *);
278 	fmt = va_arg(ap, char *);
279 	doformat(file, fmt, ap);
280 	va_end(ap);
281 }
282 
283 
284 void
285 out1fmt(va_alist)
286 	va_dcl
287 	{
288 	va_list ap;
289 	char *fmt;
290 
291 	va_start(ap);
292 	fmt = va_arg(ap, char *);
293 	doformat(out1, fmt, ap);
294 	va_end(ap);
295 }
296 
297 void
298 dprintf(va_alist)
299 	va_dcl
300 	{
301 	va_list ap;
302 	char *fmt;
303 
304 	va_start(ap);
305 	fmt = va_arg(ap, char *);
306 	doformat(out2, fmt, ap);
307 	va_end(ap);
308 	flushout(out2);
309 }
310 
311 void
312 fmtstr(va_alist)
313 	va_dcl
314 	{
315 	va_list ap;
316 	struct output strout;
317 	char *outbuf;
318 	int length;
319 	char *fmt;
320 
321 	va_start(ap);
322 	outbuf = va_arg(ap, char *);
323 	length = va_arg(ap, int);
324 	fmt = va_arg(ap, char *);
325 	strout.nextc = outbuf;
326 	strout.nleft = length;
327 	strout.fd = BLOCK_OUT;
328 	strout.flags = 0;
329 	doformat(&strout, fmt, ap);
330 	outc('\0', &strout);
331 	if (strout.flags & OUTPUT_ERR)
332 		outbuf[length - 1] = '\0';
333 }
334 #endif /* __STDC__ */
335 
336 
337 /*
338  * Formatted output.  This routine handles a subset of the printf formats:
339  * - Formats supported: d, u, o, X, s, and c.
340  * - The x format is also accepted but is treated like X.
341  * - The l and q modifiers are accepted.
342  * - The - and # flags are accepted; # only works with the o format.
343  * - Width and precision may be specified with any format except c.
344  * - An * may be given for the width or precision.
345  * - The obsolete practice of preceding the width with a zero to get
346  *   zero padding is not supported; use the precision field.
347  * - A % may be printed by writing %% in the format string.
348  */
349 
350 #define TEMPSIZE 24
351 
352 static const char digit[] = "0123456789ABCDEF";
353 
354 
355 void
356 doformat(dest, f, ap)
357 	struct output *dest;
358 	char *f;		/* format string */
359 	va_list ap;
360 	{
361 	char c;
362 	char temp[TEMPSIZE];
363 	int flushleft;
364 	int sharp;
365 	int width;
366 	int prec;
367 	int islong;
368 	int isquad;
369 	char *p;
370 	int sign;
371 #ifdef BSD4_4
372 	quad_t l;
373 	u_quad_t num;
374 #else
375 	long l;
376 	u_long num;
377 #endif
378 	unsigned base;
379 	int len;
380 	int size;
381 	int pad;
382 
383 	while ((c = *f++) != '\0') {
384 		if (c != '%') {
385 			outc(c, dest);
386 			continue;
387 		}
388 		flushleft = 0;
389 		sharp = 0;
390 		width = 0;
391 		prec = -1;
392 		islong = 0;
393 		isquad = 0;
394 		for (;;) {
395 			if (*f == '-')
396 				flushleft++;
397 			else if (*f == '#')
398 				sharp++;
399 			else
400 				break;
401 			f++;
402 		}
403 		if (*f == '*') {
404 			width = va_arg(ap, int);
405 			f++;
406 		} else {
407 			while (is_digit(*f)) {
408 				width = 10 * width + digit_val(*f++);
409 			}
410 		}
411 		if (*f == '.') {
412 			if (*++f == '*') {
413 				prec = va_arg(ap, int);
414 				f++;
415 			} else {
416 				prec = 0;
417 				while (is_digit(*f)) {
418 					prec = 10 * prec + digit_val(*f++);
419 				}
420 			}
421 		}
422 		if (*f == 'l') {
423 			islong++;
424 			f++;
425 		} else if (*f == 'q') {
426 			isquad++;
427 			f++;
428 		}
429 		switch (*f) {
430 		case 'd':
431 #ifdef BSD4_4
432 			if (isquad)
433 				l = va_arg(ap, quad_t);
434 			else
435 #endif
436 			if (islong)
437 				l = va_arg(ap, long);
438 			else
439 				l = va_arg(ap, int);
440 			sign = 0;
441 			num = l;
442 			if (l < 0) {
443 				num = -l;
444 				sign = 1;
445 			}
446 			base = 10;
447 			goto number;
448 		case 'u':
449 			base = 10;
450 			goto uns_number;
451 		case 'o':
452 			base = 8;
453 			goto uns_number;
454 		case 'x':
455 			/* we don't implement 'x'; treat like 'X' */
456 		case 'X':
457 			base = 16;
458 uns_number:	  /* an unsigned number */
459 			sign = 0;
460 #ifdef BSD4_4
461 			if (isquad)
462 				num = va_arg(ap, u_quad_t);
463 			else
464 #endif
465 			if (islong)
466 				num = va_arg(ap, unsigned long);
467 			else
468 				num = va_arg(ap, unsigned int);
469 number:		  /* process a number */
470 			p = temp + TEMPSIZE - 1;
471 			*p = '\0';
472 			while (num) {
473 				*--p = digit[num % base];
474 				num /= base;
475 			}
476 			len = (temp + TEMPSIZE - 1) - p;
477 			if (prec < 0)
478 				prec = 1;
479 			if (sharp && *f == 'o' && prec <= len)
480 				prec = len + 1;
481 			pad = 0;
482 			if (width) {
483 				size = len;
484 				if (size < prec)
485 					size = prec;
486 				size += sign;
487 				pad = width - size;
488 				if (flushleft == 0) {
489 					while (--pad >= 0)
490 						outc(' ', dest);
491 				}
492 			}
493 			if (sign)
494 				outc('-', dest);
495 			prec -= len;
496 			while (--prec >= 0)
497 				outc('0', dest);
498 			while (*p)
499 				outc(*p++, dest);
500 			while (--pad >= 0)
501 				outc(' ', dest);
502 			break;
503 		case 's':
504 			p = va_arg(ap, char *);
505 			pad = 0;
506 			if (width) {
507 				len = strlen(p);
508 				if (prec >= 0 && len > prec)
509 					len = prec;
510 				pad = width - len;
511 				if (flushleft == 0) {
512 					while (--pad >= 0)
513 						outc(' ', dest);
514 				}
515 			}
516 			prec++;
517 			while (--prec != 0 && *p)
518 				outc(*p++, dest);
519 			while (--pad >= 0)
520 				outc(' ', dest);
521 			break;
522 		case 'c':
523 			c = va_arg(ap, int);
524 			outc(c, dest);
525 			break;
526 		default:
527 			outc(*f, dest);
528 			break;
529 		}
530 		f++;
531 	}
532 }
533 
534 
535 
536 /*
537  * Version of write which resumes after a signal is caught.
538  */
539 
540 int
541 xwrite(fd, buf, nbytes)
542 	int fd;
543 	char *buf;
544 	int nbytes;
545 	{
546 	int ntry;
547 	int i;
548 	int n;
549 
550 	n = nbytes;
551 	ntry = 0;
552 	for (;;) {
553 		i = write(fd, buf, n);
554 		if (i > 0) {
555 			if ((n -= i) <= 0)
556 				return nbytes;
557 			buf += i;
558 			ntry = 0;
559 		} else if (i == 0) {
560 			if (++ntry > 10)
561 				return nbytes - n;
562 		} else if (errno != EINTR) {
563 			return -1;
564 		}
565 	}
566 }
567 
568 
569 /*
570  * Version of ioctl that retries after a signal is caught.
571  * XXX unused function
572  */
573 
574 int
575 xioctl(fd, request, arg)
576 	int fd;
577 	unsigned long request;
578 	char * arg;
579 {
580 	int i;
581 
582 	while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
583 	return i;
584 }
585