tc.printf.c revision 316958
1/* $Header: /p/tcsh/cvsroot/tcsh/tc.printf.c,v 3.38 2015/06/06 21:19:08 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.38 2015/06/06 21:19:08 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	int	doprnt		(void (*) (int), const char *, va_list);
50
51static int
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, do_ptrdiff_t = 0;
72    int     sign = 0, count = 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	    count++;
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(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	    if (*f == 'z') {	/* size_t format */
132		do_size_t++;
133		f++;
134	    }
135	    if (*f == 't') {	/* ptrdiff_t format */
136		do_ptrdiff_t++;
137		f++;
138	    }
139
140	    fmt = (unsigned char) *f;
141	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
142		do_long = 1;
143		fmt = tolower(fmt);
144	    }
145	    bp = buf;
146	    switch (fmt) {	/* do the format */
147	    case 'd':
148		switch (do_long) {
149		case 0:
150		    if (do_size_t)
151			l = (long) (va_arg(ap, size_t));
152		    else
153			l = (long) (va_arg(ap, int));
154		    break;
155		case 1:
156#ifndef HAVE_LONG_LONG
157		default:
158#endif
159		    l = va_arg(ap, long);
160		    break;
161#ifdef HAVE_LONG_LONG
162		default:
163		    l = va_arg(ap, long long);
164		    break;
165#endif
166		}
167
168		if (l < 0) {
169		    sign = 1;
170		    l = -l;
171		}
172		do {
173		    *bp++ = (char) (l % 10) + '0';
174		} while ((l /= 10) > 0);
175		if (sign)
176		    *bp++ = '-';
177		f_width = f_width - (int) (bp - buf);
178		if (!flush_left)
179		    while (f_width-- > 0)  {
180			(*addchar) (pad | attributes);
181			count++;
182		    }
183		for (bp--; bp >= buf; bp--)  {
184		    (*addchar) (((unsigned char) *bp) | attributes);
185		    count++;
186		}
187		if (flush_left)
188		    while (f_width-- > 0) {
189			(*addchar) (' ' | attributes);
190			count++;
191		    }
192		break;
193
194	    case 'p':
195		do_long = 1;
196		hash = 1;
197		fmt = 'x';
198		/*FALLTHROUGH*/
199	    case 'o':
200	    case 'x':
201	    case 'u':
202		switch (do_long) {
203		case 0:
204		    if (do_size_t)
205			u = va_arg(ap, size_t);
206		    else if (do_ptrdiff_t)
207			u = va_arg(ap, ptrdiff_t);
208		    else
209			u = va_arg(ap, unsigned int);
210		    break;
211		case 1:
212#ifndef HAVE_LONG_LONG
213		default:
214#endif
215		    u = va_arg(ap, unsigned long);
216		    break;
217#ifdef HAVE_LONG_LONG
218		default:
219		    u = va_arg(ap, unsigned long long);
220		    break;
221#endif
222		}
223		if (fmt == 'u') {	/* unsigned decimal */
224		    do {
225			*bp++ = (char) (u % 10) + '0';
226		    } while ((u /= 10) > 0);
227		}
228		else if (fmt == 'o') {	/* octal */
229		    do {
230			*bp++ = (char) (u % 8) + '0';
231		    } while ((u /= 8) > 0);
232		    if (hash)
233			*bp++ = '0';
234		}
235		else if (fmt == 'x') {	/* hex */
236		    do {
237			i = (int) (u % 16);
238			if (i < 10)
239			    *bp++ = i + '0';
240			else
241			    *bp++ = i - 10 + 'a';
242		    } while ((u /= 16) > 0);
243		    if (hash) {
244			*bp++ = 'x';
245			*bp++ = '0';
246		    }
247		}
248		i = f_width - (int) (bp - buf);
249		if (!flush_left)
250		    while (i-- > 0) {
251			(*addchar) (pad | attributes);
252			count++;
253		    }
254		for (bp--; bp >= buf; bp--)
255		    (*addchar) (((unsigned char) *bp) | attributes);
256		if (flush_left)
257		    while (i-- > 0) {
258			(*addchar) (' ' | attributes);
259			count++;
260		    }
261		break;
262
263
264	    case 'c':
265		i = va_arg(ap, int);
266		(*addchar) (i | attributes);
267		count++;
268		break;
269
270	    case 'S':
271	    case 'Q':
272#ifdef SHORT_STRINGS
273		Bp = va_arg(ap, Char *);
274		if (!Bp) {
275		    bp = NULL;
276		    goto lcase_s;
277	        }
278		f_width = f_width - Strlen(Bp);
279		if (!flush_left)
280		    while (f_width-- > 0) {
281			(*addchar) ((int) (pad | attributes));
282			count++;
283		    }
284		for (i = 0; *Bp && i < prec; i++) {
285		    char cbuf[MB_LEN_MAX];
286		    size_t pos, len;
287
288		    if (fmt == 'Q' && *Bp & QUOTE) {
289			(*addchar) ('\\' | attributes);
290			count++;
291		    }
292		    len = one_wctomb(cbuf, *Bp);
293		    for (pos = 0; pos < len; pos++) {
294			(*addchar) ((unsigned char)cbuf[pos] | attributes
295				    | (*Bp & ATTRIBUTES));
296			count++;
297		    }
298		    Bp++;
299		}
300		if (flush_left)
301		    while (f_width-- > 0) {
302			(*addchar) (' ' | attributes);
303			count++;
304		    }
305		break;
306#endif /* SHORT_STRINGS */
307
308	    case 's':
309	    case 'q':
310		bp = va_arg(ap, char *);
311lcase_s:
312		if (!bp)
313		    bp = snil;
314		f_width = f_width - strlen(bp);
315		if (!flush_left)
316		    while (f_width-- > 0) {
317			(*addchar) (pad | attributes);
318			count++;
319		    }
320		for (i = 0; *bp && i < prec; i++) {
321		    if (fmt == 'q' && *bp & QUOTE) {
322			(*addchar) ('\\' | attributes);
323			count++;
324		    }
325		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
326		    count++;
327		    bp++;
328		}
329		if (flush_left)
330		    while (f_width-- > 0) {
331			(*addchar) (' ' | attributes);
332			count++;
333		    }
334		break;
335
336	    case 'a':
337		attributes = va_arg(ap, int);
338		break;
339
340	    case '%':
341		(*addchar) ('%' | attributes);
342		count++;
343		break;
344
345	    default:
346		break;
347	    }
348	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
349	    do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
350	    sign = 0;
351	    pad = ' ';
352	}
353    }
354    return count;
355}
356
357
358static char *xstring, *xestring;
359static void
360xaddchar(int c)
361{
362    if (xestring == xstring)
363	*xstring = '\0';
364    else
365	*xstring++ = (char) c;
366}
367
368
369int
370/*VARARGS*/
371xsnprintf(char *str, size_t size, const char *fmt, ...)
372{
373    int count;
374    va_list va;
375    va_start(va, fmt);
376
377    xstring = str;
378    xestring = str + size - 1;
379    count = doprnt(xaddchar, fmt, va);
380    va_end(va);
381    *xstring++ = '\0';
382    return count;
383}
384
385int
386/*VARARGS*/
387xprintf(const char *fmt, ...)
388{
389    int count;
390    va_list va;
391    va_start(va, fmt);
392    count = doprnt(xputchar, fmt, va);
393    va_end(va);
394    return count;
395}
396
397int
398xvprintf(const char *fmt, va_list va)
399{
400    return doprnt(xputchar, fmt, va);
401}
402
403int
404xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
405{
406    int count;
407    xstring = str;
408    xestring = str + size - 1;
409    count = doprnt(xaddchar, fmt, va);
410    *xstring++ = '\0';
411    return count;
412}
413
414char *
415xvasprintf(const char *fmt, va_list va)
416{
417    size_t size;
418    char *buf;
419
420    buf = NULL;
421    size = 2048; /* Arbitrary */
422    for (;;) {
423	va_list copy;
424
425	buf = xrealloc(buf, size);
426	xstring = buf;
427	xestring = buf + size - 1;
428	va_copy(copy, va);
429	doprnt(xaddchar, fmt, copy);
430	va_end(copy);
431	if (xstring < xestring)
432	    break;
433	size *= 2;
434    }
435    *xstring++ = '\0';
436    return xrealloc(buf, xstring - buf);
437}
438
439char *
440xasprintf(const char *fmt, ...)
441{
442    va_list va;
443    char *ret;
444
445    va_start (va, fmt);
446    ret = xvasprintf(fmt, va);
447    va_end(va);
448    return ret;
449}
450
451
452#ifdef PURIFY
453/* Purify uses (some of..) the following functions to output memory-use
454 * debugging info.  Given all the messing with file descriptors that
455 * tcsh does, the easiest way I could think of to get it (Purify) to
456 * print anything was by replacing some standard functions with
457 * ones that do tcsh output directly - see dumb hook in doreaddirs()
458 * (sh.dir.c) -sg
459 */
460#ifndef FILE
461#define FILE int
462#endif
463int
464fprintf(FILE *fp, const char* fmt, ...)
465{
466    int count;
467    va_list va;
468    va_start(va, fmt);
469    count = doprnt(xputchar, fmt, va);
470    va_end(va);
471    return count;
472}
473
474int
475vfprintf(FILE *fp, const char *fmt, va_list va)
476{
477    return doprnt(xputchar, fmt, va);
478}
479
480#endif	/* PURIFY */
481