1 /*        $NetBSD: output.c,v 1.42 2024/10/03 20:14:01 rillig 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.42 2024/10/03 20:14:01 rillig 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           const 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