xref: /netbsd-src/bin/sh/output.c (revision d47295cc35d0f14450e9b401e5c838084cc9e0d2)
1*d47295ccSrillig /*	$NetBSD: output.c,v 1.42 2024/10/03 20:14:01 rillig Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*d47295ccSrillig __RCSID("$NetBSD: output.c,v 1.42 2024/10/03 20:14:01 rillig Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4461f28255Scgd /*
4561f28255Scgd  * Shell output routines.  We use our own output routines because:
4661f28255Scgd  *	When a builtin command is interrupted we have to discard
4761f28255Scgd  *		any pending output.
4861f28255Scgd  *	When a builtin command appears in back quotes, we want to
4961f28255Scgd  *		save the output of the command in a region obtained
5061f28255Scgd  *		via malloc, rather than doing a fork and reading the
5161f28255Scgd  *		output of the command via a pipe.
5261f28255Scgd  *	Our output routines may be smaller than the stdio routines.
5361f28255Scgd  */
5461f28255Scgd 
55d4fe82dfSjtc #include <sys/types.h>		/* quad_t */
56e3f5fb92Schristos #include <sys/param.h>		/* BSD4_4 */
574ce0d34aScgd #include <sys/ioctl.h>
584ce0d34aScgd 
5961f28255Scgd #include <stdio.h>	/* defines BUFSIZ */
6042f0dae5Scgd #include <string.h>
6161f28255Scgd #include <errno.h>
62a81e4124Sjtc #include <unistd.h>
6307bae7edSchristos #include <stdlib.h>
6407bae7edSchristos 
6507bae7edSchristos #include "shell.h"
6607bae7edSchristos #include "syntax.h"
6707bae7edSchristos #include "output.h"
6807bae7edSchristos #include "memalloc.h"
6907bae7edSchristos #include "error.h"
70ffc64c63Skre #include "redir.h"
71ffc64c63Skre #include "options.h"
72ffc64c63Skre #include "show.h"
7361f28255Scgd 
7461f28255Scgd 
7561f28255Scgd #define OUTBUFSIZ BUFSIZ
7661f28255Scgd #define BLOCK_OUT -2		/* output to a fixed block of memory */
7761f28255Scgd #define MEM_OUT -3		/* output to dynamically allocated memory */
7861f28255Scgd 
79ae40879bSkre #ifdef SMALL
80ae40879bSkre #define	CHAIN
81ae40879bSkre #else
82ae40879bSkre #define	CHAIN	,NULL
83ae40879bSkre #endif
84ae40879bSkre 
8561f28255Scgd 
86ffc64c63Skre 		/*      nextc  nleft  bufsize  buf     fd  flags  chain */
87ae40879bSkre struct output output = {NULL,    0, OUTBUFSIZ, NULL,    1,    0   CHAIN };
88ae40879bSkre struct output errout = {NULL,    0,       100, NULL,    2,    0   CHAIN };
89ae40879bSkre struct output memout = {NULL,    0,         0, NULL, MEM_OUT, 0   CHAIN };
9061f28255Scgd struct output *out1 = &output;
9161f28255Scgd struct output *out2 = &errout;
92ae40879bSkre #ifndef SMALL
93ffc64c63Skre struct output *outx = &errout;
94ffc64c63Skre struct output *outxtop = NULL;
95ae40879bSkre #endif
9661f28255Scgd 
9761f28255Scgd 
9861f28255Scgd #ifdef mkinit
9961f28255Scgd 
10061f28255Scgd INCLUDE "output.h"
10161f28255Scgd INCLUDE "memalloc.h"
10261f28255Scgd 
10361f28255Scgd RESET {
10461f28255Scgd 	out1 = &output;
10561f28255Scgd 	out2 = &errout;
10661f28255Scgd 	if (memout.buf != NULL) {
10761f28255Scgd 		ckfree(memout.buf);
10861f28255Scgd 		memout.buf = NULL;
10961f28255Scgd 	}
11061f28255Scgd }
11161f28255Scgd 
11261f28255Scgd #endif
11361f28255Scgd 
11461f28255Scgd 
11561f28255Scgd #ifdef notdef	/* no longer used */
11661f28255Scgd /*
11761f28255Scgd  * Set up an output file to write to memory rather than a file.
11861f28255Scgd  */
11961f28255Scgd 
12061f28255Scgd void
121c02b3bbdSchristos open_mem(char *block, int length, struct output *file)
12261f28255Scgd {
12361f28255Scgd 	file->nextc = block;
12461f28255Scgd 	file->nleft = --length;
12561f28255Scgd 	file->fd = BLOCK_OUT;
12661f28255Scgd 	file->flags = 0;
12761f28255Scgd }
12861f28255Scgd #endif
12961f28255Scgd 
13061f28255Scgd 
13161f28255Scgd void
132c02b3bbdSchristos out1str(const char *p)
13361f28255Scgd {
13461f28255Scgd 	outstr(p, out1);
13561f28255Scgd }
13661f28255Scgd 
13761f28255Scgd 
13861f28255Scgd void
139c02b3bbdSchristos out2str(const char *p)
14061f28255Scgd {
14161f28255Scgd 	outstr(p, out2);
14261f28255Scgd }
14361f28255Scgd 
144ae40879bSkre #ifndef SMALL
145ffc64c63Skre void
146ffc64c63Skre outxstr(const char *p)
147ffc64c63Skre {
148ffc64c63Skre 	outstr(p, outx);
149ffc64c63Skre }
150ae40879bSkre #endif
151ffc64c63Skre 
15261f28255Scgd 
15361f28255Scgd void
154c02b3bbdSchristos outstr(const char *p, struct output *file)
15561f28255Scgd {
156ffc64c63Skre 	char c = 0;
157ffc64c63Skre 
15861f28255Scgd 	while (*p)
159ffc64c63Skre 		outc((c = *p++), file);
160ffc64c63Skre 	if (file == out2 || (file == outx && c == '\n'))
16137ed7877Sjtc 		flushout(file);
16261f28255Scgd }
16361f28255Scgd 
16461f28255Scgd 
165bc4eb9bdSchristos void
166bc4eb9bdSchristos out2shstr(const char *p)
167bc4eb9bdSchristos {
168bc4eb9bdSchristos 	outshstr(p, out2);
169bc4eb9bdSchristos }
170bc4eb9bdSchristos 
171ae40879bSkre #ifndef SMALL
172ffc64c63Skre void
173ffc64c63Skre outxshstr(const char *p)
174ffc64c63Skre {
175ffc64c63Skre 	outshstr(p, outx);
176ffc64c63Skre }
177ae40879bSkre #endif
178bc4eb9bdSchristos 
179f5deb3baSkre /*
180f5deb3baSkre  * ' is in this list, not because it does not require quoting
181f5deb3baSkre  * (which applies to all the others) but because '' quoting cannot
182f5deb3baSkre  * be used to quote it.
183f5deb3baSkre  */
184f87bc150Schristos static const char norm_chars [] = \
185727fae80Skre     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+-=_,.'";
186f87bc150Schristos 
187f87bc150Schristos static int
188f87bc150Schristos inquote(const char *p)
189f87bc150Schristos {
190f87bc150Schristos 	size_t l = strspn(p, norm_chars);
191*d47295ccSrillig 	const char *s = strchr(p, '\'');
192f87bc150Schristos 
193f87bc150Schristos 	return s == NULL ? p[l] != '\0' : s - p > (off_t)l;
194f87bc150Schristos }
195f87bc150Schristos 
196bc4eb9bdSchristos void
197bc4eb9bdSchristos outshstr(const char *p, struct output *file)
198bc4eb9bdSchristos {
199f5deb3baSkre 	int need_q;
200f87bc150Schristos 	int inq;
201bc4eb9bdSchristos 	char c;
202bc4eb9bdSchristos 
203f5deb3baSkre 	if (strchr(p, '\'') != NULL && p[1] != '\0') {
204f5deb3baSkre 		/*
205f5deb3baSkre 		 * string contains ' in it, and is not only the '
206f5deb3baSkre 		 * see if " quoting will work
207f5deb3baSkre 		 */
208f5deb3baSkre 		size_t i = strcspn(p, "\\\"$`");
209f5deb3baSkre 
210f5deb3baSkre 		if (p[i] == '\0') {
211f5deb3baSkre 			/*
212f5deb3baSkre 			 * string contains no $ ` \ or " chars, perfect...
213f5deb3baSkre 			 *
214f5deb3baSkre 			 * We know it contains ' so needs quoting, so
215f5deb3baSkre 			 * this is easy...
216f5deb3baSkre 			 */
217f5deb3baSkre 			outc('"', file);
218f5deb3baSkre 			outstr(p, file);
219f5deb3baSkre 			outc('"', file);
220f5deb3baSkre 			return;
221f5deb3baSkre 		}
222f5deb3baSkre 	}
223f5deb3baSkre 
224f5deb3baSkre 	need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0;
225f5deb3baSkre 
226f87bc150Schristos 	/*
227f87bc150Schristos 	 * Don't emit ' unless something needs quoting before closing '
228f87bc150Schristos 	 */
229f5deb3baSkre 	if (need_q && (p[0] == 0 || inquote(p) != 0)) {
230bc4eb9bdSchristos 		outc('\'', file);
231f5deb3baSkre 		inq = 1;
232f87bc150Schristos 	} else
233f87bc150Schristos 		inq = 0;
234bc4eb9bdSchristos 
235f87bc150Schristos 	while ((c = *p++) != '\0') {
236bc4eb9bdSchristos 		if (c != '\'') {
237bc4eb9bdSchristos 			outc(c, file);
238f87bc150Schristos 			continue;
239bc4eb9bdSchristos 		}
240bc4eb9bdSchristos 
241f87bc150Schristos 		if (inq)
242f87bc150Schristos 			outc('\'', file);	/* inq = 0, implicit */
243f87bc150Schristos 		outc('\\', file);
244f87bc150Schristos 		outc(c, file);
245f87bc150Schristos 		if (need_q && *p != '\0') {
246f87bc150Schristos 			if ((inq = inquote(p)) != 0)
247f87bc150Schristos 				outc('\'', file);
248f87bc150Schristos 		} else
249f87bc150Schristos 			inq = 0;
250f87bc150Schristos 	}
251f87bc150Schristos 
252f87bc150Schristos 	if (inq)
253bc4eb9bdSchristos 		outc('\'', file);
254bc4eb9bdSchristos 
255bc4eb9bdSchristos 	if (file == out2)
256bc4eb9bdSchristos 		flushout(file);
257bc4eb9bdSchristos }
258bc4eb9bdSchristos 
259bc4eb9bdSchristos 
26061f28255Scgd char out_junk[16];
26161f28255Scgd 
26261f28255Scgd 
26361f28255Scgd void
264c02b3bbdSchristos emptyoutbuf(struct output *dest)
26561f28255Scgd {
26661f28255Scgd 	int offset;
26761f28255Scgd 
26861f28255Scgd 	if (dest->fd == BLOCK_OUT) {
26961f28255Scgd 		dest->nextc = out_junk;
27061f28255Scgd 		dest->nleft = sizeof out_junk;
27161f28255Scgd 		dest->flags |= OUTPUT_ERR;
27261f28255Scgd 	} else if (dest->buf == NULL) {
27361f28255Scgd 		INTOFF;
27461f28255Scgd 		dest->buf = ckmalloc(dest->bufsize);
27561f28255Scgd 		dest->nextc = dest->buf;
27661f28255Scgd 		dest->nleft = dest->bufsize;
27761f28255Scgd 		INTON;
278ffc64c63Skre 		VTRACE(DBG_OUTPUT, ("emptyoutbuf now %d @%p for fd %d\n",
279ffc64c63Skre 		    dest->nleft, dest->buf, dest->fd));
28061f28255Scgd 	} else if (dest->fd == MEM_OUT) {
28161f28255Scgd 		offset = dest->bufsize;
28261f28255Scgd 		INTOFF;
28361f28255Scgd 		dest->bufsize <<= 1;
28461f28255Scgd 		dest->buf = ckrealloc(dest->buf, dest->bufsize);
28561f28255Scgd 		dest->nleft = dest->bufsize - offset;
28661f28255Scgd 		dest->nextc = dest->buf + offset;
28761f28255Scgd 		INTON;
28861f28255Scgd 	} else {
28961f28255Scgd 		flushout(dest);
29061f28255Scgd 	}
29161f28255Scgd 	dest->nleft--;
29261f28255Scgd }
29361f28255Scgd 
29461f28255Scgd 
29561f28255Scgd void
296c02b3bbdSchristos flushall(void)
297c02b3bbdSchristos {
29861f28255Scgd 	flushout(&output);
29961f28255Scgd 	flushout(&errout);
30061f28255Scgd }
30161f28255Scgd 
30261f28255Scgd 
30361f28255Scgd void
304c02b3bbdSchristos flushout(struct output *dest)
30561f28255Scgd {
30661f28255Scgd 
30761f28255Scgd 	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
30861f28255Scgd 		return;
309ffc64c63Skre 	VTRACE(DBG_OUTPUT, ("flushout fd=%d %zd to write\n", dest->fd,
310ffc64c63Skre 	    (size_t)(dest->nextc - dest->buf)));
31161f28255Scgd 	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
31261f28255Scgd 		dest->flags |= OUTPUT_ERR;
31361f28255Scgd 	dest->nextc = dest->buf;
31461f28255Scgd 	dest->nleft = dest->bufsize;
31561f28255Scgd }
31661f28255Scgd 
31761f28255Scgd 
31861f28255Scgd void
319c02b3bbdSchristos freestdout(void)
320c02b3bbdSchristos {
32161f28255Scgd 	INTOFF;
32261f28255Scgd 	if (output.buf) {
32361f28255Scgd 		ckfree(output.buf);
32461f28255Scgd 		output.buf = NULL;
32561f28255Scgd 		output.nleft = 0;
32661f28255Scgd 	}
32761f28255Scgd 	INTON;
32861f28255Scgd }
32961f28255Scgd 
33061f28255Scgd 
3312b259b06Schristos void
3322b259b06Schristos outfmt(struct output *file, const char *fmt, ...)
33361f28255Scgd {
33461f28255Scgd 	va_list ap;
33561f28255Scgd 
3362b259b06Schristos 	va_start(ap, fmt);
33761f28255Scgd 	doformat(file, fmt, ap);
33861f28255Scgd 	va_end(ap);
33961f28255Scgd }
34061f28255Scgd 
34161f28255Scgd 
34261f28255Scgd void
3432b259b06Schristos out1fmt(const char *fmt, ...)
34461f28255Scgd {
34561f28255Scgd 	va_list ap;
34661f28255Scgd 
3472b259b06Schristos 	va_start(ap, fmt);
34861f28255Scgd 	doformat(out1, fmt, ap);
34961f28255Scgd 	va_end(ap);
35061f28255Scgd }
35161f28255Scgd 
35242fececdSchristos #ifdef DEBUG
35337ed7877Sjtc void
35442fececdSchristos debugprintf(const char *fmt, ...)
35537ed7877Sjtc {
35637ed7877Sjtc 	va_list ap;
35737ed7877Sjtc 
3582b259b06Schristos 	va_start(ap, fmt);
35937ed7877Sjtc 	doformat(out2, fmt, ap);
36037ed7877Sjtc 	va_end(ap);
36137ed7877Sjtc 	flushout(out2);
36237ed7877Sjtc }
36342fececdSchristos #endif
36461f28255Scgd 
36561f28255Scgd void
3662b259b06Schristos fmtstr(char *outbuf, size_t length, const char *fmt, ...)
36761f28255Scgd {
36861f28255Scgd 	va_list ap;
36961f28255Scgd 	struct output strout;
37061f28255Scgd 
3712b259b06Schristos 	va_start(ap, fmt);
37261f28255Scgd 	strout.nextc = outbuf;
37361f28255Scgd 	strout.nleft = length;
37461f28255Scgd 	strout.fd = BLOCK_OUT;
37561f28255Scgd 	strout.flags = 0;
37661f28255Scgd 	doformat(&strout, fmt, ap);
37761f28255Scgd 	outc('\0', &strout);
37861f28255Scgd 	if (strout.flags & OUTPUT_ERR)
37961f28255Scgd 		outbuf[length - 1] = '\0';
3804c999163Swiz 	va_end(ap);
38161f28255Scgd }
38261f28255Scgd 
38361f28255Scgd /*
38461f28255Scgd  * Formatted output.  This routine handles a subset of the printf formats:
3852b259b06Schristos  * - Formats supported: d, u, o, p, X, s, and c.
38661f28255Scgd  * - The x format is also accepted but is treated like X.
387dd7296f4Slukem  * - The l, ll and q modifiers are accepted.
38861f28255Scgd  * - The - and # flags are accepted; # only works with the o format.
38961f28255Scgd  * - Width and precision may be specified with any format except c.
39061f28255Scgd  * - An * may be given for the width or precision.
39161f28255Scgd  * - The obsolete practice of preceding the width with a zero to get
39261f28255Scgd  *   zero padding is not supported; use the precision field.
39361f28255Scgd  * - A % may be printed by writing %% in the format string.
39461f28255Scgd  */
39561f28255Scgd 
39661f28255Scgd #define TEMPSIZE 24
39761f28255Scgd 
39810fc746eSlukem #ifdef BSD4_4
39910fc746eSlukem #define HAVE_VASPRINTF 1
40010fc746eSlukem #endif
40110fc746eSlukem 
40261f28255Scgd void
403c02b3bbdSchristos doformat(struct output *dest, const char *f, va_list ap)
40461f28255Scgd {
40510fc746eSlukem #if	HAVE_VASPRINTF
40610fc746eSlukem 	char *s;
40710fc746eSlukem 
40810fc746eSlukem 	vasprintf(&s, f, ap);
4092174dda2Srumble 	if (s == NULL)
4102174dda2Srumble 		error("Could not allocate formatted output buffer");
41110fc746eSlukem 	outstr(s, dest);
41210fc746eSlukem 	free(s);
41310fc746eSlukem #else	/* !HAVE_VASPRINTF */
4143ea17f6cSthorpej 	static const char digit[] = "0123456789ABCDEF";
41548250187Stls 	char c;
41661f28255Scgd 	char temp[TEMPSIZE];
41761f28255Scgd 	int flushleft;
41861f28255Scgd 	int sharp;
41961f28255Scgd 	int width;
42061f28255Scgd 	int prec;
42161f28255Scgd 	int islong;
422d4fe82dfSjtc 	int isquad;
42361f28255Scgd 	char *p;
42461f28255Scgd 	int sign;
42592cdde85Schristos #ifdef BSD4_4
426d4fe82dfSjtc 	quad_t l;
427d4fe82dfSjtc 	u_quad_t num;
42892cdde85Schristos #else
42992cdde85Schristos 	long l;
43092cdde85Schristos 	u_long num;
43192cdde85Schristos #endif
43261f28255Scgd 	unsigned base;
43361f28255Scgd 	int len;
43461f28255Scgd 	int size;
43561f28255Scgd 	int pad;
43661f28255Scgd 
43761f28255Scgd 	while ((c = *f++) != '\0') {
43861f28255Scgd 		if (c != '%') {
43961f28255Scgd 			outc(c, dest);
44061f28255Scgd 			continue;
44161f28255Scgd 		}
44261f28255Scgd 		flushleft = 0;
44361f28255Scgd 		sharp = 0;
44461f28255Scgd 		width = 0;
44561f28255Scgd 		prec = -1;
44661f28255Scgd 		islong = 0;
447d4fe82dfSjtc 		isquad = 0;
44861f28255Scgd 		for (;;) {
44961f28255Scgd 			if (*f == '-')
45061f28255Scgd 				flushleft++;
45161f28255Scgd 			else if (*f == '#')
45261f28255Scgd 				sharp++;
45361f28255Scgd 			else
45461f28255Scgd 				break;
45561f28255Scgd 			f++;
45661f28255Scgd 		}
45761f28255Scgd 		if (*f == '*') {
45861f28255Scgd 			width = va_arg(ap, int);
45961f28255Scgd 			f++;
46061f28255Scgd 		} else {
46161f28255Scgd 			while (is_digit(*f)) {
46261f28255Scgd 				width = 10 * width + digit_val(*f++);
46361f28255Scgd 			}
46461f28255Scgd 		}
46561f28255Scgd 		if (*f == '.') {
46661f28255Scgd 			if (*++f == '*') {
46761f28255Scgd 				prec = va_arg(ap, int);
46861f28255Scgd 				f++;
46961f28255Scgd 			} else {
47061f28255Scgd 				prec = 0;
47161f28255Scgd 				while (is_digit(*f)) {
47261f28255Scgd 					prec = 10 * prec + digit_val(*f++);
47361f28255Scgd 				}
47461f28255Scgd 			}
47561f28255Scgd 		}
47661f28255Scgd 		if (*f == 'l') {
47761f28255Scgd 			f++;
478dd7296f4Slukem 			if (*f == 'l') {
479dd7296f4Slukem 				isquad++;
480dd7296f4Slukem 				f++;
481dd7296f4Slukem 			} else
482dd7296f4Slukem 				islong++;
483d4fe82dfSjtc 		} else if (*f == 'q') {
484d4fe82dfSjtc 			isquad++;
485d4fe82dfSjtc 			f++;
48661f28255Scgd 		}
48761f28255Scgd 		switch (*f) {
48861f28255Scgd 		case 'd':
48992cdde85Schristos #ifdef BSD4_4
490d4fe82dfSjtc 			if (isquad)
491d4fe82dfSjtc 				l = va_arg(ap, quad_t);
49292cdde85Schristos 			else
49392cdde85Schristos #endif
49492cdde85Schristos 			if (islong)
49561f28255Scgd 				l = va_arg(ap, long);
49661f28255Scgd 			else
49761f28255Scgd 				l = va_arg(ap, int);
49861f28255Scgd 			sign = 0;
49961f28255Scgd 			num = l;
50061f28255Scgd 			if (l < 0) {
50161f28255Scgd 				num = -l;
50261f28255Scgd 				sign = 1;
50361f28255Scgd 			}
50461f28255Scgd 			base = 10;
50561f28255Scgd 			goto number;
50661f28255Scgd 		case 'u':
50761f28255Scgd 			base = 10;
50861f28255Scgd 			goto uns_number;
50961f28255Scgd 		case 'o':
51061f28255Scgd 			base = 8;
51161f28255Scgd 			goto uns_number;
5122b259b06Schristos 		case 'p':
5132b259b06Schristos 			outc('0', dest);
5142b259b06Schristos 			outc('x', dest);
5152b259b06Schristos 			/*FALLTHROUGH*/
51661f28255Scgd 		case 'x':
51761f28255Scgd 			/* we don't implement 'x'; treat like 'X' */
51861f28255Scgd 		case 'X':
51961f28255Scgd 			base = 16;
52061f28255Scgd uns_number:	  /* an unsigned number */
52161f28255Scgd 			sign = 0;
52292cdde85Schristos #ifdef BSD4_4
523d4fe82dfSjtc 			if (isquad)
524d4fe82dfSjtc 				num = va_arg(ap, u_quad_t);
52592cdde85Schristos 			else
52692cdde85Schristos #endif
52792cdde85Schristos 			if (islong)
52861f28255Scgd 				num = va_arg(ap, unsigned long);
52961f28255Scgd 			else
53061f28255Scgd 				num = va_arg(ap, unsigned int);
53161f28255Scgd number:		  /* process a number */
53261f28255Scgd 			p = temp + TEMPSIZE - 1;
53361f28255Scgd 			*p = '\0';
53461f28255Scgd 			while (num) {
53561f28255Scgd 				*--p = digit[num % base];
53661f28255Scgd 				num /= base;
53761f28255Scgd 			}
53861f28255Scgd 			len = (temp + TEMPSIZE - 1) - p;
53961f28255Scgd 			if (prec < 0)
54061f28255Scgd 				prec = 1;
54161f28255Scgd 			if (sharp && *f == 'o' && prec <= len)
54261f28255Scgd 				prec = len + 1;
54361f28255Scgd 			pad = 0;
54461f28255Scgd 			if (width) {
54561f28255Scgd 				size = len;
54661f28255Scgd 				if (size < prec)
54761f28255Scgd 					size = prec;
54861f28255Scgd 				size += sign;
54961f28255Scgd 				pad = width - size;
55061f28255Scgd 				if (flushleft == 0) {
55161f28255Scgd 					while (--pad >= 0)
55261f28255Scgd 						outc(' ', dest);
55361f28255Scgd 				}
55461f28255Scgd 			}
55561f28255Scgd 			if (sign)
55661f28255Scgd 				outc('-', dest);
55761f28255Scgd 			prec -= len;
55861f28255Scgd 			while (--prec >= 0)
55961f28255Scgd 				outc('0', dest);
56061f28255Scgd 			while (*p)
56161f28255Scgd 				outc(*p++, dest);
56261f28255Scgd 			while (--pad >= 0)
56361f28255Scgd 				outc(' ', dest);
56461f28255Scgd 			break;
56561f28255Scgd 		case 's':
56661f28255Scgd 			p = va_arg(ap, char *);
56761f28255Scgd 			pad = 0;
56861f28255Scgd 			if (width) {
56961f28255Scgd 				len = strlen(p);
57061f28255Scgd 				if (prec >= 0 && len > prec)
57161f28255Scgd 					len = prec;
57261f28255Scgd 				pad = width - len;
57361f28255Scgd 				if (flushleft == 0) {
57461f28255Scgd 					while (--pad >= 0)
57561f28255Scgd 						outc(' ', dest);
57661f28255Scgd 				}
57761f28255Scgd 			}
57861f28255Scgd 			prec++;
57961f28255Scgd 			while (--prec != 0 && *p)
58061f28255Scgd 				outc(*p++, dest);
58161f28255Scgd 			while (--pad >= 0)
58261f28255Scgd 				outc(' ', dest);
58361f28255Scgd 			break;
58461f28255Scgd 		case 'c':
58561f28255Scgd 			c = va_arg(ap, int);
58661f28255Scgd 			outc(c, dest);
58761f28255Scgd 			break;
58861f28255Scgd 		default:
58961f28255Scgd 			outc(*f, dest);
59061f28255Scgd 			break;
59161f28255Scgd 		}
59261f28255Scgd 		f++;
59361f28255Scgd 	}
59410fc746eSlukem #endif	/* !HAVE_VASPRINTF */
59561f28255Scgd }
59661f28255Scgd 
59761f28255Scgd 
59861f28255Scgd 
59961f28255Scgd /*
60061f28255Scgd  * Version of write which resumes after a signal is caught.
60161f28255Scgd  */
60261f28255Scgd 
60361f28255Scgd int
604c02b3bbdSchristos xwrite(int fd, char *buf, int nbytes)
60561f28255Scgd {
60661f28255Scgd 	int ntry;
60761f28255Scgd 	int i;
60861f28255Scgd 	int n;
60961f28255Scgd 
61061f28255Scgd 	n = nbytes;
61161f28255Scgd 	ntry = 0;
6120c52b5f1Schristos 	while (n > 0) {
61361f28255Scgd 		i = write(fd, buf, n);
61461f28255Scgd 		if (i > 0) {
61561f28255Scgd 			if ((n -= i) <= 0)
61661f28255Scgd 				return nbytes;
61761f28255Scgd 			buf += i;
61861f28255Scgd 			ntry = 0;
61961f28255Scgd 		} else if (i == 0) {
62061f28255Scgd 			if (++ntry > 10)
62161f28255Scgd 				return nbytes - n;
62261f28255Scgd 		} else if (errno != EINTR) {
62361f28255Scgd 			return -1;
62461f28255Scgd 		}
62561f28255Scgd 	}
6260c52b5f1Schristos 	return nbytes;
62761f28255Scgd }
62861f28255Scgd 
629ae40879bSkre #ifndef SMALL
630ffc64c63Skre static void
631ffc64c63Skre xtrace_fd_swap(int from, int to)
632ffc64c63Skre {
633ffc64c63Skre 	struct output *o = outxtop;
634ffc64c63Skre 
635ffc64c63Skre 	while (o != NULL) {
636ffc64c63Skre 		if (o->fd == from)
637ffc64c63Skre 			o->fd = to;
638ffc64c63Skre 		o = o->chain;
639ffc64c63Skre 	}
640ffc64c63Skre }
641ffc64c63Skre 
642ffc64c63Skre /*
643ffc64c63Skre  * the -X flag is to be set or reset (not necessarily changed)
644ffc64c63Skre  * Do what is needed to make tracing go to where it should
645ffc64c63Skre  *
646ffc64c63Skre  * Note: Xflag has not yet been altered, "on" indicates what it will become
647ffc64c63Skre  */
648ffc64c63Skre 
649ffc64c63Skre void
650ffc64c63Skre xtracefdsetup(int on)
651ffc64c63Skre {
652ffc64c63Skre 	if (!on) {
653ffc64c63Skre 		flushout(outx);
654ffc64c63Skre 
655ffc64c63Skre 		if (Xflag != 1)		/* Was already +X */
656ffc64c63Skre 			return;		/* so nothing to do */
657ffc64c63Skre 
658ffc64c63Skre 		outx = out2;
659ffc64c63Skre 		CTRACE(DBG_OUTPUT, ("Tracing to stderr\n"));
660ffc64c63Skre 		return;
661ffc64c63Skre 	}
662ffc64c63Skre 
663ffc64c63Skre 	if (Xflag == 1) {				/* was already set */
664ffc64c63Skre 		/*
665ffc64c63Skre 		 * This is a change of output file only
666ffc64c63Skre 		 * Just close the current one, and reuse the struct output
667ffc64c63Skre 		 */
668ffc64c63Skre 		if (!(outx->flags & OUTPUT_CLONE))
669ffc64c63Skre 			sh_close(outx->fd);
670ffc64c63Skre 	} else if (outxtop == NULL) {
671ffc64c63Skre 		/*
672ffc64c63Skre 		 * -X is just turning on, for the forst time,
673ffc64c63Skre 		 * need a new output struct to become outx
674ffc64c63Skre 		 */
675ffc64c63Skre 		xtrace_clone(1);
676ffc64c63Skre 	} else
677ffc64c63Skre 		outx = outxtop;
678ffc64c63Skre 
679ffc64c63Skre 	if (outx != out2) {
680ffc64c63Skre 		outx->flags &= ~OUTPUT_CLONE;
681ffc64c63Skre 		outx->fd = to_upper_fd(dup(out2->fd));
682ffc64c63Skre 		register_sh_fd(outx->fd, xtrace_fd_swap);
683ffc64c63Skre 	}
684ffc64c63Skre 
685ffc64c63Skre 	CTRACE(DBG_OUTPUT, ("Tracing now to fd %d (from %d)\n",
686ffc64c63Skre 	    outx->fd, out2->fd));
687ffc64c63Skre }
688ffc64c63Skre 
689ffc64c63Skre void
690ffc64c63Skre xtrace_clone(int new)
691ffc64c63Skre {
692ffc64c63Skre 	struct output *o;
693ffc64c63Skre 
694ffc64c63Skre 	CTRACE(DBG_OUTPUT,
695ffc64c63Skre 	    ("xtrace_clone(%d): %sfd=%d buf=%p nleft=%d flags=%x ",
696ffc64c63Skre 	    new, (outx == out2 ? "out2: " : ""),
697ffc64c63Skre 	    outx->fd, outx->buf, outx->nleft, outx->flags));
698ffc64c63Skre 
699ffc64c63Skre 	flushout(outx);
700ffc64c63Skre 
701ffc64c63Skre 	if (!new && outxtop == NULL && Xflag == 0) {
702ffc64c63Skre 		/* following stderr, nothing to save */
703ffc64c63Skre 		CTRACE(DBG_OUTPUT, ("+X\n"));
704ffc64c63Skre 		return;
705ffc64c63Skre 	}
706ffc64c63Skre 
707ffc64c63Skre 	o = ckmalloc(sizeof(*o));
708ffc64c63Skre 	o->fd = outx->fd;
709ffc64c63Skre 	o->flags = OUTPUT_CLONE;
710ffc64c63Skre 	o->bufsize = outx->bufsize;
711ffc64c63Skre 	o->nleft = 0;
712ffc64c63Skre 	o->buf = NULL;
713ffc64c63Skre 	o->nextc = NULL;
714ffc64c63Skre 	o->chain = outxtop;
715ffc64c63Skre 	outx = o;
716ffc64c63Skre 	outxtop = o;
717ffc64c63Skre 
718ffc64c63Skre 	CTRACE(DBG_OUTPUT, ("-> fd=%d flags=%x[CLONE]\n", outx->fd, o->flags));
719ffc64c63Skre }
720ffc64c63Skre 
721ffc64c63Skre void
722ffc64c63Skre xtrace_pop(void)
723ffc64c63Skre {
724ffc64c63Skre 	struct output *o;
725ffc64c63Skre 
726ffc64c63Skre 	CTRACE(DBG_OUTPUT, ("trace_pop: fd=%d buf=%p nleft=%d flags=%x ",
727ffc64c63Skre 	    outx->fd, outx->buf, outx->nleft, outx->flags));
728ffc64c63Skre 
729ffc64c63Skre 	flushout(outx);
730ffc64c63Skre 
731ffc64c63Skre 	if (outxtop == NULL) {
732ffc64c63Skre 		/*
733ffc64c63Skre 		 * No -X has been used, so nothing much to do
734ffc64c63Skre 		 */
735ffc64c63Skre 		CTRACE(DBG_OUTPUT, ("+X\n"));
736ffc64c63Skre 		return;
737ffc64c63Skre 	}
738ffc64c63Skre 
739ffc64c63Skre 	o = outxtop;
740ffc64c63Skre 	outx = o->chain;
741ffc64c63Skre 	if (outx == NULL)
742ffc64c63Skre 		outx = &errout;
743ffc64c63Skre 	outxtop = o->chain;
744ffc64c63Skre 	if (o != &errout) {
745ffc64c63Skre 		if (o->fd >= 0 && !(o->flags & OUTPUT_CLONE))
746ffc64c63Skre 			sh_close(o->fd);
747ffc64c63Skre 		if (o->buf)
748ffc64c63Skre 			ckfree(o->buf);
749ffc64c63Skre 		ckfree(o);
750ffc64c63Skre 	}
751ffc64c63Skre 
752ffc64c63Skre 	CTRACE(DBG_OUTPUT, ("-> fd=%d buf=%p nleft=%d flags=%x\n",
753ffc64c63Skre 	    outx->fd, outx->buf, outx->nleft, outx->flags));
754ffc64c63Skre }
755ae40879bSkre #endif /* SMALL */
756