| /* $NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $ */ |
| |
| /*- |
| * Copyright (c) 1991, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * This code is derived from software contributed to Berkeley by |
| * Kenneth Almquist. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/cdefs.h> |
| #ifndef lint |
| #if 0 |
| static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; |
| #else |
| __RCSID("$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $"); |
| #endif |
| #endif /* not lint */ |
| |
| /* |
| * Shell output routines. We use our own output routines because: |
| * When a builtin command is interrupted we have to discard |
| * any pending output. |
| * When a builtin command appears in back quotes, we want to |
| * save the output of the command in a region obtained |
| * via malloc, rather than doing a fork and reading the |
| * output of the command via a pipe. |
| * Our output routines may be smaller than the stdio routines. |
| */ |
| |
| #include <sys/types.h> /* quad_t */ |
| #include <sys/param.h> /* BSD4_4 */ |
| #include <sys/ioctl.h> |
| |
| #include <stdio.h> /* defines BUFSIZ */ |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| |
| #include "shell.h" |
| #include "syntax.h" |
| #include "output.h" |
| #include "memalloc.h" |
| #include "error.h" |
| |
| |
| #define OUTBUFSIZ BUFSIZ |
| #define BLOCK_OUT -2 /* output to a fixed block of memory */ |
| #define MEM_OUT -3 /* output to dynamically allocated memory */ |
| #define OUTPUT_ERR 01 /* error occurred on output */ |
| |
| |
| struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; |
| struct output errout = {NULL, 0, NULL, 100, 2, 0}; |
| struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; |
| struct output *out1 = &output; |
| struct output *out2 = &errout; |
| |
| |
| |
| #ifdef mkinit |
| |
| INCLUDE "output.h" |
| INCLUDE "memalloc.h" |
| |
| RESET { |
| out1 = &output; |
| out2 = &errout; |
| if (memout.buf != NULL) { |
| ckfree(memout.buf); |
| memout.buf = NULL; |
| } |
| } |
| |
| #endif |
| |
| |
| #ifdef notdef /* no longer used */ |
| /* |
| * Set up an output file to write to memory rather than a file. |
| */ |
| |
| void |
| open_mem(char *block, int length, struct output *file) |
| { |
| file->nextc = block; |
| file->nleft = --length; |
| file->fd = BLOCK_OUT; |
| file->flags = 0; |
| } |
| #endif |
| |
| |
| void |
| out1str(const char *p) |
| { |
| outstr(p, out1); |
| } |
| |
| |
| void |
| out2str(const char *p) |
| { |
| outstr(p, out2); |
| } |
| |
| |
| void |
| outstr(const char *p, struct output *file) |
| { |
| while (*p) |
| outc(*p++, file); |
| if (file == out2) |
| flushout(file); |
| } |
| |
| |
| char out_junk[16]; |
| |
| |
| void |
| emptyoutbuf(struct output *dest) |
| { |
| int offset; |
| |
| if (dest->fd == BLOCK_OUT) { |
| dest->nextc = out_junk; |
| dest->nleft = sizeof out_junk; |
| dest->flags |= OUTPUT_ERR; |
| } else if (dest->buf == NULL) { |
| INTOFF; |
| dest->buf = ckmalloc(dest->bufsize); |
| dest->nextc = dest->buf; |
| dest->nleft = dest->bufsize; |
| INTON; |
| } else if (dest->fd == MEM_OUT) { |
| offset = dest->bufsize; |
| INTOFF; |
| dest->bufsize <<= 1; |
| dest->buf = ckrealloc(dest->buf, dest->bufsize); |
| dest->nleft = dest->bufsize - offset; |
| dest->nextc = dest->buf + offset; |
| INTON; |
| } else { |
| flushout(dest); |
| } |
| dest->nleft--; |
| } |
| |
| |
| void |
| flushall(void) |
| { |
| flushout(&output); |
| flushout(&errout); |
| } |
| |
| |
| void |
| flushout(struct output *dest) |
| { |
| |
| if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) |
| return; |
| if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) |
| dest->flags |= OUTPUT_ERR; |
| dest->nextc = dest->buf; |
| dest->nleft = dest->bufsize; |
| } |
| |
| |
| void |
| freestdout(void) |
| { |
| INTOFF; |
| if (output.buf) { |
| ckfree(output.buf); |
| output.buf = NULL; |
| output.nleft = 0; |
| } |
| INTON; |
| } |
| |
| |
| void |
| outfmt(struct output *file, const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| doformat(file, fmt, ap); |
| va_end(ap); |
| } |
| |
| |
| void |
| out1fmt(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| doformat(out1, fmt, ap); |
| va_end(ap); |
| } |
| |
| void |
| dprintf(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| doformat(out2, fmt, ap); |
| va_end(ap); |
| flushout(out2); |
| } |
| |
| void |
| fmtstr(char *outbuf, size_t length, const char *fmt, ...) |
| { |
| va_list ap; |
| struct output strout; |
| |
| va_start(ap, fmt); |
| strout.nextc = outbuf; |
| strout.nleft = length; |
| strout.fd = BLOCK_OUT; |
| strout.flags = 0; |
| doformat(&strout, fmt, ap); |
| outc('\0', &strout); |
| if (strout.flags & OUTPUT_ERR) |
| outbuf[length - 1] = '\0'; |
| va_end(ap); |
| } |
| |
| /* |
| * Formatted output. This routine handles a subset of the printf formats: |
| * - Formats supported: d, u, o, p, X, s, and c. |
| * - The x format is also accepted but is treated like X. |
| * - The l, ll and q modifiers are accepted. |
| * - The - and # flags are accepted; # only works with the o format. |
| * - Width and precision may be specified with any format except c. |
| * - An * may be given for the width or precision. |
| * - The obsolete practice of preceding the width with a zero to get |
| * zero padding is not supported; use the precision field. |
| * - A % may be printed by writing %% in the format string. |
| */ |
| |
| #define TEMPSIZE 24 |
| |
| #ifdef BSD4_4 |
| #define HAVE_VASPRINTF 1 |
| #endif |
| |
| void |
| doformat(struct output *dest, const char *f, va_list ap) |
| { |
| #if HAVE_VASPRINTF |
| char *s; |
| |
| vasprintf(&s, f, ap); |
| outstr(s, dest); |
| free(s); |
| #else /* !HAVE_VASPRINTF */ |
| static const char digit[] = "0123456789ABCDEF"; |
| char c; |
| char temp[TEMPSIZE]; |
| int flushleft; |
| int sharp; |
| int width; |
| int prec; |
| int islong; |
| int isquad; |
| char *p; |
| int sign; |
| #ifdef BSD4_4 |
| quad_t l; |
| u_quad_t num; |
| #else |
| long l; |
| u_long num; |
| #endif |
| unsigned base; |
| int len; |
| int size; |
| int pad; |
| |
| while ((c = *f++) != '\0') { |
| if (c != '%') { |
| outc(c, dest); |
| continue; |
| } |
| flushleft = 0; |
| sharp = 0; |
| width = 0; |
| prec = -1; |
| islong = 0; |
| isquad = 0; |
| for (;;) { |
| if (*f == '-') |
| flushleft++; |
| else if (*f == '#') |
| sharp++; |
| else |
| break; |
| f++; |
| } |
| if (*f == '*') { |
| width = va_arg(ap, int); |
| f++; |
| } else { |
| while (is_digit(*f)) { |
| width = 10 * width + digit_val(*f++); |
| } |
| } |
| if (*f == '.') { |
| if (*++f == '*') { |
| prec = va_arg(ap, int); |
| f++; |
| } else { |
| prec = 0; |
| while (is_digit(*f)) { |
| prec = 10 * prec + digit_val(*f++); |
| } |
| } |
| } |
| if (*f == 'l') { |
| f++; |
| if (*f == 'l') { |
| isquad++; |
| f++; |
| } else |
| islong++; |
| } else if (*f == 'q') { |
| isquad++; |
| f++; |
| } |
| switch (*f) { |
| case 'd': |
| #ifdef BSD4_4 |
| if (isquad) |
| l = va_arg(ap, quad_t); |
| else |
| #endif |
| if (islong) |
| l = va_arg(ap, long); |
| else |
| l = va_arg(ap, int); |
| sign = 0; |
| num = l; |
| if (l < 0) { |
| num = -l; |
| sign = 1; |
| } |
| base = 10; |
| goto number; |
| case 'u': |
| base = 10; |
| goto uns_number; |
| case 'o': |
| base = 8; |
| goto uns_number; |
| case 'p': |
| outc('0', dest); |
| outc('x', dest); |
| /*FALLTHROUGH*/ |
| case 'x': |
| /* we don't implement 'x'; treat like 'X' */ |
| case 'X': |
| base = 16; |
| uns_number: /* an unsigned number */ |
| sign = 0; |
| #ifdef BSD4_4 |
| if (isquad) |
| num = va_arg(ap, u_quad_t); |
| else |
| #endif |
| if (islong) |
| num = va_arg(ap, unsigned long); |
| else |
| num = va_arg(ap, unsigned int); |
| number: /* process a number */ |
| p = temp + TEMPSIZE - 1; |
| *p = '\0'; |
| while (num) { |
| *--p = digit[num % base]; |
| num /= base; |
| } |
| len = (temp + TEMPSIZE - 1) - p; |
| if (prec < 0) |
| prec = 1; |
| if (sharp && *f == 'o' && prec <= len) |
| prec = len + 1; |
| pad = 0; |
| if (width) { |
| size = len; |
| if (size < prec) |
| size = prec; |
| size += sign; |
| pad = width - size; |
| if (flushleft == 0) { |
| while (--pad >= 0) |
| outc(' ', dest); |
| } |
| } |
| if (sign) |
| outc('-', dest); |
| prec -= len; |
| while (--prec >= 0) |
| outc('0', dest); |
| while (*p) |
| outc(*p++, dest); |
| while (--pad >= 0) |
| outc(' ', dest); |
| break; |
| case 's': |
| p = va_arg(ap, char *); |
| pad = 0; |
| if (width) { |
| len = strlen(p); |
| if (prec >= 0 && len > prec) |
| len = prec; |
| pad = width - len; |
| if (flushleft == 0) { |
| while (--pad >= 0) |
| outc(' ', dest); |
| } |
| } |
| prec++; |
| while (--prec != 0 && *p) |
| outc(*p++, dest); |
| while (--pad >= 0) |
| outc(' ', dest); |
| break; |
| case 'c': |
| c = va_arg(ap, int); |
| outc(c, dest); |
| break; |
| default: |
| outc(*f, dest); |
| break; |
| } |
| f++; |
| } |
| #endif /* !HAVE_VASPRINTF */ |
| } |
| |
| |
| |
| /* |
| * Version of write which resumes after a signal is caught. |
| */ |
| |
| int |
| xwrite(int fd, char *buf, int nbytes) |
| { |
| int ntry; |
| int i; |
| int n; |
| |
| n = nbytes; |
| ntry = 0; |
| for (;;) { |
| i = write(fd, buf, n); |
| if (i > 0) { |
| if ((n -= i) <= 0) |
| return nbytes; |
| buf += i; |
| ntry = 0; |
| } else if (i == 0) { |
| if (++ntry > 10) |
| return nbytes - n; |
| } else if (errno != EINTR) { |
| return -1; |
| } |
| } |
| } |
| |
| |
| /* |
| * Version of ioctl that retries after a signal is caught. |
| * XXX unused function |
| */ |
| |
| int |
| xioctl(int fd, unsigned long request, char *arg) |
| { |
| int i; |
| |
| while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); |
| return i; |
| } |