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