xref: /freebsd/contrib/tcsh/tc.printf.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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 
37 RCSID("$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 
46 static char buf[128];
47 
48 static	void	xaddchar	__P((int));
49 static	void	doprnt		__P((void (*) __P((int)), const char *, va_list));
50 
51 static void
52 doprnt(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 *);
270 lcase_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 
308 static char *xstring, *xestring;
309 static void
310 xaddchar(c)
311     int     c;
312 {
313     if (xestring == xstring)
314 	*xstring = '\0';
315     else
316 	*xstring++ = (char) c;
317 }
318 
319 
320 pret_t
321 /*VARARGS*/
322 #ifdef FUNCPROTO
323 xsnprintf(char *str, size_t size, const char *fmt, ...)
324 #else
325 xsnprintf(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 
352 pret_t
353 /*VARARGS*/
354 #ifdef FUNCPROTO
355 xprintf(const char *fmt, ...)
356 #else
357 xprintf(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 
378 pret_t
379 xvprintf(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 
389 pret_t
390 xvsnprintf(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
418 int
419 #ifdef FUNCPROTO
420 fprintf(FILE *fp, const char* fmt, ...)
421 #else
422 fprintf(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 
442 int
443 vfprintf(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