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