1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1997-2005
5 *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35/*
36 * Shell output routines.  We use our own output routines because:
37 *	When a builtin command is interrupted we have to discard
38 *		any pending output.
39 *	When a builtin command appears in back quotes, we want to
40 *		save the output of the command in a region obtained
41 *		via malloc, rather than doing a fork and reading the
42 *		output of the command via a pipe.
43 *	Our output routines may be smaller than the stdio routines.
44 */
45
46#include <sys/types.h>		/* quad_t */
47#include <sys/param.h>		/* BSD4_4 */
48#include <sys/ioctl.h>
49
50#include <stdio.h>	/* defines BUFSIZ */
51#include <string.h>
52#include <unistd.h>
53#include <stdlib.h>
54#ifdef USE_GLIBC_STDIO
55#include <fcntl.h>
56#endif
57#include <limits.h>
58
59#include "shell.h"
60#include "syntax.h"
61#include "options.h"
62#include "output.h"
63#include "memalloc.h"
64#include "error.h"
65#include "main.h"
66#include "system.h"
67
68
69#define OUTBUFSIZ BUFSIZ
70#define MEM_OUT -3		/* output to dynamically allocated memory */
71
72
73#ifdef USE_GLIBC_STDIO
74struct output output = {
75	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 1, flags: 0
76};
77struct output errout = {
78	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
79}
80#ifdef notyet
81struct output memout = {
82	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
83};
84#endif
85#else
86struct output output = {
87	nextc: 0, end: 0, buf: 0, bufsize: OUTBUFSIZ, fd: 1, flags: 0
88};
89struct output errout = {
90	nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
91};
92struct output preverrout;
93#ifdef notyet
94struct output memout = {
95	nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
96};
97#endif
98#endif
99struct output *out1 = &output;
100struct output *out2 = &errout;
101
102
103static int xvsnprintf(char *, size_t, const char *, va_list);
104
105
106#ifdef mkinit
107
108INCLUDE "output.h"
109INCLUDE "memalloc.h"
110
111INIT {
112#ifdef USE_GLIBC_STDIO
113	initstreams();
114#endif
115}
116
117RESET {
118#ifdef notyet
119	out1 = &output;
120	out2 = &errout;
121#ifdef USE_GLIBC_STDIO
122	if (memout.stream != NULL)
123		__closememout();
124#endif
125	if (memout.buf != NULL) {
126		ckfree(memout.buf);
127		memout.buf = NULL;
128	}
129#endif
130}
131
132#endif
133
134
135void
136outmem(const char *p, size_t len, struct output *dest)
137{
138#ifdef USE_GLIBC_STDIO
139	INTOFF;
140	fwrite(p, 1, len, dest->stream);
141	INTON;
142#else
143	size_t bufsize;
144	size_t offset;
145	size_t nleft;
146
147	nleft = dest->end - dest->nextc;
148	if (likely(nleft >= len)) {
149buffered:
150		dest->nextc = mempcpy(dest->nextc, p, len);
151		return;
152	}
153
154	bufsize = dest->bufsize;
155	if (!bufsize) {
156		;
157	} else if (dest->buf == NULL) {
158#ifdef notyet
159		if (dest->fd == MEM_OUT && len > bufsize) {
160			bufsize = len;
161		}
162#endif
163		offset = 0;
164#ifdef notyet
165		goto alloc;
166	} else if (dest->fd == MEM_OUT) {
167		offset = bufsize;
168		if (bufsize >= len) {
169			bufsize <<= 1;
170		} else {
171			bufsize += len;
172		}
173		if (bufsize < offset)
174			goto err;
175alloc:
176#endif
177		INTOFF;
178		dest->buf = ckrealloc(dest->buf, bufsize);
179		dest->bufsize = bufsize;
180		dest->end = dest->buf + bufsize;
181		dest->nextc = dest->buf + offset;
182		INTON;
183	} else {
184		flushout(dest);
185	}
186
187	nleft = dest->end - dest->nextc;
188	if (nleft > len)
189		goto buffered;
190
191	if ((xwrite(dest->fd, p, len))) {
192#ifdef notyet
193err:
194#endif
195		dest->flags |= OUTPUT_ERR;
196	}
197#endif
198}
199
200
201void
202outstr(const char *p, struct output *file)
203{
204#ifdef USE_GLIBC_STDIO
205	INTOFF;
206	fputs(p, file->stream);
207	INTON;
208#else
209	size_t len;
210
211	len = strlen(p);
212	outmem(p, len, file);
213#endif
214}
215
216
217#ifndef USE_GLIBC_STDIO
218
219
220void
221outcslow(int c, struct output *dest)
222{
223	char buf = c;
224	outmem(&buf, 1, dest);
225}
226#endif
227
228
229void
230flushall(void)
231{
232	flushout(&output);
233#ifdef FLUSHERR
234	flushout(&errout);
235#endif
236}
237
238
239void
240flushout(struct output *dest)
241{
242#ifdef USE_GLIBC_STDIO
243	INTOFF;
244	fflush(dest->stream);
245	INTON;
246#else
247	size_t len;
248
249	len = dest->nextc - dest->buf;
250	if (!len || dest->fd < 0)
251		return;
252	dest->nextc = dest->buf;
253	if ((xwrite(dest->fd, dest->buf, len)))
254		dest->flags |= OUTPUT_ERR;
255#endif
256}
257
258
259void
260outfmt(struct output *file, const char *fmt, ...)
261{
262	va_list ap;
263
264	va_start(ap, fmt);
265	doformat(file, fmt, ap);
266	va_end(ap);
267}
268
269
270void
271out1fmt(const char *fmt, ...)
272{
273	va_list ap;
274
275	va_start(ap, fmt);
276	doformat(out1, fmt, ap);
277	va_end(ap);
278}
279
280
281int
282fmtstr(char *outbuf, size_t length, const char *fmt, ...)
283{
284	va_list ap;
285	int ret;
286
287	va_start(ap, fmt);
288	ret = xvsnprintf(outbuf, length, fmt, ap);
289	va_end(ap);
290	return ret;
291}
292
293
294static int xvasprintf(char **sp, size_t size, const char *f, va_list ap)
295{
296	char *s;
297	int len;
298	va_list ap2;
299
300	va_copy(ap2, ap);
301	len = xvsnprintf(*sp, size, f, ap2);
302	va_end(ap2);
303	if (len < 0)
304		sh_error("xvsnprintf failed");
305	if (len < size)
306		return len;
307
308	s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1);
309	*sp = s;
310	len = xvsnprintf(s, len + 1, f, ap);
311	return len;
312}
313
314
315int xasprintf(char **sp, const char *f, ...)
316{
317	va_list ap;
318	int ret;
319
320	va_start(ap, f);
321	ret = xvasprintf(sp, 0, f, ap);
322	va_end(ap);
323	return ret;
324}
325
326
327#ifndef USE_GLIBC_STDIO
328void
329doformat(struct output *dest, const char *f, va_list ap)
330{
331	struct stackmark smark;
332	char *s;
333	int len;
334	int olen;
335
336	setstackmark(&smark);
337	s = dest->nextc;
338	olen = dest->end - dest->nextc;
339	len = xvasprintf(&s, olen, f, ap);
340	if (likely(olen > len)) {
341		dest->nextc += len;
342		goto out;
343	}
344	outmem(s, len, dest);
345out:
346	popstackmark(&smark);
347}
348#endif
349
350
351void settitle(const char* title) {
352	if (!iflag || !isatty(0))
353		return;
354	char str[16];
355	int n = snprintf(str, sizeof(str) - 1, "\033]2;%s", title);
356	if (n < 0) {
357		return; // error
358	} else if ((size_t)n >= sizeof(str) - 1) {
359		n = sizeof(str) - 2; // truncated
360	}
361	str[n] = '\007';
362	str[n+1] = '\0';
363	out2str(str);
364}
365
366
367/*
368 * Version of write which resumes after a signal is caught.
369 */
370
371int
372xwrite(int fd, const void *p, size_t n)
373{
374	const char *buf = p;
375
376	while (n) {
377		ssize_t i;
378		size_t m;
379
380		m = n;
381		if (m > SSIZE_MAX)
382			m = SSIZE_MAX;
383		do {
384			i = write(fd, buf, m);
385		} while (i < 0 && errno == EINTR);
386		if (i < 0)
387			return -1;
388		buf += i;
389		n -= i;
390	}
391	return 0;
392}
393
394
395#ifdef notyet
396#ifdef USE_GLIBC_STDIO
397void initstreams() {
398	output.stream = stdout;
399	errout.stream = stderr;
400}
401
402
403void
404openmemout(void) {
405	INTOFF;
406	memout.stream = open_memstream(&memout.buf, &memout.bufsize);
407	INTON;
408}
409
410
411int
412__closememout(void) {
413	int error;
414	error = fclose(memout.stream);
415	memout.stream = NULL;
416	return error;
417}
418#endif
419#endif
420
421
422static int
423xvsnprintf(char *outbuf, size_t length, const char *fmt, va_list ap)
424{
425	int ret;
426
427#ifdef __sun
428	/*
429	 * vsnprintf() on older versions of Solaris returns -1 when
430	 * passed a length of 0.  To avoid this, use a dummy
431	 * 1-character buffer instead.
432	 */
433	char dummy[1];
434
435	if (length == 0) {
436		outbuf = dummy;
437		length = sizeof(dummy);
438	}
439#endif
440
441	INTOFF;
442	ret = vsnprintf(outbuf, length, fmt, ap);
443	INTON;
444	return ret;
445}
446