xref: /freebsd/bin/sh/output.c (revision 3ff369fed2a08f32dda232c10470b949bef9489f)
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