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