tc.printf.c revision 167466
1/* $Header: /p/tcsh/cvsroot/tcsh/tc.printf.c,v 3.35 2006/03/02 18:46:45 christos Exp $ */
2/*
3 * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
4 *	       through the putchar() routine.  Feel free to use for
5 *	       anything...  -- 7/17/87 Paul Placeway
6 */
7/*-
8 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35#include "sh.h"
36
37RCSID("$tcsh: tc.printf.c,v 3.35 2006/03/02 18:46:45 christos Exp $")
38
39#ifdef lint
40#undef va_arg
41#define va_arg(a, b) (a ? (b) 0 : (b) 0)
42#endif
43
44#define INF	INT_MAX		/* should be bigger than any field to print */
45
46static char snil[] = "(nil)";
47
48static	void	xaddchar	(int);
49static	void	doprnt		(void (*) (int), const char *, va_list);
50
51static void
52doprnt(void (*addchar) (int), const char *sfmt, va_list ap)
53{
54    char *bp;
55    const char *f;
56#ifdef SHORT_STRINGS
57    const Char *Bp;
58#endif /* SHORT_STRINGS */
59#ifdef HAVE_LONG_LONG
60    long long l;
61    unsigned long long u;
62#else
63    long l;
64    unsigned long u;
65#endif
66    char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */
67    int i;
68    int fmt;
69    unsigned char pad = ' ';
70    int     flush_left = 0, f_width = 0, prec = INF, hash = 0;
71    int	    do_long = 0, do_size_t = 0;
72    int     sign = 0;
73    int     attributes = 0;
74
75
76    f = sfmt;
77    for (; *f; f++) {
78	if (*f != '%') {	/* then just out the char */
79	    (*addchar) (((unsigned char)*f) | attributes);
80	}
81	else {
82	    f++;		/* skip the % */
83
84	    if (*f == '-') {	/* minus: flush left */
85		flush_left = 1;
86		f++;
87	    }
88
89	    if (*f == '0' || *f == '.') {
90		/* padding with 0 rather than blank */
91		pad = '0';
92		f++;
93	    }
94	    if (*f == '*') {	/* field width */
95		f_width = va_arg(ap, int);
96		f++;
97	    }
98	    else if (isdigit((unsigned char) *f)) {
99		f_width = atoi(f);
100		while (isdigit((unsigned char) *f))
101		    f++;	/* skip the digits */
102	    }
103
104	    if (*f == '.') {	/* precision */
105		f++;
106		if (*f == '*') {
107		    prec = va_arg(ap, int);
108		    f++;
109		}
110		else if (isdigit((unsigned char) *f)) {
111		    prec = atoi(f);
112		    while (isdigit((unsigned char) *f))
113			f++;	/* skip the digits */
114		}
115	    }
116
117	    if (*f == '#') {	/* alternate form */
118		hash = 1;
119		f++;
120	    }
121
122	    if (*f == 'l') {	/* long format */
123		do_long++;
124		f++;
125		if (*f == 'l') {
126		    do_long++;
127		    f++;
128		}
129	    }
130	    if (*f == 'z') {	/* size_t format */
131		do_size_t++;
132		f++;
133	    }
134
135	    fmt = (unsigned char) *f;
136	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
137		do_long = 1;
138		fmt = tolower(fmt);
139	    }
140	    bp = buf;
141	    switch (fmt) {	/* do the format */
142	    case 'd':
143		switch (do_long) {
144		case 0:
145		    if (do_size_t)
146			l = (long) (va_arg(ap, size_t));
147		    else
148			l = (long) (va_arg(ap, int));
149		    break;
150		case 1:
151#ifndef HAVE_LONG_LONG
152		default:
153#endif
154		    l = va_arg(ap, long);
155		    break;
156#ifdef HAVE_LONG_LONG
157		default:
158		    l = va_arg(ap, long long);
159		    break;
160#endif
161		}
162
163		if (l < 0) {
164		    sign = 1;
165		    l = -l;
166		}
167		do {
168		    *bp++ = (char) (l % 10) + '0';
169		} while ((l /= 10) > 0);
170		if (sign)
171		    *bp++ = '-';
172		f_width = f_width - (int) (bp - buf);
173		if (!flush_left)
174		    while (f_width-- > 0)
175			(*addchar) (pad | attributes);
176		for (bp--; bp >= buf; bp--)
177		    (*addchar) (((unsigned char) *bp) | attributes);
178		if (flush_left)
179		    while (f_width-- > 0)
180			(*addchar) (' ' | attributes);
181		break;
182
183	    case 'p':
184		do_long = 1;
185		hash = 1;
186		fmt = 'x';
187		/*FALLTHROUGH*/
188	    case 'o':
189	    case 'x':
190	    case 'u':
191		switch (do_long) {
192		case 0:
193		    if (do_size_t)
194			u = va_arg(ap, size_t);
195		    else
196			u = va_arg(ap, unsigned int);
197		    break;
198		case 1:
199#ifndef HAVE_LONG_LONG
200		default:
201#endif
202		    u = va_arg(ap, unsigned long);
203		    break;
204#ifdef HAVE_LONG_LONG
205		default:
206		    u = va_arg(ap, unsigned long long);
207		    break;
208#endif
209		}
210		if (fmt == 'u') {	/* unsigned decimal */
211		    do {
212			*bp++ = (char) (u % 10) + '0';
213		    } while ((u /= 10) > 0);
214		}
215		else if (fmt == 'o') {	/* octal */
216		    do {
217			*bp++ = (char) (u % 8) + '0';
218		    } while ((u /= 8) > 0);
219		    if (hash)
220			*bp++ = '0';
221		}
222		else if (fmt == 'x') {	/* hex */
223		    do {
224			i = (int) (u % 16);
225			if (i < 10)
226			    *bp++ = i + '0';
227			else
228			    *bp++ = i - 10 + 'a';
229		    } while ((u /= 16) > 0);
230		    if (hash) {
231			*bp++ = 'x';
232			*bp++ = '0';
233		    }
234		}
235		i = f_width - (int) (bp - buf);
236		if (!flush_left)
237		    while (i-- > 0)
238			(*addchar) (pad | attributes);
239		for (bp--; bp >= buf; bp--)
240		    (*addchar) (((unsigned char) *bp) | attributes);
241		if (flush_left)
242		    while (i-- > 0)
243			(*addchar) (' ' | attributes);
244		break;
245
246
247	    case 'c':
248		i = va_arg(ap, int);
249		(*addchar) (i | attributes);
250		break;
251
252	    case 'S':
253	    case 'Q':
254#ifdef SHORT_STRINGS
255		Bp = va_arg(ap, Char *);
256		if (!Bp) {
257		    bp = NULL;
258		    goto lcase_s;
259	        }
260		f_width = f_width - Strlen(Bp);
261		if (!flush_left)
262		    while (f_width-- > 0)
263			(*addchar) ((int) (pad | attributes));
264		for (i = 0; *Bp && i < prec; i++) {
265		    char cbuf[MB_LEN_MAX];
266		    size_t pos, len;
267
268		    if (fmt == 'Q' && *Bp & QUOTE)
269			(*addchar) ('\\' | attributes);
270		    len = one_wctomb(cbuf, *Bp & CHAR);
271		    for (pos = 0; pos < len; pos++)
272			(*addchar) ((unsigned char)cbuf[pos] | attributes
273				    | (*Bp & ATTRIBUTES));
274		    Bp++;
275		}
276		if (flush_left)
277		    while (f_width-- > 0)
278			(*addchar) (' ' | attributes);
279		break;
280#endif /* SHORT_STRINGS */
281
282	    case 's':
283	    case 'q':
284		bp = va_arg(ap, char *);
285lcase_s:
286		if (!bp)
287		    bp = snil;
288		f_width = f_width - strlen(bp);
289		if (!flush_left)
290		    while (f_width-- > 0)
291			(*addchar) (pad | attributes);
292		for (i = 0; *bp && i < prec; i++) {
293		    if (fmt == 'q' && *bp & QUOTE)
294			(*addchar) ('\\' | attributes);
295		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
296		    bp++;
297		}
298		if (flush_left)
299		    while (f_width-- > 0)
300			(*addchar) (' ' | attributes);
301		break;
302
303	    case 'a':
304		attributes = va_arg(ap, int);
305		break;
306
307	    case '%':
308		(*addchar) ('%' | attributes);
309		break;
310
311	    default:
312		break;
313	    }
314	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
315	    do_size_t = 0, do_long = 0;
316	    sign = 0;
317	    pad = ' ';
318	}
319    }
320}
321
322
323static char *xstring, *xestring;
324static void
325xaddchar(int c)
326{
327    if (xestring == xstring)
328	*xstring = '\0';
329    else
330	*xstring++ = (char) c;
331}
332
333
334pret_t
335/*VARARGS*/
336xsnprintf(char *str, size_t size, const char *fmt, ...)
337{
338    va_list va;
339    va_start(va, fmt);
340
341    xstring = str;
342    xestring = str + size - 1;
343    doprnt(xaddchar, fmt, va);
344    va_end(va);
345    *xstring++ = '\0';
346#ifdef PURIFY
347    return 1;
348#endif
349}
350
351pret_t
352/*VARARGS*/
353xprintf(const char *fmt, ...)
354{
355    va_list va;
356    va_start(va, fmt);
357    doprnt(xputchar, fmt, va);
358    va_end(va);
359#ifdef PURIFY
360    return 1;
361#endif
362}
363
364
365pret_t
366xvprintf(const char *fmt, va_list va)
367{
368    doprnt(xputchar, fmt, va);
369#ifdef PURIFY
370    return 1;
371#endif
372}
373
374pret_t
375xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
376{
377    xstring = str;
378    xestring = str + size - 1;
379    doprnt(xaddchar, fmt, va);
380    *xstring++ = '\0';
381#ifdef PURIFY
382    return 1;
383#endif
384}
385
386char *
387xvasprintf(const char *fmt, va_list va)
388{
389    size_t size;
390    char *buf;
391
392    buf = NULL;
393    size = 2048; /* Arbitrary */
394    for (;;) {
395	va_list copy;
396
397	buf = xrealloc(buf, size);
398	xstring = buf;
399	xestring = buf + size - 1;
400	va_copy(copy, va);
401	doprnt(xaddchar, fmt, copy);
402	va_end(copy);
403	if (xstring < xestring)
404	    break;
405	size *= 2;
406    }
407    *xstring++ = '\0';
408    return xrealloc(buf, xstring - buf);
409}
410
411char *
412xasprintf(const char *fmt, ...)
413{
414    va_list va;
415    char *ret;
416
417    va_start (va, fmt);
418    ret = xvasprintf(fmt, va);
419    va_end(va);
420    return ret;
421}
422
423
424#ifdef PURIFY
425/* Purify uses (some of..) the following functions to output memory-use
426 * debugging info.  Given all the messing with file descriptors that
427 * tcsh does, the easiest way I could think of to get it (Purify) to
428 * print anything was by replacing some standard functions with
429 * ones that do tcsh output directly - see dumb hook in doreaddirs()
430 * (sh.dir.c) -sg
431 */
432#ifndef FILE
433#define FILE int
434#endif
435int
436fprintf(FILE *fp, const char* fmt, ...)
437{
438    va_list va;
439    va_start(va, fmt);
440    doprnt(xputchar, fmt, va);
441    va_end(va);
442    return 1;
443}
444
445int
446vfprintf(FILE *fp, const char *fmt, va_list va)
447{
448    doprnt(xputchar, fmt, va);
449    return 1;
450}
451
452#endif	/* PURIFY */
453