output.c revision 97909
184865Sobrien/*-
294536Sobrien * Copyright (c) 1991, 1993
384865Sobrien *	The Regents of the University of California.  All rights reserved.
484865Sobrien *
584865Sobrien * This code is derived from software contributed to Berkeley by
684865Sobrien * Kenneth Almquist.
784865Sobrien *
884865Sobrien * Redistribution and use in source and binary forms, with or without
984865Sobrien * modification, are permitted provided that the following conditions
1084865Sobrien * are met:
1184865Sobrien * 1. Redistributions of source code must retain the above copyright
1284865Sobrien *    notice, this list of conditions and the following disclaimer.
1384865Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1484865Sobrien *    notice, this list of conditions and the following disclaimer in the
1584865Sobrien *    documentation and/or other materials provided with the distribution.
1684865Sobrien * 3. All advertising materials mentioning features or use of this software
1784865Sobrien *    must display the following acknowledgement:
1884865Sobrien *	This product includes software developed by the University of
1984865Sobrien *	California, Berkeley and its contributors.
2084865Sobrien * 4. Neither the name of the University nor the names of its contributors
2184865Sobrien *    may be used to endorse or promote products derived from this software
2284865Sobrien *    without specific prior written permission.
2384865Sobrien *
2484865Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2584865Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2684865Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2794536Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2884865Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2994536Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3084865Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3184865Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3284865Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3384865Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3484865Sobrien * SUCH DAMAGE.
3584865Sobrien */
3684865Sobrien
3784865Sobrien#ifndef lint
3884865Sobrien#if 0
3984865Sobrienstatic char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
4084865Sobrien#endif
4184865Sobrienstatic const char rcsid[] =
4294536Sobrien  "$FreeBSD: head/bin/sh/output.c 97909 2002-06-06 03:29:23Z tjr $";
4394536Sobrien#endif /* not lint */
4484865Sobrien
4584865Sobrien/*
4684865Sobrien * Shell output routines.  We use our own output routines because:
4784865Sobrien *	When a builtin command is interrupted we have to discard
4884865Sobrien *		any pending output.
4984865Sobrien *	When a builtin command appears in back quotes, we want to
5084865Sobrien *		save the output of the command in a region obtained
5184865Sobrien *		via malloc, rather than doing a fork and reading the
5284865Sobrien *		output of the command via a pipe.
5384865Sobrien *	Our output routines may be smaller than the stdio routines.
5484865Sobrien */
5584865Sobrien
5684865Sobrien#include <sys/types.h>        /* quad_t */
5794536Sobrien#include <sys/ioctl.h>
5894536Sobrien
5984865Sobrien#include <stdio.h>	/* defines BUFSIZ */
6084865Sobrien#include <string.h>
6184865Sobrien#include <stdarg.h>
6284865Sobrien#include <errno.h>
6384865Sobrien#include <unistd.h>
6484865Sobrien#include <stdlib.h>
6584865Sobrien
6684865Sobrien#include "shell.h"
6784865Sobrien#include "syntax.h"
6884865Sobrien#include "output.h"
6984865Sobrien#include "memalloc.h"
7084865Sobrien#include "error.h"
7184865Sobrien#include "var.h"
7284865Sobrien
7384865Sobrien
7484865Sobrien#define OUTBUFSIZ BUFSIZ
7584865Sobrien#define BLOCK_OUT -2		/* output to a fixed block of memory */
7684865Sobrien#define MEM_OUT -3		/* output to dynamically allocated memory */
7794536Sobrien#define OUTPUT_ERR 01		/* error occurred on output */
7894536Sobrien
7984865Sobrien
8084865Sobrienstruct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
8184865Sobrienstruct output errout = {NULL, 0, NULL, 100, 2, 0};
8284865Sobrienstruct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
8384865Sobrienstruct output *out1 = &output;
8484865Sobrienstruct output *out2 = &errout;
8584865Sobrien
8684865Sobrien
8784865Sobrien
8884865Sobrien#ifdef mkinit
8984865Sobrien
9084865SobrienINCLUDE "output.h"
9184865SobrienINCLUDE "memalloc.h"
9284865Sobrien
9384865SobrienRESET {
9484865Sobrien	out1 = &output;
9594536Sobrien	out2 = &errout;
9684865Sobrien	if (memout.buf != NULL) {
9794536Sobrien		ckfree(memout.buf);
9884865Sobrien		memout.buf = NULL;
9984865Sobrien	}
10084865Sobrien}
10184865Sobrien
10284865Sobrien#endif
10384865Sobrien
10484865Sobrien
10584865Sobrienvoid
10684865Sobrienout1str(const char *p)
10784865Sobrien{
10884865Sobrien	outstr(p, out1);
10984865Sobrien}
11084865Sobrien
11184865Sobrienvoid
11284865Sobrienout1qstr(const char *p)
11384865Sobrien{
11484865Sobrien	outqstr(p, out1);
11584865Sobrien}
11684865Sobrien
11784865Sobrienvoid
11884865Sobrienout2str(const char *p)
11984865Sobrien{
12084865Sobrien	outstr(p, out2);
12184865Sobrien}
12284865Sobrien
12384865Sobrienvoid
12484865Sobrienout2qstr(const char *p)
12584865Sobrien{
12684865Sobrien	outqstr(p, out2);
12784865Sobrien}
12884865Sobrien
12984865Sobrienvoid
13084865Sobrienoutstr(const char *p, struct output *file)
13184865Sobrien{
13284865Sobrien	while (*p)
13384865Sobrien		outc(*p++, file);
13484865Sobrien	if (file == out2)
13584865Sobrien		flushout(file);
13684865Sobrien}
13784865Sobrien
13884865Sobrien/* Like outstr(), but quote for re-input into the shell. */
13984865Sobrienvoid
14084865Sobrienoutqstr(const char *p, struct output *file)
14184865Sobrien{
14284865Sobrien	char ch;
14384865Sobrien
14484865Sobrien	if (p[strcspn(p, "|&;<>()$`\\\"'")] == '\0' && (!ifsset() ||
14584865Sobrien	    p[strcspn(p, ifsval())] == '\0')) {
14684865Sobrien		outstr(p, file);
14784865Sobrien		return;
14884865Sobrien	}
14984865Sobrien
15084865Sobrien	out1c('\'');
15184865Sobrien	while ((ch = *p++) != '\0') {
15284865Sobrien		switch (ch) {
15384865Sobrien		case '\'':
15484865Sobrien			/*
15584865Sobrien			 * Can't quote single quotes inside single quotes;
15684865Sobrien			 * close them, write escaped single quote, open again.
15784865Sobrien			 */
15884865Sobrien			outstr("'\\''", file);
15984865Sobrien			break;
16084865Sobrien		default:
16184865Sobrien			outc(ch, file);
16294536Sobrien		}
16384865Sobrien	}
16484865Sobrien	out1c('\'');
16594536Sobrien}
16694536Sobrien
16784865Sobrienchar out_junk[16];
16884865Sobrien
16984865Sobrienvoid
17084865Sobrienemptyoutbuf(struct output *dest)
17184865Sobrien{
17284865Sobrien	int offset;
17394536Sobrien
17494536Sobrien	if (dest->fd == BLOCK_OUT) {
17584865Sobrien		dest->nextc = out_junk;
17684865Sobrien		dest->nleft = sizeof out_junk;
17784865Sobrien		dest->flags |= OUTPUT_ERR;
17884865Sobrien	} else if (dest->buf == NULL) {
17984865Sobrien		INTOFF;
18084865Sobrien		dest->buf = ckmalloc(dest->bufsize);
18184865Sobrien		dest->nextc = dest->buf;
18284865Sobrien		dest->nleft = dest->bufsize;
18384865Sobrien		INTON;
18484865Sobrien	} else if (dest->fd == MEM_OUT) {
18584865Sobrien		offset = dest->bufsize;
18684865Sobrien		INTOFF;
18784865Sobrien		dest->bufsize <<= 1;
18884865Sobrien		dest->buf = ckrealloc(dest->buf, dest->bufsize);
18984865Sobrien		dest->nleft = dest->bufsize - offset;
19084865Sobrien		dest->nextc = dest->buf + offset;
19184865Sobrien		INTON;
19284865Sobrien	} else {
19384865Sobrien		flushout(dest);
19484865Sobrien	}
19584865Sobrien	dest->nleft--;
19684865Sobrien}
19784865Sobrien
19884865Sobrien
19984865Sobrienvoid
20084865Sobrienflushall(void)
20184865Sobrien{
20284865Sobrien	flushout(&output);
20384865Sobrien	flushout(&errout);
20484865Sobrien}
20584865Sobrien
20684865Sobrien
20784865Sobrienvoid
20884865Sobrienflushout(struct output *dest)
20984865Sobrien{
21084865Sobrien
21184865Sobrien	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
21284865Sobrien		return;
21384865Sobrien	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
21494536Sobrien		dest->flags |= OUTPUT_ERR;
21584865Sobrien	dest->nextc = dest->buf;
21684865Sobrien	dest->nleft = dest->bufsize;
21784865Sobrien}
21884865Sobrien
21984865Sobrien
22084865Sobrienvoid
22184865Sobrienfreestdout(void)
22284865Sobrien{
22384865Sobrien	INTOFF;
22484865Sobrien	if (output.buf) {
22584865Sobrien		ckfree(output.buf);
22684865Sobrien		output.buf = NULL;
22784865Sobrien		output.nleft = 0;
22884865Sobrien	}
22984865Sobrien	INTON;
23094536Sobrien}
23184865Sobrien
23284865Sobrien
23384865Sobrienvoid
23484865Sobrienoutfmt(struct output *file, const char *fmt, ...)
23584865Sobrien{
23684865Sobrien	va_list ap;
23784865Sobrien
23884865Sobrien	va_start(ap, fmt);
23984865Sobrien	doformat(file, fmt, ap);
24084865Sobrien	va_end(ap);
24184865Sobrien}
24284865Sobrien
24384865Sobrien
24484865Sobrienvoid
24584865Sobrienout1fmt(const char *fmt, ...)
24684865Sobrien{
24784865Sobrien	va_list ap;
24884865Sobrien
24984865Sobrien	va_start(ap, fmt);
25084865Sobrien	doformat(out1, fmt, ap);
25184865Sobrien	va_end(ap);
25294536Sobrien}
25384865Sobrien
25484865Sobrienvoid
25584865Sobriendprintf(const char *fmt, ...)
25684865Sobrien{
25784865Sobrien	va_list ap;
25884865Sobrien
25984865Sobrien	va_start(ap, fmt);
26084865Sobrien	doformat(out2, fmt, ap);
26194536Sobrien	va_end(ap);
26294536Sobrien	flushout(out2);
26394536Sobrien}
26494536Sobrien
26584865Sobrienvoid
26684865Sobrienfmtstr(char *outbuf, int length, const char *fmt, ...)
26784865Sobrien{
26894536Sobrien	va_list ap;
26984865Sobrien	struct output strout;
27084865Sobrien
27184865Sobrien	va_start(ap, fmt);
27284865Sobrien	strout.nextc = outbuf;
27384865Sobrien	strout.nleft = length;
27484865Sobrien	strout.fd = BLOCK_OUT;
27584865Sobrien	strout.flags = 0;
27694536Sobrien	doformat(&strout, fmt, ap);
27784865Sobrien	outc('\0', &strout);
27894536Sobrien	if (strout.flags & OUTPUT_ERR)
27994536Sobrien		outbuf[length - 1] = '\0';
28094536Sobrien}
28194536Sobrien
28294536Sobrien/*
28394536Sobrien * Formatted output.  This routine handles a subset of the printf formats:
28484865Sobrien * - Formats supported: d, u, o, X, s, and c.
28584865Sobrien * - The x format is also accepted but is treated like X.
28684865Sobrien * - The l and q modifiers are accepted.
28784865Sobrien * - The - and # flags are accepted; # only works with the o format.
28884865Sobrien * - Width and precision may be specified with any format except c.
28984865Sobrien * - An * may be given for the width or precision.
29084865Sobrien * - The obsolete practice of preceding the width with a zero to get
29184865Sobrien *   zero padding is not supported; use the precision field.
29284865Sobrien * - A % may be printed by writing %% in the format string.
29384865Sobrien */
29484865Sobrien
29584865Sobrien#define TEMPSIZE 24
29684865Sobrien
29784865Sobrienstatic const char digit[] = "0123456789ABCDEF";
29884865Sobrien
29984865Sobrien
30084865Sobrienvoid
30184865Sobriendoformat(struct output *dest, const char *f, va_list ap)
30284865Sobrien{
30384865Sobrien	char c;
30484865Sobrien	char temp[TEMPSIZE];
30584865Sobrien	int flushleft;
30684865Sobrien	int sharp;
30784865Sobrien	int width;
30884865Sobrien	int prec;
30984865Sobrien	int islong;
31084865Sobrien	int isquad;
31184865Sobrien	char *p;
31284865Sobrien	int sign;
31384865Sobrien	quad_t l;
31484865Sobrien	u_quad_t num;
31584865Sobrien	unsigned base;
31684865Sobrien	int len;
31784865Sobrien	int size;
31884865Sobrien	int pad;
31984865Sobrien
32084865Sobrien	while ((c = *f++) != '\0') {
32184865Sobrien		if (c != '%') {
32284865Sobrien			outc(c, dest);
32384865Sobrien			continue;
32484865Sobrien		}
32584865Sobrien		flushleft = 0;
32684865Sobrien		sharp = 0;
32784865Sobrien		width = 0;
32884865Sobrien		prec = -1;
32984865Sobrien		islong = 0;
33084865Sobrien		isquad = 0;
33184865Sobrien		for (;;) {
33284865Sobrien			if (*f == '-')
33384865Sobrien				flushleft++;
33484865Sobrien			else if (*f == '#')
33594536Sobrien				sharp++;
33684865Sobrien			else
33784865Sobrien				break;
33884865Sobrien			f++;
33984865Sobrien		}
34084865Sobrien		if (*f == '*') {
34184865Sobrien			width = va_arg(ap, int);
34284865Sobrien			f++;
34384865Sobrien		} else {
34484865Sobrien			while (is_digit(*f)) {
34584865Sobrien				width = 10 * width + digit_val(*f++);
34684865Sobrien			}
34784865Sobrien		}
34884865Sobrien		if (*f == '.') {
34984865Sobrien			if (*++f == '*') {
35084865Sobrien				prec = va_arg(ap, int);
35184865Sobrien				f++;
35294536Sobrien			} else {
35384865Sobrien				prec = 0;
35484865Sobrien				while (is_digit(*f)) {
35584865Sobrien					prec = 10 * prec + digit_val(*f++);
35684865Sobrien				}
35784865Sobrien			}
35884865Sobrien		}
35984865Sobrien		if (*f == 'l') {
36084865Sobrien			islong++;
36184865Sobrien			f++;
36284865Sobrien		} else if (*f == 'q') {
36384865Sobrien			isquad++;
36484865Sobrien			f++;
36584865Sobrien		}
36684865Sobrien		switch (*f) {
36784865Sobrien		case 'd':
36884865Sobrien			if (isquad)
36984865Sobrien				l = va_arg(ap, quad_t);
37084865Sobrien			else if (islong)
37194536Sobrien				l = va_arg(ap, long);
37284865Sobrien			else
37384865Sobrien				l = va_arg(ap, int);
37484865Sobrien			sign = 0;
37584865Sobrien			num = l;
37684865Sobrien			if (l < 0) {
37794536Sobrien				num = -l;
37894536Sobrien				sign = 1;
37994536Sobrien			}
38094536Sobrien			base = 10;
38194536Sobrien			goto number;
38284865Sobrien		case 'u':
38384865Sobrien			base = 10;
38484865Sobrien			goto uns_number;
38594536Sobrien		case 'o':
38684865Sobrien			base = 8;
38784865Sobrien			goto uns_number;
38884865Sobrien		case 'x':
38984865Sobrien			/* we don't implement 'x'; treat like 'X' */
39084865Sobrien		case 'X':
39184865Sobrien			base = 16;
39284865Sobrienuns_number:	  /* an unsigned number */
39384865Sobrien			sign = 0;
39484865Sobrien			if (isquad)
39584865Sobrien				num = va_arg(ap, u_quad_t);
39684865Sobrien			else if (islong)
39784865Sobrien				num = va_arg(ap, unsigned long);
39884865Sobrien			else
39984865Sobrien				num = va_arg(ap, unsigned int);
40084865Sobriennumber:		  /* process a number */
40184865Sobrien			p = temp + TEMPSIZE - 1;
40284865Sobrien			*p = '\0';
40384865Sobrien			while (num) {
40484865Sobrien				*--p = digit[num % base];
40584865Sobrien				num /= base;
40684865Sobrien			}
40784865Sobrien			len = (temp + TEMPSIZE - 1) - p;
40884865Sobrien			if (prec < 0)
40984865Sobrien				prec = 1;
41084865Sobrien			if (sharp && *f == 'o' && prec <= len)
41184865Sobrien				prec = len + 1;
41284865Sobrien			pad = 0;
41384865Sobrien			if (width) {
41484865Sobrien				size = len;
41584865Sobrien				if (size < prec)
41684865Sobrien					size = prec;
41784865Sobrien				size += sign;
41884865Sobrien				pad = width - size;
41984865Sobrien				if (flushleft == 0) {
42084865Sobrien					while (--pad >= 0)
42184865Sobrien						outc(' ', dest);
42284865Sobrien				}
42384865Sobrien			}
42484865Sobrien			if (sign)
42584865Sobrien				outc('-', dest);
42694536Sobrien			prec -= len;
42784865Sobrien			while (--prec >= 0)
42884865Sobrien				outc('0', dest);
42984865Sobrien			while (*p)
43084865Sobrien				outc(*p++, dest);
43184865Sobrien			while (--pad >= 0)
43284865Sobrien				outc(' ', dest);
43384865Sobrien			break;
43484865Sobrien		case 's':
43584865Sobrien			p = va_arg(ap, char *);
43684865Sobrien			pad = 0;
43784865Sobrien			if (width) {
43884865Sobrien				len = strlen(p);
43984865Sobrien				if (prec >= 0 && len > prec)
44084865Sobrien					len = prec;
44184865Sobrien				pad = width - len;
44284865Sobrien				if (flushleft == 0) {
44384865Sobrien					while (--pad >= 0)
44484865Sobrien						outc(' ', dest);
44584865Sobrien				}
44684865Sobrien			}
44784865Sobrien			prec++;
44884865Sobrien			while (--prec != 0 && *p)
44984865Sobrien				outc(*p++, dest);
45084865Sobrien			while (--pad >= 0)
45184865Sobrien				outc(' ', dest);
45284865Sobrien			break;
45384865Sobrien		case 'c':
45484865Sobrien			c = va_arg(ap, int);
45584865Sobrien			outc(c, dest);
45684865Sobrien			break;
45784865Sobrien		default:
45884865Sobrien			outc(*f, dest);
45984865Sobrien			break;
46084865Sobrien		}
46184865Sobrien		f++;
46284865Sobrien	}
46384865Sobrien}
46484865Sobrien
46584865Sobrien
46684865Sobrien
46784865Sobrien/*
46884865Sobrien * Version of write which resumes after a signal is caught.
46984865Sobrien */
47084865Sobrien
47184865Sobrienint
47284865Sobrienxwrite(int fd, char *buf, int nbytes)
47384865Sobrien{
47484865Sobrien	int ntry;
47584865Sobrien	int i;
47684865Sobrien	int n;
47784865Sobrien
47884865Sobrien	n = nbytes;
47984865Sobrien	ntry = 0;
48084865Sobrien	for (;;) {
48184865Sobrien		i = write(fd, buf, n);
48284865Sobrien		if (i > 0) {
48394536Sobrien			if ((n -= i) <= 0)
48484865Sobrien				return nbytes;
48594536Sobrien			buf += i;
48684865Sobrien			ntry = 0;
48784865Sobrien		} else if (i == 0) {
48884865Sobrien			if (++ntry > 10)
48984865Sobrien				return nbytes - n;
49084865Sobrien		} else if (errno != EINTR) {
49184865Sobrien			return -1;
49284865Sobrien		}
49384865Sobrien	}
49484865Sobrien}
49584865Sobrien