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