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