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