1/* $NetBSD: output.c,v 1.41 2023/04/07 10:34:13 kre Exp $ */ 2 3/*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36#ifndef lint 37#if 0 38static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; 39#else 40__RCSID("$NetBSD: output.c,v 1.41 2023/04/07 10:34:13 kre Exp $"); 41#endif 42#endif /* not lint */ 43 44/* 45 * Shell output routines. We use our own output routines because: 46 * When a builtin command is interrupted we have to discard 47 * any pending output. 48 * When a builtin command appears in back quotes, we want to 49 * save the output of the command in a region obtained 50 * via malloc, rather than doing a fork and reading the 51 * output of the command via a pipe. 52 * Our output routines may be smaller than the stdio routines. 53 */ 54 55#include <sys/types.h> /* quad_t */ 56#include <sys/param.h> /* BSD4_4 */ 57#include <sys/ioctl.h> 58 59#include <stdio.h> /* defines BUFSIZ */ 60#include <string.h> 61#include <errno.h> 62#include <unistd.h> 63#include <stdlib.h> 64 65#include "shell.h" 66#include "syntax.h" 67#include "output.h" 68#include "memalloc.h" 69#include "error.h" 70#include "redir.h" 71#include "options.h" 72#include "show.h" 73 74 75#define OUTBUFSIZ BUFSIZ 76#define BLOCK_OUT -2 /* output to a fixed block of memory */ 77#define MEM_OUT -3 /* output to dynamically allocated memory */ 78 79#ifdef SMALL 80#define CHAIN 81#else 82#define CHAIN ,NULL 83#endif 84 85 86 /* nextc nleft bufsize buf fd flags chain */ 87struct output output = {NULL, 0, OUTBUFSIZ, NULL, 1, 0 CHAIN }; 88struct output errout = {NULL, 0, 100, NULL, 2, 0 CHAIN }; 89struct output memout = {NULL, 0, 0, NULL, MEM_OUT, 0 CHAIN }; 90struct output *out1 = &output; 91struct output *out2 = &errout; 92#ifndef SMALL 93struct output *outx = &errout; 94struct output *outxtop = NULL; 95#endif 96 97 98#ifdef mkinit 99 100INCLUDE "output.h" 101INCLUDE "memalloc.h" 102 103RESET { 104 out1 = &output; 105 out2 = &errout; 106 if (memout.buf != NULL) { 107 ckfree(memout.buf); 108 memout.buf = NULL; 109 } 110} 111 112#endif 113 114 115#ifdef notdef /* no longer used */ 116/* 117 * Set up an output file to write to memory rather than a file. 118 */ 119 120void 121open_mem(char *block, int length, struct output *file) 122{ 123 file->nextc = block; 124 file->nleft = --length; 125 file->fd = BLOCK_OUT; 126 file->flags = 0; 127} 128#endif 129 130 131void 132out1str(const char *p) 133{ 134 outstr(p, out1); 135} 136 137 138void 139out2str(const char *p) 140{ 141 outstr(p, out2); 142} 143 144#ifndef SMALL 145void 146outxstr(const char *p) 147{ 148 outstr(p, outx); 149} 150#endif 151 152 153void 154outstr(const char *p, struct output *file) 155{ 156 char c = 0; 157 158 while (*p) 159 outc((c = *p++), file); 160 if (file == out2 || (file == outx && c == '\n')) 161 flushout(file); 162} 163 164 165void 166out2shstr(const char *p) 167{ 168 outshstr(p, out2); 169} 170 171#ifndef SMALL 172void 173outxshstr(const char *p) 174{ 175 outshstr(p, outx); 176} 177#endif 178 179/* 180 * ' is in this list, not because it does not require quoting 181 * (which applies to all the others) but because '' quoting cannot 182 * be used to quote it. 183 */ 184static const char norm_chars [] = \ 185 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+-=_,.'"; 186 187static int 188inquote(const char *p) 189{ 190 size_t l = strspn(p, norm_chars); 191 char *s = strchr(p, '\''); 192 193 return s == NULL ? p[l] != '\0' : s - p > (off_t)l; 194} 195 196void 197outshstr(const char *p, struct output *file) 198{ 199 int need_q; 200 int inq; 201 char c; 202 203 if (strchr(p, '\'') != NULL && p[1] != '\0') { 204 /* 205 * string contains ' in it, and is not only the ' 206 * see if " quoting will work 207 */ 208 size_t i = strcspn(p, "\\\"$`"); 209 210 if (p[i] == '\0') { 211 /* 212 * string contains no $ ` \ or " chars, perfect... 213 * 214 * We know it contains ' so needs quoting, so 215 * this is easy... 216 */ 217 outc('"', file); 218 outstr(p, file); 219 outc('"', file); 220 return; 221 } 222 } 223 224 need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0; 225 226 /* 227 * Don't emit ' unless something needs quoting before closing ' 228 */ 229 if (need_q && (p[0] == 0 || inquote(p) != 0)) { 230 outc('\'', file); 231 inq = 1; 232 } else 233 inq = 0; 234 235 while ((c = *p++) != '\0') { 236 if (c != '\'') { 237 outc(c, file); 238 continue; 239 } 240 241 if (inq) 242 outc('\'', file); /* inq = 0, implicit */ 243 outc('\\', file); 244 outc(c, file); 245 if (need_q && *p != '\0') { 246 if ((inq = inquote(p)) != 0) 247 outc('\'', file); 248 } else 249 inq = 0; 250 } 251 252 if (inq) 253 outc('\'', file); 254 255 if (file == out2) 256 flushout(file); 257} 258 259 260char out_junk[16]; 261 262 263void 264emptyoutbuf(struct output *dest) 265{ 266 int offset; 267 268 if (dest->fd == BLOCK_OUT) { 269 dest->nextc = out_junk; 270 dest->nleft = sizeof out_junk; 271 dest->flags |= OUTPUT_ERR; 272 } else if (dest->buf == NULL) { 273 INTOFF; 274 dest->buf = ckmalloc(dest->bufsize); 275 dest->nextc = dest->buf; 276 dest->nleft = dest->bufsize; 277 INTON; 278 VTRACE(DBG_OUTPUT, ("emptyoutbuf now %d @%p for fd %d\n", 279 dest->nleft, dest->buf, dest->fd)); 280 } else if (dest->fd == MEM_OUT) { 281 offset = dest->bufsize; 282 INTOFF; 283 dest->bufsize <<= 1; 284 dest->buf = ckrealloc(dest->buf, dest->bufsize); 285 dest->nleft = dest->bufsize - offset; 286 dest->nextc = dest->buf + offset; 287 INTON; 288 } else { 289 flushout(dest); 290 } 291 dest->nleft--; 292} 293 294 295void 296flushall(void) 297{ 298 flushout(&output); 299 flushout(&errout); 300} 301 302 303void 304flushout(struct output *dest) 305{ 306 307 if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) 308 return; 309 VTRACE(DBG_OUTPUT, ("flushout fd=%d %zd to write\n", dest->fd, 310 (size_t)(dest->nextc - dest->buf))); 311 if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) 312 dest->flags |= OUTPUT_ERR; 313 dest->nextc = dest->buf; 314 dest->nleft = dest->bufsize; 315} 316 317 318void 319freestdout(void) 320{ 321 INTOFF; 322 if (output.buf) { 323 ckfree(output.buf); 324 output.buf = NULL; 325 output.nleft = 0; 326 } 327 INTON; 328} 329 330 331void 332outfmt(struct output *file, const char *fmt, ...) 333{ 334 va_list ap; 335 336 va_start(ap, fmt); 337 doformat(file, fmt, ap); 338 va_end(ap); 339} 340 341 342void 343out1fmt(const char *fmt, ...) 344{ 345 va_list ap; 346 347 va_start(ap, fmt); 348 doformat(out1, fmt, ap); 349 va_end(ap); 350} 351 352#ifdef DEBUG 353void 354debugprintf(const char *fmt, ...) 355{ 356 va_list ap; 357 358 va_start(ap, fmt); 359 doformat(out2, fmt, ap); 360 va_end(ap); 361 flushout(out2); 362} 363#endif 364 365void 366fmtstr(char *outbuf, size_t length, const char *fmt, ...) 367{ 368 va_list ap; 369 struct output strout; 370 371 va_start(ap, fmt); 372 strout.nextc = outbuf; 373 strout.nleft = length; 374 strout.fd = BLOCK_OUT; 375 strout.flags = 0; 376 doformat(&strout, fmt, ap); 377 outc('\0', &strout); 378 if (strout.flags & OUTPUT_ERR) 379 outbuf[length - 1] = '\0'; 380 va_end(ap); 381} 382 383/* 384 * Formatted output. This routine handles a subset of the printf formats: 385 * - Formats supported: d, u, o, p, X, s, and c. 386 * - The x format is also accepted but is treated like X. 387 * - The l, ll and q modifiers are accepted. 388 * - The - and # flags are accepted; # only works with the o format. 389 * - Width and precision may be specified with any format except c. 390 * - An * may be given for the width or precision. 391 * - The obsolete practice of preceding the width with a zero to get 392 * zero padding is not supported; use the precision field. 393 * - A % may be printed by writing %% in the format string. 394 */ 395 396#define TEMPSIZE 24 397 398#ifdef BSD4_4 399#define HAVE_VASPRINTF 1 400#endif 401 402void 403doformat(struct output *dest, const char *f, va_list ap) 404{ 405#if HAVE_VASPRINTF 406 char *s; 407 408 vasprintf(&s, f, ap); 409 if (s == NULL) 410 error("Could not allocate formatted output buffer"); 411 outstr(s, dest); 412 free(s); 413#else /* !HAVE_VASPRINTF */ 414 static const char digit[] = "0123456789ABCDEF"; 415 char c; 416 char temp[TEMPSIZE]; 417 int flushleft; 418 int sharp; 419 int width; 420 int prec; 421 int islong; 422 int isquad; 423 char *p; 424 int sign; 425#ifdef BSD4_4 426 quad_t l; 427 u_quad_t num; 428#else 429 long l; 430 u_long num; 431#endif 432 unsigned base; 433 int len; 434 int size; 435 int pad; 436 437 while ((c = *f++) != '\0') { 438 if (c != '%') { 439 outc(c, dest); 440 continue; 441 } 442 flushleft = 0; 443 sharp = 0; 444 width = 0; 445 prec = -1; 446 islong = 0; 447 isquad = 0; 448 for (;;) { 449 if (*f == '-') 450 flushleft++; 451 else if (*f == '#') 452 sharp++; 453 else 454 break; 455 f++; 456 } 457 if (*f == '*') { 458 width = va_arg(ap, int); 459 f++; 460 } else { 461 while (is_digit(*f)) { 462 width = 10 * width + digit_val(*f++); 463 } 464 } 465 if (*f == '.') { 466 if (*++f == '*') { 467 prec = va_arg(ap, int); 468 f++; 469 } else { 470 prec = 0; 471 while (is_digit(*f)) { 472 prec = 10 * prec + digit_val(*f++); 473 } 474 } 475 } 476 if (*f == 'l') { 477 f++; 478 if (*f == 'l') { 479 isquad++; 480 f++; 481 } else 482 islong++; 483 } else if (*f == 'q') { 484 isquad++; 485 f++; 486 } 487 switch (*f) { 488 case 'd': 489#ifdef BSD4_4 490 if (isquad) 491 l = va_arg(ap, quad_t); 492 else 493#endif 494 if (islong) 495 l = va_arg(ap, long); 496 else 497 l = va_arg(ap, int); 498 sign = 0; 499 num = l; 500 if (l < 0) { 501 num = -l; 502 sign = 1; 503 } 504 base = 10; 505 goto number; 506 case 'u': 507 base = 10; 508 goto uns_number; 509 case 'o': 510 base = 8; 511 goto uns_number; 512 case 'p': 513 outc('0', dest); 514 outc('x', dest); 515 /*FALLTHROUGH*/ 516 case 'x': 517 /* we don't implement 'x'; treat like 'X' */ 518 case 'X': 519 base = 16; 520uns_number: /* an unsigned number */ 521 sign = 0; 522#ifdef BSD4_4 523 if (isquad) 524 num = va_arg(ap, u_quad_t); 525 else 526#endif 527 if (islong) 528 num = va_arg(ap, unsigned long); 529 else 530 num = va_arg(ap, unsigned int); 531number: /* process a number */ 532 p = temp + TEMPSIZE - 1; 533 *p = '\0'; 534 while (num) { 535 *--p = digit[num % base]; 536 num /= base; 537 } 538 len = (temp + TEMPSIZE - 1) - p; 539 if (prec < 0) 540 prec = 1; 541 if (sharp && *f == 'o' && prec <= len) 542 prec = len + 1; 543 pad = 0; 544 if (width) { 545 size = len; 546 if (size < prec) 547 size = prec; 548 size += sign; 549 pad = width - size; 550 if (flushleft == 0) { 551 while (--pad >= 0) 552 outc(' ', dest); 553 } 554 } 555 if (sign) 556 outc('-', dest); 557 prec -= len; 558 while (--prec >= 0) 559 outc('0', dest); 560 while (*p) 561 outc(*p++, dest); 562 while (--pad >= 0) 563 outc(' ', dest); 564 break; 565 case 's': 566 p = va_arg(ap, char *); 567 pad = 0; 568 if (width) { 569 len = strlen(p); 570 if (prec >= 0 && len > prec) 571 len = prec; 572 pad = width - len; 573 if (flushleft == 0) { 574 while (--pad >= 0) 575 outc(' ', dest); 576 } 577 } 578 prec++; 579 while (--prec != 0 && *p) 580 outc(*p++, dest); 581 while (--pad >= 0) 582 outc(' ', dest); 583 break; 584 case 'c': 585 c = va_arg(ap, int); 586 outc(c, dest); 587 break; 588 default: 589 outc(*f, dest); 590 break; 591 } 592 f++; 593 } 594#endif /* !HAVE_VASPRINTF */ 595} 596 597 598 599/* 600 * Version of write which resumes after a signal is caught. 601 */ 602 603int 604xwrite(int fd, char *buf, int nbytes) 605{ 606 int ntry; 607 int i; 608 int n; 609 610 n = nbytes; 611 ntry = 0; 612 while (n > 0) { 613 i = write(fd, buf, n); 614 if (i > 0) { 615 if ((n -= i) <= 0) 616 return nbytes; 617 buf += i; 618 ntry = 0; 619 } else if (i == 0) { 620 if (++ntry > 10) 621 return nbytes - n; 622 } else if (errno != EINTR) { 623 return -1; 624 } 625 } 626 return nbytes; 627} 628 629#ifndef SMALL 630static void 631xtrace_fd_swap(int from, int to) 632{ 633 struct output *o = outxtop; 634 635 while (o != NULL) { 636 if (o->fd == from) 637 o->fd = to; 638 o = o->chain; 639 } 640} 641 642/* 643 * the -X flag is to be set or reset (not necessarily changed) 644 * Do what is needed to make tracing go to where it should 645 * 646 * Note: Xflag has not yet been altered, "on" indicates what it will become 647 */ 648 649void 650xtracefdsetup(int on) 651{ 652 if (!on) { 653 flushout(outx); 654 655 if (Xflag != 1) /* Was already +X */ 656 return; /* so nothing to do */ 657 658 outx = out2; 659 CTRACE(DBG_OUTPUT, ("Tracing to stderr\n")); 660 return; 661 } 662 663 if (Xflag == 1) { /* was already set */ 664 /* 665 * This is a change of output file only 666 * Just close the current one, and reuse the struct output 667 */ 668 if (!(outx->flags & OUTPUT_CLONE)) 669 sh_close(outx->fd); 670 } else if (outxtop == NULL) { 671 /* 672 * -X is just turning on, for the forst time, 673 * need a new output struct to become outx 674 */ 675 xtrace_clone(1); 676 } else 677 outx = outxtop; 678 679 if (outx != out2) { 680 outx->flags &= ~OUTPUT_CLONE; 681 outx->fd = to_upper_fd(dup(out2->fd)); 682 register_sh_fd(outx->fd, xtrace_fd_swap); 683 } 684 685 CTRACE(DBG_OUTPUT, ("Tracing now to fd %d (from %d)\n", 686 outx->fd, out2->fd)); 687} 688 689void 690xtrace_clone(int new) 691{ 692 struct output *o; 693 694 CTRACE(DBG_OUTPUT, 695 ("xtrace_clone(%d): %sfd=%d buf=%p nleft=%d flags=%x ", 696 new, (outx == out2 ? "out2: " : ""), 697 outx->fd, outx->buf, outx->nleft, outx->flags)); 698 699 flushout(outx); 700 701 if (!new && outxtop == NULL && Xflag == 0) { 702 /* following stderr, nothing to save */ 703 CTRACE(DBG_OUTPUT, ("+X\n")); 704 return; 705 } 706 707 o = ckmalloc(sizeof(*o)); 708 o->fd = outx->fd; 709 o->flags = OUTPUT_CLONE; 710 o->bufsize = outx->bufsize; 711 o->nleft = 0; 712 o->buf = NULL; 713 o->nextc = NULL; 714 o->chain = outxtop; 715 outx = o; 716 outxtop = o; 717 718 CTRACE(DBG_OUTPUT, ("-> fd=%d flags=%x[CLONE]\n", outx->fd, o->flags)); 719} 720 721void 722xtrace_pop(void) 723{ 724 struct output *o; 725 726 CTRACE(DBG_OUTPUT, ("trace_pop: fd=%d buf=%p nleft=%d flags=%x ", 727 outx->fd, outx->buf, outx->nleft, outx->flags)); 728 729 flushout(outx); 730 731 if (outxtop == NULL) { 732 /* 733 * No -X has been used, so nothing much to do 734 */ 735 CTRACE(DBG_OUTPUT, ("+X\n")); 736 return; 737 } 738 739 o = outxtop; 740 outx = o->chain; 741 if (outx == NULL) 742 outx = &errout; 743 outxtop = o->chain; 744 if (o != &errout) { 745 if (o->fd >= 0 && !(o->flags & OUTPUT_CLONE)) 746 sh_close(o->fd); 747 if (o->buf) 748 ckfree(o->buf); 749 ckfree(o); 750 } 751 752 CTRACE(DBG_OUTPUT, ("-> fd=%d buf=%p nleft=%d flags=%x\n", 753 outx->fd, outx->buf, outx->nleft, outx->flags)); 754} 755#endif /* SMALL */ 756