/*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1997-2005 * Herbert Xu . 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. */ /* * 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 /* quad_t */ #include /* BSD4_4 */ #include #include /* defines BUFSIZ */ #include #include #include #ifdef USE_GLIBC_STDIO #include #endif #include #include "shell.h" #include "syntax.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "main.h" #include "system.h" #define OUTBUFSIZ BUFSIZ #define MEM_OUT -3 /* output to dynamically allocated memory */ #ifdef USE_GLIBC_STDIO struct output output = { .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 1, .flags = 0 }; struct output errout = { .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 2, .flags = 0 } #ifdef notyet struct output memout = { .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = MEM_OUT, .flags = 0 }; #endif #else struct output output = { .nextc = 0, .end = 0, .buf = 0, .bufsize = OUTBUFSIZ, .fd = 1, .flags = 0 }; struct output errout = { .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 2, .flags = 0 }; struct output preverrout; #ifdef notyet struct output memout = { .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = MEM_OUT, .flags = 0 }; #endif #endif struct output *out1 = &output; struct output *out2 = &errout; #ifndef USE_GLIBC_STDIO static void __outstr(const char *, size_t, struct output *); #endif static int xvsnprintf(char *, size_t, const char *, va_list); #ifdef mkinit INCLUDE "output.h" INCLUDE "memalloc.h" INIT { #ifdef USE_GLIBC_STDIO initstreams(); #endif } RESET { #ifdef notyet out1 = &output; out2 = &errout; #ifdef USE_GLIBC_STDIO if (memout.stream != NULL) __closememout(); #endif if (memout.buf != NULL) { ckfree(memout.buf); memout.buf = NULL; } #endif } #endif #ifndef USE_GLIBC_STDIO static void __outstr(const char *p, size_t len, struct output *dest) { size_t bufsize; size_t offset; size_t nleft; nleft = dest->end - dest->nextc; if (nleft >= len) { buffered: dest->nextc = mempcpy(dest->nextc, p, len); return; } bufsize = dest->bufsize; if (!bufsize) { ; } else if (dest->buf == NULL) { if (dest->fd == MEM_OUT && len > bufsize) { bufsize = len; } offset = 0; goto alloc; } else if (dest->fd == MEM_OUT) { offset = bufsize; if (bufsize >= len) { bufsize <<= 1; } else { bufsize += len; } if (bufsize < offset) goto err; alloc: INTOFF; dest->buf = ckrealloc(dest->buf, bufsize); dest->bufsize = bufsize; dest->end = dest->buf + bufsize; dest->nextc = dest->buf + offset; INTON; } else { flushout(dest); } nleft = dest->end - dest->nextc; if (nleft > len) goto buffered; if ((xwrite(dest->fd, p, len))) { err: dest->flags |= OUTPUT_ERR; } } #endif void outstr(const char *p, struct output *file) { #ifdef USE_GLIBC_STDIO INTOFF; fputs(p, file->stream); INTON; #else size_t len; len = strlen(p); __outstr(p, len, file); #endif } #ifndef USE_GLIBC_STDIO void outcslow(int c, struct output *dest) { char buf = c; __outstr(&buf, 1, dest); } #endif void flushall(void) { flushout(&output); #ifdef FLUSHERR flushout(&errout); #endif } void flushout(struct output *dest) { #ifdef USE_GLIBC_STDIO INTOFF; fflush(dest->stream); INTON; #else size_t len; len = dest->nextc - dest->buf; if (!len || dest->fd < 0) return; dest->nextc = dest->buf; if ((xwrite(dest->fd, dest->buf, len))) dest->flags |= OUTPUT_ERR; #endif } 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); } int fmtstr(char *outbuf, size_t length, const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = xvsnprintf(outbuf, length, fmt, ap); va_end(ap); return ret; } #ifndef USE_GLIBC_STDIO void doformat(struct output *dest, const char *f, va_list ap) { struct stackmark smark; char *s; int len, ret; size_t size; va_list ap2; va_copy(ap2, ap); size = dest->end - dest->nextc; len = xvsnprintf(dest->nextc, size, f, ap2); va_end(ap2); if (len < 0) { dest->flags |= OUTPUT_ERR; return; } if (len < size) { dest->nextc += len; return; } setstackmark(&smark); s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); ret = xvsnprintf(s, len + 1, f, ap); if (ret == len) __outstr(s, len, dest); else dest->flags |= OUTPUT_ERR; popstackmark(&smark); } #endif /* * Version of write which resumes after a signal is caught. */ int xwrite(int fd, const void *p, size_t n) { const char *buf = p; while (n) { ssize_t i; size_t m; m = n; if (m > SSIZE_MAX) m = SSIZE_MAX; do { i = write(fd, buf, m); } while (i < 0 && errno == EINTR); if (i < 0) return -1; buf += i; n -= i; } return 0; } #ifdef notyet #ifdef USE_GLIBC_STDIO void initstreams() { output.stream = stdout; errout.stream = stderr; } void openmemout(void) { INTOFF; memout.stream = open_memstream(&memout.buf, &memout.bufsize); INTON; } int __closememout(void) { int error; error = fclose(memout.stream); memout.stream = NULL; return error; } #endif #endif static int xvsnprintf(char *outbuf, size_t length, const char *fmt, va_list ap) { int ret; #ifdef __sun /* * vsnprintf() on older versions of Solaris returns -1 when * passed a length of 0. To avoid this, use a dummy * 1-character buffer instead. */ char dummy[1]; if (length == 0) { outbuf = dummy; length = sizeof(dummy); } #endif INTOFF; ret = vsnprintf(outbuf, length, fmt, ap); INTON; return ret; }