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