xref: /freebsd/contrib/tcsh/tc.printf.c (revision faf25f48d601ae39f5752602f3020e2e92605625)
1 /*
2  * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
3  *	       through the putchar() routine.  Feel free to use for
4  *	       anything...  -- 7/17/87 Paul Placeway
5  */
6 /*-
7  * Copyright (c) 1980, 1991 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include "sh.h"
35 
36 #ifdef lint
37 #undef va_arg
38 #define va_arg(a, b) (a ? (b) 0 : (b) 0)
39 #endif
40 
41 #define INF	INT_MAX		/* should be bigger than any field to print */
42 
43 static char snil[] = "(nil)";
44 
45 static	void	xaddchar	(int);
46 static	int	doprnt		(void (*) (int), const char *, va_list);
47 
48 static int
49 doprnt(void (*addchar) (int), const char *sfmt, va_list ap)
50 {
51     char *bp;
52     const char *f;
53 #ifdef SHORT_STRINGS
54     const Char *Bp;
55 #endif /* SHORT_STRINGS */
56 #ifdef HAVE_LONG_LONG
57     long long l;
58     unsigned long long u;
59 #else
60     long l;
61     unsigned long u;
62 #endif
63     char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */
64     int i;
65     int fmt;
66     unsigned char pad = ' ';
67     int     flush_left = 0, f_width = 0, prec = INF, hash = 0;
68     int	    do_long = 0, do_size_t = 0, do_ptrdiff_t = 0;
69     int     sign = 0, count = 0;
70     int     attributes = 0;
71 
72 
73     f = sfmt;
74     for (; *f; f++) {
75 	if (*f != '%') {	/* then just out the char */
76 	    (*addchar) (((unsigned char)*f) | attributes);
77 	    count++;
78 	}
79 	else {
80 	    f++;		/* skip the % */
81 
82 	    if (*f == '-') {	/* minus: flush left */
83 		flush_left = 1;
84 		f++;
85 	    }
86 
87 	    if (*f == '0' || *f == '.') {
88 		/* padding with 0 rather than blank */
89 		pad = '0';
90 		f++;
91 	    }
92 	    if (*f == '*') {	/* field width */
93 		f_width = va_arg(ap, int);
94 		f++;
95 	    }
96 	    else if (isdigit((unsigned char) *f)) {
97 		f_width = atoi(f);
98 		while (isdigit((unsigned char) *f))
99 		    f++;	/* skip the digits */
100 	    }
101 
102 	    if (*f == '.') {	/* precision */
103 		f++;
104 		if (*f == '*') {
105 		    prec = va_arg(ap, int);
106 		    f++;
107 		}
108 		else if (isdigit((unsigned char) *f)) {
109 		    prec = atoi(f);
110 		    while (isdigit((unsigned char) *f))
111 			f++;	/* skip the digits */
112 		}
113 	    }
114 
115 	    if (*f == '#') {	/* alternate form */
116 		hash = 1;
117 		f++;
118 	    }
119 
120 	    if (*f == 'l') {	/* long format */
121 		do_long++;
122 		f++;
123 		if (*f == 'l') {
124 		    do_long++;
125 		    f++;
126 		}
127 	    }
128 	    if (*f == 'z') {	/* size_t format */
129 		do_size_t++;
130 		f++;
131 	    }
132 	    if (*f == 't') {	/* ptrdiff_t format */
133 		do_ptrdiff_t++;
134 		f++;
135 	    }
136 
137 	    fmt = (unsigned char) *f;
138 	    if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
139 		do_long = 1;
140 		fmt = tolower(fmt);
141 	    }
142 	    bp = buf;
143 	    switch (fmt) {	/* do the format */
144 	    case 'd':
145 		switch (do_long) {
146 		case 0:
147 		    if (do_size_t)
148 			l = (long) (va_arg(ap, size_t));
149 		    else
150 			l = (long) (va_arg(ap, int));
151 		    break;
152 		case 1:
153 #ifndef HAVE_LONG_LONG
154 		default:
155 #endif
156 		    l = va_arg(ap, long);
157 		    break;
158 #ifdef HAVE_LONG_LONG
159 		default:
160 		    l = va_arg(ap, long long);
161 		    break;
162 #endif
163 		}
164 
165 		if (l < 0) {
166 		    sign = 1;
167 		    l = -l;
168 		}
169 		do {
170 		    *bp++ = (char) (l % 10) + '0';
171 		} while ((l /= 10) > 0);
172 		if (sign)
173 		    *bp++ = '-';
174 		f_width = f_width - (int) (bp - buf);
175 		if (!flush_left)
176 		    while (f_width-- > 0)  {
177 			(*addchar) (pad | attributes);
178 			count++;
179 		    }
180 		for (bp--; bp >= buf; bp--)  {
181 		    (*addchar) (((unsigned char) *bp) | attributes);
182 		    count++;
183 		}
184 		if (flush_left)
185 		    while (f_width-- > 0) {
186 			(*addchar) (' ' | attributes);
187 			count++;
188 		    }
189 		break;
190 
191 	    case 'p':
192 		do_long = 1;
193 		hash = 1;
194 		fmt = 'x';
195 		/*FALLTHROUGH*/
196 	    case 'o':
197 	    case 'x':
198 	    case 'u':
199 		switch (do_long) {
200 		case 0:
201 		    if (do_size_t)
202 			u = va_arg(ap, size_t);
203 		    else if (do_ptrdiff_t)
204 			u = va_arg(ap, ptrdiff_t);
205 		    else
206 			u = va_arg(ap, unsigned int);
207 		    break;
208 		case 1:
209 #ifndef HAVE_LONG_LONG
210 		default:
211 #endif
212 		    u = va_arg(ap, unsigned long);
213 		    break;
214 #ifdef HAVE_LONG_LONG
215 		default:
216 		    u = va_arg(ap, unsigned long long);
217 		    break;
218 #endif
219 		}
220 		if (fmt == 'u') {	/* unsigned decimal */
221 		    do {
222 			*bp++ = (char) (u % 10) + '0';
223 		    } while ((u /= 10) > 0);
224 		}
225 		else if (fmt == 'o') {	/* octal */
226 		    do {
227 			*bp++ = (char) (u % 8) + '0';
228 		    } while ((u /= 8) > 0);
229 		    if (hash)
230 			*bp++ = '0';
231 		}
232 		else if (fmt == 'x') {	/* hex */
233 		    do {
234 			i = (int) (u % 16);
235 			if (i < 10)
236 			    *bp++ = i + '0';
237 			else
238 			    *bp++ = i - 10 + 'a';
239 		    } while ((u /= 16) > 0);
240 		    if (hash) {
241 			*bp++ = 'x';
242 			*bp++ = '0';
243 		    }
244 		}
245 		i = f_width - (int) (bp - buf);
246 		if (!flush_left)
247 		    while (i-- > 0) {
248 			(*addchar) (pad | attributes);
249 			count++;
250 		    }
251 		for (bp--; bp >= buf; bp--)
252 		    (*addchar) (((unsigned char) *bp) | attributes);
253 		if (flush_left)
254 		    while (i-- > 0) {
255 			(*addchar) (' ' | attributes);
256 			count++;
257 		    }
258 		break;
259 
260 
261 	    case 'c':
262 		i = va_arg(ap, int);
263 		(*addchar) (i | attributes);
264 		count++;
265 		break;
266 
267 	    case 'S':
268 	    case 'Q':
269 #ifdef SHORT_STRINGS
270 		Bp = va_arg(ap, Char *);
271 		if (!Bp) {
272 		    bp = NULL;
273 		    goto lcase_s;
274 	        }
275 		f_width = f_width - Strlen(Bp);
276 		if (!flush_left)
277 		    while (f_width-- > 0) {
278 			(*addchar) ((int) (pad | attributes));
279 			count++;
280 		    }
281 		for (i = 0; *Bp && i < prec; i++) {
282 		    char cbuf[MB_LEN_MAX];
283 		    size_t pos, len;
284 
285 		    if (fmt == 'Q' && *Bp & QUOTE) {
286 			(*addchar) ('\\' | attributes);
287 			count++;
288 		    }
289 		    len = one_wctomb(cbuf, *Bp);
290 		    for (pos = 0; pos < len; pos++) {
291 			(*addchar) ((unsigned char)cbuf[pos] | attributes
292 				    | (*Bp & ATTRIBUTES));
293 			count++;
294 		    }
295 		    Bp++;
296 		}
297 		if (flush_left)
298 		    while (f_width-- > 0) {
299 			(*addchar) (' ' | attributes);
300 			count++;
301 		    }
302 		break;
303 #endif /* SHORT_STRINGS */
304 
305 	    case 's':
306 	    case 'q':
307 		bp = va_arg(ap, char *);
308 lcase_s:
309 		if (!bp)
310 		    bp = snil;
311 		f_width = f_width - strlen(bp);
312 		if (!flush_left)
313 		    while (f_width-- > 0) {
314 			(*addchar) (pad | attributes);
315 			count++;
316 		    }
317 		for (i = 0; *bp && i < prec; i++) {
318 		    if (fmt == 'q' && *bp & QUOTE) {
319 			(*addchar) ('\\' | attributes);
320 			count++;
321 		    }
322 		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
323 		    count++;
324 		    bp++;
325 		}
326 		if (flush_left)
327 		    while (f_width-- > 0) {
328 			(*addchar) (' ' | attributes);
329 			count++;
330 		    }
331 		break;
332 
333 	    case 'a':
334 		attributes = va_arg(ap, int);
335 		break;
336 
337 	    case '%':
338 		(*addchar) ('%' | attributes);
339 		count++;
340 		break;
341 
342 	    default:
343 		break;
344 	    }
345 	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
346 	    do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
347 	    sign = 0;
348 	    pad = ' ';
349 	}
350     }
351     return count;
352 }
353 
354 
355 static char *xstring, *xestring;
356 static void
357 xaddchar(int c)
358 {
359     if (xestring == xstring)
360 	*xstring = '\0';
361     else
362 	*xstring++ = (char) c;
363 }
364 
365 
366 int
367 /*VARARGS*/
368 xsnprintf(char *str, size_t size, const char *fmt, ...)
369 {
370     int count;
371     va_list va;
372     va_start(va, fmt);
373 
374     xstring = str;
375     xestring = str + size - 1;
376     count = doprnt(xaddchar, fmt, va);
377     va_end(va);
378     *xstring++ = '\0';
379     return count;
380 }
381 
382 int
383 /*VARARGS*/
384 xprintf(const char *fmt, ...)
385 {
386     int count;
387     va_list va;
388     va_start(va, fmt);
389     count = doprnt(xputchar, fmt, va);
390     va_end(va);
391     return count;
392 }
393 
394 int
395 xvprintf(const char *fmt, va_list va)
396 {
397     return doprnt(xputchar, fmt, va);
398 }
399 
400 int
401 xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
402 {
403     int count;
404     xstring = str;
405     xestring = str + size - 1;
406     count = doprnt(xaddchar, fmt, va);
407     *xstring++ = '\0';
408     return count;
409 }
410 
411 char *
412 xvasprintf(const char *fmt, va_list va)
413 {
414     size_t size;
415     char *buf;
416 
417     buf = NULL;
418     size = 2048; /* Arbitrary */
419     for (;;) {
420 	va_list copy;
421 
422 	buf = xrealloc(buf, size);
423 	xstring = buf;
424 	xestring = buf + size - 1;
425 	va_copy(copy, va);
426 	doprnt(xaddchar, fmt, copy);
427 	va_end(copy);
428 	if (xstring < xestring)
429 	    break;
430 	size *= 2;
431     }
432     *xstring++ = '\0';
433     return xrealloc(buf, xstring - buf);
434 }
435 
436 char *
437 xasprintf(const char *fmt, ...)
438 {
439     va_list va;
440     char *ret;
441 
442     va_start (va, fmt);
443     ret = xvasprintf(fmt, va);
444     va_end(va);
445     return ret;
446 }
447 
448 
449 #ifdef PURIFY
450 /* Purify uses (some of..) the following functions to output memory-use
451  * debugging info.  Given all the messing with file descriptors that
452  * tcsh does, the easiest way I could think of to get it (Purify) to
453  * print anything was by replacing some standard functions with
454  * ones that do tcsh output directly - see dumb hook in doreaddirs()
455  * (sh.dir.c) -sg
456  */
457 #ifndef FILE
458 #define FILE int
459 #endif
460 int
461 fprintf(FILE *fp, const char* fmt, ...)
462 {
463     int count;
464     va_list va;
465     va_start(va, fmt);
466     count = doprnt(xputchar, fmt, va);
467     va_end(va);
468     return count;
469 }
470 
471 int
472 vfprintf(FILE *fp, const char *fmt, va_list va)
473 {
474     return doprnt(xputchar, fmt, va);
475 }
476 
477 #endif	/* PURIFY */
478