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