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