xref: /freebsd/contrib/tcsh/tc.printf.c (revision ae83180158c4c937f170e31eff311b18c0286a93)
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 
37 RCSID("$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 
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 '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 *);
265 lcase_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 
303 static char *xstring, *xestring;
304 static void
305 xaddchar(c)
306     int     c;
307 {
308     if (xestring == xstring)
309 	*xstring = '\0';
310     else
311 	*xstring++ = (char) c;
312 }
313 
314 
315 pret_t
316 /*VARARGS*/
317 #ifdef FUNCPROTO
318 xsnprintf(char *str, size_t size, const char *fmt, ...)
319 #else
320 xsnprintf(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 
347 pret_t
348 /*VARARGS*/
349 #ifdef FUNCPROTO
350 xprintf(const char *fmt, ...)
351 #else
352 xprintf(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 
373 pret_t
374 xvprintf(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 
384 pret_t
385 xvsnprintf(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
413 int
414 #ifdef FUNCPROTO
415 fprintf(FILE *fp, const char* fmt, ...)
416 #else
417 fprintf(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 
437 int
438 vfprintf(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