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