xref: /freebsd/bin/sh/output.c (revision c11e094d96120a2e0e726ed9705ae0ec08db49b6)
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 
72 
73 #define OUTBUFSIZ BUFSIZ
74 #define BLOCK_OUT -2		/* output to a fixed block of memory */
75 #define MEM_OUT -3		/* output to dynamically allocated memory */
76 #define OUTPUT_ERR 01		/* error occurred on output */
77 
78 
79 struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
80 struct output errout = {NULL, 0, NULL, 100, 2, 0};
81 struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
82 struct output *out1 = &output;
83 struct output *out2 = &errout;
84 
85 
86 
87 #ifdef mkinit
88 
89 INCLUDE "output.h"
90 INCLUDE "memalloc.h"
91 
92 RESET {
93 	out1 = &output;
94 	out2 = &errout;
95 	if (memout.buf != NULL) {
96 		ckfree(memout.buf);
97 		memout.buf = NULL;
98 	}
99 }
100 
101 #endif
102 
103 
104 void
105 out1str(const char *p)
106 {
107 	outstr(p, out1);
108 }
109 
110 
111 void
112 out2str(const char *p)
113 {
114 	outstr(p, out2);
115 }
116 
117 
118 void
119 outstr(const char *p, struct output *file)
120 {
121 	while (*p)
122 		outc(*p++, file);
123 	if (file == out2)
124 		flushout(file);
125 }
126 
127 
128 char out_junk[16];
129 
130 
131 void
132 emptyoutbuf(struct output *dest)
133 {
134 	int offset;
135 
136 	if (dest->fd == BLOCK_OUT) {
137 		dest->nextc = out_junk;
138 		dest->nleft = sizeof out_junk;
139 		dest->flags |= OUTPUT_ERR;
140 	} else if (dest->buf == NULL) {
141 		INTOFF;
142 		dest->buf = ckmalloc(dest->bufsize);
143 		dest->nextc = dest->buf;
144 		dest->nleft = dest->bufsize;
145 		INTON;
146 	} else if (dest->fd == MEM_OUT) {
147 		offset = dest->bufsize;
148 		INTOFF;
149 		dest->bufsize <<= 1;
150 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
151 		dest->nleft = dest->bufsize - offset;
152 		dest->nextc = dest->buf + offset;
153 		INTON;
154 	} else {
155 		flushout(dest);
156 	}
157 	dest->nleft--;
158 }
159 
160 
161 void
162 flushall(void)
163 {
164 	flushout(&output);
165 	flushout(&errout);
166 }
167 
168 
169 void
170 flushout(struct output *dest)
171 {
172 
173 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
174 		return;
175 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
176 		dest->flags |= OUTPUT_ERR;
177 	dest->nextc = dest->buf;
178 	dest->nleft = dest->bufsize;
179 }
180 
181 
182 void
183 freestdout(void)
184 {
185 	INTOFF;
186 	if (output.buf) {
187 		ckfree(output.buf);
188 		output.buf = NULL;
189 		output.nleft = 0;
190 	}
191 	INTON;
192 }
193 
194 
195 void
196 outfmt(struct output *file, const char *fmt, ...)
197 {
198 	va_list ap;
199 
200 	va_start(ap, fmt);
201 	doformat(file, fmt, ap);
202 	va_end(ap);
203 }
204 
205 
206 void
207 out1fmt(const char *fmt, ...)
208 {
209 	va_list ap;
210 
211 	va_start(ap, fmt);
212 	doformat(out1, fmt, ap);
213 	va_end(ap);
214 }
215 
216 void
217 dprintf(const char *fmt, ...)
218 {
219 	va_list ap;
220 
221 	va_start(ap, fmt);
222 	doformat(out2, fmt, ap);
223 	va_end(ap);
224 	flushout(out2);
225 }
226 
227 void
228 fmtstr(char *outbuf, int length, const char *fmt, ...)
229 {
230 	va_list ap;
231 	struct output strout;
232 
233 	va_start(ap, fmt);
234 	strout.nextc = outbuf;
235 	strout.nleft = length;
236 	strout.fd = BLOCK_OUT;
237 	strout.flags = 0;
238 	doformat(&strout, fmt, ap);
239 	outc('\0', &strout);
240 	if (strout.flags & OUTPUT_ERR)
241 		outbuf[length - 1] = '\0';
242 }
243 
244 /*
245  * Formatted output.  This routine handles a subset of the printf formats:
246  * - Formats supported: d, u, o, X, s, and c.
247  * - The x format is also accepted but is treated like X.
248  * - The l and q modifiers are accepted.
249  * - The - and # flags are accepted; # only works with the o format.
250  * - Width and precision may be specified with any format except c.
251  * - An * may be given for the width or precision.
252  * - The obsolete practice of preceding the width with a zero to get
253  *   zero padding is not supported; use the precision field.
254  * - A % may be printed by writing %% in the format string.
255  */
256 
257 #define TEMPSIZE 24
258 
259 static const char digit[] = "0123456789ABCDEF";
260 
261 
262 void
263 doformat(struct output *dest, const char *f, va_list ap)
264 {
265 	char c;
266 	char temp[TEMPSIZE];
267 	int flushleft;
268 	int sharp;
269 	int width;
270 	int prec;
271 	int islong;
272 	int isquad;
273 	char *p;
274 	int sign;
275 	quad_t l;
276 	u_quad_t num;
277 	unsigned base;
278 	int len;
279 	int size;
280 	int pad;
281 
282 	while ((c = *f++) != '\0') {
283 		if (c != '%') {
284 			outc(c, dest);
285 			continue;
286 		}
287 		flushleft = 0;
288 		sharp = 0;
289 		width = 0;
290 		prec = -1;
291 		islong = 0;
292 		isquad = 0;
293 		for (;;) {
294 			if (*f == '-')
295 				flushleft++;
296 			else if (*f == '#')
297 				sharp++;
298 			else
299 				break;
300 			f++;
301 		}
302 		if (*f == '*') {
303 			width = va_arg(ap, int);
304 			f++;
305 		} else {
306 			while (is_digit(*f)) {
307 				width = 10 * width + digit_val(*f++);
308 			}
309 		}
310 		if (*f == '.') {
311 			if (*++f == '*') {
312 				prec = va_arg(ap, int);
313 				f++;
314 			} else {
315 				prec = 0;
316 				while (is_digit(*f)) {
317 					prec = 10 * prec + digit_val(*f++);
318 				}
319 			}
320 		}
321 		if (*f == 'l') {
322 			islong++;
323 			f++;
324 		} else if (*f == 'q') {
325 			isquad++;
326 			f++;
327 		}
328 		switch (*f) {
329 		case 'd':
330 			if (isquad)
331 				l = va_arg(ap, quad_t);
332 			else if (islong)
333 				l = va_arg(ap, long);
334 			else
335 				l = va_arg(ap, int);
336 			sign = 0;
337 			num = l;
338 			if (l < 0) {
339 				num = -l;
340 				sign = 1;
341 			}
342 			base = 10;
343 			goto number;
344 		case 'u':
345 			base = 10;
346 			goto uns_number;
347 		case 'o':
348 			base = 8;
349 			goto uns_number;
350 		case 'x':
351 			/* we don't implement 'x'; treat like 'X' */
352 		case 'X':
353 			base = 16;
354 uns_number:	  /* an unsigned number */
355 			sign = 0;
356 			if (isquad)
357 				num = va_arg(ap, u_quad_t);
358 			else if (islong)
359 				num = va_arg(ap, unsigned long);
360 			else
361 				num = va_arg(ap, unsigned int);
362 number:		  /* process a number */
363 			p = temp + TEMPSIZE - 1;
364 			*p = '\0';
365 			while (num) {
366 				*--p = digit[num % base];
367 				num /= base;
368 			}
369 			len = (temp + TEMPSIZE - 1) - p;
370 			if (prec < 0)
371 				prec = 1;
372 			if (sharp && *f == 'o' && prec <= len)
373 				prec = len + 1;
374 			pad = 0;
375 			if (width) {
376 				size = len;
377 				if (size < prec)
378 					size = prec;
379 				size += sign;
380 				pad = width - size;
381 				if (flushleft == 0) {
382 					while (--pad >= 0)
383 						outc(' ', dest);
384 				}
385 			}
386 			if (sign)
387 				outc('-', dest);
388 			prec -= len;
389 			while (--prec >= 0)
390 				outc('0', dest);
391 			while (*p)
392 				outc(*p++, dest);
393 			while (--pad >= 0)
394 				outc(' ', dest);
395 			break;
396 		case 's':
397 			p = va_arg(ap, char *);
398 			pad = 0;
399 			if (width) {
400 				len = strlen(p);
401 				if (prec >= 0 && len > prec)
402 					len = prec;
403 				pad = width - len;
404 				if (flushleft == 0) {
405 					while (--pad >= 0)
406 						outc(' ', dest);
407 				}
408 			}
409 			prec++;
410 			while (--prec != 0 && *p)
411 				outc(*p++, dest);
412 			while (--pad >= 0)
413 				outc(' ', dest);
414 			break;
415 		case 'c':
416 			c = va_arg(ap, int);
417 			outc(c, dest);
418 			break;
419 		default:
420 			outc(*f, dest);
421 			break;
422 		}
423 		f++;
424 	}
425 }
426 
427 
428 
429 /*
430  * Version of write which resumes after a signal is caught.
431  */
432 
433 int
434 xwrite(int fd, char *buf, int nbytes)
435 {
436 	int ntry;
437 	int i;
438 	int n;
439 
440 	n = nbytes;
441 	ntry = 0;
442 	for (;;) {
443 		i = write(fd, buf, n);
444 		if (i > 0) {
445 			if ((n -= i) <= 0)
446 				return nbytes;
447 			buf += i;
448 			ntry = 0;
449 		} else if (i == 0) {
450 			if (++ntry > 10)
451 				return nbytes - n;
452 		} else if (errno != EINTR) {
453 			return -1;
454 		}
455 	}
456 }
457