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