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