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