xref: /netbsd/bin/sh/output.c (revision 14acc88f)
1 /*	$NetBSD: output.c,v 1.41 2023/04/07 10:34:13 kre 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.41 2023/04/07 10:34:13 kre 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 #include "redir.h"
71 #include "options.h"
72 #include "show.h"
73 
74 
75 #define OUTBUFSIZ BUFSIZ
76 #define BLOCK_OUT -2		/* output to a fixed block of memory */
77 #define MEM_OUT -3		/* output to dynamically allocated memory */
78 
79 #ifdef SMALL
80 #define	CHAIN
81 #else
82 #define	CHAIN	,NULL
83 #endif
84 
85 
86 		/*      nextc  nleft  bufsize  buf     fd  flags  chain */
87 struct output output = {NULL,    0, OUTBUFSIZ, NULL,    1,    0   CHAIN };
88 struct output errout = {NULL,    0,       100, NULL,    2,    0   CHAIN };
89 struct output memout = {NULL,    0,         0, NULL, MEM_OUT, 0   CHAIN };
90 struct output *out1 = &output;
91 struct output *out2 = &errout;
92 #ifndef SMALL
93 struct output *outx = &errout;
94 struct output *outxtop = NULL;
95 #endif
96 
97 
98 #ifdef mkinit
99 
100 INCLUDE "output.h"
101 INCLUDE "memalloc.h"
102 
103 RESET {
104 	out1 = &output;
105 	out2 = &errout;
106 	if (memout.buf != NULL) {
107 		ckfree(memout.buf);
108 		memout.buf = NULL;
109 	}
110 }
111 
112 #endif
113 
114 
115 #ifdef notdef	/* no longer used */
116 /*
117  * Set up an output file to write to memory rather than a file.
118  */
119 
120 void
open_mem(char * block,int length,struct output * file)121 open_mem(char *block, int length, struct output *file)
122 {
123 	file->nextc = block;
124 	file->nleft = --length;
125 	file->fd = BLOCK_OUT;
126 	file->flags = 0;
127 }
128 #endif
129 
130 
131 void
out1str(const char * p)132 out1str(const char *p)
133 {
134 	outstr(p, out1);
135 }
136 
137 
138 void
out2str(const char * p)139 out2str(const char *p)
140 {
141 	outstr(p, out2);
142 }
143 
144 #ifndef SMALL
145 void
outxstr(const char * p)146 outxstr(const char *p)
147 {
148 	outstr(p, outx);
149 }
150 #endif
151 
152 
153 void
outstr(const char * p,struct output * file)154 outstr(const char *p, struct output *file)
155 {
156 	char c = 0;
157 
158 	while (*p)
159 		outc((c = *p++), file);
160 	if (file == out2 || (file == outx && c == '\n'))
161 		flushout(file);
162 }
163 
164 
165 void
out2shstr(const char * p)166 out2shstr(const char *p)
167 {
168 	outshstr(p, out2);
169 }
170 
171 #ifndef SMALL
172 void
outxshstr(const char * p)173 outxshstr(const char *p)
174 {
175 	outshstr(p, outx);
176 }
177 #endif
178 
179 /*
180  * ' is in this list, not because it does not require quoting
181  * (which applies to all the others) but because '' quoting cannot
182  * be used to quote it.
183  */
184 static const char norm_chars [] = \
185     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+-=_,.'";
186 
187 static int
inquote(const char * p)188 inquote(const char *p)
189 {
190 	size_t l = strspn(p, norm_chars);
191 	char *s = strchr(p, '\'');
192 
193 	return s == NULL ? p[l] != '\0' : s - p > (off_t)l;
194 }
195 
196 void
outshstr(const char * p,struct output * file)197 outshstr(const char *p, struct output *file)
198 {
199 	int need_q;
200 	int inq;
201 	char c;
202 
203 	if (strchr(p, '\'') != NULL && p[1] != '\0') {
204 		/*
205 		 * string contains ' in it, and is not only the '
206 		 * see if " quoting will work
207 		 */
208 		size_t i = strcspn(p, "\\\"$`");
209 
210 		if (p[i] == '\0') {
211 			/*
212 			 * string contains no $ ` \ or " chars, perfect...
213 			 *
214 			 * We know it contains ' so needs quoting, so
215 			 * this is easy...
216 			 */
217 			outc('"', file);
218 			outstr(p, file);
219 			outc('"', file);
220 			return;
221 		}
222 	}
223 
224 	need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0;
225 
226 	/*
227 	 * Don't emit ' unless something needs quoting before closing '
228 	 */
229 	if (need_q && (p[0] == 0 || inquote(p) != 0)) {
230 		outc('\'', file);
231 		inq = 1;
232 	} else
233 		inq = 0;
234 
235 	while ((c = *p++) != '\0') {
236 		if (c != '\'') {
237 			outc(c, file);
238 			continue;
239 		}
240 
241 		if (inq)
242 			outc('\'', file);	/* inq = 0, implicit */
243 		outc('\\', file);
244 		outc(c, file);
245 		if (need_q && *p != '\0') {
246 			if ((inq = inquote(p)) != 0)
247 				outc('\'', file);
248 		} else
249 			inq = 0;
250 	}
251 
252 	if (inq)
253 		outc('\'', file);
254 
255 	if (file == out2)
256 		flushout(file);
257 }
258 
259 
260 char out_junk[16];
261 
262 
263 void
emptyoutbuf(struct output * dest)264 emptyoutbuf(struct output *dest)
265 {
266 	int offset;
267 
268 	if (dest->fd == BLOCK_OUT) {
269 		dest->nextc = out_junk;
270 		dest->nleft = sizeof out_junk;
271 		dest->flags |= OUTPUT_ERR;
272 	} else if (dest->buf == NULL) {
273 		INTOFF;
274 		dest->buf = ckmalloc(dest->bufsize);
275 		dest->nextc = dest->buf;
276 		dest->nleft = dest->bufsize;
277 		INTON;
278 		VTRACE(DBG_OUTPUT, ("emptyoutbuf now %d @%p for fd %d\n",
279 		    dest->nleft, dest->buf, dest->fd));
280 	} else if (dest->fd == MEM_OUT) {
281 		offset = dest->bufsize;
282 		INTOFF;
283 		dest->bufsize <<= 1;
284 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
285 		dest->nleft = dest->bufsize - offset;
286 		dest->nextc = dest->buf + offset;
287 		INTON;
288 	} else {
289 		flushout(dest);
290 	}
291 	dest->nleft--;
292 }
293 
294 
295 void
flushall(void)296 flushall(void)
297 {
298 	flushout(&output);
299 	flushout(&errout);
300 }
301 
302 
303 void
flushout(struct output * dest)304 flushout(struct output *dest)
305 {
306 
307 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
308 		return;
309 	VTRACE(DBG_OUTPUT, ("flushout fd=%d %zd to write\n", dest->fd,
310 	    (size_t)(dest->nextc - dest->buf)));
311 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
312 		dest->flags |= OUTPUT_ERR;
313 	dest->nextc = dest->buf;
314 	dest->nleft = dest->bufsize;
315 }
316 
317 
318 void
freestdout(void)319 freestdout(void)
320 {
321 	INTOFF;
322 	if (output.buf) {
323 		ckfree(output.buf);
324 		output.buf = NULL;
325 		output.nleft = 0;
326 	}
327 	INTON;
328 }
329 
330 
331 void
outfmt(struct output * file,const char * fmt,...)332 outfmt(struct output *file, const char *fmt, ...)
333 {
334 	va_list ap;
335 
336 	va_start(ap, fmt);
337 	doformat(file, fmt, ap);
338 	va_end(ap);
339 }
340 
341 
342 void
out1fmt(const char * fmt,...)343 out1fmt(const char *fmt, ...)
344 {
345 	va_list ap;
346 
347 	va_start(ap, fmt);
348 	doformat(out1, fmt, ap);
349 	va_end(ap);
350 }
351 
352 #ifdef DEBUG
353 void
debugprintf(const char * fmt,...)354 debugprintf(const char *fmt, ...)
355 {
356 	va_list ap;
357 
358 	va_start(ap, fmt);
359 	doformat(out2, fmt, ap);
360 	va_end(ap);
361 	flushout(out2);
362 }
363 #endif
364 
365 void
fmtstr(char * outbuf,size_t length,const char * fmt,...)366 fmtstr(char *outbuf, size_t length, const char *fmt, ...)
367 {
368 	va_list ap;
369 	struct output strout;
370 
371 	va_start(ap, fmt);
372 	strout.nextc = outbuf;
373 	strout.nleft = length;
374 	strout.fd = BLOCK_OUT;
375 	strout.flags = 0;
376 	doformat(&strout, fmt, ap);
377 	outc('\0', &strout);
378 	if (strout.flags & OUTPUT_ERR)
379 		outbuf[length - 1] = '\0';
380 	va_end(ap);
381 }
382 
383 /*
384  * Formatted output.  This routine handles a subset of the printf formats:
385  * - Formats supported: d, u, o, p, X, s, and c.
386  * - The x format is also accepted but is treated like X.
387  * - The l, ll and q modifiers are accepted.
388  * - The - and # flags are accepted; # only works with the o format.
389  * - Width and precision may be specified with any format except c.
390  * - An * may be given for the width or precision.
391  * - The obsolete practice of preceding the width with a zero to get
392  *   zero padding is not supported; use the precision field.
393  * - A % may be printed by writing %% in the format string.
394  */
395 
396 #define TEMPSIZE 24
397 
398 #ifdef BSD4_4
399 #define HAVE_VASPRINTF 1
400 #endif
401 
402 void
doformat(struct output * dest,const char * f,va_list ap)403 doformat(struct output *dest, const char *f, va_list ap)
404 {
405 #if	HAVE_VASPRINTF
406 	char *s;
407 
408 	vasprintf(&s, f, ap);
409 	if (s == NULL)
410 		error("Could not allocate formatted output buffer");
411 	outstr(s, dest);
412 	free(s);
413 #else	/* !HAVE_VASPRINTF */
414 	static const char digit[] = "0123456789ABCDEF";
415 	char c;
416 	char temp[TEMPSIZE];
417 	int flushleft;
418 	int sharp;
419 	int width;
420 	int prec;
421 	int islong;
422 	int isquad;
423 	char *p;
424 	int sign;
425 #ifdef BSD4_4
426 	quad_t l;
427 	u_quad_t num;
428 #else
429 	long l;
430 	u_long num;
431 #endif
432 	unsigned base;
433 	int len;
434 	int size;
435 	int pad;
436 
437 	while ((c = *f++) != '\0') {
438 		if (c != '%') {
439 			outc(c, dest);
440 			continue;
441 		}
442 		flushleft = 0;
443 		sharp = 0;
444 		width = 0;
445 		prec = -1;
446 		islong = 0;
447 		isquad = 0;
448 		for (;;) {
449 			if (*f == '-')
450 				flushleft++;
451 			else if (*f == '#')
452 				sharp++;
453 			else
454 				break;
455 			f++;
456 		}
457 		if (*f == '*') {
458 			width = va_arg(ap, int);
459 			f++;
460 		} else {
461 			while (is_digit(*f)) {
462 				width = 10 * width + digit_val(*f++);
463 			}
464 		}
465 		if (*f == '.') {
466 			if (*++f == '*') {
467 				prec = va_arg(ap, int);
468 				f++;
469 			} else {
470 				prec = 0;
471 				while (is_digit(*f)) {
472 					prec = 10 * prec + digit_val(*f++);
473 				}
474 			}
475 		}
476 		if (*f == 'l') {
477 			f++;
478 			if (*f == 'l') {
479 				isquad++;
480 				f++;
481 			} else
482 				islong++;
483 		} else if (*f == 'q') {
484 			isquad++;
485 			f++;
486 		}
487 		switch (*f) {
488 		case 'd':
489 #ifdef BSD4_4
490 			if (isquad)
491 				l = va_arg(ap, quad_t);
492 			else
493 #endif
494 			if (islong)
495 				l = va_arg(ap, long);
496 			else
497 				l = va_arg(ap, int);
498 			sign = 0;
499 			num = l;
500 			if (l < 0) {
501 				num = -l;
502 				sign = 1;
503 			}
504 			base = 10;
505 			goto number;
506 		case 'u':
507 			base = 10;
508 			goto uns_number;
509 		case 'o':
510 			base = 8;
511 			goto uns_number;
512 		case 'p':
513 			outc('0', dest);
514 			outc('x', dest);
515 			/*FALLTHROUGH*/
516 		case 'x':
517 			/* we don't implement 'x'; treat like 'X' */
518 		case 'X':
519 			base = 16;
520 uns_number:	  /* an unsigned number */
521 			sign = 0;
522 #ifdef BSD4_4
523 			if (isquad)
524 				num = va_arg(ap, u_quad_t);
525 			else
526 #endif
527 			if (islong)
528 				num = va_arg(ap, unsigned long);
529 			else
530 				num = va_arg(ap, unsigned int);
531 number:		  /* process a number */
532 			p = temp + TEMPSIZE - 1;
533 			*p = '\0';
534 			while (num) {
535 				*--p = digit[num % base];
536 				num /= base;
537 			}
538 			len = (temp + TEMPSIZE - 1) - p;
539 			if (prec < 0)
540 				prec = 1;
541 			if (sharp && *f == 'o' && prec <= len)
542 				prec = len + 1;
543 			pad = 0;
544 			if (width) {
545 				size = len;
546 				if (size < prec)
547 					size = prec;
548 				size += sign;
549 				pad = width - size;
550 				if (flushleft == 0) {
551 					while (--pad >= 0)
552 						outc(' ', dest);
553 				}
554 			}
555 			if (sign)
556 				outc('-', dest);
557 			prec -= len;
558 			while (--prec >= 0)
559 				outc('0', dest);
560 			while (*p)
561 				outc(*p++, dest);
562 			while (--pad >= 0)
563 				outc(' ', dest);
564 			break;
565 		case 's':
566 			p = va_arg(ap, char *);
567 			pad = 0;
568 			if (width) {
569 				len = strlen(p);
570 				if (prec >= 0 && len > prec)
571 					len = prec;
572 				pad = width - len;
573 				if (flushleft == 0) {
574 					while (--pad >= 0)
575 						outc(' ', dest);
576 				}
577 			}
578 			prec++;
579 			while (--prec != 0 && *p)
580 				outc(*p++, dest);
581 			while (--pad >= 0)
582 				outc(' ', dest);
583 			break;
584 		case 'c':
585 			c = va_arg(ap, int);
586 			outc(c, dest);
587 			break;
588 		default:
589 			outc(*f, dest);
590 			break;
591 		}
592 		f++;
593 	}
594 #endif	/* !HAVE_VASPRINTF */
595 }
596 
597 
598 
599 /*
600  * Version of write which resumes after a signal is caught.
601  */
602 
603 int
xwrite(int fd,char * buf,int nbytes)604 xwrite(int fd, char *buf, int nbytes)
605 {
606 	int ntry;
607 	int i;
608 	int n;
609 
610 	n = nbytes;
611 	ntry = 0;
612 	while (n > 0) {
613 		i = write(fd, buf, n);
614 		if (i > 0) {
615 			if ((n -= i) <= 0)
616 				return nbytes;
617 			buf += i;
618 			ntry = 0;
619 		} else if (i == 0) {
620 			if (++ntry > 10)
621 				return nbytes - n;
622 		} else if (errno != EINTR) {
623 			return -1;
624 		}
625 	}
626 	return nbytes;
627 }
628 
629 #ifndef SMALL
630 static void
xtrace_fd_swap(int from,int to)631 xtrace_fd_swap(int from, int to)
632 {
633 	struct output *o = outxtop;
634 
635 	while (o != NULL) {
636 		if (o->fd == from)
637 			o->fd = to;
638 		o = o->chain;
639 	}
640 }
641 
642 /*
643  * the -X flag is to be set or reset (not necessarily changed)
644  * Do what is needed to make tracing go to where it should
645  *
646  * Note: Xflag has not yet been altered, "on" indicates what it will become
647  */
648 
649 void
xtracefdsetup(int on)650 xtracefdsetup(int on)
651 {
652 	if (!on) {
653 		flushout(outx);
654 
655 		if (Xflag != 1)		/* Was already +X */
656 			return;		/* so nothing to do */
657 
658 		outx = out2;
659 		CTRACE(DBG_OUTPUT, ("Tracing to stderr\n"));
660 		return;
661 	}
662 
663 	if (Xflag == 1) {				/* was already set */
664 		/*
665 		 * This is a change of output file only
666 		 * Just close the current one, and reuse the struct output
667 		 */
668 		if (!(outx->flags & OUTPUT_CLONE))
669 			sh_close(outx->fd);
670 	} else if (outxtop == NULL) {
671 		/*
672 		 * -X is just turning on, for the forst time,
673 		 * need a new output struct to become outx
674 		 */
675 		xtrace_clone(1);
676 	} else
677 		outx = outxtop;
678 
679 	if (outx != out2) {
680 		outx->flags &= ~OUTPUT_CLONE;
681 		outx->fd = to_upper_fd(dup(out2->fd));
682 		register_sh_fd(outx->fd, xtrace_fd_swap);
683 	}
684 
685 	CTRACE(DBG_OUTPUT, ("Tracing now to fd %d (from %d)\n",
686 	    outx->fd, out2->fd));
687 }
688 
689 void
xtrace_clone(int new)690 xtrace_clone(int new)
691 {
692 	struct output *o;
693 
694 	CTRACE(DBG_OUTPUT,
695 	    ("xtrace_clone(%d): %sfd=%d buf=%p nleft=%d flags=%x ",
696 	    new, (outx == out2 ? "out2: " : ""),
697 	    outx->fd, outx->buf, outx->nleft, outx->flags));
698 
699 	flushout(outx);
700 
701 	if (!new && outxtop == NULL && Xflag == 0) {
702 		/* following stderr, nothing to save */
703 		CTRACE(DBG_OUTPUT, ("+X\n"));
704 		return;
705 	}
706 
707 	o = ckmalloc(sizeof(*o));
708 	o->fd = outx->fd;
709 	o->flags = OUTPUT_CLONE;
710 	o->bufsize = outx->bufsize;
711 	o->nleft = 0;
712 	o->buf = NULL;
713 	o->nextc = NULL;
714 	o->chain = outxtop;
715 	outx = o;
716 	outxtop = o;
717 
718 	CTRACE(DBG_OUTPUT, ("-> fd=%d flags=%x[CLONE]\n", outx->fd, o->flags));
719 }
720 
721 void
xtrace_pop(void)722 xtrace_pop(void)
723 {
724 	struct output *o;
725 
726 	CTRACE(DBG_OUTPUT, ("trace_pop: fd=%d buf=%p nleft=%d flags=%x ",
727 	    outx->fd, outx->buf, outx->nleft, outx->flags));
728 
729 	flushout(outx);
730 
731 	if (outxtop == NULL) {
732 		/*
733 		 * No -X has been used, so nothing much to do
734 		 */
735 		CTRACE(DBG_OUTPUT, ("+X\n"));
736 		return;
737 	}
738 
739 	o = outxtop;
740 	outx = o->chain;
741 	if (outx == NULL)
742 		outx = &errout;
743 	outxtop = o->chain;
744 	if (o != &errout) {
745 		if (o->fd >= 0 && !(o->flags & OUTPUT_CLONE))
746 			sh_close(o->fd);
747 		if (o->buf)
748 			ckfree(o->buf);
749 		ckfree(o);
750 	}
751 
752 	CTRACE(DBG_OUTPUT, ("-> fd=%d buf=%p nleft=%d flags=%x\n",
753 	    outx->fd, outx->buf, outx->nleft, outx->flags));
754 }
755 #endif /* SMALL */
756