output.c revision 97815
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
4036150Scharnier#endif
4136150Scharnierstatic const char rcsid[] =
4250471Speter  "$FreeBSD: head/bin/sh/output.c 97815 2002-06-04 12:59:12Z tjr $";
431556Srgrimes#endif /* not lint */
441556Srgrimes
451556Srgrimes/*
461556Srgrimes * Shell output routines.  We use our own output routines because:
471556Srgrimes *	When a builtin command is interrupted we have to discard
481556Srgrimes *		any pending output.
491556Srgrimes *	When a builtin command appears in back quotes, we want to
501556Srgrimes *		save the output of the command in a region obtained
511556Srgrimes *		via malloc, rather than doing a fork and reading the
521556Srgrimes *		output of the command via a pipe.
531556Srgrimes *	Our output routines may be smaller than the stdio routines.
541556Srgrimes */
551556Srgrimes
5620425Ssteve#include <sys/types.h>        /* quad_t */
5717987Speter#include <sys/ioctl.h>
5817987Speter
591556Srgrimes#include <stdio.h>	/* defines BUFSIZ */
6017987Speter#include <string.h>
6117987Speter#include <stdarg.h>
621556Srgrimes#include <errno.h>
6317987Speter#include <unistd.h>
6417987Speter#include <stdlib.h>
651556Srgrimes
6617987Speter#include "shell.h"
6717987Speter#include "syntax.h"
6817987Speter#include "output.h"
6917987Speter#include "memalloc.h"
7017987Speter#include "error.h"
711556Srgrimes
7217987Speter
731556Srgrimes#define OUTBUFSIZ BUFSIZ
741556Srgrimes#define BLOCK_OUT -2		/* output to a fixed block of memory */
751556Srgrimes#define MEM_OUT -3		/* output to dynamically allocated memory */
761556Srgrimes#define OUTPUT_ERR 01		/* error occurred on output */
771556Srgrimes
781556Srgrimes
791556Srgrimesstruct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
8025232Sstevestruct output errout = {NULL, 0, NULL, 100, 2, 0};
811556Srgrimesstruct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
821556Srgrimesstruct output *out1 = &output;
831556Srgrimesstruct output *out2 = &errout;
841556Srgrimes
851556Srgrimes
861556Srgrimes
871556Srgrimes#ifdef mkinit
881556Srgrimes
891556SrgrimesINCLUDE "output.h"
901556SrgrimesINCLUDE "memalloc.h"
911556Srgrimes
921556SrgrimesRESET {
931556Srgrimes	out1 = &output;
941556Srgrimes	out2 = &errout;
951556Srgrimes	if (memout.buf != NULL) {
961556Srgrimes		ckfree(memout.buf);
971556Srgrimes		memout.buf = NULL;
981556Srgrimes	}
991556Srgrimes}
1001556Srgrimes
1011556Srgrimes#endif
1021556Srgrimes
1031556Srgrimes
1041556Srgrimesvoid
10590111Simpout1str(const char *p)
10690111Simp{
1071556Srgrimes	outstr(p, out1);
1081556Srgrimes}
1091556Srgrimes
11097815Stjrvoid
11197815Stjrout1qstr(const char *p)
11297815Stjr{
11397815Stjr	outqstr(p, out1);
11497815Stjr}
1151556Srgrimes
1161556Srgrimesvoid
11790111Simpout2str(const char *p)
11890111Simp{
1191556Srgrimes	outstr(p, out2);
1201556Srgrimes}
1211556Srgrimes
12297815Stjrvoid
12397815Stjrout2qstr(const char *p)
12497815Stjr{
12597815Stjr	outqstr(p, out2);
12697815Stjr}
1271556Srgrimes
1281556Srgrimesvoid
12990111Simpoutstr(const char *p, struct output *file)
13090111Simp{
1311556Srgrimes	while (*p)
1321556Srgrimes		outc(*p++, file);
1331556Srgrimes	if (file == out2)
1341556Srgrimes		flushout(file);
1351556Srgrimes}
1361556Srgrimes
13797815Stjr/* Like outstr(), but quote for re-input into the shell. */
13897815Stjrvoid
13997815Stjroutqstr(const char *p, struct output *file)
14097815Stjr{
14197815Stjr	char ch;
1421556Srgrimes
14397815Stjr	out1c('\'');
14497815Stjr	while ((ch = *p++) != '\0') {
14597815Stjr		switch (ch) {
14697815Stjr		case '\'':
14797815Stjr			/*
14897815Stjr			 * Can't quote single quotes inside single quotes;
14997815Stjr			 * close them, write escaped single quote, open again.
15097815Stjr			 */
15197815Stjr			outstr("'\\''", file);
15297815Stjr			break;
15397815Stjr		default:
15497815Stjr			outc(ch, file);
15597815Stjr		}
15697815Stjr	}
15797815Stjr	out1c('\'');
15897815Stjr}
15997815Stjr
1601556Srgrimeschar out_junk[16];
1611556Srgrimes
1621556Srgrimesvoid
16390111Simpemptyoutbuf(struct output *dest)
16490111Simp{
1651556Srgrimes	int offset;
1661556Srgrimes
1671556Srgrimes	if (dest->fd == BLOCK_OUT) {
1681556Srgrimes		dest->nextc = out_junk;
1691556Srgrimes		dest->nleft = sizeof out_junk;
1701556Srgrimes		dest->flags |= OUTPUT_ERR;
1711556Srgrimes	} else if (dest->buf == NULL) {
1721556Srgrimes		INTOFF;
1731556Srgrimes		dest->buf = ckmalloc(dest->bufsize);
1741556Srgrimes		dest->nextc = dest->buf;
1751556Srgrimes		dest->nleft = dest->bufsize;
1761556Srgrimes		INTON;
1771556Srgrimes	} else if (dest->fd == MEM_OUT) {
1781556Srgrimes		offset = dest->bufsize;
1791556Srgrimes		INTOFF;
1801556Srgrimes		dest->bufsize <<= 1;
1811556Srgrimes		dest->buf = ckrealloc(dest->buf, dest->bufsize);
1821556Srgrimes		dest->nleft = dest->bufsize - offset;
1831556Srgrimes		dest->nextc = dest->buf + offset;
1841556Srgrimes		INTON;
1851556Srgrimes	} else {
1861556Srgrimes		flushout(dest);
1871556Srgrimes	}
1881556Srgrimes	dest->nleft--;
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimes
1921556Srgrimesvoid
19390111Simpflushall(void)
19490111Simp{
1951556Srgrimes	flushout(&output);
1961556Srgrimes	flushout(&errout);
1971556Srgrimes}
1981556Srgrimes
1991556Srgrimes
2001556Srgrimesvoid
20190111Simpflushout(struct output *dest)
20290111Simp{
2031556Srgrimes
2041556Srgrimes	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
2051556Srgrimes		return;
2061556Srgrimes	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
2071556Srgrimes		dest->flags |= OUTPUT_ERR;
2081556Srgrimes	dest->nextc = dest->buf;
2091556Srgrimes	dest->nleft = dest->bufsize;
2101556Srgrimes}
2111556Srgrimes
2121556Srgrimes
2131556Srgrimesvoid
21490111Simpfreestdout(void)
21590111Simp{
2161556Srgrimes	INTOFF;
2171556Srgrimes	if (output.buf) {
2181556Srgrimes		ckfree(output.buf);
2191556Srgrimes		output.buf = NULL;
2201556Srgrimes		output.nleft = 0;
2211556Srgrimes	}
2221556Srgrimes	INTON;
2231556Srgrimes}
2241556Srgrimes
2251556Srgrimes
2261556Srgrimesvoid
22790111Simpoutfmt(struct output *file, const char *fmt, ...)
22890111Simp{
2291556Srgrimes	va_list ap;
2301556Srgrimes
2311556Srgrimes	va_start(ap, fmt);
2321556Srgrimes	doformat(file, fmt, ap);
2331556Srgrimes	va_end(ap);
2341556Srgrimes}
2351556Srgrimes
2361556Srgrimes
2371556Srgrimesvoid
23890111Simpout1fmt(const char *fmt, ...)
23990111Simp{
2401556Srgrimes	va_list ap;
2411556Srgrimes
2421556Srgrimes	va_start(ap, fmt);
2431556Srgrimes	doformat(out1, fmt, ap);
2441556Srgrimes	va_end(ap);
2451556Srgrimes}
2461556Srgrimes
2471556Srgrimesvoid
24890111Simpdprintf(const char *fmt, ...)
24990111Simp{
2501556Srgrimes	va_list ap;
2511556Srgrimes
2521556Srgrimes	va_start(ap, fmt);
2531556Srgrimes	doformat(out2, fmt, ap);
2541556Srgrimes	va_end(ap);
2551556Srgrimes	flushout(out2);
2561556Srgrimes}
2571556Srgrimes
2581556Srgrimesvoid
25990111Simpfmtstr(char *outbuf, int length, const char *fmt, ...)
26090111Simp{
2611556Srgrimes	va_list ap;
2621556Srgrimes	struct output strout;
2631556Srgrimes
2641556Srgrimes	va_start(ap, fmt);
2651556Srgrimes	strout.nextc = outbuf;
2661556Srgrimes	strout.nleft = length;
2671556Srgrimes	strout.fd = BLOCK_OUT;
2681556Srgrimes	strout.flags = 0;
2691556Srgrimes	doformat(&strout, fmt, ap);
2701556Srgrimes	outc('\0', &strout);
2711556Srgrimes	if (strout.flags & OUTPUT_ERR)
2721556Srgrimes		outbuf[length - 1] = '\0';
2731556Srgrimes}
2741556Srgrimes
2751556Srgrimes/*
2761556Srgrimes * Formatted output.  This routine handles a subset of the printf formats:
2771556Srgrimes * - Formats supported: d, u, o, X, s, and c.
2781556Srgrimes * - The x format is also accepted but is treated like X.
27920425Ssteve * - The l and q modifiers are accepted.
2801556Srgrimes * - The - and # flags are accepted; # only works with the o format.
2811556Srgrimes * - Width and precision may be specified with any format except c.
2821556Srgrimes * - An * may be given for the width or precision.
2831556Srgrimes * - The obsolete practice of preceding the width with a zero to get
2841556Srgrimes *   zero padding is not supported; use the precision field.
2851556Srgrimes * - A % may be printed by writing %% in the format string.
2861556Srgrimes */
2871556Srgrimes
2881556Srgrimes#define TEMPSIZE 24
2891556Srgrimes
29018018Speterstatic const char digit[] = "0123456789ABCDEF";
2911556Srgrimes
2921556Srgrimes
2931556Srgrimesvoid
29490111Simpdoformat(struct output *dest, const char *f, va_list ap)
29590111Simp{
29625232Ssteve	char c;
2971556Srgrimes	char temp[TEMPSIZE];
2981556Srgrimes	int flushleft;
2991556Srgrimes	int sharp;
3001556Srgrimes	int width;
3011556Srgrimes	int prec;
3021556Srgrimes	int islong;
30318018Speter	int isquad;
3041556Srgrimes	char *p;
3051556Srgrimes	int sign;
30618018Speter	quad_t l;
30718018Speter	u_quad_t num;
3081556Srgrimes	unsigned base;
3091556Srgrimes	int len;
3101556Srgrimes	int size;
3111556Srgrimes	int pad;
3121556Srgrimes
3131556Srgrimes	while ((c = *f++) != '\0') {
3141556Srgrimes		if (c != '%') {
3151556Srgrimes			outc(c, dest);
3161556Srgrimes			continue;
3171556Srgrimes		}
3181556Srgrimes		flushleft = 0;
3191556Srgrimes		sharp = 0;
3201556Srgrimes		width = 0;
3211556Srgrimes		prec = -1;
3221556Srgrimes		islong = 0;
32318018Speter		isquad = 0;
3241556Srgrimes		for (;;) {
3251556Srgrimes			if (*f == '-')
3261556Srgrimes				flushleft++;
3271556Srgrimes			else if (*f == '#')
3281556Srgrimes				sharp++;
3291556Srgrimes			else
3301556Srgrimes				break;
3311556Srgrimes			f++;
3321556Srgrimes		}
3331556Srgrimes		if (*f == '*') {
3341556Srgrimes			width = va_arg(ap, int);
3351556Srgrimes			f++;
3361556Srgrimes		} else {
3371556Srgrimes			while (is_digit(*f)) {
3381556Srgrimes				width = 10 * width + digit_val(*f++);
3391556Srgrimes			}
3401556Srgrimes		}
3411556Srgrimes		if (*f == '.') {
3421556Srgrimes			if (*++f == '*') {
3431556Srgrimes				prec = va_arg(ap, int);
3441556Srgrimes				f++;
3451556Srgrimes			} else {
3461556Srgrimes				prec = 0;
3471556Srgrimes				while (is_digit(*f)) {
3481556Srgrimes					prec = 10 * prec + digit_val(*f++);
3491556Srgrimes				}
3501556Srgrimes			}
3511556Srgrimes		}
3521556Srgrimes		if (*f == 'l') {
3531556Srgrimes			islong++;
3541556Srgrimes			f++;
35518018Speter		} else if (*f == 'q') {
35618018Speter			isquad++;
35718018Speter			f++;
3581556Srgrimes		}
3591556Srgrimes		switch (*f) {
3601556Srgrimes		case 'd':
36120425Ssteve			if (isquad)
36220425Ssteve				l = va_arg(ap, quad_t);
36320425Ssteve			else if (islong)
3641556Srgrimes				l = va_arg(ap, long);
3651556Srgrimes			else
3661556Srgrimes				l = va_arg(ap, int);
3671556Srgrimes			sign = 0;
3681556Srgrimes			num = l;
3691556Srgrimes			if (l < 0) {
3701556Srgrimes				num = -l;
3711556Srgrimes				sign = 1;
3721556Srgrimes			}
3731556Srgrimes			base = 10;
3741556Srgrimes			goto number;
3751556Srgrimes		case 'u':
3761556Srgrimes			base = 10;
3771556Srgrimes			goto uns_number;
3781556Srgrimes		case 'o':
3791556Srgrimes			base = 8;
3801556Srgrimes			goto uns_number;
3811556Srgrimes		case 'x':
3821556Srgrimes			/* we don't implement 'x'; treat like 'X' */
3831556Srgrimes		case 'X':
3841556Srgrimes			base = 16;
3851556Srgrimesuns_number:	  /* an unsigned number */
3861556Srgrimes			sign = 0;
38720425Ssteve			if (isquad)
38820425Ssteve				num = va_arg(ap, u_quad_t);
38920425Ssteve			else if (islong)
3901556Srgrimes				num = va_arg(ap, unsigned long);
3911556Srgrimes			else
3921556Srgrimes				num = va_arg(ap, unsigned int);
3931556Srgrimesnumber:		  /* process a number */
3941556Srgrimes			p = temp + TEMPSIZE - 1;
3951556Srgrimes			*p = '\0';
3961556Srgrimes			while (num) {
3971556Srgrimes				*--p = digit[num % base];
3981556Srgrimes				num /= base;
3991556Srgrimes			}
4001556Srgrimes			len = (temp + TEMPSIZE - 1) - p;
4011556Srgrimes			if (prec < 0)
4021556Srgrimes				prec = 1;
4031556Srgrimes			if (sharp && *f == 'o' && prec <= len)
4041556Srgrimes				prec = len + 1;
4051556Srgrimes			pad = 0;
4061556Srgrimes			if (width) {
4071556Srgrimes				size = len;
4081556Srgrimes				if (size < prec)
4091556Srgrimes					size = prec;
4101556Srgrimes				size += sign;
4111556Srgrimes				pad = width - size;
4121556Srgrimes				if (flushleft == 0) {
4131556Srgrimes					while (--pad >= 0)
4141556Srgrimes						outc(' ', dest);
4151556Srgrimes				}
4161556Srgrimes			}
4171556Srgrimes			if (sign)
4181556Srgrimes				outc('-', dest);
4191556Srgrimes			prec -= len;
4201556Srgrimes			while (--prec >= 0)
4211556Srgrimes				outc('0', dest);
4221556Srgrimes			while (*p)
4231556Srgrimes				outc(*p++, dest);
4241556Srgrimes			while (--pad >= 0)
4251556Srgrimes				outc(' ', dest);
4261556Srgrimes			break;
4271556Srgrimes		case 's':
4281556Srgrimes			p = va_arg(ap, char *);
4291556Srgrimes			pad = 0;
4301556Srgrimes			if (width) {
4311556Srgrimes				len = strlen(p);
4321556Srgrimes				if (prec >= 0 && len > prec)
4331556Srgrimes					len = prec;
4341556Srgrimes				pad = width - len;
4351556Srgrimes				if (flushleft == 0) {
4361556Srgrimes					while (--pad >= 0)
4371556Srgrimes						outc(' ', dest);
4381556Srgrimes				}
4391556Srgrimes			}
4401556Srgrimes			prec++;
4411556Srgrimes			while (--prec != 0 && *p)
4421556Srgrimes				outc(*p++, dest);
4431556Srgrimes			while (--pad >= 0)
4441556Srgrimes				outc(' ', dest);
4451556Srgrimes			break;
4461556Srgrimes		case 'c':
4471556Srgrimes			c = va_arg(ap, int);
4481556Srgrimes			outc(c, dest);
4491556Srgrimes			break;
4501556Srgrimes		default:
4511556Srgrimes			outc(*f, dest);
4521556Srgrimes			break;
4531556Srgrimes		}
4541556Srgrimes		f++;
4551556Srgrimes	}
4561556Srgrimes}
4571556Srgrimes
4581556Srgrimes
4591556Srgrimes
4601556Srgrimes/*
4611556Srgrimes * Version of write which resumes after a signal is caught.
4621556Srgrimes */
4631556Srgrimes
4641556Srgrimesint
46590111Simpxwrite(int fd, char *buf, int nbytes)
46690111Simp{
4671556Srgrimes	int ntry;
4681556Srgrimes	int i;
4691556Srgrimes	int n;
4701556Srgrimes
4711556Srgrimes	n = nbytes;
4721556Srgrimes	ntry = 0;
4731556Srgrimes	for (;;) {
4741556Srgrimes		i = write(fd, buf, n);
4751556Srgrimes		if (i > 0) {
4761556Srgrimes			if ((n -= i) <= 0)
4771556Srgrimes				return nbytes;
4781556Srgrimes			buf += i;
4791556Srgrimes			ntry = 0;
4801556Srgrimes		} else if (i == 0) {
4811556Srgrimes			if (++ntry > 10)
4821556Srgrimes				return nbytes - n;
4831556Srgrimes		} else if (errno != EINTR) {
4841556Srgrimes			return -1;
4851556Srgrimes		}
4861556Srgrimes	}
4871556Srgrimes}
488