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