xref: /dragonfly/bin/sh/output.c (revision 851dc90d)
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.3 2003/08/24 16:26:00 drhodus 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 STATIC 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 }
491