xref: /freebsd/contrib/tcsh/tc.printf.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.21 2001/06/21 23:26:54 kim 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. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 #include "sh.h"
40 
41 RCSID("$Id: tc.printf.c,v 3.21 2001/06/21 23:26:54 kim Exp $")
42 
43 #ifdef lint
44 #undef va_arg
45 #define va_arg(a, b) (a ? (b) 0 : (b) 0)
46 #endif
47 
48 #define INF	32766		/* should be bigger than any field to print */
49 
50 static char buf[128];
51 
52 static	void	xaddchar	__P((int));
53 static	void	doprnt		__P((void (*) __P((int)), const char *, va_list));
54 
55 static void
56 doprnt(addchar, sfmt, ap)
57     void    (*addchar) __P((int));
58     const char   *sfmt;
59     va_list ap;
60 {
61     register char *bp;
62     register const char *f;
63 #ifdef SHORT_STRINGS
64     register Char *Bp;
65 #endif /* SHORT_STRINGS */
66     register long l;
67     register unsigned long u;
68     register int i;
69     register int fmt;
70     register unsigned char pad = ' ';
71     int     flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 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) ((int) (((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((char *) 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 = 1;
124 		f++;
125 	    }
126 
127 	    fmt = (unsigned char) *f;
128 	    if (fmt != 'S' && fmt != 'Q' && Isupper(fmt)) {
129 		do_long = 1;
130 		fmt = Tolower(fmt);
131 	    }
132 	    bp = buf;
133 	    switch (fmt) {	/* do the format */
134 	    case 'd':
135 		if (do_long)
136 		    l = va_arg(ap, long);
137 		else
138 		    l = (long) (va_arg(ap, int));
139 		if (l < 0) {
140 		    sign = 1;
141 		    l = -l;
142 		}
143 		do {
144 		    *bp++ = (char) (l % 10) + '0';
145 		} while ((l /= 10) > 0);
146 		if (sign)
147 		    *bp++ = '-';
148 		f_width = f_width - (int) (bp - buf);
149 		if (!flush_left)
150 		    while (f_width-- > 0)
151 			(*addchar) ((int) (pad | attributes));
152 		for (bp--; bp >= buf; bp--)
153 		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
154 		if (flush_left)
155 		    while (f_width-- > 0)
156 			(*addchar) ((int) (' ' | attributes));
157 		break;
158 
159 	    case 'o':
160 	    case 'x':
161 	    case 'u':
162 		if (do_long)
163 		    u = va_arg(ap, unsigned long);
164 		else
165 		    u = (unsigned long) (va_arg(ap, unsigned int));
166 		if (fmt == 'u') {	/* unsigned decimal */
167 		    do {
168 			*bp++ = (char) (u % 10) + '0';
169 		    } while ((u /= 10) > 0);
170 		}
171 		else if (fmt == 'o') {	/* octal */
172 		    do {
173 			*bp++ = (char) (u % 8) + '0';
174 		    } while ((u /= 8) > 0);
175 		    if (hash)
176 			*bp++ = '0';
177 		}
178 		else if (fmt == 'x') {	/* hex */
179 		    do {
180 			i = (int) (u % 16);
181 			if (i < 10)
182 			    *bp++ = i + '0';
183 			else
184 			    *bp++ = i - 10 + 'a';
185 		    } while ((u /= 16) > 0);
186 		    if (hash) {
187 			*bp++ = 'x';
188 			*bp++ = '0';
189 		    }
190 		}
191 		i = f_width - (int) (bp - buf);
192 		if (!flush_left)
193 		    while (i-- > 0)
194 			(*addchar) ((int) (pad | attributes));
195 		for (bp--; bp >= buf; bp--)
196 		    (*addchar) ((int) (((unsigned char) *bp) | attributes));
197 		if (flush_left)
198 		    while (i-- > 0)
199 			(*addchar) ((int) (' ' | attributes));
200 		break;
201 
202 
203 	    case 'c':
204 		i = va_arg(ap, int);
205 		(*addchar) ((int) (i | attributes));
206 		break;
207 
208 	    case 'S':
209 	    case 'Q':
210 #ifdef SHORT_STRINGS
211 		Bp = va_arg(ap, Char *);
212 		if (!Bp) {
213 		    bp = NULL;
214 		    goto lcase_s;
215 	        }
216 		f_width = f_width - Strlen(Bp);
217 		if (!flush_left)
218 		    while (f_width-- > 0)
219 			(*addchar) ((int) (pad | attributes));
220 		for (i = 0; *Bp && i < prec; i++) {
221 		    if (fmt == 'Q' && *Bp & QUOTE)
222 			(*addchar) ((int) ('\\' | attributes));
223 		    (*addchar) ((int) ((*Bp & TRIM) | attributes));
224 		    Bp++;
225 		}
226 		if (flush_left)
227 		    while (f_width-- > 0)
228 			(*addchar) ((int) (' ' | attributes));
229 		break;
230 #endif /* SHORT_STRINGS */
231 
232 	    case 's':
233 	    case 'q':
234 		bp = va_arg(ap, char *);
235 lcase_s:
236 		if (!bp)
237 		    bp = "(nil)";
238 		f_width = f_width - strlen((char *) bp);
239 		if (!flush_left)
240 		    while (f_width-- > 0)
241 			(*addchar) ((int) (pad | attributes));
242 		for (i = 0; *bp && i < prec; i++) {
243 		    if (fmt == 'q' && *bp & QUOTE)
244 			(*addchar) ((int) ('\\' | attributes));
245 		    (*addchar) ((int) (((unsigned char) *bp & TRIM) |
246 				   	attributes));
247 		    bp++;
248 		}
249 		if (flush_left)
250 		    while (f_width-- > 0)
251 			(*addchar) ((int) (' ' | attributes));
252 		break;
253 
254 	    case 'a':
255 		attributes = va_arg(ap, int);
256 		break;
257 
258 	    case '%':
259 		(*addchar) ((int) ('%' | attributes));
260 		break;
261 
262 	    default:
263 		break;
264 	    }
265 	    flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
266 	    sign = 0;
267 	    pad = ' ';
268 	}
269     }
270 }
271 
272 
273 static char *xstring, *xestring;
274 static void
275 xaddchar(c)
276     int     c;
277 {
278     if (xestring == xstring)
279 	*xstring = '\0';
280     else
281 	*xstring++ = (char) c;
282 }
283 
284 
285 pret_t
286 /*VARARGS*/
287 #ifdef FUNCPROTO
288 xsnprintf(char *str, size_t size, const char *fmt, ...)
289 #else
290 xsnprintf(va_alist)
291     va_dcl
292 #endif
293 {
294     va_list va;
295 #ifdef FUNCPROTO
296     va_start(va, fmt);
297 #else
298     char *str, *fmt;
299     size_t size;
300 
301     va_start(va);
302     str = va_arg(va, char *);
303     size = va_arg(va, size_t);
304     fmt = va_arg(va, char *);
305 #endif
306 
307     xstring = str;
308     xestring = str + size - 1;
309     doprnt(xaddchar, fmt, va);
310     va_end(va);
311     *xstring++ = '\0';
312 #ifdef PURIFY
313     return 1;
314 #endif
315 }
316 
317 pret_t
318 /*VARARGS*/
319 #ifdef FUNCPROTO
320 xprintf(const char *fmt, ...)
321 #else
322 xprintf(va_alist)
323     va_dcl
324 #endif
325 {
326     va_list va;
327 #ifdef FUNCPROTO
328     va_start(va, fmt);
329 #else
330     char   *fmt;
331 
332     va_start(va);
333     fmt = va_arg(va, char *);
334 #endif
335     doprnt(xputchar, fmt, va);
336     va_end(va);
337 #ifdef PURIFY
338     return 1;
339 #endif
340 }
341 
342 
343 pret_t
344 xvprintf(fmt, va)
345     const char   *fmt;
346     va_list va;
347 {
348     doprnt(xputchar, fmt, va);
349 #ifdef PURIFY
350     return 1;
351 #endif
352 }
353 
354 pret_t
355 xvsnprintf(str, size, fmt, va)
356     char   *str;
357     size_t size;
358     const char   *fmt;
359     va_list va;
360 {
361     xstring = str;
362     xestring = str + size - 1;
363     doprnt(xaddchar, fmt, va);
364     *xstring++ = '\0';
365 #ifdef PURIFY
366     return 1;
367 #endif
368 }
369 
370 
371 
372 #ifdef PURIFY
373 /* Purify uses (some of..) the following functions to output memory-use
374  * debugging info.  Given all the messing with file descriptors that
375  * tcsh does, the easiest way I could think of to get it (Purify) to
376  * print anything was by replacing some standard functions with
377  * ones that do tcsh output directly - see dumb hook in doreaddirs()
378  * (sh.dir.c) -sg
379  */
380 #ifndef FILE
381 #define FILE int
382 #endif
383 int
384 #ifdef FUNCPROTO
385 fprintf(FILE *fp, const char* fmt, ...)
386 #else
387 fprintf(va_alist)
388     va_dcl
389 #endif
390 {
391     va_list va;
392 #ifdef FUNCPROTO
393     va_start(va, fmt);
394 #else
395     FILE *fp;
396     const char   *fmt;
397 
398     va_start(va);
399     fp = va_arg(va, FILE *);
400     fmt = va_arg(va, const char *);
401 #endif
402     doprnt(xputchar, fmt, va);
403     va_end(va);
404     return 1;
405 }
406 
407 int
408 vfprintf(fp, fmt, va)
409     FILE *fp;
410     const char   *fmt;
411     va_list va;
412 {
413     doprnt(xputchar, fmt, va);
414     return 1;
415 }
416 
417 #endif	/* PURIFY */
418