14ad28ceobrien/*
24ad28ceobrien * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
34ad28ceobrien *	       through the putchar() routine.  Feel free to use for
44ad28ceobrien *	       anything...  -- 7/17/87 Paul Placeway
54ad28ceobrien */
64ad28ceobrien/*-
74ad28ceobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
84ad28ceobrien * All rights reserved.
94ad28ceobrien *
104ad28ceobrien * Redistribution and use in source and binary forms, with or without
114ad28ceobrien * modification, are permitted provided that the following conditions
124ad28ceobrien * are met:
134ad28ceobrien * 1. Redistributions of source code must retain the above copyright
144ad28ceobrien *    notice, this list of conditions and the following disclaimer.
154ad28ceobrien * 2. Redistributions in binary form must reproduce the above copyright
164ad28ceobrien *    notice, this list of conditions and the following disclaimer in the
174ad28ceobrien *    documentation and/or other materials provided with the distribution.
18f2c2aa2mp * 3. Neither the name of the University nor the names of its contributors
194ad28ceobrien *    may be used to endorse or promote products derived from this software
204ad28ceobrien *    without specific prior written permission.
214ad28ceobrien *
224ad28ceobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
234ad28ceobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
244ad28ceobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254ad28ceobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
264ad28ceobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
274ad28ceobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
284ad28ceobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
294ad28ceobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
304ad28ceobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
314ad28ceobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
324ad28ceobrien * SUCH DAMAGE.
334ad28ceobrien */
344ad28ceobrien#include "sh.h"
354ad28ceobrien
364ad28ceobrien#ifdef lint
374ad28ceobrien#undef va_arg
384ad28ceobrien#define va_arg(a, b) (a ? (b) 0 : (b) 0)
394ad28ceobrien#endif
404ad28ceobrien
41a409803mp#define INF	INT_MAX		/* should be bigger than any field to print */
424ad28ceobrien
4394a109bmpstatic char snil[] = "(nil)";
444ad28ceobrien
45a409803mpstatic	void	xaddchar	(int);
4655e0b5adchaginstatic	int	doprnt		(void (*) (int), const char *, va_list);
474ad28ceobrien
4855e0b5adchaginstatic int
49a409803mpdoprnt(void (*addchar) (int), const char *sfmt, va_list ap)
504ad28ceobrien{
51f2c2aa2mp    char *bp;
52f2c2aa2mp    const char *f;
534ad28ceobrien#ifdef SHORT_STRINGS
54a409803mp    const Char *Bp;
554ad28ceobrien#endif /* SHORT_STRINGS */
5694a109bmp#ifdef HAVE_LONG_LONG
57f2c2aa2mp    long long l;
58f2c2aa2mp    unsigned long long u;
59f2c2aa2mp#else
60f2c2aa2mp    long l;
61f2c2aa2mp    unsigned long u;
62f2c2aa2mp#endif
63a409803mp    char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */
64f2c2aa2mp    int i;
65f2c2aa2mp    int fmt;
66f2c2aa2mp    unsigned char pad = ' ';
67a409803mp    int     flush_left = 0, f_width = 0, prec = INF, hash = 0;
6855e0b5adchagin    int	    do_long = 0, do_size_t = 0, do_ptrdiff_t = 0;
6955e0b5adchagin    int     sign = 0, count = 0;
704ad28ceobrien    int     attributes = 0;
714ad28ceobrien
724ad28ceobrien
734ad28ceobrien    f = sfmt;
744ad28ceobrien    for (; *f; f++) {
754ad28ceobrien	if (*f != '%') {	/* then just out the char */
76a409803mp	    (*addchar) (((unsigned char)*f) | attributes);
7755e0b5adchagin	    count++;
784ad28ceobrien	}
794ad28ceobrien	else {
804ad28ceobrien	    f++;		/* skip the % */
814ad28ceobrien
824ad28ceobrien	    if (*f == '-') {	/* minus: flush left */
834ad28ceobrien		flush_left = 1;
844ad28ceobrien		f++;
854ad28ceobrien	    }
864ad28ceobrien
874ad28ceobrien	    if (*f == '0' || *f == '.') {
884ad28ceobrien		/* padding with 0 rather than blank */
894ad28ceobrien		pad = '0';
904ad28ceobrien		f++;
914ad28ceobrien	    }
924ad28ceobrien	    if (*f == '*') {	/* field width */
934ad28ceobrien		f_width = va_arg(ap, int);
944ad28ceobrien		f++;
954ad28ceobrien	    }
9694a109bmp	    else if (isdigit((unsigned char) *f)) {
974ad28ceobrien		f_width = atoi(f);
9894a109bmp		while (isdigit((unsigned char) *f))
994ad28ceobrien		    f++;	/* skip the digits */
1004ad28ceobrien	    }
1014ad28ceobrien
1024ad28ceobrien	    if (*f == '.') {	/* precision */
1034ad28ceobrien		f++;
1044ad28ceobrien		if (*f == '*') {
1054ad28ceobrien		    prec = va_arg(ap, int);
1064ad28ceobrien		    f++;
1074ad28ceobrien		}
10894a109bmp		else if (isdigit((unsigned char) *f)) {
10994a109bmp		    prec = atoi(f);
11094a109bmp		    while (isdigit((unsigned char) *f))
1114ad28ceobrien			f++;	/* skip the digits */
1124ad28ceobrien		}
1134ad28ceobrien	    }
1144ad28ceobrien
1154ad28ceobrien	    if (*f == '#') {	/* alternate form */
1164ad28ceobrien		hash = 1;
1174ad28ceobrien		f++;
1184ad28ceobrien	    }
1194ad28ceobrien
1204ad28ceobrien	    if (*f == 'l') {	/* long format */
121f2c2aa2mp		do_long++;
1224ad28ceobrien		f++;
123f2c2aa2mp		if (*f == 'l') {
124f2c2aa2mp		    do_long++;
125f2c2aa2mp		    f++;
126f2c2aa2mp		}
1274ad28ceobrien	    }
128a409803mp	    if (*f == 'z') {	/* size_t format */
129a409803mp		do_size_t++;
130a409803mp		f++;
131a409803mp	    }
13255e0b5adchagin	    if (*f == 't') {	/* ptrdiff_t format */
13355e0b5adchagin		do_ptrdiff_t++;
13455e0b5adchagin		f++;
13555e0b5adchagin	    }
1364ad28ceobrien
1374ad28ceobrien	    fmt = (unsigned char) *f;
13894a109bmp	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
1394ad28ceobrien		do_long = 1;
14094a109bmp		fmt = tolower(fmt);
1414ad28ceobrien	    }
1424ad28ceobrien	    bp = buf;
1434ad28ceobrien	    switch (fmt) {	/* do the format */
1444ad28ceobrien	    case 'd':
145f2c2aa2mp		switch (do_long) {
146f2c2aa2mp		case 0:
147a409803mp		    if (do_size_t)
148a409803mp			l = (long) (va_arg(ap, size_t));
149a409803mp		    else
150a409803mp			l = (long) (va_arg(ap, int));
151f2c2aa2mp		    break;
152f2c2aa2mp		case 1:
15394a109bmp#ifndef HAVE_LONG_LONG
154f2c2aa2mp		default:
155f2c2aa2mp#endif
156f2c2aa2mp		    l = va_arg(ap, long);
157f2c2aa2mp		    break;
15894a109bmp#ifdef HAVE_LONG_LONG
159f2c2aa2mp		default:
160f2c2aa2mp		    l = va_arg(ap, long long);
161f2c2aa2mp		    break;
162f2c2aa2mp#endif
163f2c2aa2mp		}
164f2c2aa2mp
1654ad28ceobrien		if (l < 0) {
1664ad28ceobrien		    sign = 1;
1674ad28ceobrien		    l = -l;
1684ad28ceobrien		}
1694ad28ceobrien		do {
1704ad28ceobrien		    *bp++ = (char) (l % 10) + '0';
1714ad28ceobrien		} while ((l /= 10) > 0);
1724ad28ceobrien		if (sign)
1734ad28ceobrien		    *bp++ = '-';
1744ad28ceobrien		f_width = f_width - (int) (bp - buf);
1754ad28ceobrien		if (!flush_left)
17655e0b5adchagin		    while (f_width-- > 0)  {
177a409803mp			(*addchar) (pad | attributes);
17855e0b5adchagin			count++;
17955e0b5adchagin		    }
18055e0b5adchagin		for (bp--; bp >= buf; bp--)  {
181a409803mp		    (*addchar) (((unsigned char) *bp) | attributes);
18255e0b5adchagin		    count++;
18355e0b5adchagin		}
1844ad28ceobrien		if (flush_left)
18555e0b5adchagin		    while (f_width-- > 0) {
186a409803mp			(*addchar) (' ' | attributes);
18755e0b5adchagin			count++;
18855e0b5adchagin		    }
1894ad28ceobrien		break;
1904ad28ceobrien
191bbd1addmp	    case 'p':
192bbd1addmp		do_long = 1;
193bbd1addmp		hash = 1;
194bbd1addmp		fmt = 'x';
195bbd1addmp		/*FALLTHROUGH*/
1964ad28ceobrien	    case 'o':
1974ad28ceobrien	    case 'x':
1984ad28ceobrien	    case 'u':
199f2c2aa2mp		switch (do_long) {
200f2c2aa2mp		case 0:
201a409803mp		    if (do_size_t)
202a409803mp			u = va_arg(ap, size_t);
20355e0b5adchagin		    else if (do_ptrdiff_t)
20455e0b5adchagin			u = va_arg(ap, ptrdiff_t);
205a409803mp		    else
206a409803mp			u = va_arg(ap, unsigned int);
207f2c2aa2mp		    break;
208f2c2aa2mp		case 1:
20994a109bmp#ifndef HAVE_LONG_LONG
210f2c2aa2mp		default:
211f2c2aa2mp#endif
212f2c2aa2mp		    u = va_arg(ap, unsigned long);
213f2c2aa2mp		    break;
21494a109bmp#ifdef HAVE_LONG_LONG
215f2c2aa2mp		default:
216f2c2aa2mp		    u = va_arg(ap, unsigned long long);
217f2c2aa2mp		    break;
218f2c2aa2mp#endif
219f2c2aa2mp		}
2204ad28ceobrien		if (fmt == 'u') {	/* unsigned decimal */
2214ad28ceobrien		    do {
2224ad28ceobrien			*bp++ = (char) (u % 10) + '0';
2234ad28ceobrien		    } while ((u /= 10) > 0);
2244ad28ceobrien		}
2254ad28ceobrien		else if (fmt == 'o') {	/* octal */
2264ad28ceobrien		    do {
2274ad28ceobrien			*bp++ = (char) (u % 8) + '0';
2284ad28ceobrien		    } while ((u /= 8) > 0);
2294ad28ceobrien		    if (hash)
2304ad28ceobrien			*bp++ = '0';
2314ad28ceobrien		}
2324ad28ceobrien		else if (fmt == 'x') {	/* hex */
2334ad28ceobrien		    do {
2344ad28ceobrien			i = (int) (u % 16);
2354ad28ceobrien			if (i < 10)
2364ad28ceobrien			    *bp++ = i + '0';
2374ad28ceobrien			else
2384ad28ceobrien			    *bp++ = i - 10 + 'a';
2394ad28ceobrien		    } while ((u /= 16) > 0);
2404ad28ceobrien		    if (hash) {
2414ad28ceobrien			*bp++ = 'x';
2424ad28ceobrien			*bp++ = '0';
2434ad28ceobrien		    }
2444ad28ceobrien		}
2454ad28ceobrien		i = f_width - (int) (bp - buf);
2464ad28ceobrien		if (!flush_left)
24755e0b5adchagin		    while (i-- > 0) {
248a409803mp			(*addchar) (pad | attributes);
24955e0b5adchagin			count++;
25055e0b5adchagin		    }
2514ad28ceobrien		for (bp--; bp >= buf; bp--)
252a409803mp		    (*addchar) (((unsigned char) *bp) | attributes);
2534ad28ceobrien		if (flush_left)
25455e0b5adchagin		    while (i-- > 0) {
255a409803mp			(*addchar) (' ' | attributes);
25655e0b5adchagin			count++;
25755e0b5adchagin		    }
2584ad28ceobrien		break;
2594ad28ceobrien
2604ad28ceobrien
2614ad28ceobrien	    case 'c':
2624ad28ceobrien		i = va_arg(ap, int);
263a409803mp		(*addchar) (i | attributes);
26455e0b5adchagin		count++;
2654ad28ceobrien		break;
2664ad28ceobrien
2674ad28ceobrien	    case 'S':
2684ad28ceobrien	    case 'Q':
269538cdbcmp#ifdef SHORT_STRINGS
2704ad28ceobrien		Bp = va_arg(ap, Char *);
2714ad28ceobrien		if (!Bp) {
2724ad28ceobrien		    bp = NULL;
2734ad28ceobrien		    goto lcase_s;
2744ad28ceobrien	        }
2754ad28ceobrien		f_width = f_width - Strlen(Bp);
2764ad28ceobrien		if (!flush_left)
27755e0b5adchagin		    while (f_width-- > 0) {
2784ad28ceobrien			(*addchar) ((int) (pad | attributes));
27955e0b5adchagin			count++;
28055e0b5adchagin		    }
2814ad28ceobrien		for (i = 0; *Bp && i < prec; i++) {
28294a109bmp		    char cbuf[MB_LEN_MAX];
28394a109bmp		    size_t pos, len;
28494a109bmp
28555e0b5adchagin		    if (fmt == 'Q' && *Bp & QUOTE) {
286a409803mp			(*addchar) ('\\' | attributes);
28755e0b5adchagin			count++;
28855e0b5adchagin		    }
28955e0b5adchagin		    len = one_wctomb(cbuf, *Bp);
29055e0b5adchagin		    for (pos = 0; pos < len; pos++) {
291a409803mp			(*addchar) ((unsigned char)cbuf[pos] | attributes
292a409803mp				    | (*Bp & ATTRIBUTES));
29355e0b5adchagin			count++;
29455e0b5adchagin		    }
2954ad28ceobrien		    Bp++;
2964ad28ceobrien		}
2974ad28ceobrien		if (flush_left)
29855e0b5adchagin		    while (f_width-- > 0) {
299a409803mp			(*addchar) (' ' | attributes);
30055e0b5adchagin			count++;
30155e0b5adchagin		    }
3024ad28ceobrien		break;
303538cdbcmp#endif /* SHORT_STRINGS */
3044ad28ceobrien
3054ad28ceobrien	    case 's':
3064ad28ceobrien	    case 'q':
3074ad28ceobrien		bp = va_arg(ap, char *);
3084ad28ceobrienlcase_s:
3094ad28ceobrien		if (!bp)
31094a109bmp		    bp = snil;
311a409803mp		f_width = f_width - strlen(bp);
3124ad28ceobrien		if (!flush_left)
31355e0b5adchagin		    while (f_width-- > 0) {
314a409803mp			(*addchar) (pad | attributes);
31555e0b5adchagin			count++;
31655e0b5adchagin		    }
3174ad28ceobrien		for (i = 0; *bp && i < prec; i++) {
31855e0b5adchagin		    if (fmt == 'q' && *bp & QUOTE) {
319a409803mp			(*addchar) ('\\' | attributes);
32055e0b5adchagin			count++;
32155e0b5adchagin		    }
322a409803mp		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
32355e0b5adchagin		    count++;
3244ad28ceobrien		    bp++;
3254ad28ceobrien		}
3264ad28ceobrien		if (flush_left)
32755e0b5adchagin		    while (f_width-- > 0) {
328a409803mp			(*addchar) (' ' | attributes);
32955e0b5adchagin			count++;
33055e0b5adchagin		    }
3314ad28ceobrien		break;
3324ad28ceobrien
3334ad28ceobrien	    case 'a':
3344ad28ceobrien		attributes = va_arg(ap, int);
3354ad28ceobrien		break;
3364ad28ceobrien
3374ad28ceobrien	    case '%':
338a409803mp		(*addchar) ('%' | attributes);
33955e0b5adchagin		count++;
3404ad28ceobrien		break;
3414ad28ceobrien
3424ad28ceobrien	    default:
3434ad28ceobrien		break;
3444ad28ceobrien	    }
345a409803mp	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
34655e0b5adchagin	    do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
3474ad28ceobrien	    sign = 0;
3484ad28ceobrien	    pad = ' ';
3494ad28ceobrien	}
3504ad28ceobrien    }
35155e0b5adchagin    return count;
3524ad28ceobrien}
3534ad28ceobrien
3544ad28ceobrien
3554ad28ceobrienstatic char *xstring, *xestring;
3564ad28ceobrienstatic void
357a409803mpxaddchar(int c)
3584ad28ceobrien{
3594ad28ceobrien    if (xestring == xstring)
3604ad28ceobrien	*xstring = '\0';
3614ad28ceobrien    else
3624ad28ceobrien	*xstring++ = (char) c;
3634ad28ceobrien}
3644ad28ceobrien
3654ad28ceobrien
36655e0b5adchaginint
3674ad28ceobrien/*VARARGS*/
3684ad28ceobrienxsnprintf(char *str, size_t size, const char *fmt, ...)
3694ad28ceobrien{
37055e0b5adchagin    int count;
3714ad28ceobrien    va_list va;
3724ad28ceobrien    va_start(va, fmt);
3734ad28ceobrien
3744ad28ceobrien    xstring = str;
3754ad28ceobrien    xestring = str + size - 1;
37655e0b5adchagin    count = doprnt(xaddchar, fmt, va);
3774ad28ceobrien    va_end(va);
3784ad28ceobrien    *xstring++ = '\0';
37955e0b5adchagin    return count;
3804ad28ceobrien}
3814ad28ceobrien
38255e0b5adchaginint
3834ad28ceobrien/*VARARGS*/
3844ad28ceobrienxprintf(const char *fmt, ...)
3854ad28ceobrien{
38655e0b5adchagin    int count;
3874ad28ceobrien    va_list va;
3884ad28ceobrien    va_start(va, fmt);
38955e0b5adchagin    count = doprnt(xputchar, fmt, va);
3904ad28ceobrien    va_end(va);
39155e0b5adchagin    return count;
3924ad28ceobrien}
3934ad28ceobrien
39455e0b5adchaginint
395a409803mpxvprintf(const char *fmt, va_list va)
3964ad28ceobrien{
39755e0b5adchagin    return doprnt(xputchar, fmt, va);
3984ad28ceobrien}
3994ad28ceobrien
40055e0b5adchaginint
401a409803mpxvsnprintf(char *str, size_t size, const char *fmt, va_list va)
4024ad28ceobrien{
40355e0b5adchagin    int count;
4044ad28ceobrien    xstring = str;
4054ad28ceobrien    xestring = str + size - 1;
40655e0b5adchagin    count = doprnt(xaddchar, fmt, va);
4074ad28ceobrien    *xstring++ = '\0';
40855e0b5adchagin    return count;
4094ad28ceobrien}
4104ad28ceobrien
411a409803mpchar *
412a409803mpxvasprintf(const char *fmt, va_list va)
413a409803mp{
414a409803mp    size_t size;
415a409803mp    char *buf;
416a409803mp
417a409803mp    buf = NULL;
418a409803mp    size = 2048; /* Arbitrary */
419a409803mp    for (;;) {
420a409803mp	va_list copy;
421a409803mp
422a409803mp	buf = xrealloc(buf, size);
423a409803mp	xstring = buf;
424a409803mp	xestring = buf + size - 1;
425a409803mp	va_copy(copy, va);
426a409803mp	doprnt(xaddchar, fmt, copy);
427a409803mp	va_end(copy);
428a409803mp	if (xstring < xestring)
429a409803mp	    break;
430a409803mp	size *= 2;
431a409803mp    }
432a409803mp    *xstring++ = '\0';
433a409803mp    return xrealloc(buf, xstring - buf);
434a409803mp}
435a409803mp
436a409803mpchar *
437a409803mpxasprintf(const char *fmt, ...)
438a409803mp{
439a409803mp    va_list va;
440a409803mp    char *ret;
441a409803mp
442a409803mp    va_start (va, fmt);
443a409803mp    ret = xvasprintf(fmt, va);
444a409803mp    va_end(va);
445a409803mp    return ret;
446a409803mp}
4474ad28ceobrien
4484ad28ceobrien
4494ad28ceobrien#ifdef PURIFY
4504ad28ceobrien/* Purify uses (some of..) the following functions to output memory-use
4514ad28ceobrien * debugging info.  Given all the messing with file descriptors that
4524ad28ceobrien * tcsh does, the easiest way I could think of to get it (Purify) to
4534ad28ceobrien * print anything was by replacing some standard functions with
4544ad28ceobrien * ones that do tcsh output directly - see dumb hook in doreaddirs()
4554ad28ceobrien * (sh.dir.c) -sg
4564ad28ceobrien */
4574ad28ceobrien#ifndef FILE
4584ad28ceobrien#define FILE int
4594ad28ceobrien#endif
4604ad28ceobrienint
4614ad28ceobrienfprintf(FILE *fp, const char* fmt, ...)
4624ad28ceobrien{
46355e0b5adchagin    int count;
4644ad28ceobrien    va_list va;
4654ad28ceobrien    va_start(va, fmt);
46655e0b5adchagin    count = doprnt(xputchar, fmt, va);
4674ad28ceobrien    va_end(va);
46855e0b5adchagin    return count;
4694ad28ceobrien}
4704ad28ceobrien
4714ad28ceobrienint
472a409803mpvfprintf(FILE *fp, const char *fmt, va_list va)
4734ad28ceobrien{
47455e0b5adchagin    return doprnt(xputchar, fmt, va);
4754ad28ceobrien}
4764ad28ceobrien
4774ad28ceobrien#endif	/* PURIFY */
478