1/*
2 * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
3 *	       through the putchar() routine.  Feel free to use for
4 *	       anything...  -- 7/17/87 Paul Placeway
5 */
6/*-
7 * Copyright (c) 1980, 1991 The Regents of the University of California.
8 * All rights reserved.
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#include "sh.h"
35
36#ifdef lint
37#undef va_arg
38#define va_arg(a, b) (a ? (b) 0 : (b) 0)
39#endif
40
41#define INF	INT_MAX		/* should be bigger than any field to print */
42
43static char snil[] = "(nil)";
44
45static	void	xaddchar	(int);
46static	int	doprnt		(void (*) (int), const char *, va_list);
47
48static int
49doprnt(void (*addchar) (int), const char *sfmt, va_list ap)
50{
51    char *bp;
52    const char *f;
53#ifdef SHORT_STRINGS
54    const Char *Bp;
55#endif /* SHORT_STRINGS */
56#ifdef HAVE_LONG_LONG
57    long long l;
58    unsigned long long u;
59#else
60    long l;
61    unsigned long u;
62#endif
63    char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */
64    int i;
65    int fmt;
66    unsigned char pad = ' ';
67    int     flush_left = 0, f_width = 0, prec = INF, hash = 0;
68    int	    do_long = 0, do_size_t = 0, do_ptrdiff_t = 0;
69    int     sign = 0, count = 0;
70    int     attributes = 0;
71
72
73    f = sfmt;
74    for (; *f; f++) {
75	if (*f != '%') {	/* then just out the char */
76	    (*addchar) (((unsigned char)*f) | attributes);
77	    count++;
78	}
79	else {
80	    f++;		/* skip the % */
81
82	    if (*f == '-') {	/* minus: flush left */
83		flush_left = 1;
84		f++;
85	    }
86
87	    if (*f == '0' || *f == '.') {
88		/* padding with 0 rather than blank */
89		pad = '0';
90		f++;
91	    }
92	    if (*f == '*') {	/* field width */
93		f_width = va_arg(ap, int);
94		f++;
95	    }
96	    else if (isdigit((unsigned char) *f)) {
97		f_width = atoi(f);
98		while (isdigit((unsigned char) *f))
99		    f++;	/* skip the digits */
100	    }
101
102	    if (*f == '.') {	/* precision */
103		f++;
104		if (*f == '*') {
105		    prec = va_arg(ap, int);
106		    f++;
107		}
108		else if (isdigit((unsigned char) *f)) {
109		    prec = atoi(f);
110		    while (isdigit((unsigned char) *f))
111			f++;	/* skip the digits */
112		}
113	    }
114
115	    if (*f == '#') {	/* alternate form */
116		hash = 1;
117		f++;
118	    }
119
120	    if (*f == 'l') {	/* long format */
121		do_long++;
122		f++;
123		if (*f == 'l') {
124		    do_long++;
125		    f++;
126		}
127	    }
128	    if (*f == 'z') {	/* size_t format */
129		do_size_t++;
130		f++;
131	    }
132	    if (*f == 't') {	/* ptrdiff_t format */
133		do_ptrdiff_t++;
134		f++;
135	    }
136
137	    fmt = (unsigned char) *f;
138	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
139		do_long = 1;
140		fmt = tolower(fmt);
141	    }
142	    bp = buf;
143	    switch (fmt) {	/* do the format */
144	    case 'd':
145		switch (do_long) {
146		case 0:
147		    if (do_size_t)
148			l = (long) (va_arg(ap, size_t));
149		    else
150			l = (long) (va_arg(ap, int));
151		    break;
152		case 1:
153#ifndef HAVE_LONG_LONG
154		default:
155#endif
156		    l = va_arg(ap, long);
157		    break;
158#ifdef HAVE_LONG_LONG
159		default:
160		    l = va_arg(ap, long long);
161		    break;
162#endif
163		}
164
165		if (l < 0) {
166		    sign = 1;
167		    l = -l;
168		}
169		do {
170		    *bp++ = (char) (l % 10) + '0';
171		} while ((l /= 10) > 0);
172		if (sign)
173		    *bp++ = '-';
174		f_width = f_width - (int) (bp - buf);
175		if (!flush_left)
176		    while (f_width-- > 0)  {
177			(*addchar) (pad | attributes);
178			count++;
179		    }
180		for (bp--; bp >= buf; bp--)  {
181		    (*addchar) (((unsigned char) *bp) | attributes);
182		    count++;
183		}
184		if (flush_left)
185		    while (f_width-- > 0) {
186			(*addchar) (' ' | attributes);
187			count++;
188		    }
189		break;
190
191	    case 'p':
192		do_long = 1;
193		hash = 1;
194		fmt = 'x';
195		/*FALLTHROUGH*/
196	    case 'o':
197	    case 'x':
198	    case 'u':
199		switch (do_long) {
200		case 0:
201		    if (do_size_t)
202			u = va_arg(ap, size_t);
203		    else if (do_ptrdiff_t)
204			u = va_arg(ap, ptrdiff_t);
205		    else
206			u = va_arg(ap, unsigned int);
207		    break;
208		case 1:
209#ifndef HAVE_LONG_LONG
210		default:
211#endif
212		    u = va_arg(ap, unsigned long);
213		    break;
214#ifdef HAVE_LONG_LONG
215		default:
216		    u = va_arg(ap, unsigned long long);
217		    break;
218#endif
219		}
220		if (fmt == 'u') {	/* unsigned decimal */
221		    do {
222			*bp++ = (char) (u % 10) + '0';
223		    } while ((u /= 10) > 0);
224		}
225		else if (fmt == 'o') {	/* octal */
226		    do {
227			*bp++ = (char) (u % 8) + '0';
228		    } while ((u /= 8) > 0);
229		    if (hash)
230			*bp++ = '0';
231		}
232		else if (fmt == 'x') {	/* hex */
233		    do {
234			i = (int) (u % 16);
235			if (i < 10)
236			    *bp++ = i + '0';
237			else
238			    *bp++ = i - 10 + 'a';
239		    } while ((u /= 16) > 0);
240		    if (hash) {
241			*bp++ = 'x';
242			*bp++ = '0';
243		    }
244		}
245		i = f_width - (int) (bp - buf);
246		if (!flush_left)
247		    while (i-- > 0) {
248			(*addchar) (pad | attributes);
249			count++;
250		    }
251		for (bp--; bp >= buf; bp--)
252		    (*addchar) (((unsigned char) *bp) | attributes);
253		if (flush_left)
254		    while (i-- > 0) {
255			(*addchar) (' ' | attributes);
256			count++;
257		    }
258		break;
259
260
261	    case 'c':
262		i = va_arg(ap, int);
263		(*addchar) (i | attributes);
264		count++;
265		break;
266
267	    case 'S':
268	    case 'Q':
269#ifdef SHORT_STRINGS
270		Bp = va_arg(ap, Char *);
271		if (!Bp) {
272		    bp = NULL;
273		    goto lcase_s;
274	        }
275		f_width = f_width - Strlen(Bp);
276		if (!flush_left)
277		    while (f_width-- > 0) {
278			(*addchar) ((int) (pad | attributes));
279			count++;
280		    }
281		for (i = 0; *Bp && i < prec; i++) {
282		    char cbuf[MB_LEN_MAX];
283		    size_t pos, len;
284
285		    if (fmt == 'Q' && *Bp & QUOTE) {
286			(*addchar) ('\\' | attributes);
287			count++;
288		    }
289		    len = one_wctomb(cbuf, *Bp);
290		    for (pos = 0; pos < len; pos++) {
291			(*addchar) ((unsigned char)cbuf[pos] | attributes
292				    | (*Bp & ATTRIBUTES));
293			count++;
294		    }
295		    Bp++;
296		}
297		if (flush_left)
298		    while (f_width-- > 0) {
299			(*addchar) (' ' | attributes);
300			count++;
301		    }
302		break;
303#endif /* SHORT_STRINGS */
304
305	    case 's':
306	    case 'q':
307		bp = va_arg(ap, char *);
308lcase_s:
309		if (!bp)
310		    bp = snil;
311		f_width = f_width - strlen(bp);
312		if (!flush_left)
313		    while (f_width-- > 0) {
314			(*addchar) (pad | attributes);
315			count++;
316		    }
317		for (i = 0; *bp && i < prec; i++) {
318		    if (fmt == 'q' && *bp & QUOTE) {
319			(*addchar) ('\\' | attributes);
320			count++;
321		    }
322		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
323		    count++;
324		    bp++;
325		}
326		if (flush_left)
327		    while (f_width-- > 0) {
328			(*addchar) (' ' | attributes);
329			count++;
330		    }
331		break;
332
333	    case 'a':
334		attributes = va_arg(ap, int);
335		break;
336
337	    case '%':
338		(*addchar) ('%' | attributes);
339		count++;
340		break;
341
342	    default:
343		break;
344	    }
345	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
346	    do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
347	    sign = 0;
348	    pad = ' ';
349	}
350    }
351    return count;
352}
353
354
355static char *xstring, *xestring;
356static void
357xaddchar(int c)
358{
359    if (xestring == xstring)
360	*xstring = '\0';
361    else
362	*xstring++ = (char) c;
363}
364
365
366int
367/*VARARGS*/
368xsnprintf(char *str, size_t size, const char *fmt, ...)
369{
370    int count;
371    va_list va;
372    va_start(va, fmt);
373
374    xstring = str;
375    xestring = str + size - 1;
376    count = doprnt(xaddchar, fmt, va);
377    va_end(va);
378    *xstring++ = '\0';
379    return count;
380}
381
382int
383/*VARARGS*/
384xprintf(const char *fmt, ...)
385{
386    int count;
387    va_list va;
388    va_start(va, fmt);
389    count = doprnt(xputchar, fmt, va);
390    va_end(va);
391    return count;
392}
393
394int
395xvprintf(const char *fmt, va_list va)
396{
397    return doprnt(xputchar, fmt, va);
398}
399
400int
401xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
402{
403    int count;
404    xstring = str;
405    xestring = str + size - 1;
406    count = doprnt(xaddchar, fmt, va);
407    *xstring++ = '\0';
408    return count;
409}
410
411char *
412xvasprintf(const char *fmt, va_list va)
413{
414    size_t size;
415    char *buf;
416
417    buf = NULL;
418    size = 2048; /* Arbitrary */
419    for (;;) {
420	va_list copy;
421
422	buf = xrealloc(buf, size);
423	xstring = buf;
424	xestring = buf + size - 1;
425	va_copy(copy, va);
426	doprnt(xaddchar, fmt, copy);
427	va_end(copy);
428	if (xstring < xestring)
429	    break;
430	size *= 2;
431    }
432    *xstring++ = '\0';
433    return xrealloc(buf, xstring - buf);
434}
435
436char *
437xasprintf(const char *fmt, ...)
438{
439    va_list va;
440    char *ret;
441
442    va_start (va, fmt);
443    ret = xvasprintf(fmt, va);
444    va_end(va);
445    return ret;
446}
447
448
449#ifdef PURIFY
450/* Purify uses (some of..) the following functions to output memory-use
451 * debugging info.  Given all the messing with file descriptors that
452 * tcsh does, the easiest way I could think of to get it (Purify) to
453 * print anything was by replacing some standard functions with
454 * ones that do tcsh output directly - see dumb hook in doreaddirs()
455 * (sh.dir.c) -sg
456 */
457#ifndef FILE
458#define FILE int
459#endif
460int
461fprintf(FILE *fp, const char* fmt, ...)
462{
463    int count;
464    va_list va;
465    va_start(va, fmt);
466    count = doprnt(xputchar, fmt, va);
467    va_end(va);
468    return count;
469}
470
471int
472vfprintf(FILE *fp, const char *fmt, va_list va)
473{
474    return doprnt(xputchar, fmt, va);
475}
476
477#endif	/* PURIFY */
478