output.c revision 97815
1/*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38#if 0 39static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; 40#endif 41static const char rcsid[] = 42 "$FreeBSD: head/bin/sh/output.c 97815 2002-06-04 12:59:12Z tjr $"; 43#endif /* not lint */ 44 45/* 46 * Shell output routines. We use our own output routines because: 47 * When a builtin command is interrupted we have to discard 48 * any pending output. 49 * When a builtin command appears in back quotes, we want to 50 * save the output of the command in a region obtained 51 * via malloc, rather than doing a fork and reading the 52 * output of the command via a pipe. 53 * Our output routines may be smaller than the stdio routines. 54 */ 55 56#include <sys/types.h> /* quad_t */ 57#include <sys/ioctl.h> 58 59#include <stdio.h> /* defines BUFSIZ */ 60#include <string.h> 61#include <stdarg.h> 62#include <errno.h> 63#include <unistd.h> 64#include <stdlib.h> 65 66#include "shell.h" 67#include "syntax.h" 68#include "output.h" 69#include "memalloc.h" 70#include "error.h" 71 72 73#define OUTBUFSIZ BUFSIZ 74#define BLOCK_OUT -2 /* output to a fixed block of memory */ 75#define MEM_OUT -3 /* output to dynamically allocated memory */ 76#define OUTPUT_ERR 01 /* error occurred on output */ 77 78 79struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; 80struct output errout = {NULL, 0, NULL, 100, 2, 0}; 81struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; 82struct output *out1 = &output; 83struct output *out2 = &errout; 84 85 86 87#ifdef mkinit 88 89INCLUDE "output.h" 90INCLUDE "memalloc.h" 91 92RESET { 93 out1 = &output; 94 out2 = &errout; 95 if (memout.buf != NULL) { 96 ckfree(memout.buf); 97 memout.buf = NULL; 98 } 99} 100 101#endif 102 103 104void 105out1str(const char *p) 106{ 107 outstr(p, out1); 108} 109 110void 111out1qstr(const char *p) 112{ 113 outqstr(p, out1); 114} 115 116void 117out2str(const char *p) 118{ 119 outstr(p, out2); 120} 121 122void 123out2qstr(const char *p) 124{ 125 outqstr(p, out2); 126} 127 128void 129outstr(const char *p, struct output *file) 130{ 131 while (*p) 132 outc(*p++, file); 133 if (file == out2) 134 flushout(file); 135} 136 137/* Like outstr(), but quote for re-input into the shell. */ 138void 139outqstr(const char *p, struct output *file) 140{ 141 char ch; 142 143 out1c('\''); 144 while ((ch = *p++) != '\0') { 145 switch (ch) { 146 case '\'': 147 /* 148 * Can't quote single quotes inside single quotes; 149 * close them, write escaped single quote, open again. 150 */ 151 outstr("'\\''", file); 152 break; 153 default: 154 outc(ch, file); 155 } 156 } 157 out1c('\''); 158} 159 160char out_junk[16]; 161 162void 163emptyoutbuf(struct output *dest) 164{ 165 int offset; 166 167 if (dest->fd == BLOCK_OUT) { 168 dest->nextc = out_junk; 169 dest->nleft = sizeof out_junk; 170 dest->flags |= OUTPUT_ERR; 171 } else if (dest->buf == NULL) { 172 INTOFF; 173 dest->buf = ckmalloc(dest->bufsize); 174 dest->nextc = dest->buf; 175 dest->nleft = dest->bufsize; 176 INTON; 177 } else if (dest->fd == MEM_OUT) { 178 offset = dest->bufsize; 179 INTOFF; 180 dest->bufsize <<= 1; 181 dest->buf = ckrealloc(dest->buf, dest->bufsize); 182 dest->nleft = dest->bufsize - offset; 183 dest->nextc = dest->buf + offset; 184 INTON; 185 } else { 186 flushout(dest); 187 } 188 dest->nleft--; 189} 190 191 192void 193flushall(void) 194{ 195 flushout(&output); 196 flushout(&errout); 197} 198 199 200void 201flushout(struct output *dest) 202{ 203 204 if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) 205 return; 206 if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) 207 dest->flags |= OUTPUT_ERR; 208 dest->nextc = dest->buf; 209 dest->nleft = dest->bufsize; 210} 211 212 213void 214freestdout(void) 215{ 216 INTOFF; 217 if (output.buf) { 218 ckfree(output.buf); 219 output.buf = NULL; 220 output.nleft = 0; 221 } 222 INTON; 223} 224 225 226void 227outfmt(struct output *file, const char *fmt, ...) 228{ 229 va_list ap; 230 231 va_start(ap, fmt); 232 doformat(file, fmt, ap); 233 va_end(ap); 234} 235 236 237void 238out1fmt(const char *fmt, ...) 239{ 240 va_list ap; 241 242 va_start(ap, fmt); 243 doformat(out1, fmt, ap); 244 va_end(ap); 245} 246 247void 248dprintf(const char *fmt, ...) 249{ 250 va_list ap; 251 252 va_start(ap, fmt); 253 doformat(out2, fmt, ap); 254 va_end(ap); 255 flushout(out2); 256} 257 258void 259fmtstr(char *outbuf, int length, const char *fmt, ...) 260{ 261 va_list ap; 262 struct output strout; 263 264 va_start(ap, fmt); 265 strout.nextc = outbuf; 266 strout.nleft = length; 267 strout.fd = BLOCK_OUT; 268 strout.flags = 0; 269 doformat(&strout, fmt, ap); 270 outc('\0', &strout); 271 if (strout.flags & OUTPUT_ERR) 272 outbuf[length - 1] = '\0'; 273} 274 275/* 276 * Formatted output. This routine handles a subset of the printf formats: 277 * - Formats supported: d, u, o, X, s, and c. 278 * - The x format is also accepted but is treated like X. 279 * - The l and q modifiers are accepted. 280 * - The - and # flags are accepted; # only works with the o format. 281 * - Width and precision may be specified with any format except c. 282 * - An * may be given for the width or precision. 283 * - The obsolete practice of preceding the width with a zero to get 284 * zero padding is not supported; use the precision field. 285 * - A % may be printed by writing %% in the format string. 286 */ 287 288#define TEMPSIZE 24 289 290static const char digit[] = "0123456789ABCDEF"; 291 292 293void 294doformat(struct output *dest, const char *f, va_list ap) 295{ 296 char c; 297 char temp[TEMPSIZE]; 298 int flushleft; 299 int sharp; 300 int width; 301 int prec; 302 int islong; 303 int isquad; 304 char *p; 305 int sign; 306 quad_t l; 307 u_quad_t num; 308 unsigned base; 309 int len; 310 int size; 311 int pad; 312 313 while ((c = *f++) != '\0') { 314 if (c != '%') { 315 outc(c, dest); 316 continue; 317 } 318 flushleft = 0; 319 sharp = 0; 320 width = 0; 321 prec = -1; 322 islong = 0; 323 isquad = 0; 324 for (;;) { 325 if (*f == '-') 326 flushleft++; 327 else if (*f == '#') 328 sharp++; 329 else 330 break; 331 f++; 332 } 333 if (*f == '*') { 334 width = va_arg(ap, int); 335 f++; 336 } else { 337 while (is_digit(*f)) { 338 width = 10 * width + digit_val(*f++); 339 } 340 } 341 if (*f == '.') { 342 if (*++f == '*') { 343 prec = va_arg(ap, int); 344 f++; 345 } else { 346 prec = 0; 347 while (is_digit(*f)) { 348 prec = 10 * prec + digit_val(*f++); 349 } 350 } 351 } 352 if (*f == 'l') { 353 islong++; 354 f++; 355 } else if (*f == 'q') { 356 isquad++; 357 f++; 358 } 359 switch (*f) { 360 case 'd': 361 if (isquad) 362 l = va_arg(ap, quad_t); 363 else if (islong) 364 l = va_arg(ap, long); 365 else 366 l = va_arg(ap, int); 367 sign = 0; 368 num = l; 369 if (l < 0) { 370 num = -l; 371 sign = 1; 372 } 373 base = 10; 374 goto number; 375 case 'u': 376 base = 10; 377 goto uns_number; 378 case 'o': 379 base = 8; 380 goto uns_number; 381 case 'x': 382 /* we don't implement 'x'; treat like 'X' */ 383 case 'X': 384 base = 16; 385uns_number: /* an unsigned number */ 386 sign = 0; 387 if (isquad) 388 num = va_arg(ap, u_quad_t); 389 else if (islong) 390 num = va_arg(ap, unsigned long); 391 else 392 num = va_arg(ap, unsigned int); 393number: /* process a number */ 394 p = temp + TEMPSIZE - 1; 395 *p = '\0'; 396 while (num) { 397 *--p = digit[num % base]; 398 num /= base; 399 } 400 len = (temp + TEMPSIZE - 1) - p; 401 if (prec < 0) 402 prec = 1; 403 if (sharp && *f == 'o' && prec <= len) 404 prec = len + 1; 405 pad = 0; 406 if (width) { 407 size = len; 408 if (size < prec) 409 size = prec; 410 size += sign; 411 pad = width - size; 412 if (flushleft == 0) { 413 while (--pad >= 0) 414 outc(' ', dest); 415 } 416 } 417 if (sign) 418 outc('-', dest); 419 prec -= len; 420 while (--prec >= 0) 421 outc('0', dest); 422 while (*p) 423 outc(*p++, dest); 424 while (--pad >= 0) 425 outc(' ', dest); 426 break; 427 case 's': 428 p = va_arg(ap, char *); 429 pad = 0; 430 if (width) { 431 len = strlen(p); 432 if (prec >= 0 && len > prec) 433 len = prec; 434 pad = width - len; 435 if (flushleft == 0) { 436 while (--pad >= 0) 437 outc(' ', dest); 438 } 439 } 440 prec++; 441 while (--prec != 0 && *p) 442 outc(*p++, dest); 443 while (--pad >= 0) 444 outc(' ', dest); 445 break; 446 case 'c': 447 c = va_arg(ap, int); 448 outc(c, dest); 449 break; 450 default: 451 outc(*f, dest); 452 break; 453 } 454 f++; 455 } 456} 457 458 459 460/* 461 * Version of write which resumes after a signal is caught. 462 */ 463 464int 465xwrite(int fd, char *buf, int nbytes) 466{ 467 int ntry; 468 int i; 469 int n; 470 471 n = nbytes; 472 ntry = 0; 473 for (;;) { 474 i = write(fd, buf, n); 475 if (i > 0) { 476 if ((n -= i) <= 0) 477 return nbytes; 478 buf += i; 479 ntry = 0; 480 } else if (i == 0) { 481 if (++ntry > 10) 482 return nbytes - n; 483 } else if (errno != EINTR) { 484 return -1; 485 } 486 } 487} 488