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