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