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