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