tc.printf.c revision 145479
1264269Ssbruno/* $Header: /src/pub/tcsh/tc.printf.c,v 3.27 2005/01/05 16:06:15 christos Exp $ */
2264269Ssbruno/*
3264269Ssbruno * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
4264269Ssbruno *	       through the putchar() routine.  Feel free to use for
5264269Ssbruno *	       anything...  -- 7/17/87 Paul Placeway
6264269Ssbruno */
7264269Ssbruno/*-
8264269Ssbruno * Copyright (c) 1980, 1991 The Regents of the University of California.
9264269Ssbruno * All rights reserved.
10264269Ssbruno *
11264269Ssbruno * Redistribution and use in source and binary forms, with or without
12264269Ssbruno * modification, are permitted provided that the following conditions
13264269Ssbruno * are met:
14264269Ssbruno * 1. Redistributions of source code must retain the above copyright
15264269Ssbruno *    notice, this list of conditions and the following disclaimer.
16264269Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
17264269Ssbruno *    notice, this list of conditions and the following disclaimer in the
18264269Ssbruno *    documentation and/or other materials provided with the distribution.
19264269Ssbruno * 3. Neither the name of the University nor the names of its contributors
20264269Ssbruno *    may be used to endorse or promote products derived from this software
21264269Ssbruno *    without specific prior written permission.
22264269Ssbruno *
23264269Ssbruno * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24264269Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25264269Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26264269Ssbruno * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27264269Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28264269Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29264269Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30264269Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31264269Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32264269Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33264269Ssbruno * SUCH DAMAGE.
34264269Ssbruno */
35264269Ssbruno#include "sh.h"
36264269Ssbruno
37264269SsbrunoRCSID("$Id: tc.printf.c,v 3.27 2005/01/05 16:06:15 christos Exp $")
38264269Ssbruno
39264269Ssbruno#ifdef lint
40264269Ssbruno#undef va_arg
41264269Ssbruno#define va_arg(a, b) (a ? (b) 0 : (b) 0)
42264269Ssbruno#endif
43264269Ssbruno
44264269Ssbruno#define INF	32766		/* should be bigger than any field to print */
45264269Ssbruno
46264269Ssbrunostatic char buf[128];
47264269Ssbrunostatic char snil[] = "(nil)";
48264269Ssbruno
49264269Ssbrunostatic	void	xaddchar	__P((int));
50264269Ssbrunostatic	void	doprnt		__P((void (*) __P((int)), const char *, va_list));
51264269Ssbruno
52264269Ssbrunostatic void
53264269Ssbrunodoprnt(addchar, sfmt, ap)
54264269Ssbruno    void    (*addchar) __P((int));
55264269Ssbruno    const char   *sfmt;
56264269Ssbruno    va_list ap;
57264269Ssbruno{
58264269Ssbruno    char *bp;
59264269Ssbruno    const char *f;
60264269Ssbruno#ifdef SHORT_STRINGS
61264269Ssbruno    Char *Bp;
62264269Ssbruno#endif /* SHORT_STRINGS */
63264269Ssbruno#ifdef HAVE_LONG_LONG
64264269Ssbruno    long long l;
65264269Ssbruno    unsigned long long u;
66264269Ssbruno#else
67264269Ssbruno    long l;
68264269Ssbruno    unsigned long u;
69264269Ssbruno#endif
70264269Ssbruno    int i;
71264269Ssbruno    int fmt;
72264269Ssbruno    unsigned char pad = ' ';
73264269Ssbruno    int     flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
74264269Ssbruno    int     sign = 0;
75264269Ssbruno    int     attributes = 0;
76264269Ssbruno
77264269Ssbruno
78264269Ssbruno    f = sfmt;
79264269Ssbruno    for (; *f; f++) {
80264269Ssbruno	if (*f != '%') {	/* then just out the char */
81264269Ssbruno	    (*addchar) ((int) (((unsigned char)*f) | attributes));
82264269Ssbruno	}
83264269Ssbruno	else {
84264269Ssbruno	    f++;		/* skip the % */
85264269Ssbruno
86264269Ssbruno	    if (*f == '-') {	/* minus: flush left */
87264269Ssbruno		flush_left = 1;
88264269Ssbruno		f++;
89264269Ssbruno	    }
90264269Ssbruno
91264269Ssbruno	    if (*f == '0' || *f == '.') {
92264269Ssbruno		/* padding with 0 rather than blank */
93264269Ssbruno		pad = '0';
94264269Ssbruno		f++;
95264269Ssbruno	    }
96264269Ssbruno	    if (*f == '*') {	/* field width */
97264269Ssbruno		f_width = va_arg(ap, int);
98264269Ssbruno		f++;
99264269Ssbruno	    }
100264269Ssbruno	    else if (isdigit((unsigned char) *f)) {
101264269Ssbruno		f_width = atoi(f);
102264269Ssbruno		while (isdigit((unsigned char) *f))
103264269Ssbruno		    f++;	/* skip the digits */
104264269Ssbruno	    }
105264269Ssbruno
106264269Ssbruno	    if (*f == '.') {	/* precision */
107264269Ssbruno		f++;
108264269Ssbruno		if (*f == '*') {
109264269Ssbruno		    prec = va_arg(ap, int);
110264269Ssbruno		    f++;
111264269Ssbruno		}
112264269Ssbruno		else if (isdigit((unsigned char) *f)) {
113264269Ssbruno		    prec = atoi(f);
114264269Ssbruno		    while (isdigit((unsigned char) *f))
115264269Ssbruno			f++;	/* skip the digits */
116264269Ssbruno		}
117264269Ssbruno	    }
118264269Ssbruno
119264269Ssbruno	    if (*f == '#') {	/* alternate form */
120264269Ssbruno		hash = 1;
121264269Ssbruno		f++;
122264269Ssbruno	    }
123264269Ssbruno
124264269Ssbruno	    if (*f == 'l') {	/* long format */
125264269Ssbruno		do_long++;
126264269Ssbruno		f++;
127264269Ssbruno		if (*f == 'l') {
128264269Ssbruno		    do_long++;
129264269Ssbruno		    f++;
130264269Ssbruno		}
131264269Ssbruno	    }
132264269Ssbruno
133264269Ssbruno	    fmt = (unsigned char) *f;
134264269Ssbruno	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
135264269Ssbruno		do_long = 1;
136264269Ssbruno		fmt = tolower(fmt);
137264269Ssbruno	    }
138264269Ssbruno	    bp = buf;
139264269Ssbruno	    switch (fmt) {	/* do the format */
140264269Ssbruno	    case 'd':
141264269Ssbruno		switch (do_long) {
142264269Ssbruno		case 0:
143264269Ssbruno		    l = (long) (va_arg(ap, int));
144264269Ssbruno		    break;
145264269Ssbruno		case 1:
146264269Ssbruno#ifndef HAVE_LONG_LONG
147264269Ssbruno		default:
148264269Ssbruno#endif
149264269Ssbruno		    l = va_arg(ap, long);
150264269Ssbruno		    break;
151264269Ssbruno#ifdef HAVE_LONG_LONG
152264269Ssbruno		default:
153264269Ssbruno		    l = va_arg(ap, long long);
154264269Ssbruno		    break;
155264269Ssbruno#endif
156264269Ssbruno		}
157264269Ssbruno
158264269Ssbruno		if (l < 0) {
159264269Ssbruno		    sign = 1;
160264269Ssbruno		    l = -l;
161264269Ssbruno		}
162264269Ssbruno		do {
163264269Ssbruno		    *bp++ = (char) (l % 10) + '0';
164264269Ssbruno		} while ((l /= 10) > 0);
165264269Ssbruno		if (sign)
166264269Ssbruno		    *bp++ = '-';
167264269Ssbruno		f_width = f_width - (int) (bp - buf);
168264269Ssbruno		if (!flush_left)
169264269Ssbruno		    while (f_width-- > 0)
170264269Ssbruno			(*addchar) ((int) (pad | attributes));
171264269Ssbruno		for (bp--; bp >= buf; bp--)
172264269Ssbruno		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
173264269Ssbruno		if (flush_left)
174264269Ssbruno		    while (f_width-- > 0)
175264269Ssbruno			(*addchar) ((int) (' ' | attributes));
176264269Ssbruno		break;
177264269Ssbruno
178264269Ssbruno	    case 'p':
179264269Ssbruno		do_long = 1;
180264269Ssbruno		hash = 1;
181264269Ssbruno		fmt = 'x';
182264269Ssbruno		/*FALLTHROUGH*/
183264269Ssbruno	    case 'o':
184264269Ssbruno	    case 'x':
185264269Ssbruno	    case 'u':
186264269Ssbruno		switch (do_long) {
187264269Ssbruno		case 0:
188264269Ssbruno		    u = (unsigned long) (va_arg(ap, unsigned int));
189264269Ssbruno		    break;
190264269Ssbruno		case 1:
191264269Ssbruno#ifndef HAVE_LONG_LONG
192264269Ssbruno		default:
193264269Ssbruno#endif
194264269Ssbruno		    u = va_arg(ap, unsigned long);
195264269Ssbruno		    break;
196264269Ssbruno#ifdef HAVE_LONG_LONG
197264269Ssbruno		default:
198264269Ssbruno		    u = va_arg(ap, unsigned long long);
199264269Ssbruno		    break;
200264269Ssbruno#endif
201264269Ssbruno		}
202264269Ssbruno		if (fmt == 'u') {	/* unsigned decimal */
203264269Ssbruno		    do {
204264269Ssbruno			*bp++ = (char) (u % 10) + '0';
205264269Ssbruno		    } while ((u /= 10) > 0);
206264269Ssbruno		}
207264269Ssbruno		else if (fmt == 'o') {	/* octal */
208264269Ssbruno		    do {
209264269Ssbruno			*bp++ = (char) (u % 8) + '0';
210264269Ssbruno		    } while ((u /= 8) > 0);
211264269Ssbruno		    if (hash)
212264269Ssbruno			*bp++ = '0';
213264269Ssbruno		}
214264269Ssbruno		else if (fmt == 'x') {	/* hex */
215264269Ssbruno		    do {
216264269Ssbruno			i = (int) (u % 16);
217264269Ssbruno			if (i < 10)
218264269Ssbruno			    *bp++ = i + '0';
219264269Ssbruno			else
220264269Ssbruno			    *bp++ = i - 10 + 'a';
221264269Ssbruno		    } while ((u /= 16) > 0);
222264269Ssbruno		    if (hash) {
223264269Ssbruno			*bp++ = 'x';
224264269Ssbruno			*bp++ = '0';
225264269Ssbruno		    }
226264269Ssbruno		}
227264269Ssbruno		i = f_width - (int) (bp - buf);
228264269Ssbruno		if (!flush_left)
229264269Ssbruno		    while (i-- > 0)
230264269Ssbruno			(*addchar) ((int) (pad | attributes));
231264269Ssbruno		for (bp--; bp >= buf; bp--)
232264269Ssbruno		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
233264269Ssbruno		if (flush_left)
234264269Ssbruno		    while (i-- > 0)
235264269Ssbruno			(*addchar) ((int) (' ' | attributes));
236264269Ssbruno		break;
237264269Ssbruno
238264269Ssbruno
239264269Ssbruno	    case 'c':
240264269Ssbruno		i = va_arg(ap, int);
241264269Ssbruno		(*addchar) ((int) (i | attributes));
242264269Ssbruno		break;
243264269Ssbruno
244264269Ssbruno	    case 'S':
245264269Ssbruno	    case 'Q':
246264269Ssbruno#ifdef SHORT_STRINGS
247264269Ssbruno		Bp = va_arg(ap, Char *);
248264269Ssbruno		if (!Bp) {
249264269Ssbruno		    bp = NULL;
250264269Ssbruno		    goto lcase_s;
251264269Ssbruno	        }
252264269Ssbruno		f_width = f_width - Strlen(Bp);
253264269Ssbruno		if (!flush_left)
254264269Ssbruno		    while (f_width-- > 0)
255264269Ssbruno			(*addchar) ((int) (pad | attributes));
256264269Ssbruno		for (i = 0; *Bp && i < prec; i++) {
257264269Ssbruno		    char cbuf[MB_LEN_MAX];
258264269Ssbruno		    size_t pos, len;
259264269Ssbruno
260264269Ssbruno		    if (fmt == 'Q' && *Bp & QUOTE)
261264269Ssbruno			(*addchar) ((int) ('\\' | attributes));
262264269Ssbruno		    len = one_wctomb(cbuf, *Bp & CHAR);
263264269Ssbruno		    for (pos = 0; pos < len; pos++)
264264269Ssbruno			(*addchar) ((int) ((unsigned char)cbuf[pos]
265264269Ssbruno					   | attributes | (*Bp & ATTRIBUTES)));
266264269Ssbruno		    Bp++;
267264269Ssbruno		}
268264269Ssbruno		if (flush_left)
269264269Ssbruno		    while (f_width-- > 0)
270264269Ssbruno			(*addchar) ((int) (' ' | attributes));
271264269Ssbruno		break;
272264269Ssbruno#endif /* SHORT_STRINGS */
273264269Ssbruno
274264269Ssbruno	    case 's':
275264269Ssbruno	    case 'q':
276264269Ssbruno		bp = va_arg(ap, char *);
277264269Ssbrunolcase_s:
278264269Ssbruno		if (!bp)
279264269Ssbruno		    bp = snil;
280264269Ssbruno		f_width = f_width - strlen((char *) bp);
281264269Ssbruno		if (!flush_left)
282264269Ssbruno		    while (f_width-- > 0)
283264269Ssbruno			(*addchar) ((int) (pad | attributes));
284264269Ssbruno		for (i = 0; *bp && i < prec; i++) {
285264269Ssbruno		    if (fmt == 'q' && *bp & QUOTE)
286264269Ssbruno			(*addchar) ((int) ('\\' | attributes));
287264269Ssbruno		    (*addchar) ((int) (((unsigned char) *bp & TRIM) |
288264269Ssbruno				   	attributes));
289264269Ssbruno		    bp++;
290264269Ssbruno		}
291264269Ssbruno		if (flush_left)
292264269Ssbruno		    while (f_width-- > 0)
293264269Ssbruno			(*addchar) ((int) (' ' | attributes));
294264269Ssbruno		break;
295264269Ssbruno
296264269Ssbruno	    case 'a':
297264269Ssbruno		attributes = va_arg(ap, int);
298264269Ssbruno		break;
299264269Ssbruno
300264269Ssbruno	    case '%':
301264269Ssbruno		(*addchar) ((int) ('%' | attributes));
302264269Ssbruno		break;
303264269Ssbruno
304264269Ssbruno	    default:
305264269Ssbruno		break;
306264269Ssbruno	    }
307264269Ssbruno	    flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
308264269Ssbruno	    sign = 0;
309264269Ssbruno	    pad = ' ';
310264269Ssbruno	}
311264269Ssbruno    }
312264269Ssbruno}
313264269Ssbruno
314264269Ssbruno
315264269Ssbrunostatic char *xstring, *xestring;
316264269Ssbrunostatic void
317264269Ssbrunoxaddchar(c)
318264269Ssbruno    int     c;
319264269Ssbruno{
320264269Ssbruno    if (xestring == xstring)
321264269Ssbruno	*xstring = '\0';
322264269Ssbruno    else
323264269Ssbruno	*xstring++ = (char) c;
324264269Ssbruno}
325264269Ssbruno
326264269Ssbruno
327264269Ssbrunopret_t
328264269Ssbruno/*VARARGS*/
329264269Ssbruno#ifdef PROTOTYPES
330264269Ssbrunoxsnprintf(char *str, size_t size, const char *fmt, ...)
331264269Ssbruno#else
332264269Ssbrunoxsnprintf(va_alist)
333264269Ssbruno    va_dcl
334264269Ssbruno#endif
335264269Ssbruno{
336264269Ssbruno    va_list va;
337264269Ssbruno#ifdef PROTOTYPES
338264269Ssbruno    va_start(va, fmt);
339264269Ssbruno#else
340264269Ssbruno    char *str, *fmt;
341264269Ssbruno    size_t size;
342264269Ssbruno
343264269Ssbruno    va_start(va);
344264269Ssbruno    str = va_arg(va, char *);
345264269Ssbruno    size = va_arg(va, size_t);
346264269Ssbruno    fmt = va_arg(va, char *);
347264269Ssbruno#endif
348264269Ssbruno
349264269Ssbruno    xstring = str;
350264269Ssbruno    xestring = str + size - 1;
351264269Ssbruno    doprnt(xaddchar, fmt, va);
352264269Ssbruno    va_end(va);
353264269Ssbruno    *xstring++ = '\0';
354264269Ssbruno#ifdef PURIFY
355264269Ssbruno    return 1;
356264269Ssbruno#endif
357264269Ssbruno}
358264269Ssbruno
359264269Ssbrunopret_t
360264269Ssbruno/*VARARGS*/
361264269Ssbruno#ifdef PROTOTYPES
362264269Ssbrunoxprintf(const char *fmt, ...)
363264269Ssbruno#else
364264269Ssbrunoxprintf(va_alist)
365264269Ssbruno    va_dcl
366287463Ssbruno#endif
367264269Ssbruno{
368264269Ssbruno    va_list va;
369264269Ssbruno#ifdef PROTOTYPES
370264269Ssbruno    va_start(va, fmt);
371264269Ssbruno#else
372264269Ssbruno    char   *fmt;
373264269Ssbruno
374278961Ssbruno    va_start(va);
375264269Ssbruno    fmt = va_arg(va, char *);
376278961Ssbruno#endif
377278961Ssbruno    doprnt(xputchar, fmt, va);
378264269Ssbruno    va_end(va);
379264269Ssbruno#ifdef PURIFY
380264269Ssbruno    return 1;
381264269Ssbruno#endif
382264269Ssbruno}
383264269Ssbruno
384264269Ssbruno
385264269Ssbrunopret_t
386264269Ssbrunoxvprintf(fmt, va)
387264269Ssbruno    const char   *fmt;
388264269Ssbruno    va_list va;
389264269Ssbruno{
390264269Ssbruno    doprnt(xputchar, fmt, va);
391264269Ssbruno#ifdef PURIFY
392264269Ssbruno    return 1;
393264269Ssbruno#endif
394264269Ssbruno}
395264269Ssbruno
396264269Ssbrunopret_t
397264269Ssbrunoxvsnprintf(str, size, fmt, va)
398264269Ssbruno    char   *str;
399264269Ssbruno    size_t size;
400264269Ssbruno    const char   *fmt;
401264269Ssbruno    va_list va;
402264269Ssbruno{
403264269Ssbruno    xstring = str;
404264269Ssbruno    xestring = str + size - 1;
405264269Ssbruno    doprnt(xaddchar, fmt, va);
406264269Ssbruno    *xstring++ = '\0';
407264269Ssbruno#ifdef PURIFY
408264269Ssbruno    return 1;
409264269Ssbruno#endif
410264269Ssbruno}
411264269Ssbruno
412264269Ssbruno
413264269Ssbruno
414264269Ssbruno#ifdef PURIFY
415264269Ssbruno/* Purify uses (some of..) the following functions to output memory-use
416264269Ssbruno * debugging info.  Given all the messing with file descriptors that
417264269Ssbruno * tcsh does, the easiest way I could think of to get it (Purify) to
418264269Ssbruno * print anything was by replacing some standard functions with
419264269Ssbruno * ones that do tcsh output directly - see dumb hook in doreaddirs()
420264269Ssbruno * (sh.dir.c) -sg
421264269Ssbruno */
422264269Ssbruno#ifndef FILE
423264269Ssbruno#define FILE int
424264269Ssbruno#endif
425264269Ssbrunoint
426264269Ssbruno#ifdef PROTOTYPES
427264269Ssbrunofprintf(FILE *fp, const char* fmt, ...)
428264269Ssbruno#else
429264269Ssbrunofprintf(va_alist)
430264269Ssbruno    va_dcl
431264269Ssbruno#endif
432264269Ssbruno{
433264269Ssbruno    va_list va;
434264269Ssbruno#ifdef PROTOTYPES
435264269Ssbruno    va_start(va, fmt);
436264269Ssbruno#else
437264269Ssbruno    FILE *fp;
438264269Ssbruno    const char   *fmt;
439264269Ssbruno
440264269Ssbruno    va_start(va);
441264269Ssbruno    fp = va_arg(va, FILE *);
442264269Ssbruno    fmt = va_arg(va, const char *);
443264269Ssbruno#endif
444264269Ssbruno    doprnt(xputchar, fmt, va);
445264269Ssbruno    va_end(va);
446264269Ssbruno    return 1;
447264269Ssbruno}
448264269Ssbruno
449264269Ssbrunoint
450264269Ssbrunovfprintf(fp, fmt, va)
451264269Ssbruno    FILE *fp;
452264269Ssbruno    const char   *fmt;
453264269Ssbruno    va_list va;
454264269Ssbruno{
455264269Ssbruno    doprnt(xputchar, fmt, va);
456264269Ssbruno    return 1;
457264269Ssbruno}
458264269Ssbruno
459264269Ssbruno#endif	/* PURIFY */
460264269Ssbruno