xref: /dragonfly/bin/sh/output.c (revision d4ef6694)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)output.c	8.2 (Berkeley) 5/4/95
33  * $FreeBSD: head/bin/sh/output.c 253649 2013-07-25 13:09:17Z jilles $
34  */
35 
36 /*
37  * Shell output routines.  We use our own output routines because:
38  *	When a builtin command is interrupted we have to discard
39  *		any pending output.
40  *	When a builtin command appears in back quotes, we want to
41  *		save the output of the command in a region obtained
42  *		via malloc, rather than doing a fork and reading the
43  *		output of the command via a pipe.
44  */
45 
46 #include <stdio.h>	/* defines BUFSIZ */
47 #include <string.h>
48 #include <stdarg.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 
53 #include "shell.h"
54 #include "syntax.h"
55 #include "output.h"
56 #include "memalloc.h"
57 #include "error.h"
58 #include "var.h"
59 
60 
61 #define OUTBUFSIZ BUFSIZ
62 #define MEM_OUT -2		/* output to dynamically allocated memory */
63 #define OUTPUT_ERR 01		/* error occurred on output */
64 
65 static int doformat_wr(void *, const char *, int);
66 
67 struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
68 struct output errout = {NULL, 0, NULL, 256, 2, 0};
69 struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
70 struct output *out1 = &output;
71 struct output *out2 = &errout;
72 
73 void
74 outcslow(int c, struct output *file)
75 {
76 	outc(c, file);
77 }
78 
79 void
80 out1str(const char *p)
81 {
82 	outstr(p, out1);
83 }
84 
85 void
86 out1qstr(const char *p)
87 {
88 	outqstr(p, out1);
89 }
90 
91 void
92 out2str(const char *p)
93 {
94 	outstr(p, out2);
95 }
96 
97 void
98 out2qstr(const char *p)
99 {
100 	outqstr(p, out2);
101 }
102 
103 void
104 outstr(const char *p, struct output *file)
105 {
106 	outbin(p, strlen(p), file);
107 }
108 
109 /* Like outstr(), but quote for re-input into the shell. */
110 void
111 outqstr(const char *p, struct output *file)
112 {
113 	char ch;
114 	int inquotes;
115 
116 	if (p[0] == '\0') {
117 		outstr("''", file);
118 		return;
119 	}
120 	/* Caller will handle '=' if necessary */
121 	if (p[strcspn(p, "|&;<>()$`\\\"' \t\n*?[~#")] == '\0' ||
122 			strcmp(p, "[") == 0) {
123 		outstr(p, file);
124 		return;
125 	}
126 
127 	inquotes = 0;
128 	while ((ch = *p++) != '\0') {
129 		switch (ch) {
130 		case '\'':
131 			/* Can't quote single quotes inside single quotes. */
132 			if (inquotes)
133 				outcslow('\'', file);
134 			inquotes = 0;
135 			outstr("\\'", file);
136 			break;
137 		default:
138 			if (!inquotes)
139 				outcslow('\'', file);
140 			inquotes = 1;
141 			outc(ch, file);
142 		}
143 	}
144 	if (inquotes)
145 		outcslow('\'', file);
146 }
147 
148 void
149 outbin(const void *data, size_t len, struct output *file)
150 {
151 	const char *p;
152 
153 	p = data;
154 	while (len-- > 0)
155 		outc(*p++, file);
156 }
157 
158 void
159 emptyoutbuf(struct output *dest)
160 {
161 	int offset;
162 
163 	if (dest->buf == NULL) {
164 		INTOFF;
165 		dest->buf = ckmalloc(dest->bufsize);
166 		dest->nextc = dest->buf;
167 		dest->nleft = dest->bufsize;
168 		INTON;
169 	} else if (dest->fd == MEM_OUT) {
170 		offset = dest->bufsize;
171 		INTOFF;
172 		dest->bufsize <<= 1;
173 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
174 		dest->nleft = dest->bufsize - offset;
175 		dest->nextc = dest->buf + offset;
176 		INTON;
177 	} else {
178 		flushout(dest);
179 	}
180 	dest->nleft--;
181 }
182 
183 
184 void
185 flushall(void)
186 {
187 	flushout(&output);
188 	flushout(&errout);
189 }
190 
191 
192 void
193 flushout(struct output *dest)
194 {
195 
196 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
197 		return;
198 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
199 		dest->flags |= OUTPUT_ERR;
200 	dest->nextc = dest->buf;
201 	dest->nleft = dest->bufsize;
202 }
203 
204 
205 void
206 freestdout(void)
207 {
208 	INTOFF;
209 	if (output.buf) {
210 		ckfree(output.buf);
211 		output.buf = NULL;
212 		output.nleft = 0;
213 	}
214 	INTON;
215 }
216 
217 
218 int
219 outiserror(struct output *file)
220 {
221 	return (file->flags & OUTPUT_ERR);
222 }
223 
224 
225 void
226 outclearerror(struct output *file)
227 {
228 	file->flags &= ~OUTPUT_ERR;
229 }
230 
231 
232 void
233 outfmt(struct output *file, const char *fmt, ...)
234 {
235 	va_list ap;
236 
237 	va_start(ap, fmt);
238 	doformat(file, fmt, ap);
239 	va_end(ap);
240 }
241 
242 
243 void
244 out1fmt(const char *fmt, ...)
245 {
246 	va_list ap;
247 
248 	va_start(ap, fmt);
249 	doformat(out1, fmt, ap);
250 	va_end(ap);
251 }
252 
253 void
254 out2fmt_flush(const char *fmt, ...)
255 {
256 	va_list ap;
257 
258 	va_start(ap, fmt);
259 	doformat(out2, fmt, ap);
260 	va_end(ap);
261 	flushout(out2);
262 }
263 
264 void
265 fmtstr(char *outbuf, int length, const char *fmt, ...)
266 {
267 	va_list ap;
268 
269 	INTOFF;
270 	va_start(ap, fmt);
271 	vsnprintf(outbuf, length, fmt, ap);
272 	va_end(ap);
273 	INTON;
274 }
275 
276 static int
277 doformat_wr(void *cookie, const char *buf, int len)
278 {
279 	struct output *o;
280 
281 	o = (struct output *)cookie;
282 	outbin(buf, len, o);
283 
284 	return (len);
285 }
286 
287 void
288 doformat(struct output *dest, const char *f, va_list ap)
289 {
290 	FILE *fp;
291 
292 	if ((fp = fwopen(dest, doformat_wr)) != NULL) {
293 		vfprintf(fp, f, ap);
294 		fclose(fp);
295 	}
296 }
297 
298 /*
299  * Version of write which resumes after a signal is caught.
300  */
301 
302 int
303 xwrite(int fd, const char *buf, int nbytes)
304 {
305 	int ntry;
306 	int i;
307 	int n;
308 
309 	n = nbytes;
310 	ntry = 0;
311 	for (;;) {
312 		i = write(fd, buf, n);
313 		if (i > 0) {
314 			if ((n -= i) <= 0)
315 				return nbytes;
316 			buf += i;
317 			ntry = 0;
318 		} else if (i == 0) {
319 			if (++ntry > 10)
320 				return nbytes - n;
321 		} else if (errno != EINTR) {
322 			return -1;
323 		}
324 	}
325 }
326