tc.printf.c revision 83098
1/* $Header: /src/pub/tcsh/tc.printf.c,v 3.21 2001/06/21 23:26:54 kim 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. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39#include "sh.h"
40
41RCSID("$Id: tc.printf.c,v 3.21 2001/06/21 23:26:54 kim Exp $")
42
43#ifdef lint
44#undef va_arg
45#define va_arg(a, b) (a ? (b) 0 : (b) 0)
46#endif
47
48#define INF	32766		/* should be bigger than any field to print */
49
50static char buf[128];
51
52static	void	xaddchar	__P((int));
53static	void	doprnt		__P((void (*) __P((int)), const char *, va_list));
54
55static void
56doprnt(addchar, sfmt, ap)
57    void    (*addchar) __P((int));
58    const char   *sfmt;
59    va_list ap;
60{
61    register char *bp;
62    register const char *f;
63#ifdef SHORT_STRINGS
64    register Char *Bp;
65#endif /* SHORT_STRINGS */
66    register long l;
67    register unsigned long u;
68    register int i;
69    register int fmt;
70    register unsigned char pad = ' ';
71    int     flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 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) ((int) (((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((char *) 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 = 1;
124		f++;
125	    }
126
127	    fmt = (unsigned char) *f;
128	    if (fmt != 'S' && fmt != 'Q' && Isupper(fmt)) {
129		do_long = 1;
130		fmt = Tolower(fmt);
131	    }
132	    bp = buf;
133	    switch (fmt) {	/* do the format */
134	    case 'd':
135		if (do_long)
136		    l = va_arg(ap, long);
137		else
138		    l = (long) (va_arg(ap, int));
139		if (l < 0) {
140		    sign = 1;
141		    l = -l;
142		}
143		do {
144		    *bp++ = (char) (l % 10) + '0';
145		} while ((l /= 10) > 0);
146		if (sign)
147		    *bp++ = '-';
148		f_width = f_width - (int) (bp - buf);
149		if (!flush_left)
150		    while (f_width-- > 0)
151			(*addchar) ((int) (pad | attributes));
152		for (bp--; bp >= buf; bp--)
153		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
154		if (flush_left)
155		    while (f_width-- > 0)
156			(*addchar) ((int) (' ' | attributes));
157		break;
158
159	    case 'o':
160	    case 'x':
161	    case 'u':
162		if (do_long)
163		    u = va_arg(ap, unsigned long);
164		else
165		    u = (unsigned long) (va_arg(ap, unsigned int));
166		if (fmt == 'u') {	/* unsigned decimal */
167		    do {
168			*bp++ = (char) (u % 10) + '0';
169		    } while ((u /= 10) > 0);
170		}
171		else if (fmt == 'o') {	/* octal */
172		    do {
173			*bp++ = (char) (u % 8) + '0';
174		    } while ((u /= 8) > 0);
175		    if (hash)
176			*bp++ = '0';
177		}
178		else if (fmt == 'x') {	/* hex */
179		    do {
180			i = (int) (u % 16);
181			if (i < 10)
182			    *bp++ = i + '0';
183			else
184			    *bp++ = i - 10 + 'a';
185		    } while ((u /= 16) > 0);
186		    if (hash) {
187			*bp++ = 'x';
188			*bp++ = '0';
189		    }
190		}
191		i = f_width - (int) (bp - buf);
192		if (!flush_left)
193		    while (i-- > 0)
194			(*addchar) ((int) (pad | attributes));
195		for (bp--; bp >= buf; bp--)
196		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
197		if (flush_left)
198		    while (i-- > 0)
199			(*addchar) ((int) (' ' | attributes));
200		break;
201
202
203	    case 'c':
204		i = va_arg(ap, int);
205		(*addchar) ((int) (i | attributes));
206		break;
207
208	    case 'S':
209	    case 'Q':
210#ifdef SHORT_STRINGS
211		Bp = va_arg(ap, Char *);
212		if (!Bp) {
213		    bp = NULL;
214		    goto lcase_s;
215	        }
216		f_width = f_width - Strlen(Bp);
217		if (!flush_left)
218		    while (f_width-- > 0)
219			(*addchar) ((int) (pad | attributes));
220		for (i = 0; *Bp && i < prec; i++) {
221		    if (fmt == 'Q' && *Bp & QUOTE)
222			(*addchar) ((int) ('\\' | attributes));
223		    (*addchar) ((int) ((*Bp & TRIM) | attributes));
224		    Bp++;
225		}
226		if (flush_left)
227		    while (f_width-- > 0)
228			(*addchar) ((int) (' ' | attributes));
229		break;
230#endif /* SHORT_STRINGS */
231
232	    case 's':
233	    case 'q':
234		bp = va_arg(ap, char *);
235lcase_s:
236		if (!bp)
237		    bp = "(nil)";
238		f_width = f_width - strlen((char *) bp);
239		if (!flush_left)
240		    while (f_width-- > 0)
241			(*addchar) ((int) (pad | attributes));
242		for (i = 0; *bp && i < prec; i++) {
243		    if (fmt == 'q' && *bp & QUOTE)
244			(*addchar) ((int) ('\\' | attributes));
245		    (*addchar) ((int) (((unsigned char) *bp & TRIM) |
246				   	attributes));
247		    bp++;
248		}
249		if (flush_left)
250		    while (f_width-- > 0)
251			(*addchar) ((int) (' ' | attributes));
252		break;
253
254	    case 'a':
255		attributes = va_arg(ap, int);
256		break;
257
258	    case '%':
259		(*addchar) ((int) ('%' | attributes));
260		break;
261
262	    default:
263		break;
264	    }
265	    flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
266	    sign = 0;
267	    pad = ' ';
268	}
269    }
270}
271
272
273static char *xstring, *xestring;
274static void
275xaddchar(c)
276    int     c;
277{
278    if (xestring == xstring)
279	*xstring = '\0';
280    else
281	*xstring++ = (char) c;
282}
283
284
285pret_t
286/*VARARGS*/
287#ifdef FUNCPROTO
288xsnprintf(char *str, size_t size, const char *fmt, ...)
289#else
290xsnprintf(va_alist)
291    va_dcl
292#endif
293{
294    va_list va;
295#ifdef FUNCPROTO
296    va_start(va, fmt);
297#else
298    char *str, *fmt;
299    size_t size;
300
301    va_start(va);
302    str = va_arg(va, char *);
303    size = va_arg(va, size_t);
304    fmt = va_arg(va, char *);
305#endif
306
307    xstring = str;
308    xestring = str + size - 1;
309    doprnt(xaddchar, fmt, va);
310    va_end(va);
311    *xstring++ = '\0';
312#ifdef PURIFY
313    return 1;
314#endif
315}
316
317pret_t
318/*VARARGS*/
319#ifdef FUNCPROTO
320xprintf(const char *fmt, ...)
321#else
322xprintf(va_alist)
323    va_dcl
324#endif
325{
326    va_list va;
327#ifdef FUNCPROTO
328    va_start(va, fmt);
329#else
330    char   *fmt;
331
332    va_start(va);
333    fmt = va_arg(va, char *);
334#endif
335    doprnt(xputchar, fmt, va);
336    va_end(va);
337#ifdef PURIFY
338    return 1;
339#endif
340}
341
342
343pret_t
344xvprintf(fmt, va)
345    const char   *fmt;
346    va_list va;
347{
348    doprnt(xputchar, fmt, va);
349#ifdef PURIFY
350    return 1;
351#endif
352}
353
354pret_t
355xvsnprintf(str, size, fmt, va)
356    char   *str;
357    size_t size;
358    const char   *fmt;
359    va_list va;
360{
361    xstring = str;
362    xestring = str + size - 1;
363    doprnt(xaddchar, fmt, va);
364    *xstring++ = '\0';
365#ifdef PURIFY
366    return 1;
367#endif
368}
369
370
371
372#ifdef PURIFY
373/* Purify uses (some of..) the following functions to output memory-use
374 * debugging info.  Given all the messing with file descriptors that
375 * tcsh does, the easiest way I could think of to get it (Purify) to
376 * print anything was by replacing some standard functions with
377 * ones that do tcsh output directly - see dumb hook in doreaddirs()
378 * (sh.dir.c) -sg
379 */
380#ifndef FILE
381#define FILE int
382#endif
383int
384#ifdef FUNCPROTO
385fprintf(FILE *fp, const char* fmt, ...)
386#else
387fprintf(va_alist)
388    va_dcl
389#endif
390{
391    va_list va;
392#ifdef FUNCPROTO
393    va_start(va, fmt);
394#else
395    FILE *fp;
396    const char   *fmt;
397
398    va_start(va);
399    fp = va_arg(va, FILE *);
400    fmt = va_arg(va, const char *);
401#endif
402    doprnt(xputchar, fmt, va);
403    va_end(va);
404    return 1;
405}
406
407int
408vfprintf(fp, fmt, va)
409    FILE *fp;
410    const char   *fmt;
411    va_list va;
412{
413    doprnt(xputchar, fmt, va);
414    return 1;
415}
416
417#endif	/* PURIFY */
418