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