output.c revision 1556
1251881Speter/*-
2251881Speter * Copyright (c) 1991, 1993
3251881Speter *	The Regents of the University of California.  All rights reserved.
4251881Speter *
5251881Speter * This code is derived from software contributed to Berkeley by
6251881Speter * Kenneth Almquist.
7251881Speter *
8251881Speter * Redistribution and use in source and binary forms, with or without
9251881Speter * modification, are permitted provided that the following conditions
10251881Speter * are met:
11251881Speter * 1. Redistributions of source code must retain the above copyright
12251881Speter *    notice, this list of conditions and the following disclaimer.
13251881Speter * 2. Redistributions in binary form must reproduce the above copyright
14251881Speter *    notice, this list of conditions and the following disclaimer in the
15251881Speter *    documentation and/or other materials provided with the distribution.
16251881Speter * 3. All advertising materials mentioning features or use of this software
17251881Speter *    must display the following acknowledgement:
18251881Speter *	This product includes software developed by the University of
19251881Speter *	California, Berkeley and its contributors.
20251881Speter * 4. Neither the name of the University nor the names of its contributors
21251881Speter *    may be used to endorse or promote products derived from this software
22251881Speter *    without specific prior written permission.
23251881Speter *
24251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27251881Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34251881Speter * SUCH DAMAGE.
35251881Speter */
36251881Speter
37251881Speter#ifndef lint
38251881Speterstatic char sccsid[] = "@(#)output.c	8.1 (Berkeley) 5/31/93";
39251881Speter#endif /* not lint */
40251881Speter
41251881Speter/*
42251881Speter * Shell output routines.  We use our own output routines because:
43251881Speter *	When a builtin command is interrupted we have to discard
44251881Speter *		any pending output.
45251881Speter *	When a builtin command appears in back quotes, we want to
46251881Speter *		save the output of the command in a region obtained
47251881Speter *		via malloc, rather than doing a fork and reading the
48251881Speter *		output of the command via a pipe.
49251881Speter *	Our output routines may be smaller than the stdio routines.
50251881Speter */
51251881Speter
52251881Speter#include <stdio.h>	/* defines BUFSIZ */
53251881Speter#include "shell.h"
54251881Speter#include "syntax.h"
55251881Speter#include "output.h"
56251881Speter#include "memalloc.h"
57251881Speter#include "error.h"
58251881Speter#ifdef __STDC__
59251881Speter#include "stdarg.h"
60251881Speter#else
61251881Speter#include <varargs.h>
62251881Speter#endif
63251881Speter#include <errno.h>
64251881Speter
65251881Speter
66251881Speter#define OUTBUFSIZ BUFSIZ
67251881Speter#define BLOCK_OUT -2		/* output to a fixed block of memory */
68251881Speter#define MEM_OUT -3		/* output to dynamically allocated memory */
69251881Speter#define OUTPUT_ERR 01		/* error occurred on output */
70251881Speter
71251881Speter
72251881Speterstruct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
73251881Speterstruct output errout = {NULL, 0, NULL, 100, 2, 0};;
74251881Speterstruct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
75251881Speterstruct output *out1 = &output;
76251881Speterstruct output *out2 = &errout;
77251881Speter
78251881Speter
79251881Speter
80251881Speter#ifdef mkinit
81251881Speter
82251881SpeterINCLUDE "output.h"
83251881SpeterINCLUDE "memalloc.h"
84251881Speter
85251881SpeterRESET {
86251881Speter	out1 = &output;
87251881Speter	out2 = &errout;
88251881Speter	if (memout.buf != NULL) {
89251881Speter		ckfree(memout.buf);
90251881Speter		memout.buf = NULL;
91251881Speter	}
92251881Speter}
93251881Speter
94251881Speter#endif
95251881Speter
96251881Speter
97251881Speter#ifdef notdef	/* no longer used */
98251881Speter/*
99251881Speter * Set up an output file to write to memory rather than a file.
100251881Speter */
101251881Speter
102251881Spetervoid
103251881Speteropen_mem(block, length, file)
104251881Speter	char *block;
105251881Speter	int length;
106251881Speter	struct output *file;
107251881Speter	{
108251881Speter	file->nextc = block;
109251881Speter	file->nleft = --length;
110251881Speter	file->fd = BLOCK_OUT;
111251881Speter	file->flags = 0;
112251881Speter}
113251881Speter#endif
114251881Speter
115251881Speter
116251881Spetervoid
117251881Speterout1str(p)
118251881Speter	char *p;
119251881Speter	{
120251881Speter	outstr(p, out1);
121251881Speter}
122251881Speter
123251881Speter
124251881Spetervoid
125251881Speterout2str(p)
126251881Speter	char *p;
127251881Speter	{
128251881Speter	outstr(p, out2);
129251881Speter}
130251881Speter
131251881Speter
132251881Spetervoid
133251881Speteroutstr(p, file)
134251881Speter	register char *p;
135251881Speter	register struct output *file;
136251881Speter	{
137251881Speter	while (*p)
138251881Speter		outc(*p++, file);
139251881Speter	if (file == out2)
140251881Speter		flushout(file);
141251881Speter}
142251881Speter
143251881Speter
144251881Speterchar out_junk[16];
145251881Speter
146251881Speter
147251881Spetervoid
148251881Speteremptyoutbuf(dest)
149251881Speter	struct output *dest;
150251881Speter	{
151251881Speter	int offset;
152251881Speter
153251881Speter	if (dest->fd == BLOCK_OUT) {
154251881Speter		dest->nextc = out_junk;
155251881Speter		dest->nleft = sizeof out_junk;
156251881Speter		dest->flags |= OUTPUT_ERR;
157251881Speter	} else if (dest->buf == NULL) {
158251881Speter		INTOFF;
159251881Speter		dest->buf = ckmalloc(dest->bufsize);
160251881Speter		dest->nextc = dest->buf;
161251881Speter		dest->nleft = dest->bufsize;
162251881Speter		INTON;
163251881Speter	} else if (dest->fd == MEM_OUT) {
164251881Speter		offset = dest->bufsize;
165251881Speter		INTOFF;
166251881Speter		dest->bufsize <<= 1;
167251881Speter		dest->buf = ckrealloc(dest->buf, dest->bufsize);
168251881Speter		dest->nleft = dest->bufsize - offset;
169251881Speter		dest->nextc = dest->buf + offset;
170251881Speter		INTON;
171251881Speter	} else {
172251881Speter		flushout(dest);
173251881Speter	}
174251881Speter	dest->nleft--;
175251881Speter}
176251881Speter
177251881Speter
178251881Spetervoid
179251881Speterflushall() {
180251881Speter	flushout(&output);
181251881Speter	flushout(&errout);
182251881Speter}
183251881Speter
184251881Speter
185251881Spetervoid
186251881Speterflushout(dest)
187251881Speter	struct output *dest;
188251881Speter	{
189251881Speter
190251881Speter	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
191251881Speter		return;
192251881Speter	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
193251881Speter		dest->flags |= OUTPUT_ERR;
194251881Speter	dest->nextc = dest->buf;
195251881Speter	dest->nleft = dest->bufsize;
196251881Speter}
197251881Speter
198251881Speter
199251881Spetervoid
200251881Speterfreestdout() {
201251881Speter	INTOFF;
202251881Speter	if (output.buf) {
203251881Speter		ckfree(output.buf);
204251881Speter		output.buf = NULL;
205251881Speter		output.nleft = 0;
206251881Speter	}
207251881Speter	INTON;
208251881Speter}
209251881Speter
210251881Speter
211251881Speter#ifdef __STDC__
212251881Spetervoid
213251881Speteroutfmt(struct output *file, char *fmt, ...) {
214251881Speter	va_list ap;
215251881Speter
216251881Speter	va_start(ap, fmt);
217251881Speter	doformat(file, fmt, ap);
218251881Speter	va_end(ap);
219251881Speter}
220251881Speter
221251881Speter
222251881Spetervoid
223251881Speterout1fmt(char *fmt, ...) {
224251881Speter	va_list ap;
225251881Speter
226251881Speter	va_start(ap, fmt);
227251881Speter	doformat(out1, fmt, ap);
228251881Speter	va_end(ap);
229251881Speter}
230251881Speter
231251881Spetervoid
232251881Speterdprintf(char *fmt, ...) {
233251881Speter	va_list ap;
234251881Speter
235251881Speter	va_start(ap, fmt);
236251881Speter	doformat(out2, fmt, ap);
237251881Speter	va_end(ap);
238251881Speter	flushout(out2);
239251881Speter}
240251881Speter
241251881Spetervoid
242251881Speterfmtstr(char *outbuf, int length, char *fmt, ...) {
243251881Speter	va_list ap;
244251881Speter	struct output strout;
245251881Speter
246251881Speter	va_start(ap, fmt);
247251881Speter	strout.nextc = outbuf;
248251881Speter	strout.nleft = length;
249251881Speter	strout.fd = BLOCK_OUT;
250251881Speter	strout.flags = 0;
251251881Speter	doformat(&strout, fmt, ap);
252251881Speter	outc('\0', &strout);
253251881Speter	if (strout.flags & OUTPUT_ERR)
254251881Speter		outbuf[length - 1] = '\0';
255251881Speter}
256251881Speter
257251881Speter#else /* not __STDC__ */
258251881Speter
259251881Spetervoid
260251881Speteroutfmt(va_alist)
261251881Speter	va_dcl
262251881Speter	{
263251881Speter	va_list ap;
264251881Speter	struct output *file;
265251881Speter	char *fmt;
266251881Speter
267251881Speter	va_start(ap);
268251881Speter	file = va_arg(ap, struct output *);
269251881Speter	fmt = va_arg(ap, char *);
270251881Speter	doformat(file, fmt, ap);
271251881Speter	va_end(ap);
272251881Speter}
273251881Speter
274251881Speter
275251881Spetervoid
276251881Speterout1fmt(va_alist)
277251881Speter	va_dcl
278251881Speter	{
279251881Speter	va_list ap;
280251881Speter	char *fmt;
281251881Speter
282251881Speter	va_start(ap);
283251881Speter	fmt = va_arg(ap, char *);
284251881Speter	doformat(out1, fmt, ap);
285251881Speter	va_end(ap);
286251881Speter}
287251881Speter
288251881Spetervoid
289251881Speterdprintf(va_alist)
290251881Speter	va_dcl
291251881Speter	{
292251881Speter	va_list ap;
293251881Speter	char *fmt;
294251881Speter
295251881Speter	va_start(ap);
296251881Speter	fmt = va_arg(ap, char *);
297251881Speter	doformat(out2, fmt, ap);
298251881Speter	va_end(ap);
299251881Speter	flushout(out2);
300251881Speter}
301251881Speter
302251881Spetervoid
303251881Speterfmtstr(va_alist)
304251881Speter	va_dcl
305251881Speter	{
306251881Speter	va_list ap;
307251881Speter	struct output strout;
308251881Speter	char *outbuf;
309251881Speter	int length;
310251881Speter	char *fmt;
311251881Speter
312251881Speter	va_start(ap);
313251881Speter	outbuf = va_arg(ap, char *);
314251881Speter	length = va_arg(ap, int);
315251881Speter	fmt = va_arg(ap, char *);
316251881Speter	strout.nextc = outbuf;
317251881Speter	strout.nleft = length;
318251881Speter	strout.fd = BLOCK_OUT;
319251881Speter	strout.flags = 0;
320251881Speter	doformat(&strout, fmt, ap);
321251881Speter	outc('\0', &strout);
322251881Speter	if (strout.flags & OUTPUT_ERR)
323251881Speter		outbuf[length - 1] = '\0';
324251881Speter}
325251881Speter#endif /* __STDC__ */
326251881Speter
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