xref: /dragonfly/contrib/tcsh-6/tc.printf.c (revision b97fef05)
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 #ifdef SHORT_STRINGS
309 lcase_s:
310 #endif
311 		if (!bp)
312 		    bp = snil;
313 		f_width = f_width - strlen(bp);
314 		if (!flush_left)
315 		    while (f_width-- > 0) {
316 			(*addchar) (pad | attributes);
317 			count++;
318 		    }
319 		for (i = 0; *bp && i < prec; i++) {
320 		    if (fmt == 'q' && *bp & QUOTE) {
321 			(*addchar) ('\\' | attributes);
322 			count++;
323 		    }
324 		    (*addchar) (((unsigned char) *bp & TRIM) | attributes);
325 		    count++;
326 		    bp++;
327 		}
328 		if (flush_left)
329 		    while (f_width-- > 0) {
330 			(*addchar) (' ' | attributes);
331 			count++;
332 		    }
333 		break;
334 
335 	    case 'a':
336 		attributes = va_arg(ap, int);
337 		break;
338 
339 	    case '%':
340 		(*addchar) ('%' | attributes);
341 		count++;
342 		break;
343 
344 	    default:
345 		break;
346 	    }
347 	    flush_left = 0, f_width = 0, prec = INF, hash = 0;
348 	    do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
349 	    sign = 0;
350 	    pad = ' ';
351 	}
352     }
353     return count;
354 }
355 
356 
357 static char *xstring, *xestring;
358 static void
359 xaddchar(int c)
360 {
361     if (xestring == xstring)
362 	*xstring = '\0';
363     else
364 	*xstring++ = (char) c;
365 }
366 
367 
368 int
369 /*VARARGS*/
370 xsnprintf(char *str, size_t size, const char *fmt, ...)
371 {
372     int count;
373     va_list va;
374     va_start(va, fmt);
375 
376     xstring = str;
377     xestring = str + size - 1;
378     count = doprnt(xaddchar, fmt, va);
379     va_end(va);
380     *xstring++ = '\0';
381     return count;
382 }
383 
384 int
385 /*VARARGS*/
386 xprintf(const char *fmt, ...)
387 {
388     int count;
389     va_list va;
390     va_start(va, fmt);
391     count = doprnt(xputchar, fmt, va);
392     va_end(va);
393     return count;
394 }
395 
396 int
397 xvprintf(const char *fmt, va_list va)
398 {
399     return doprnt(xputchar, fmt, va);
400 }
401 
402 int
403 xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
404 {
405     int count;
406     xstring = str;
407     xestring = str + size - 1;
408     count = doprnt(xaddchar, fmt, va);
409     *xstring++ = '\0';
410     return count;
411 }
412 
413 char *
414 xvasprintf(const char *fmt, va_list va)
415 {
416     size_t size;
417     char *buf;
418 
419     buf = NULL;
420     size = 2048; /* Arbitrary */
421     for (;;) {
422 	va_list copy;
423 
424 	buf = xrealloc(buf, size);
425 	xstring = buf;
426 	xestring = buf + size - 1;
427 	va_copy(copy, va);
428 	doprnt(xaddchar, fmt, copy);
429 	va_end(copy);
430 	if (xstring < xestring)
431 	    break;
432 	size *= 2;
433     }
434     *xstring++ = '\0';
435     return xrealloc(buf, xstring - buf);
436 }
437 
438 char *
439 xasprintf(const char *fmt, ...)
440 {
441     va_list va;
442     char *ret;
443 
444     va_start (va, fmt);
445     ret = xvasprintf(fmt, va);
446     va_end(va);
447     return ret;
448 }
449 
450 
451 #ifdef PURIFY
452 /* Purify uses (some of..) the following functions to output memory-use
453  * debugging info.  Given all the messing with file descriptors that
454  * tcsh does, the easiest way I could think of to get it (Purify) to
455  * print anything was by replacing some standard functions with
456  * ones that do tcsh output directly - see dumb hook in doreaddirs()
457  * (sh.dir.c) -sg
458  */
459 #ifndef FILE
460 #define FILE int
461 #endif
462 int
463 fprintf(FILE *fp, const char* fmt, ...)
464 {
465     int count;
466     va_list va;
467     va_start(va, fmt);
468     count = doprnt(xputchar, fmt, va);
469     va_end(va);
470     return count;
471 }
472 
473 int
474 vfprintf(FILE *fp, const char *fmt, va_list va)
475 {
476     return doprnt(xputchar, fmt, va);
477 }
478 
479 #endif	/* PURIFY */
480