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