9c1e8cd77135dd50315b3faca2eb67818d7471af
[dragonfly.git] / bin / sh / output.c
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.2 2003/06/17 04:22:50 dillon 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 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 }