Commit | Line | Data |
---|---|---|
22c7c5fb WD |
1 | /* |
2 | * NOTE: If you change this file, please merge it into rsync, samba, etc. | |
3 | */ | |
4 | ||
2f098547 AT |
5 | /* |
6 | * Copyright Patrick Powell 1995 | |
7 | * This code is based on code written by Patrick Powell (papowell@astart.com) | |
8 | * It may be used for any purpose as long as this notice remains intact | |
9 | * on all source code distributions | |
f8be5ef4 AT |
10 | */ |
11 | ||
12 | /************************************************************** | |
13 | * Original: | |
14 | * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 | |
15 | * A bombproof version of doprnt (dopr) included. | |
16 | * Sigh. This sort of thing is always nasty do deal with. Note that | |
17 | * the version here does not include floating point... | |
18 | * | |
19 | * snprintf() is used instead of sprintf() as it does limit checks | |
20 | * for string length. This covers a nasty loophole. | |
21 | * | |
22 | * The other functions are there to prevent NULL pointers from | |
23 | * causing nast effects. | |
24 | * | |
25 | * More Recently: | |
26 | * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 | |
27 | * This was ugly. It is still ugly. I opted out of floating point | |
28 | * numbers, but the formatter understands just about everything | |
29 | * from the normal C string format, at least as far as I can tell from | |
30 | * the Solaris 2.5 printf(3S) man page. | |
31 | * | |
32 | * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 | |
33 | * Ok, added some minimal floating point support, which means this | |
34 | * probably requires libm on most operating systems. Don't yet | |
35 | * support the exponent (e,E) and sigfig (g,G). Also, fmtint() | |
36 | * was pretty badly broken, it just wasn't being exercised in ways | |
37 | * which showed it, so that's been fixed. Also, formated the code | |
38 | * to mutt conventions, and removed dead code left over from the | |
39 | * original. Also, there is now a builtin-test, just compile with: | |
40 | * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm | |
41 | * and run snprintf for results. | |
42 | * | |
43 | * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i | |
44 | * The PGP code was using unsigned hexadecimal formats. | |
45 | * Unfortunately, unsigned formats simply didn't work. | |
46 | * | |
47 | * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 | |
48 | * The original code assumed that both snprintf() and vsnprintf() were | |
49 | * missing. Some systems only have snprintf() but not vsnprintf(), so | |
50 | * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. | |
51 | * | |
9e3c856a | 52 | * Andrew Tridgell (tridge@samba.org) Oct 1998 |
10600500 AT |
53 | * fixed handling of %.0f |
54 | * added test for HAVE_LONG_DOUBLE | |
55 | * | |
8950ac03 AT |
56 | * tridge@samba.org, idra@samba.org, April 2001 |
57 | * got rid of fcvt code (twas buggy and made testing harder) | |
58 | * added C99 semantics | |
59 | * | |
22c7c5fb WD |
60 | * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0 |
61 | * actually print args for %g and %e | |
62 | * | |
63 | * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0 | |
64 | * Since includes.h isn't included here, VA_COPY has to be defined here. I don't | |
65 | * see any include file that is guaranteed to be here, so I'm defining it | |
66 | * locally. Fixes AIX and Solaris builds. | |
67 | * | |
68 | * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13 | |
69 | * put the ifdef for HAVE_VA_COPY in one place rather than in lots of | |
70 | * functions | |
71 | * | |
72 | * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4 | |
73 | * Fix usage of va_list passed as an arg. Use __va_copy before using it | |
74 | * when it exists. | |
75 | * | |
76 | * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14 | |
77 | * Fix incorrect zpadlen handling in fmtfp. | |
78 | * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it. | |
79 | * few mods to make it easier to compile the tests. | |
80 | * addedd the "Ollie" test to the floating point ones. | |
81 | * | |
82 | * Martin Pool (mbp@samba.org) April 2003 | |
83 | * Remove NO_CONFIG_H so that the test case can be built within a source | |
84 | * tree with less trouble. | |
85 | * Remove unnecessary SAFE_FREE() definition. | |
86 | * | |
87 | * Martin Pool (mbp@samba.org) May 2003 | |
88 | * Put in a prototype for dummy_snprintf() to quiet compiler warnings. | |
89 | * | |
90 | * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even | |
91 | * if the C library has some snprintf functions already. | |
2fff0a4f WD |
92 | * |
93 | * Darren Tucker (dtucker@zip.com.au) 2005 | |
94 | * Fix bug allowing read overruns of the source string with "%.*s" | |
95 | * Usually harmless unless the read runs outside the process' allocation | |
96 | * (eg if your malloc does guard pages) in which case it will segfault. | |
97 | * From OpenSSH. Also added test for same. | |
98 | * | |
99 | * Simo Sorce (idra@samba.org) Jan 2006 | |
100 | * | |
101 | * Add support for position independent parameters | |
102 | * fix fmtstr now it conforms to sprintf wrt min.max | |
103 | * | |
f8be5ef4 AT |
104 | **************************************************************/ |
105 | ||
2fff0a4f | 106 | #include "../config.h" |
22c7c5fb WD |
107 | |
108 | #ifdef TEST_SNPRINTF /* need math library headers for testing */ | |
109 | ||
110 | /* In test mode, we pretend that this system doesn't have any snprintf | |
111 | * functions, regardless of what config.h says. */ | |
112 | # undef HAVE_SNPRINTF | |
113 | # undef HAVE_VSNPRINTF | |
114 | # undef HAVE_C99_VSNPRINTF | |
115 | # undef HAVE_ASPRINTF | |
116 | # undef HAVE_VASPRINTF | |
117 | # include <math.h> | |
118 | #endif /* TEST_SNPRINTF */ | |
f8be5ef4 | 119 | |
8950ac03 | 120 | #ifdef HAVE_STRING_H |
f8be5ef4 | 121 | #include <string.h> |
8950ac03 | 122 | #endif |
f8be5ef4 | 123 | |
8950ac03 AT |
124 | #ifdef HAVE_STRINGS_H |
125 | #include <strings.h> | |
126 | #endif | |
127 | #ifdef HAVE_CTYPE_H | |
128 | #include <ctype.h> | |
129 | #endif | |
130 | #include <sys/types.h> | |
131 | #include <stdarg.h> | |
132 | #ifdef HAVE_STDLIB_H | |
133 | #include <stdlib.h> | |
134 | #endif | |
f8be5ef4 | 135 | |
8950ac03 AT |
136 | #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) |
137 | /* only include stdio.h if we are not re-defining snprintf or vsnprintf */ | |
138 | #include <stdio.h> | |
139 | /* make the compiler happy with an empty file */ | |
22c7c5fb | 140 | void dummy_snprintf(void); |
8950ac03 | 141 | void dummy_snprintf(void) {} |
22c7c5fb | 142 | #endif /* HAVE_SNPRINTF, etc */ |
f8be5ef4 | 143 | |
2fff0a4f WD |
144 | /* yes this really must be a ||. Don't muck with this (tridge) */ |
145 | #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) | |
146 | ||
10600500 AT |
147 | #ifdef HAVE_LONG_DOUBLE |
148 | #define LDOUBLE long double | |
149 | #else | |
150 | #define LDOUBLE double | |
151 | #endif | |
152 | ||
2fff0a4f WD |
153 | #if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG |
154 | #define HAVE_LONG_LONG 1 | |
155 | #endif | |
156 | #ifdef HAVE_LONG_LONG | |
8950ac03 AT |
157 | #define LLONG long long |
158 | #else | |
159 | #define LLONG long | |
160 | #endif | |
f8be5ef4 | 161 | |
22c7c5fb | 162 | #ifndef VA_COPY |
92f0b9d6 | 163 | #if defined HAVE_VA_COPY || defined va_copy |
22c7c5fb WD |
164 | #define VA_COPY(dest, src) va_copy(dest, src) |
165 | #else | |
166 | #ifdef HAVE___VA_COPY | |
167 | #define VA_COPY(dest, src) __va_copy(dest, src) | |
168 | #else | |
169 | #define VA_COPY(dest, src) (dest) = (src) | |
170 | #endif | |
171 | #endif | |
f8be5ef4 AT |
172 | |
173 | /* | |
174 | * dopr(): poor man's version of doprintf | |
175 | */ | |
176 | ||
177 | /* format read states */ | |
178 | #define DP_S_DEFAULT 0 | |
179 | #define DP_S_FLAGS 1 | |
180 | #define DP_S_MIN 2 | |
181 | #define DP_S_DOT 3 | |
182 | #define DP_S_MAX 4 | |
183 | #define DP_S_MOD 5 | |
184 | #define DP_S_CONV 6 | |
185 | #define DP_S_DONE 7 | |
186 | ||
187 | /* format flags - Bits */ | |
188 | #define DP_F_MINUS (1 << 0) | |
189 | #define DP_F_PLUS (1 << 1) | |
190 | #define DP_F_SPACE (1 << 2) | |
191 | #define DP_F_NUM (1 << 3) | |
192 | #define DP_F_ZERO (1 << 4) | |
193 | #define DP_F_UP (1 << 5) | |
194 | #define DP_F_UNSIGNED (1 << 6) | |
195 | ||
196 | /* Conversion Flags */ | |
2fff0a4f WD |
197 | #define DP_C_CHAR 1 |
198 | #define DP_C_SHORT 2 | |
199 | #define DP_C_LONG 3 | |
200 | #define DP_C_LDOUBLE 4 | |
201 | #define DP_C_LLONG 5 | |
202 | #define DP_C_SIZET 6 | |
203 | ||
204 | /* Chunk types */ | |
205 | #define CNK_FMT_STR 0 | |
206 | #define CNK_INT 1 | |
207 | #define CNK_OCTAL 2 | |
208 | #define CNK_UINT 3 | |
209 | #define CNK_HEX 4 | |
210 | #define CNK_FLOAT 5 | |
211 | #define CNK_CHAR 6 | |
212 | #define CNK_STRING 7 | |
213 | #define CNK_PTR 8 | |
214 | #define CNK_NUM 9 | |
215 | #define CNK_PRCNT 10 | |
f8be5ef4 | 216 | |
8950ac03 AT |
217 | #define char_to_int(p) ((p)- '0') |
218 | #ifndef MAX | |
219 | #define MAX(p,q) (((p) >= (q)) ? (p) : (q)) | |
220 | #endif | |
f8be5ef4 | 221 | |
2fff0a4f WD |
222 | struct pr_chunk { |
223 | int type; /* chunk type */ | |
224 | int num; /* parameter number */ | |
225 | int min; | |
226 | int max; | |
227 | int flags; | |
228 | int cflags; | |
229 | int start; | |
230 | int len; | |
231 | LLONG value; | |
232 | LDOUBLE fvalue; | |
233 | char *strvalue; | |
234 | void *pnum; | |
235 | struct pr_chunk *min_star; | |
236 | struct pr_chunk *max_star; | |
237 | struct pr_chunk *next; | |
238 | }; | |
239 | ||
240 | struct pr_chunk_x { | |
241 | struct pr_chunk **chunks; | |
242 | int num; | |
243 | }; | |
244 | ||
245 | static int dopr(char *buffer, size_t maxlen, const char *format, | |
22c7c5fb WD |
246 | va_list args_in); |
247 | static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, | |
248 | char *value, int flags, int min, int max); | |
249 | static void fmtint(char *buffer, size_t *currlen, size_t maxlen, | |
2fff0a4f | 250 | LLONG value, int base, int min, int max, int flags); |
22c7c5fb WD |
251 | static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, |
252 | LDOUBLE fvalue, int min, int max, int flags); | |
253 | static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); | |
2fff0a4f WD |
254 | static struct pr_chunk *new_chunk(void); |
255 | static int add_cnk_list_entry(struct pr_chunk_x **list, | |
256 | int max_num, struct pr_chunk *chunk); | |
22c7c5fb | 257 | |
2fff0a4f | 258 | static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) |
f8be5ef4 | 259 | { |
8950ac03 | 260 | char ch; |
8950ac03 | 261 | int state; |
2fff0a4f WD |
262 | int pflag; |
263 | int pnum; | |
264 | int pfirst; | |
8950ac03 | 265 | size_t currlen; |
22c7c5fb | 266 | va_list args; |
2fff0a4f WD |
267 | const char *base; |
268 | struct pr_chunk *chunks = NULL; | |
269 | struct pr_chunk *cnk = NULL; | |
270 | struct pr_chunk_x *clist = NULL; | |
271 | int max_pos; | |
272 | int ret = -1; | |
22c7c5fb WD |
273 | |
274 | VA_COPY(args, args_in); | |
2fff0a4f | 275 | |
8950ac03 | 276 | state = DP_S_DEFAULT; |
2fff0a4f WD |
277 | pfirst = 1; |
278 | pflag = 0; | |
279 | pnum = 0; | |
280 | ||
281 | max_pos = 0; | |
282 | base = format; | |
f8be5ef4 | 283 | ch = *format++; |
8950ac03 | 284 | |
2fff0a4f | 285 | /* retrieve the string structure as chunks */ |
8950ac03 AT |
286 | while (state != DP_S_DONE) { |
287 | if (ch == '\0') | |
288 | state = DP_S_DONE; | |
289 | ||
290 | switch(state) { | |
291 | case DP_S_DEFAULT: | |
2fff0a4f WD |
292 | |
293 | if (cnk) { | |
294 | cnk->next = new_chunk(); | |
295 | cnk = cnk->next; | |
296 | } else { | |
297 | cnk = new_chunk(); | |
298 | } | |
299 | if (!cnk) goto done; | |
300 | if (!chunks) chunks = cnk; | |
301 | ||
302 | if (ch == '%') { | |
8950ac03 | 303 | state = DP_S_FLAGS; |
2fff0a4f WD |
304 | ch = *format++; |
305 | } else { | |
306 | cnk->type = CNK_FMT_STR; | |
307 | cnk->start = format - base -1; | |
308 | while ((ch != '\0') && (ch != '%')) ch = *format++; | |
309 | cnk->len = format - base - cnk->start -1; | |
310 | } | |
8950ac03 AT |
311 | break; |
312 | case DP_S_FLAGS: | |
313 | switch (ch) { | |
314 | case '-': | |
2fff0a4f | 315 | cnk->flags |= DP_F_MINUS; |
8950ac03 AT |
316 | ch = *format++; |
317 | break; | |
318 | case '+': | |
2fff0a4f | 319 | cnk->flags |= DP_F_PLUS; |
8950ac03 AT |
320 | ch = *format++; |
321 | break; | |
322 | case ' ': | |
2fff0a4f | 323 | cnk->flags |= DP_F_SPACE; |
8950ac03 AT |
324 | ch = *format++; |
325 | break; | |
326 | case '#': | |
2fff0a4f | 327 | cnk->flags |= DP_F_NUM; |
8950ac03 AT |
328 | ch = *format++; |
329 | break; | |
330 | case '0': | |
2fff0a4f WD |
331 | cnk->flags |= DP_F_ZERO; |
332 | ch = *format++; | |
333 | break; | |
334 | case 'I': | |
335 | /* internationalization not supported yet */ | |
8950ac03 AT |
336 | ch = *format++; |
337 | break; | |
338 | default: | |
339 | state = DP_S_MIN; | |
340 | break; | |
341 | } | |
342 | break; | |
343 | case DP_S_MIN: | |
344 | if (isdigit((unsigned char)ch)) { | |
2fff0a4f WD |
345 | cnk->min = 10 * cnk->min + char_to_int (ch); |
346 | ch = *format++; | |
347 | } else if (ch == '$') { | |
348 | if (!pfirst && !pflag) { | |
349 | /* parameters must be all positioned or none */ | |
350 | goto done; | |
351 | } | |
352 | if (pfirst) { | |
353 | pfirst = 0; | |
354 | pflag = 1; | |
355 | } | |
356 | if (cnk->min == 0) /* what ?? */ | |
357 | goto done; | |
358 | cnk->num = cnk->min; | |
359 | cnk->min = 0; | |
8950ac03 AT |
360 | ch = *format++; |
361 | } else if (ch == '*') { | |
2fff0a4f WD |
362 | if (pfirst) pfirst = 0; |
363 | cnk->min_star = new_chunk(); | |
364 | if (!cnk->min_star) /* out of memory :-( */ | |
365 | goto done; | |
366 | cnk->min_star->type = CNK_INT; | |
367 | if (pflag) { | |
368 | int num; | |
369 | ch = *format++; | |
370 | if (!isdigit((unsigned char)ch)) { | |
371 | /* parameters must be all positioned or none */ | |
372 | goto done; | |
373 | } | |
374 | for (num = 0; isdigit((unsigned char)ch); ch = *format++) { | |
375 | num = 10 * num + char_to_int(ch); | |
376 | } | |
377 | cnk->min_star->num = num; | |
378 | if (ch != '$') /* what ?? */ | |
379 | goto done; | |
380 | } else { | |
381 | cnk->min_star->num = ++pnum; | |
382 | } | |
383 | max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star); | |
384 | if (max_pos == 0) /* out of memory :-( */ | |
385 | goto done; | |
8950ac03 AT |
386 | ch = *format++; |
387 | state = DP_S_DOT; | |
388 | } else { | |
2fff0a4f | 389 | if (pfirst) pfirst = 0; |
8950ac03 AT |
390 | state = DP_S_DOT; |
391 | } | |
392 | break; | |
393 | case DP_S_DOT: | |
394 | if (ch == '.') { | |
395 | state = DP_S_MAX; | |
396 | ch = *format++; | |
397 | } else { | |
398 | state = DP_S_MOD; | |
399 | } | |
400 | break; | |
401 | case DP_S_MAX: | |
402 | if (isdigit((unsigned char)ch)) { | |
2fff0a4f WD |
403 | if (cnk->max < 0) |
404 | cnk->max = 0; | |
405 | cnk->max = 10 * cnk->max + char_to_int (ch); | |
406 | ch = *format++; | |
407 | } else if (ch == '$') { | |
408 | if (!pfirst && !pflag) { | |
409 | /* parameters must be all positioned or none */ | |
410 | goto done; | |
411 | } | |
412 | if (cnk->max <= 0) /* what ?? */ | |
413 | goto done; | |
414 | cnk->num = cnk->max; | |
415 | cnk->max = -1; | |
8950ac03 AT |
416 | ch = *format++; |
417 | } else if (ch == '*') { | |
2fff0a4f WD |
418 | cnk->max_star = new_chunk(); |
419 | if (!cnk->max_star) /* out of memory :-( */ | |
420 | goto done; | |
421 | cnk->max_star->type = CNK_INT; | |
422 | if (pflag) { | |
423 | int num; | |
424 | ch = *format++; | |
425 | if (!isdigit((unsigned char)ch)) { | |
426 | /* parameters must be all positioned or none */ | |
427 | goto done; | |
428 | } | |
429 | for (num = 0; isdigit((unsigned char)ch); ch = *format++) { | |
430 | num = 10 * num + char_to_int(ch); | |
431 | } | |
432 | cnk->max_star->num = num; | |
433 | if (ch != '$') /* what ?? */ | |
434 | goto done; | |
435 | } else { | |
436 | cnk->max_star->num = ++pnum; | |
437 | } | |
438 | max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star); | |
439 | if (max_pos == 0) /* out of memory :-( */ | |
440 | goto done; | |
441 | ||
8950ac03 AT |
442 | ch = *format++; |
443 | state = DP_S_MOD; | |
444 | } else { | |
445 | state = DP_S_MOD; | |
446 | } | |
447 | break; | |
448 | case DP_S_MOD: | |
449 | switch (ch) { | |
450 | case 'h': | |
2fff0a4f | 451 | cnk->cflags = DP_C_SHORT; |
8950ac03 | 452 | ch = *format++; |
2fff0a4f WD |
453 | if (ch == 'h') { |
454 | cnk->cflags = DP_C_CHAR; | |
455 | ch = *format++; | |
456 | } | |
8950ac03 AT |
457 | break; |
458 | case 'l': | |
2fff0a4f | 459 | cnk->cflags = DP_C_LONG; |
8950ac03 AT |
460 | ch = *format++; |
461 | if (ch == 'l') { /* It's a long long */ | |
2fff0a4f | 462 | cnk->cflags = DP_C_LLONG; |
8950ac03 AT |
463 | ch = *format++; |
464 | } | |
465 | break; | |
466 | case 'L': | |
2fff0a4f WD |
467 | cnk->cflags = DP_C_LDOUBLE; |
468 | ch = *format++; | |
469 | break; | |
470 | case 'z': | |
471 | cnk->cflags = DP_C_SIZET; | |
8950ac03 AT |
472 | ch = *format++; |
473 | break; | |
474 | default: | |
475 | break; | |
476 | } | |
477 | state = DP_S_CONV; | |
478 | break; | |
479 | case DP_S_CONV: | |
2fff0a4f WD |
480 | if (cnk->num == 0) cnk->num = ++pnum; |
481 | max_pos = add_cnk_list_entry(&clist, max_pos, cnk); | |
482 | if (max_pos == 0) /* out of memory :-( */ | |
483 | goto done; | |
484 | ||
8950ac03 AT |
485 | switch (ch) { |
486 | case 'd': | |
487 | case 'i': | |
2fff0a4f | 488 | cnk->type = CNK_INT; |
8950ac03 AT |
489 | break; |
490 | case 'o': | |
2fff0a4f WD |
491 | cnk->type = CNK_OCTAL; |
492 | cnk->flags |= DP_F_UNSIGNED; | |
8950ac03 AT |
493 | break; |
494 | case 'u': | |
2fff0a4f WD |
495 | cnk->type = CNK_UINT; |
496 | cnk->flags |= DP_F_UNSIGNED; | |
8950ac03 AT |
497 | break; |
498 | case 'X': | |
2fff0a4f | 499 | cnk->flags |= DP_F_UP; |
8950ac03 | 500 | case 'x': |
2fff0a4f WD |
501 | cnk->type = CNK_HEX; |
502 | cnk->flags |= DP_F_UNSIGNED; | |
8950ac03 | 503 | break; |
2fff0a4f WD |
504 | case 'A': |
505 | /* hex float not supported yet */ | |
8950ac03 | 506 | case 'E': |
8950ac03 | 507 | case 'G': |
2fff0a4f WD |
508 | case 'F': |
509 | cnk->flags |= DP_F_UP; | |
510 | case 'a': | |
511 | /* hex float not supported yet */ | |
512 | case 'e': | |
513 | case 'f': | |
8950ac03 | 514 | case 'g': |
2fff0a4f | 515 | cnk->type = CNK_FLOAT; |
8950ac03 AT |
516 | break; |
517 | case 'c': | |
2fff0a4f | 518 | cnk->type = CNK_CHAR; |
8950ac03 AT |
519 | break; |
520 | case 's': | |
2fff0a4f | 521 | cnk->type = CNK_STRING; |
8950ac03 AT |
522 | break; |
523 | case 'p': | |
2fff0a4f WD |
524 | cnk->type = CNK_PTR; |
525 | cnk->flags |= DP_F_UNSIGNED; | |
8950ac03 AT |
526 | break; |
527 | case 'n': | |
2fff0a4f | 528 | cnk->type = CNK_NUM; |
8950ac03 AT |
529 | break; |
530 | case '%': | |
2fff0a4f | 531 | cnk->type = CNK_PRCNT; |
8950ac03 AT |
532 | break; |
533 | default: | |
2fff0a4f WD |
534 | /* Unknown, bail out*/ |
535 | goto done; | |
8950ac03 AT |
536 | } |
537 | ch = *format++; | |
538 | state = DP_S_DEFAULT; | |
8950ac03 AT |
539 | break; |
540 | case DP_S_DONE: | |
541 | break; | |
542 | default: | |
543 | /* hmm? */ | |
544 | break; /* some picky compilers need this */ | |
545 | } | |
546 | } | |
2fff0a4f WD |
547 | |
548 | /* retrieve the format arguments */ | |
549 | for (pnum = 0; pnum < max_pos; pnum++) { | |
550 | int i; | |
551 | ||
552 | if (clist[pnum].num == 0) { | |
553 | /* ignoring a parameter should not be permitted | |
554 | * all parameters must be matched at least once | |
555 | * BUT seem some system ignore this rule ... | |
556 | * at least my glibc based system does --SSS | |
557 | */ | |
558 | #ifdef DEBUG_SNPRINTF | |
559 | printf("parameter at position %d not used\n", pnum+1); | |
560 | #endif | |
561 | /* eat the parameter */ | |
562 | va_arg (args, int); | |
563 | continue; | |
564 | } | |
565 | for (i = 1; i < clist[pnum].num; i++) { | |
566 | if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) { | |
567 | /* nooo noo no! | |
568 | * all the references to a parameter | |
569 | * must be of the same type | |
570 | */ | |
571 | goto done; | |
572 | } | |
573 | } | |
574 | cnk = clist[pnum].chunks[0]; | |
575 | switch (cnk->type) { | |
576 | case CNK_INT: | |
577 | if (cnk->cflags == DP_C_SHORT) | |
578 | cnk->value = va_arg (args, int); | |
579 | else if (cnk->cflags == DP_C_LONG) | |
580 | cnk->value = va_arg (args, long int); | |
581 | else if (cnk->cflags == DP_C_LLONG) | |
582 | cnk->value = va_arg (args, LLONG); | |
583 | else if (cnk->cflags == DP_C_SIZET) | |
584 | cnk->value = va_arg (args, ssize_t); | |
585 | else | |
586 | cnk->value = va_arg (args, int); | |
587 | ||
588 | for (i = 1; i < clist[pnum].num; i++) { | |
589 | clist[pnum].chunks[i]->value = cnk->value; | |
590 | } | |
591 | break; | |
592 | ||
593 | case CNK_OCTAL: | |
594 | case CNK_UINT: | |
595 | case CNK_HEX: | |
596 | if (cnk->cflags == DP_C_SHORT) | |
597 | cnk->value = va_arg (args, unsigned int); | |
598 | else if (cnk->cflags == DP_C_LONG) | |
599 | cnk->value = (unsigned long int)va_arg (args, unsigned long int); | |
600 | else if (cnk->cflags == DP_C_LLONG) | |
601 | cnk->value = (LLONG)va_arg (args, unsigned LLONG); | |
602 | else if (cnk->cflags == DP_C_SIZET) | |
603 | cnk->value = (size_t)va_arg (args, size_t); | |
604 | else | |
605 | cnk->value = (unsigned int)va_arg (args, unsigned int); | |
606 | ||
607 | for (i = 1; i < clist[pnum].num; i++) { | |
608 | clist[pnum].chunks[i]->value = cnk->value; | |
609 | } | |
610 | break; | |
611 | ||
612 | case CNK_FLOAT: | |
613 | if (cnk->cflags == DP_C_LDOUBLE) | |
614 | cnk->fvalue = va_arg (args, LDOUBLE); | |
615 | else | |
616 | cnk->fvalue = va_arg (args, double); | |
617 | ||
618 | for (i = 1; i < clist[pnum].num; i++) { | |
619 | clist[pnum].chunks[i]->fvalue = cnk->fvalue; | |
620 | } | |
621 | break; | |
622 | ||
623 | case CNK_CHAR: | |
624 | cnk->value = va_arg (args, int); | |
625 | ||
626 | for (i = 1; i < clist[pnum].num; i++) { | |
627 | clist[pnum].chunks[i]->value = cnk->value; | |
628 | } | |
629 | break; | |
630 | ||
631 | case CNK_STRING: | |
632 | cnk->strvalue = va_arg (args, char *); | |
633 | if (!cnk->strvalue) cnk->strvalue = "(NULL)"; | |
634 | ||
635 | for (i = 1; i < clist[pnum].num; i++) { | |
636 | clist[pnum].chunks[i]->strvalue = cnk->strvalue; | |
637 | } | |
638 | break; | |
639 | ||
640 | case CNK_PTR: | |
641 | cnk->strvalue = va_arg (args, void *); | |
642 | for (i = 1; i < clist[pnum].num; i++) { | |
643 | clist[pnum].chunks[i]->strvalue = cnk->strvalue; | |
644 | } | |
645 | break; | |
646 | ||
647 | case CNK_NUM: | |
648 | if (cnk->cflags == DP_C_CHAR) | |
649 | cnk->pnum = va_arg (args, char *); | |
650 | else if (cnk->cflags == DP_C_SHORT) | |
651 | cnk->pnum = va_arg (args, short int *); | |
652 | else if (cnk->cflags == DP_C_LONG) | |
653 | cnk->pnum = va_arg (args, long int *); | |
654 | else if (cnk->cflags == DP_C_LLONG) | |
655 | cnk->pnum = va_arg (args, LLONG *); | |
656 | else if (cnk->cflags == DP_C_SIZET) | |
657 | cnk->pnum = va_arg (args, ssize_t *); | |
658 | else | |
659 | cnk->pnum = va_arg (args, int *); | |
660 | ||
661 | for (i = 1; i < clist[pnum].num; i++) { | |
662 | clist[pnum].chunks[i]->pnum = cnk->pnum; | |
663 | } | |
664 | break; | |
665 | ||
666 | case CNK_PRCNT: | |
667 | break; | |
668 | ||
669 | default: | |
670 | /* what ?? */ | |
671 | goto done; | |
672 | } | |
673 | } | |
674 | /* print out the actual string from chunks */ | |
675 | currlen = 0; | |
676 | cnk = chunks; | |
677 | while (cnk) { | |
678 | int len, min, max; | |
679 | ||
680 | if (cnk->min_star) min = cnk->min_star->value; | |
681 | else min = cnk->min; | |
682 | if (cnk->max_star) max = cnk->max_star->value; | |
683 | else max = cnk->max; | |
684 | ||
685 | switch (cnk->type) { | |
686 | ||
687 | case CNK_FMT_STR: | |
688 | if (maxlen != 0 && maxlen > currlen) { | |
689 | if (maxlen > (currlen + cnk->len)) len = cnk->len; | |
690 | else len = maxlen - currlen; | |
691 | ||
692 | memcpy(&(buffer[currlen]), &(base[cnk->start]), len); | |
693 | } | |
694 | currlen += cnk->len; | |
695 | ||
696 | break; | |
697 | ||
698 | case CNK_INT: | |
699 | case CNK_UINT: | |
700 | fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags); | |
701 | break; | |
702 | ||
703 | case CNK_OCTAL: | |
704 | fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags); | |
705 | break; | |
706 | ||
707 | case CNK_HEX: | |
708 | fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags); | |
709 | break; | |
710 | ||
711 | case CNK_FLOAT: | |
712 | fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags); | |
713 | break; | |
714 | ||
715 | case CNK_CHAR: | |
716 | dopr_outch (buffer, &currlen, maxlen, cnk->value); | |
717 | break; | |
718 | ||
719 | case CNK_STRING: | |
720 | if (max == -1) { | |
721 | max = strlen(cnk->strvalue); | |
722 | } | |
723 | fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max); | |
724 | break; | |
725 | ||
726 | case CNK_PTR: | |
727 | fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags); | |
728 | break; | |
729 | ||
730 | case CNK_NUM: | |
731 | if (cnk->cflags == DP_C_CHAR) | |
732 | *((char *)(cnk->pnum)) = (char)currlen; | |
733 | else if (cnk->cflags == DP_C_SHORT) | |
734 | *((short int *)(cnk->pnum)) = (short int)currlen; | |
735 | else if (cnk->cflags == DP_C_LONG) | |
736 | *((long int *)(cnk->pnum)) = (long int)currlen; | |
737 | else if (cnk->cflags == DP_C_LLONG) | |
738 | *((LLONG *)(cnk->pnum)) = (LLONG)currlen; | |
739 | else if (cnk->cflags == DP_C_SIZET) | |
740 | *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen; | |
741 | else | |
742 | *((int *)(cnk->pnum)) = (int)currlen; | |
743 | break; | |
744 | ||
745 | case CNK_PRCNT: | |
746 | dopr_outch (buffer, &currlen, maxlen, '%'); | |
747 | break; | |
748 | ||
749 | default: | |
750 | /* what ?? */ | |
751 | goto done; | |
752 | } | |
753 | cnk = cnk->next; | |
754 | } | |
8950ac03 AT |
755 | if (maxlen != 0) { |
756 | if (currlen < maxlen - 1) | |
757 | buffer[currlen] = '\0'; | |
758 | else if (maxlen > 0) | |
759 | buffer[maxlen - 1] = '\0'; | |
760 | } | |
2fff0a4f WD |
761 | ret = currlen; |
762 | ||
763 | done: | |
764 | va_end(args); | |
765 | ||
766 | while (chunks) { | |
767 | cnk = chunks->next; | |
768 | free(chunks); | |
769 | chunks = cnk; | |
770 | } | |
771 | if (clist) { | |
772 | for (pnum = 0; pnum < max_pos; pnum++) { | |
773 | if (clist[pnum].chunks) free(clist[pnum].chunks); | |
774 | } | |
775 | free(clist); | |
776 | } | |
777 | return ret; | |
f8be5ef4 AT |
778 | } |
779 | ||
8950ac03 | 780 | static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, |
f8be5ef4 AT |
781 | char *value, int flags, int min, int max) |
782 | { | |
8950ac03 AT |
783 | int padlen, strln; /* amount to pad */ |
784 | int cnt = 0; | |
785 | ||
786 | #ifdef DEBUG_SNPRINTF | |
787 | printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); | |
788 | #endif | |
789 | if (value == 0) { | |
790 | value = "<NULL>"; | |
791 | } | |
792 | ||
2fff0a4f | 793 | for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ |
8950ac03 AT |
794 | padlen = min - strln; |
795 | if (padlen < 0) | |
796 | padlen = 0; | |
797 | if (flags & DP_F_MINUS) | |
798 | padlen = -padlen; /* Left Justify */ | |
799 | ||
2fff0a4f | 800 | while (padlen > 0) { |
8950ac03 AT |
801 | dopr_outch (buffer, currlen, maxlen, ' '); |
802 | --padlen; | |
8950ac03 AT |
803 | } |
804 | while (*value && (cnt < max)) { | |
805 | dopr_outch (buffer, currlen, maxlen, *value++); | |
806 | ++cnt; | |
807 | } | |
2fff0a4f | 808 | while (padlen < 0) { |
8950ac03 AT |
809 | dopr_outch (buffer, currlen, maxlen, ' '); |
810 | ++padlen; | |
8950ac03 | 811 | } |
f8be5ef4 AT |
812 | } |
813 | ||
814 | /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ | |
815 | ||
8950ac03 | 816 | static void fmtint(char *buffer, size_t *currlen, size_t maxlen, |
2fff0a4f | 817 | LLONG value, int base, int min, int max, int flags) |
f8be5ef4 | 818 | { |
8950ac03 | 819 | int signvalue = 0; |
2fff0a4f | 820 | unsigned LLONG uvalue; |
8950ac03 AT |
821 | char convert[20]; |
822 | int place = 0; | |
823 | int spadlen = 0; /* amount to space pad */ | |
824 | int zpadlen = 0; /* amount to zero pad */ | |
825 | int caps = 0; | |
826 | ||
827 | if (max < 0) | |
828 | max = 0; | |
829 | ||
830 | uvalue = value; | |
831 | ||
832 | if(!(flags & DP_F_UNSIGNED)) { | |
833 | if( value < 0 ) { | |
834 | signvalue = '-'; | |
835 | uvalue = -value; | |
836 | } else { | |
837 | if (flags & DP_F_PLUS) /* Do a sign (+/i) */ | |
838 | signvalue = '+'; | |
839 | else if (flags & DP_F_SPACE) | |
840 | signvalue = ' '; | |
841 | } | |
842 | } | |
f8be5ef4 | 843 | |
8950ac03 AT |
844 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ |
845 | ||
846 | do { | |
847 | convert[place++] = | |
848 | (caps? "0123456789ABCDEF":"0123456789abcdef") | |
849 | [uvalue % (unsigned)base ]; | |
850 | uvalue = (uvalue / (unsigned)base ); | |
851 | } while(uvalue && (place < 20)); | |
852 | if (place == 20) place--; | |
853 | convert[place] = 0; | |
854 | ||
855 | zpadlen = max - place; | |
856 | spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); | |
857 | if (zpadlen < 0) zpadlen = 0; | |
858 | if (spadlen < 0) spadlen = 0; | |
859 | if (flags & DP_F_ZERO) { | |
860 | zpadlen = MAX(zpadlen, spadlen); | |
861 | spadlen = 0; | |
862 | } | |
863 | if (flags & DP_F_MINUS) | |
864 | spadlen = -spadlen; /* Left Justifty */ | |
f8be5ef4 AT |
865 | |
866 | #ifdef DEBUG_SNPRINTF | |
8950ac03 AT |
867 | printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", |
868 | zpadlen, spadlen, min, max, place); | |
f8be5ef4 AT |
869 | #endif |
870 | ||
8950ac03 AT |
871 | /* Spaces */ |
872 | while (spadlen > 0) { | |
873 | dopr_outch (buffer, currlen, maxlen, ' '); | |
874 | --spadlen; | |
875 | } | |
876 | ||
877 | /* Sign */ | |
878 | if (signvalue) | |
879 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
880 | ||
881 | /* Zeros */ | |
882 | if (zpadlen > 0) { | |
883 | while (zpadlen > 0) { | |
884 | dopr_outch (buffer, currlen, maxlen, '0'); | |
885 | --zpadlen; | |
886 | } | |
887 | } | |
888 | ||
889 | /* Digits */ | |
890 | while (place > 0) | |
891 | dopr_outch (buffer, currlen, maxlen, convert[--place]); | |
f8be5ef4 | 892 | |
8950ac03 AT |
893 | /* Left Justified spaces */ |
894 | while (spadlen < 0) { | |
895 | dopr_outch (buffer, currlen, maxlen, ' '); | |
896 | ++spadlen; | |
897 | } | |
f8be5ef4 AT |
898 | } |
899 | ||
8950ac03 | 900 | static LDOUBLE abs_val(LDOUBLE value) |
f8be5ef4 | 901 | { |
8950ac03 | 902 | LDOUBLE result = value; |
f8be5ef4 | 903 | |
8950ac03 AT |
904 | if (value < 0) |
905 | result = -value; | |
906 | ||
907 | return result; | |
f8be5ef4 AT |
908 | } |
909 | ||
8950ac03 | 910 | static LDOUBLE POW10(int exp) |
f8be5ef4 | 911 | { |
8950ac03 AT |
912 | LDOUBLE result = 1; |
913 | ||
914 | while (exp) { | |
915 | result *= 10; | |
916 | exp--; | |
917 | } | |
f8be5ef4 | 918 | |
8950ac03 | 919 | return result; |
f8be5ef4 AT |
920 | } |
921 | ||
8950ac03 | 922 | static LLONG ROUND(LDOUBLE value) |
f8be5ef4 | 923 | { |
8950ac03 | 924 | LLONG intpart; |
f8be5ef4 | 925 | |
8950ac03 AT |
926 | intpart = (LLONG)value; |
927 | value = value - intpart; | |
928 | if (value >= 0.5) intpart++; | |
929 | ||
930 | return intpart; | |
931 | } | |
f8be5ef4 | 932 | |
8950ac03 AT |
933 | /* a replacement for modf that doesn't need the math library. Should |
934 | be portable, but slow */ | |
935 | static double my_modf(double x0, double *iptr) | |
936 | { | |
937 | int i; | |
2fff0a4f | 938 | LLONG l=0; |
8950ac03 AT |
939 | double x = x0; |
940 | double f = 1.0; | |
941 | ||
942 | for (i=0;i<100;i++) { | |
943 | l = (long)x; | |
2fff0a4f | 944 | if (l <= (x+1) && l >= (x-1)) break; |
8950ac03 AT |
945 | x *= 0.1; |
946 | f *= 10.0; | |
947 | } | |
948 | ||
2fff0a4f WD |
949 | if (i == 100) { |
950 | /* yikes! the number is beyond what we can handle. What do we do? */ | |
951 | (*iptr) = 0; | |
952 | return 0; | |
953 | } | |
954 | ||
955 | if (i != 0) { | |
956 | double i2; | |
957 | double ret; | |
958 | ||
959 | ret = my_modf(x0-l*f, &i2); | |
960 | (*iptr) = l*f + i2; | |
961 | return ret; | |
962 | } | |
963 | ||
964 | (*iptr) = l; | |
965 | return x - (*iptr); | |
f8be5ef4 AT |
966 | } |
967 | ||
8950ac03 | 968 | |
f8be5ef4 | 969 | static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, |
10600500 | 970 | LDOUBLE fvalue, int min, int max, int flags) |
f8be5ef4 | 971 | { |
8950ac03 AT |
972 | int signvalue = 0; |
973 | double ufvalue; | |
974 | char iconvert[311]; | |
975 | char fconvert[311]; | |
976 | int iplace = 0; | |
977 | int fplace = 0; | |
978 | int padlen = 0; /* amount to pad */ | |
979 | int zpadlen = 0; | |
980 | int caps = 0; | |
22c7c5fb | 981 | int idx; |
8950ac03 AT |
982 | double intpart; |
983 | double fracpart; | |
984 | double temp; | |
f8be5ef4 | 985 | |
8950ac03 AT |
986 | /* |
987 | * AIX manpage says the default is 0, but Solaris says the default | |
988 | * is 6, and sprintf on AIX defaults to 6 | |
989 | */ | |
990 | if (max < 0) | |
991 | max = 6; | |
f8be5ef4 | 992 | |
8950ac03 | 993 | ufvalue = abs_val (fvalue); |
f8be5ef4 | 994 | |
8950ac03 AT |
995 | if (fvalue < 0) { |
996 | signvalue = '-'; | |
997 | } else { | |
998 | if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ | |
999 | signvalue = '+'; | |
1000 | } else { | |
1001 | if (flags & DP_F_SPACE) | |
1002 | signvalue = ' '; | |
1003 | } | |
1004 | } | |
f8be5ef4 | 1005 | |
8950ac03 AT |
1006 | #if 0 |
1007 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | |
f8be5ef4 AT |
1008 | #endif |
1009 | ||
8950ac03 AT |
1010 | #if 0 |
1011 | if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ | |
1012 | #endif | |
f8be5ef4 | 1013 | |
8950ac03 | 1014 | /* |
2fff0a4f | 1015 | * Sorry, we only support 9 digits past the decimal because of our |
8950ac03 AT |
1016 | * conversion method |
1017 | */ | |
2fff0a4f WD |
1018 | if (max > 9) |
1019 | max = 9; | |
8950ac03 AT |
1020 | |
1021 | /* We "cheat" by converting the fractional part to integer by | |
1022 | * multiplying by a factor of 10 | |
1023 | */ | |
1024 | ||
1025 | temp = ufvalue; | |
1026 | my_modf(temp, &intpart); | |
1027 | ||
1028 | fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); | |
1029 | ||
1030 | if (fracpart >= POW10(max)) { | |
1031 | intpart++; | |
1032 | fracpart -= POW10(max); | |
1033 | } | |
1034 | ||
1035 | ||
1036 | /* Convert integer part */ | |
1037 | do { | |
22c7c5fb WD |
1038 | temp = intpart*0.1; |
1039 | my_modf(temp, &intpart); | |
1040 | idx = (int) ((temp -intpart +0.05)* 10.0); | |
1041 | /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ | |
1042 | /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ | |
8950ac03 | 1043 | iconvert[iplace++] = |
22c7c5fb | 1044 | (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; |
8950ac03 AT |
1045 | } while (intpart && (iplace < 311)); |
1046 | if (iplace == 311) iplace--; | |
1047 | iconvert[iplace] = 0; | |
1048 | ||
1049 | /* Convert fractional part */ | |
1050 | if (fracpart) | |
1051 | { | |
1052 | do { | |
22c7c5fb WD |
1053 | temp = fracpart*0.1; |
1054 | my_modf(temp, &fracpart); | |
1055 | idx = (int) ((temp -fracpart +0.05)* 10.0); | |
1056 | /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ | |
1057 | /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ | |
8950ac03 | 1058 | fconvert[fplace++] = |
22c7c5fb | 1059 | (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; |
8950ac03 AT |
1060 | } while(fracpart && (fplace < 311)); |
1061 | if (fplace == 311) fplace--; | |
1062 | } | |
1063 | fconvert[fplace] = 0; | |
1064 | ||
1065 | /* -1 for decimal point, another -1 if we are printing a sign */ | |
1066 | padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); | |
1067 | zpadlen = max - fplace; | |
1068 | if (zpadlen < 0) zpadlen = 0; | |
1069 | if (padlen < 0) | |
1070 | padlen = 0; | |
1071 | if (flags & DP_F_MINUS) | |
1072 | padlen = -padlen; /* Left Justifty */ | |
1073 | ||
1074 | if ((flags & DP_F_ZERO) && (padlen > 0)) { | |
1075 | if (signvalue) { | |
1076 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
1077 | --padlen; | |
1078 | signvalue = 0; | |
1079 | } | |
1080 | while (padlen > 0) { | |
1081 | dopr_outch (buffer, currlen, maxlen, '0'); | |
1082 | --padlen; | |
1083 | } | |
1084 | } | |
1085 | while (padlen > 0) { | |
1086 | dopr_outch (buffer, currlen, maxlen, ' '); | |
1087 | --padlen; | |
1088 | } | |
1089 | if (signvalue) | |
1090 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
1091 | ||
1092 | while (iplace > 0) | |
1093 | dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); | |
10600500 AT |
1094 | |
1095 | #ifdef DEBUG_SNPRINTF | |
8950ac03 | 1096 | printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); |
10600500 AT |
1097 | #endif |
1098 | ||
8950ac03 AT |
1099 | /* |
1100 | * Decimal point. This should probably use locale to find the correct | |
1101 | * char to print out. | |
1102 | */ | |
1103 | if (max > 0) { | |
1104 | dopr_outch (buffer, currlen, maxlen, '.'); | |
1105 | ||
990ff150 | 1106 | while (zpadlen > 0) { |
22c7c5fb WD |
1107 | dopr_outch (buffer, currlen, maxlen, '0'); |
1108 | --zpadlen; | |
990ff150 PG |
1109 | } |
1110 | ||
8950ac03 AT |
1111 | while (fplace > 0) |
1112 | dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); | |
1113 | } | |
22c7c5fb | 1114 | |
8950ac03 AT |
1115 | while (padlen < 0) { |
1116 | dopr_outch (buffer, currlen, maxlen, ' '); | |
1117 | ++padlen; | |
1118 | } | |
f8be5ef4 AT |
1119 | } |
1120 | ||
8950ac03 | 1121 | static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) |
f8be5ef4 | 1122 | { |
8950ac03 AT |
1123 | if (*currlen < maxlen) { |
1124 | buffer[(*currlen)] = c; | |
1125 | } | |
1126 | (*currlen)++; | |
f8be5ef4 | 1127 | } |
f8be5ef4 | 1128 | |
2fff0a4f WD |
1129 | static struct pr_chunk *new_chunk(void) { |
1130 | struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk)); | |
1131 | ||
1132 | if (!new_c) | |
1133 | return NULL; | |
1134 | ||
1135 | new_c->type = 0; | |
1136 | new_c->num = 0; | |
1137 | new_c->min = 0; | |
1138 | new_c->min_star = NULL; | |
1139 | new_c->max = -1; | |
1140 | new_c->max_star = NULL; | |
1141 | new_c->flags = 0; | |
1142 | new_c->cflags = 0; | |
1143 | new_c->start = 0; | |
1144 | new_c->len = 0; | |
1145 | new_c->value = 0; | |
1146 | new_c->fvalue = 0; | |
1147 | new_c->strvalue = NULL; | |
1148 | new_c->pnum = NULL; | |
1149 | new_c->next = NULL; | |
1150 | ||
1151 | return new_c; | |
1152 | } | |
1153 | ||
1154 | static int add_cnk_list_entry(struct pr_chunk_x **list, | |
1155 | int max_num, struct pr_chunk *chunk) { | |
1156 | struct pr_chunk_x *l; | |
1157 | struct pr_chunk **c; | |
1158 | int max; | |
1159 | int cnum; | |
1160 | int i, pos; | |
1161 | ||
1162 | if (chunk->num > max_num) { | |
1163 | max = chunk->num; | |
1164 | ||
1165 | if (*list == NULL) { | |
1166 | l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max); | |
1167 | pos = 0; | |
1168 | } else { | |
1169 | l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max); | |
1170 | pos = max_num; | |
1171 | } | |
1172 | if (l == NULL) { | |
1173 | for (i = 0; i < max; i++) { | |
1174 | if ((*list)[i].chunks) free((*list)[i].chunks); | |
1175 | } | |
1176 | return 0; | |
1177 | } | |
1178 | for (i = pos; i < max; i++) { | |
1179 | l[i].chunks = NULL; | |
1180 | l[i].num = 0; | |
1181 | } | |
1182 | } else { | |
1183 | l = *list; | |
1184 | max = max_num; | |
1185 | } | |
1186 | ||
1187 | i = chunk->num - 1; | |
1188 | cnum = l[i].num + 1; | |
1189 | if (l[i].chunks == NULL) { | |
1190 | c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum); | |
1191 | } else { | |
1192 | c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum); | |
1193 | } | |
1194 | if (c == NULL) { | |
1195 | for (i = 0; i < max; i++) { | |
1196 | if (l[i].chunks) free(l[i].chunks); | |
1197 | } | |
1198 | return 0; | |
1199 | } | |
1200 | c[l[i].num] = chunk; | |
1201 | l[i].chunks = c; | |
1202 | l[i].num = cnum; | |
1203 | ||
1204 | *list = l; | |
1205 | return max; | |
1206 | } | |
1207 | ||
22c7c5fb | 1208 | int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args) |
f8be5ef4 | 1209 | { |
8950ac03 | 1210 | return dopr(str, count, fmt, args); |
f8be5ef4 | 1211 | } |
22c7c5fb | 1212 | #define vsnprintf rsync_vsnprintf |
f8be5ef4 | 1213 | #endif |
8950ac03 | 1214 | |
22c7c5fb WD |
1215 | /* yes this really must be a ||. Don't muck with this (tridge) |
1216 | * | |
1217 | * The logic for these two is that we need our own definition if the | |
1218 | * OS *either* has no definition of *sprintf, or if it does have one | |
1219 | * that doesn't work properly according to the autoconf test. | |
1220 | */ | |
8950ac03 | 1221 | #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF) |
22c7c5fb | 1222 | int rsync_snprintf(char *str,size_t count,const char *fmt,...) |
f8be5ef4 | 1223 | { |
8950ac03 AT |
1224 | size_t ret; |
1225 | va_list ap; | |
f8be5ef4 | 1226 | |
8950ac03 AT |
1227 | va_start(ap, fmt); |
1228 | ret = vsnprintf(str, count, fmt, ap); | |
1229 | va_end(ap); | |
1230 | return ret; | |
f8be5ef4 | 1231 | } |
22c7c5fb | 1232 | #define snprintf rsync_snprintf |
8950ac03 | 1233 | #endif |
f8be5ef4 | 1234 | |
8950ac03 | 1235 | #endif |
10600500 | 1236 | |
8950ac03 AT |
1237 | #ifndef HAVE_VASPRINTF |
1238 | int vasprintf(char **ptr, const char *format, va_list ap) | |
1239 | { | |
1240 | int ret; | |
22c7c5fb WD |
1241 | va_list ap2; |
1242 | ||
1243 | VA_COPY(ap2, ap); | |
22c7c5fb | 1244 | ret = vsnprintf(NULL, 0, format, ap2); |
2fff0a4f WD |
1245 | va_end(ap2); |
1246 | if (ret < 0) return ret; | |
10600500 | 1247 | |
8950ac03 AT |
1248 | (*ptr) = (char *)malloc(ret+1); |
1249 | if (!*ptr) return -1; | |
22c7c5fb WD |
1250 | |
1251 | VA_COPY(ap2, ap); | |
22c7c5fb | 1252 | ret = vsnprintf(*ptr, ret+1, format, ap2); |
2fff0a4f | 1253 | va_end(ap2); |
8950ac03 AT |
1254 | |
1255 | return ret; | |
1256 | } | |
1257 | #endif | |
1258 | ||
1259 | ||
1260 | #ifndef HAVE_ASPRINTF | |
1261 | int asprintf(char **ptr, const char *format, ...) | |
1262 | { | |
1263 | va_list ap; | |
1264 | int ret; | |
1265 | ||
22c7c5fb | 1266 | *ptr = NULL; |
8950ac03 AT |
1267 | va_start(ap, format); |
1268 | ret = vasprintf(ptr, format, ap); | |
1269 | va_end(ap); | |
1270 | ||
1271 | return ret; | |
1272 | } | |
f8be5ef4 | 1273 | #endif |
8950ac03 AT |
1274 | |
1275 | #ifdef TEST_SNPRINTF | |
1276 | ||
1277 | int sprintf(char *str,const char *fmt,...); | |
2fff0a4f | 1278 | int printf(const char *fmt,...); |
8950ac03 | 1279 | |
2f098547 | 1280 | int main (void) |
f8be5ef4 | 1281 | { |
8950ac03 AT |
1282 | char buf1[1024]; |
1283 | char buf2[1024]; | |
2fff0a4f | 1284 | char *buf3; |
8950ac03 AT |
1285 | char *fp_fmt[] = { |
1286 | "%1.1f", | |
1287 | "%-1.5f", | |
1288 | "%1.5f", | |
1289 | "%123.9f", | |
1290 | "%10.5f", | |
1291 | "% 10.5f", | |
1292 | "%+22.9f", | |
1293 | "%+4.9f", | |
1294 | "%01.3f", | |
1295 | "%4f", | |
1296 | "%3.1f", | |
1297 | "%3.2f", | |
1298 | "%.0f", | |
1299 | "%f", | |
2fff0a4f WD |
1300 | "%-8.8f", |
1301 | "%-9.9f", | |
8950ac03 AT |
1302 | NULL |
1303 | }; | |
22c7c5fb WD |
1304 | double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, |
1305 | 0.9996, 1.996, 4.136, 5.030201, 0.00205, | |
1306 | /* END LIST */ 0}; | |
8950ac03 AT |
1307 | char *int_fmt[] = { |
1308 | "%-1.5d", | |
1309 | "%1.5d", | |
1310 | "%123.9d", | |
1311 | "%5.5d", | |
1312 | "%10.5d", | |
1313 | "% 10.5d", | |
1314 | "%+22.33d", | |
1315 | "%01.3d", | |
1316 | "%4d", | |
1317 | "%d", | |
1318 | NULL | |
1319 | }; | |
2fff0a4f | 1320 | long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0}; |
8950ac03 | 1321 | char *str_fmt[] = { |
2fff0a4f WD |
1322 | "%10.5s", |
1323 | "%-10.5s", | |
1324 | "%5.10s", | |
1325 | "%-5.10s", | |
1326 | "%10.1s", | |
1327 | "%0.10s", | |
1328 | "%10.0s", | |
1329 | "%1.10s", | |
8950ac03 AT |
1330 | "%s", |
1331 | "%.1s", | |
1332 | "%.10s", | |
1333 | "%10s", | |
1334 | NULL | |
1335 | }; | |
1336 | char *str_vals[] = {"hello", "a", "", "a longer string", NULL}; | |
2fff0a4f WD |
1337 | #ifdef HAVE_LONG_LONG |
1338 | char *ll_fmt[] = { | |
1339 | "%llu", | |
1340 | NULL | |
1341 | }; | |
1342 | LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0}; | |
1343 | #endif | |
8950ac03 AT |
1344 | int x, y; |
1345 | int fail = 0; | |
1346 | int num = 0; | |
2fff0a4f WD |
1347 | int l1, l2; |
1348 | char *ss_fmt[] = { | |
1349 | "%zd", | |
1350 | "%zu", | |
1351 | NULL | |
1352 | }; | |
1353 | size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0}; | |
8950ac03 AT |
1354 | |
1355 | printf ("Testing snprintf format codes against system sprintf...\n"); | |
1356 | ||
1357 | for (x = 0; fp_fmt[x] ; x++) { | |
1358 | for (y = 0; fp_nums[y] != 0 ; y++) { | |
2fff0a4f WD |
1359 | buf1[0] = buf2[0] = '\0'; |
1360 | l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); | |
1361 | l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]); | |
1362 | buf1[1023] = buf2[1023] = '\0'; | |
1363 | if (strcmp (buf1, buf2) || (l1 != l2)) { | |
1364 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1365 | fp_fmt[x], l1, buf1, l2, buf2); | |
8950ac03 AT |
1366 | fail++; |
1367 | } | |
1368 | num++; | |
1369 | } | |
1370 | } | |
1371 | ||
1372 | for (x = 0; int_fmt[x] ; x++) { | |
1373 | for (y = 0; int_nums[y] != 0 ; y++) { | |
2fff0a4f WD |
1374 | buf1[0] = buf2[0] = '\0'; |
1375 | l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); | |
1376 | l2 = sprintf (buf2, int_fmt[x], int_nums[y]); | |
1377 | buf1[1023] = buf2[1023] = '\0'; | |
1378 | if (strcmp (buf1, buf2) || (l1 != l2)) { | |
1379 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1380 | int_fmt[x], l1, buf1, l2, buf2); | |
8950ac03 AT |
1381 | fail++; |
1382 | } | |
1383 | num++; | |
1384 | } | |
1385 | } | |
1386 | ||
1387 | for (x = 0; str_fmt[x] ; x++) { | |
1388 | for (y = 0; str_vals[y] != 0 ; y++) { | |
2fff0a4f WD |
1389 | buf1[0] = buf2[0] = '\0'; |
1390 | l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]); | |
1391 | l2 = sprintf (buf2, str_fmt[x], str_vals[y]); | |
1392 | buf1[1023] = buf2[1023] = '\0'; | |
1393 | if (strcmp (buf1, buf2) || (l1 != l2)) { | |
1394 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1395 | str_fmt[x], l1, buf1, l2, buf2); | |
1396 | fail++; | |
1397 | } | |
1398 | num++; | |
1399 | } | |
1400 | } | |
1401 | ||
1402 | #ifdef HAVE_LONG_LONG | |
1403 | for (x = 0; ll_fmt[x] ; x++) { | |
1404 | for (y = 0; ll_nums[y] != 0 ; y++) { | |
1405 | buf1[0] = buf2[0] = '\0'; | |
1406 | l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]); | |
1407 | l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]); | |
1408 | buf1[1023] = buf2[1023] = '\0'; | |
1409 | if (strcmp (buf1, buf2) || (l1 != l2)) { | |
1410 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1411 | ll_fmt[x], l1, buf1, l2, buf2); | |
8950ac03 AT |
1412 | fail++; |
1413 | } | |
2fff0a4f WD |
1414 | num++; |
1415 | } | |
1416 | } | |
1417 | #endif | |
1418 | ||
1419 | #define BUFSZ 2048 | |
1420 | ||
1421 | buf1[0] = buf2[0] = '\0'; | |
1422 | if ((buf3 = malloc(BUFSZ)) == NULL) { | |
1423 | fail++; | |
1424 | } else { | |
1425 | num++; | |
1426 | memset(buf3, 'a', BUFSZ); | |
1427 | snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3); | |
1428 | buf1[1023] = '\0'; | |
1429 | if (strcmp(buf1, "a") != 0) { | |
1430 | printf("length limit buf1 '%s' expected 'a'\n", buf1); | |
1431 | fail++; | |
1432 | } | |
1433 | } | |
1434 | ||
1435 | buf1[0] = buf2[0] = '\0'; | |
1436 | l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9); | |
1437 | l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9); | |
1438 | buf1[1023] = buf2[1023] = '\0'; | |
1439 | if (strcmp(buf1, buf2) || (l1 != l2)) { | |
1440 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1441 | "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); | |
1442 | fail++; | |
1443 | } | |
1444 | ||
1445 | buf1[0] = buf2[0] = '\0'; | |
1446 | l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9); | |
1447 | l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9); | |
1448 | buf1[1023] = buf2[1023] = '\0'; | |
1449 | if (strcmp(buf1, buf2)) { | |
1450 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1451 | "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); | |
1452 | fail++; | |
1453 | } | |
1454 | ||
1455 | for (x = 0; ss_fmt[x] ; x++) { | |
1456 | for (y = 0; ss_nums[y] != 0 ; y++) { | |
1457 | buf1[0] = buf2[0] = '\0'; | |
1458 | l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]); | |
1459 | l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]); | |
1460 | buf1[1023] = buf2[1023] = '\0'; | |
1461 | if (strcmp (buf1, buf2) || (l1 != l2)) { | |
1462 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1463 | ss_fmt[x], l1, buf1, l2, buf2); | |
8950ac03 AT |
1464 | fail++; |
1465 | } | |
1466 | num++; | |
1467 | } | |
1468 | } | |
2fff0a4f WD |
1469 | #if 0 |
1470 | buf1[0] = buf2[0] = '\0'; | |
1471 | l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890); | |
1472 | l2 = sprintf(buf2, "%lld", (LLONG)1234567890); | |
1473 | buf1[1023] = buf2[1023] = '\0'; | |
1474 | if (strcmp(buf1, buf2)) { | |
1475 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1476 | "%lld", l1, buf1, l2, buf2); | |
1477 | fail++; | |
1478 | } | |
8950ac03 | 1479 | |
2fff0a4f WD |
1480 | buf1[0] = buf2[0] = '\0'; |
1481 | l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123); | |
1482 | l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123); | |
1483 | buf1[1023] = buf2[1023] = '\0'; | |
1484 | if (strcmp(buf1, buf2)) { | |
1485 | printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
1486 | "%Lf", l1, buf1, l2, buf2); | |
1487 | fail++; | |
1488 | } | |
1489 | #endif | |
8950ac03 AT |
1490 | printf ("%d tests failed out of %d.\n", fail, num); |
1491 | ||
1492 | printf("seeing how many digits we support\n"); | |
1493 | { | |
1494 | double v0 = 0.12345678901234567890123456789012345678901; | |
1495 | for (x=0; x<100; x++) { | |
22c7c5fb WD |
1496 | double p = pow(10, x); |
1497 | double r = v0*p; | |
1498 | snprintf(buf1, sizeof(buf1), "%1.1f", r); | |
1499 | sprintf(buf2, "%1.1f", r); | |
8950ac03 AT |
1500 | if (strcmp(buf1, buf2)) { |
1501 | printf("we seem to support %d digits\n", x-1); | |
1502 | break; | |
1503 | } | |
1504 | } | |
1505 | } | |
1506 | ||
1507 | return 0; | |
f8be5ef4 | 1508 | } |
22c7c5fb | 1509 | #endif /* TEST_SNPRINTF */ |