1 /* $NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc 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.28 2003/08/07 09:05:36 agc 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 outstr(s, dest); 287 free(s); 288 #else /* !HAVE_VASPRINTF */ 289 static const char digit[] = "0123456789ABCDEF"; 290 char c; 291 char temp[TEMPSIZE]; 292 int flushleft; 293 int sharp; 294 int width; 295 int prec; 296 int islong; 297 int isquad; 298 char *p; 299 int sign; 300 #ifdef BSD4_4 301 quad_t l; 302 u_quad_t num; 303 #else 304 long l; 305 u_long num; 306 #endif 307 unsigned base; 308 int len; 309 int size; 310 int pad; 311 312 while ((c = *f++) != '\0') { 313 if (c != '%') { 314 outc(c, dest); 315 continue; 316 } 317 flushleft = 0; 318 sharp = 0; 319 width = 0; 320 prec = -1; 321 islong = 0; 322 isquad = 0; 323 for (;;) { 324 if (*f == '-') 325 flushleft++; 326 else if (*f == '#') 327 sharp++; 328 else 329 break; 330 f++; 331 } 332 if (*f == '*') { 333 width = va_arg(ap, int); 334 f++; 335 } else { 336 while (is_digit(*f)) { 337 width = 10 * width + digit_val(*f++); 338 } 339 } 340 if (*f == '.') { 341 if (*++f == '*') { 342 prec = va_arg(ap, int); 343 f++; 344 } else { 345 prec = 0; 346 while (is_digit(*f)) { 347 prec = 10 * prec + digit_val(*f++); 348 } 349 } 350 } 351 if (*f == 'l') { 352 f++; 353 if (*f == 'l') { 354 isquad++; 355 f++; 356 } else 357 islong++; 358 } else if (*f == 'q') { 359 isquad++; 360 f++; 361 } 362 switch (*f) { 363 case 'd': 364 #ifdef BSD4_4 365 if (isquad) 366 l = va_arg(ap, quad_t); 367 else 368 #endif 369 if (islong) 370 l = va_arg(ap, long); 371 else 372 l = va_arg(ap, int); 373 sign = 0; 374 num = l; 375 if (l < 0) { 376 num = -l; 377 sign = 1; 378 } 379 base = 10; 380 goto number; 381 case 'u': 382 base = 10; 383 goto uns_number; 384 case 'o': 385 base = 8; 386 goto uns_number; 387 case 'p': 388 outc('0', dest); 389 outc('x', dest); 390 /*FALLTHROUGH*/ 391 case 'x': 392 /* we don't implement 'x'; treat like 'X' */ 393 case 'X': 394 base = 16; 395 uns_number: /* an unsigned number */ 396 sign = 0; 397 #ifdef BSD4_4 398 if (isquad) 399 num = va_arg(ap, u_quad_t); 400 else 401 #endif 402 if (islong) 403 num = va_arg(ap, unsigned long); 404 else 405 num = va_arg(ap, unsigned int); 406 number: /* process a number */ 407 p = temp + TEMPSIZE - 1; 408 *p = '\0'; 409 while (num) { 410 *--p = digit[num % base]; 411 num /= base; 412 } 413 len = (temp + TEMPSIZE - 1) - p; 414 if (prec < 0) 415 prec = 1; 416 if (sharp && *f == 'o' && prec <= len) 417 prec = len + 1; 418 pad = 0; 419 if (width) { 420 size = len; 421 if (size < prec) 422 size = prec; 423 size += sign; 424 pad = width - size; 425 if (flushleft == 0) { 426 while (--pad >= 0) 427 outc(' ', dest); 428 } 429 } 430 if (sign) 431 outc('-', dest); 432 prec -= len; 433 while (--prec >= 0) 434 outc('0', dest); 435 while (*p) 436 outc(*p++, dest); 437 while (--pad >= 0) 438 outc(' ', dest); 439 break; 440 case 's': 441 p = va_arg(ap, char *); 442 pad = 0; 443 if (width) { 444 len = strlen(p); 445 if (prec >= 0 && len > prec) 446 len = prec; 447 pad = width - len; 448 if (flushleft == 0) { 449 while (--pad >= 0) 450 outc(' ', dest); 451 } 452 } 453 prec++; 454 while (--prec != 0 && *p) 455 outc(*p++, dest); 456 while (--pad >= 0) 457 outc(' ', dest); 458 break; 459 case 'c': 460 c = va_arg(ap, int); 461 outc(c, dest); 462 break; 463 default: 464 outc(*f, dest); 465 break; 466 } 467 f++; 468 } 469 #endif /* !HAVE_VASPRINTF */ 470 } 471 472 473 474 /* 475 * Version of write which resumes after a signal is caught. 476 */ 477 478 int 479 xwrite(int fd, char *buf, int nbytes) 480 { 481 int ntry; 482 int i; 483 int n; 484 485 n = nbytes; 486 ntry = 0; 487 for (;;) { 488 i = write(fd, buf, n); 489 if (i > 0) { 490 if ((n -= i) <= 0) 491 return nbytes; 492 buf += i; 493 ntry = 0; 494 } else if (i == 0) { 495 if (++ntry > 10) 496 return nbytes - n; 497 } else if (errno != EINTR) { 498 return -1; 499 } 500 } 501 } 502 503 504 /* 505 * Version of ioctl that retries after a signal is caught. 506 * XXX unused function 507 */ 508 509 int 510 xioctl(int fd, unsigned long request, char *arg) 511 { 512 int i; 513 514 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); 515 return i; 516 } 517