tc.printf.c revision 100616
1/* $Header: /src/pub/tcsh/tc.printf.c,v 3.23 2002/03/08 17:36:47 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.23 2002/03/08 17:36:47 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 'o':
178	    case 'x':
179	    case 'u':
180		switch (do_long) {
181		case 0:
182		    u = (unsigned long) (va_arg(ap, unsigned int));
183		    break;
184		case 1:
185#ifndef HAVE_QUAD
186		default:
187#endif
188		    u = va_arg(ap, unsigned long);
189		    break;
190#ifdef HAVE_QUAD
191		default:
192		    u = va_arg(ap, unsigned long long);
193		    break;
194#endif
195		}
196		if (fmt == 'u') {	/* unsigned decimal */
197		    do {
198			*bp++ = (char) (u % 10) + '0';
199		    } while ((u /= 10) > 0);
200		}
201		else if (fmt == 'o') {	/* octal */
202		    do {
203			*bp++ = (char) (u % 8) + '0';
204		    } while ((u /= 8) > 0);
205		    if (hash)
206			*bp++ = '0';
207		}
208		else if (fmt == 'x') {	/* hex */
209		    do {
210			i = (int) (u % 16);
211			if (i < 10)
212			    *bp++ = i + '0';
213			else
214			    *bp++ = i - 10 + 'a';
215		    } while ((u /= 16) > 0);
216		    if (hash) {
217			*bp++ = 'x';
218			*bp++ = '0';
219		    }
220		}
221		i = f_width - (int) (bp - buf);
222		if (!flush_left)
223		    while (i-- > 0)
224			(*addchar) ((int) (pad | attributes));
225		for (bp--; bp >= buf; bp--)
226		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
227		if (flush_left)
228		    while (i-- > 0)
229			(*addchar) ((int) (' ' | attributes));
230		break;
231
232
233	    case 'c':
234		i = va_arg(ap, int);
235		(*addchar) ((int) (i | attributes));
236		break;
237
238	    case 'S':
239	    case 'Q':
240#ifdef SHORT_STRINGS
241		Bp = va_arg(ap, Char *);
242		if (!Bp) {
243		    bp = NULL;
244		    goto lcase_s;
245	        }
246		f_width = f_width - Strlen(Bp);
247		if (!flush_left)
248		    while (f_width-- > 0)
249			(*addchar) ((int) (pad | attributes));
250		for (i = 0; *Bp && i < prec; i++) {
251		    if (fmt == 'Q' && *Bp & QUOTE)
252			(*addchar) ((int) ('\\' | attributes));
253		    (*addchar) ((int) ((*Bp & TRIM) | attributes));
254		    Bp++;
255		}
256		if (flush_left)
257		    while (f_width-- > 0)
258			(*addchar) ((int) (' ' | attributes));
259		break;
260#endif /* SHORT_STRINGS */
261
262	    case 's':
263	    case 'q':
264		bp = va_arg(ap, char *);
265lcase_s:
266		if (!bp)
267		    bp = "(nil)";
268		f_width = f_width - strlen((char *) bp);
269		if (!flush_left)
270		    while (f_width-- > 0)
271			(*addchar) ((int) (pad | attributes));
272		for (i = 0; *bp && i < prec; i++) {
273		    if (fmt == 'q' && *bp & QUOTE)
274			(*addchar) ((int) ('\\' | attributes));
275		    (*addchar) ((int) (((unsigned char) *bp & TRIM) |
276				   	attributes));
277		    bp++;
278		}
279		if (flush_left)
280		    while (f_width-- > 0)
281			(*addchar) ((int) (' ' | attributes));
282		break;
283
284	    case 'a':
285		attributes = va_arg(ap, int);
286		break;
287
288	    case '%':
289		(*addchar) ((int) ('%' | attributes));
290		break;
291
292	    default:
293		break;
294	    }
295	    flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
296	    sign = 0;
297	    pad = ' ';
298	}
299    }
300}
301
302
303static char *xstring, *xestring;
304static void
305xaddchar(c)
306    int     c;
307{
308    if (xestring == xstring)
309	*xstring = '\0';
310    else
311	*xstring++ = (char) c;
312}
313
314
315pret_t
316/*VARARGS*/
317#ifdef FUNCPROTO
318xsnprintf(char *str, size_t size, const char *fmt, ...)
319#else
320xsnprintf(va_alist)
321    va_dcl
322#endif
323{
324    va_list va;
325#ifdef FUNCPROTO
326    va_start(va, fmt);
327#else
328    char *str, *fmt;
329    size_t size;
330
331    va_start(va);
332    str = va_arg(va, char *);
333    size = va_arg(va, size_t);
334    fmt = va_arg(va, char *);
335#endif
336
337    xstring = str;
338    xestring = str + size - 1;
339    doprnt(xaddchar, fmt, va);
340    va_end(va);
341    *xstring++ = '\0';
342#ifdef PURIFY
343    return 1;
344#endif
345}
346
347pret_t
348/*VARARGS*/
349#ifdef FUNCPROTO
350xprintf(const char *fmt, ...)
351#else
352xprintf(va_alist)
353    va_dcl
354#endif
355{
356    va_list va;
357#ifdef FUNCPROTO
358    va_start(va, fmt);
359#else
360    char   *fmt;
361
362    va_start(va);
363    fmt = va_arg(va, char *);
364#endif
365    doprnt(xputchar, fmt, va);
366    va_end(va);
367#ifdef PURIFY
368    return 1;
369#endif
370}
371
372
373pret_t
374xvprintf(fmt, va)
375    const char   *fmt;
376    va_list va;
377{
378    doprnt(xputchar, fmt, va);
379#ifdef PURIFY
380    return 1;
381#endif
382}
383
384pret_t
385xvsnprintf(str, size, fmt, va)
386    char   *str;
387    size_t size;
388    const char   *fmt;
389    va_list va;
390{
391    xstring = str;
392    xestring = str + size - 1;
393    doprnt(xaddchar, fmt, va);
394    *xstring++ = '\0';
395#ifdef PURIFY
396    return 1;
397#endif
398}
399
400
401
402#ifdef PURIFY
403/* Purify uses (some of..) the following functions to output memory-use
404 * debugging info.  Given all the messing with file descriptors that
405 * tcsh does, the easiest way I could think of to get it (Purify) to
406 * print anything was by replacing some standard functions with
407 * ones that do tcsh output directly - see dumb hook in doreaddirs()
408 * (sh.dir.c) -sg
409 */
410#ifndef FILE
411#define FILE int
412#endif
413int
414#ifdef FUNCPROTO
415fprintf(FILE *fp, const char* fmt, ...)
416#else
417fprintf(va_alist)
418    va_dcl
419#endif
420{
421    va_list va;
422#ifdef FUNCPROTO
423    va_start(va, fmt);
424#else
425    FILE *fp;
426    const char   *fmt;
427
428    va_start(va);
429    fp = va_arg(va, FILE *);
430    fmt = va_arg(va, const char *);
431#endif
432    doprnt(xputchar, fmt, va);
433    va_end(va);
434    return 1;
435}
436
437int
438vfprintf(fp, fmt, va)
439    FILE *fp;
440    const char   *fmt;
441    va_list va;
442{
443    doprnt(xputchar, fmt, va);
444    return 1;
445}
446
447#endif	/* PURIFY */
448