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