Commit | Line | Data |
---|---|---|
2f098547 AT |
1 | /* |
2 | * Copyright Patrick Powell 1995 | |
3 | * This code is based on code written by Patrick Powell (papowell@astart.com) | |
4 | * It may be used for any purpose as long as this notice remains intact | |
5 | * on all source code distributions | |
f8be5ef4 AT |
6 | */ |
7 | ||
8 | /************************************************************** | |
9 | * Original: | |
10 | * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 | |
11 | * A bombproof version of doprnt (dopr) included. | |
12 | * Sigh. This sort of thing is always nasty do deal with. Note that | |
13 | * the version here does not include floating point... | |
14 | * | |
15 | * snprintf() is used instead of sprintf() as it does limit checks | |
16 | * for string length. This covers a nasty loophole. | |
17 | * | |
18 | * The other functions are there to prevent NULL pointers from | |
19 | * causing nast effects. | |
20 | * | |
21 | * More Recently: | |
22 | * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 | |
23 | * This was ugly. It is still ugly. I opted out of floating point | |
24 | * numbers, but the formatter understands just about everything | |
25 | * from the normal C string format, at least as far as I can tell from | |
26 | * the Solaris 2.5 printf(3S) man page. | |
27 | * | |
28 | * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 | |
29 | * Ok, added some minimal floating point support, which means this | |
30 | * probably requires libm on most operating systems. Don't yet | |
31 | * support the exponent (e,E) and sigfig (g,G). Also, fmtint() | |
32 | * was pretty badly broken, it just wasn't being exercised in ways | |
33 | * which showed it, so that's been fixed. Also, formated the code | |
34 | * to mutt conventions, and removed dead code left over from the | |
35 | * original. Also, there is now a builtin-test, just compile with: | |
36 | * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm | |
37 | * and run snprintf for results. | |
38 | * | |
39 | * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i | |
40 | * The PGP code was using unsigned hexadecimal formats. | |
41 | * Unfortunately, unsigned formats simply didn't work. | |
42 | * | |
43 | * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 | |
44 | * The original code assumed that both snprintf() and vsnprintf() were | |
45 | * missing. Some systems only have snprintf() but not vsnprintf(), so | |
46 | * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. | |
47 | * | |
9e3c856a | 48 | * Andrew Tridgell (tridge@samba.org) Oct 1998 |
10600500 AT |
49 | * fixed handling of %.0f |
50 | * added test for HAVE_LONG_DOUBLE | |
51 | * | |
8950ac03 AT |
52 | * tridge@samba.org, idra@samba.org, April 2001 |
53 | * got rid of fcvt code (twas buggy and made testing harder) | |
54 | * added C99 semantics | |
55 | * | |
990ff150 PG |
56 | * Paul Green (paulg@samba.org) April 9, 2003 |
57 | * fixed handling of %f when converting fractions with leading zeros. | |
58 | * (e.g., 0.025). | |
f8be5ef4 AT |
59 | **************************************************************/ |
60 | ||
8950ac03 | 61 | #ifndef NO_CONFIG_H /* for some tests */ |
f8be5ef4 | 62 | #include "config.h" |
8950ac03 | 63 | #endif |
f8be5ef4 | 64 | |
8950ac03 | 65 | #ifdef HAVE_STRING_H |
f8be5ef4 | 66 | #include <string.h> |
8950ac03 | 67 | #endif |
f8be5ef4 | 68 | |
8950ac03 AT |
69 | #ifdef HAVE_STRINGS_H |
70 | #include <strings.h> | |
71 | #endif | |
72 | #ifdef HAVE_CTYPE_H | |
73 | #include <ctype.h> | |
74 | #endif | |
75 | #include <sys/types.h> | |
76 | #include <stdarg.h> | |
77 | #ifdef HAVE_STDLIB_H | |
78 | #include <stdlib.h> | |
79 | #endif | |
f8be5ef4 | 80 | |
8950ac03 AT |
81 | #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) |
82 | /* only include stdio.h if we are not re-defining snprintf or vsnprintf */ | |
83 | #include <stdio.h> | |
84 | /* make the compiler happy with an empty file */ | |
85 | void dummy_snprintf(void) {} | |
f8be5ef4 | 86 | #else |
f8be5ef4 | 87 | |
10600500 AT |
88 | #ifdef HAVE_LONG_DOUBLE |
89 | #define LDOUBLE long double | |
90 | #else | |
91 | #define LDOUBLE double | |
92 | #endif | |
93 | ||
8950ac03 AT |
94 | #ifdef HAVE_LONG_LONG |
95 | #define LLONG long long | |
96 | #else | |
97 | #define LLONG long | |
98 | #endif | |
f8be5ef4 | 99 | |
8950ac03 AT |
100 | static size_t dopr(char *buffer, size_t maxlen, const char *format, |
101 | va_list args); | |
102 | static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, | |
f8be5ef4 | 103 | char *value, int flags, int min, int max); |
8950ac03 | 104 | static void fmtint(char *buffer, size_t *currlen, size_t maxlen, |
f8be5ef4 | 105 | long value, int base, int min, int max, int flags); |
8950ac03 | 106 | static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, |
10600500 | 107 | LDOUBLE fvalue, int min, int max, int flags); |
8950ac03 | 108 | static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); |
f8be5ef4 AT |
109 | |
110 | /* | |
111 | * dopr(): poor man's version of doprintf | |
112 | */ | |
113 | ||
114 | /* format read states */ | |
115 | #define DP_S_DEFAULT 0 | |
116 | #define DP_S_FLAGS 1 | |
117 | #define DP_S_MIN 2 | |
118 | #define DP_S_DOT 3 | |
119 | #define DP_S_MAX 4 | |
120 | #define DP_S_MOD 5 | |
121 | #define DP_S_CONV 6 | |
122 | #define DP_S_DONE 7 | |
123 | ||
124 | /* format flags - Bits */ | |
125 | #define DP_F_MINUS (1 << 0) | |
126 | #define DP_F_PLUS (1 << 1) | |
127 | #define DP_F_SPACE (1 << 2) | |
128 | #define DP_F_NUM (1 << 3) | |
129 | #define DP_F_ZERO (1 << 4) | |
130 | #define DP_F_UP (1 << 5) | |
131 | #define DP_F_UNSIGNED (1 << 6) | |
132 | ||
133 | /* Conversion Flags */ | |
134 | #define DP_C_SHORT 1 | |
135 | #define DP_C_LONG 2 | |
136 | #define DP_C_LDOUBLE 3 | |
8950ac03 | 137 | #define DP_C_LLONG 4 |
f8be5ef4 | 138 | |
8950ac03 AT |
139 | #define char_to_int(p) ((p)- '0') |
140 | #ifndef MAX | |
141 | #define MAX(p,q) (((p) >= (q)) ? (p) : (q)) | |
142 | #endif | |
f8be5ef4 | 143 | |
8950ac03 | 144 | static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args) |
f8be5ef4 | 145 | { |
8950ac03 AT |
146 | char ch; |
147 | LLONG value; | |
148 | LDOUBLE fvalue; | |
149 | char *strvalue; | |
150 | int min; | |
151 | int max; | |
152 | int state; | |
153 | int flags; | |
154 | int cflags; | |
155 | size_t currlen; | |
156 | ||
157 | state = DP_S_DEFAULT; | |
158 | currlen = flags = cflags = min = 0; | |
159 | max = -1; | |
f8be5ef4 | 160 | ch = *format++; |
8950ac03 AT |
161 | |
162 | while (state != DP_S_DONE) { | |
163 | if (ch == '\0') | |
164 | state = DP_S_DONE; | |
165 | ||
166 | switch(state) { | |
167 | case DP_S_DEFAULT: | |
168 | if (ch == '%') | |
169 | state = DP_S_FLAGS; | |
170 | else | |
171 | dopr_outch (buffer, &currlen, maxlen, ch); | |
172 | ch = *format++; | |
173 | break; | |
174 | case DP_S_FLAGS: | |
175 | switch (ch) { | |
176 | case '-': | |
177 | flags |= DP_F_MINUS; | |
178 | ch = *format++; | |
179 | break; | |
180 | case '+': | |
181 | flags |= DP_F_PLUS; | |
182 | ch = *format++; | |
183 | break; | |
184 | case ' ': | |
185 | flags |= DP_F_SPACE; | |
186 | ch = *format++; | |
187 | break; | |
188 | case '#': | |
189 | flags |= DP_F_NUM; | |
190 | ch = *format++; | |
191 | break; | |
192 | case '0': | |
193 | flags |= DP_F_ZERO; | |
194 | ch = *format++; | |
195 | break; | |
196 | default: | |
197 | state = DP_S_MIN; | |
198 | break; | |
199 | } | |
200 | break; | |
201 | case DP_S_MIN: | |
202 | if (isdigit((unsigned char)ch)) { | |
203 | min = 10*min + char_to_int (ch); | |
204 | ch = *format++; | |
205 | } else if (ch == '*') { | |
206 | min = va_arg (args, int); | |
207 | ch = *format++; | |
208 | state = DP_S_DOT; | |
209 | } else { | |
210 | state = DP_S_DOT; | |
211 | } | |
212 | break; | |
213 | case DP_S_DOT: | |
214 | if (ch == '.') { | |
215 | state = DP_S_MAX; | |
216 | ch = *format++; | |
217 | } else { | |
218 | state = DP_S_MOD; | |
219 | } | |
220 | break; | |
221 | case DP_S_MAX: | |
222 | if (isdigit((unsigned char)ch)) { | |
223 | if (max < 0) | |
224 | max = 0; | |
225 | max = 10*max + char_to_int (ch); | |
226 | ch = *format++; | |
227 | } else if (ch == '*') { | |
228 | max = va_arg (args, int); | |
229 | ch = *format++; | |
230 | state = DP_S_MOD; | |
231 | } else { | |
232 | state = DP_S_MOD; | |
233 | } | |
234 | break; | |
235 | case DP_S_MOD: | |
236 | switch (ch) { | |
237 | case 'h': | |
238 | cflags = DP_C_SHORT; | |
239 | ch = *format++; | |
240 | break; | |
241 | case 'l': | |
242 | cflags = DP_C_LONG; | |
243 | ch = *format++; | |
244 | if (ch == 'l') { /* It's a long long */ | |
245 | cflags = DP_C_LLONG; | |
246 | ch = *format++; | |
247 | } | |
248 | break; | |
249 | case 'L': | |
250 | cflags = DP_C_LDOUBLE; | |
251 | ch = *format++; | |
252 | break; | |
253 | default: | |
254 | break; | |
255 | } | |
256 | state = DP_S_CONV; | |
257 | break; | |
258 | case DP_S_CONV: | |
259 | switch (ch) { | |
260 | case 'd': | |
261 | case 'i': | |
262 | if (cflags == DP_C_SHORT) | |
263 | value = va_arg (args, int); | |
264 | else if (cflags == DP_C_LONG) | |
265 | value = va_arg (args, long int); | |
266 | else if (cflags == DP_C_LLONG) | |
267 | value = va_arg (args, LLONG); | |
268 | else | |
269 | value = va_arg (args, int); | |
270 | fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); | |
271 | break; | |
272 | case 'o': | |
273 | flags |= DP_F_UNSIGNED; | |
274 | if (cflags == DP_C_SHORT) | |
275 | value = va_arg (args, unsigned int); | |
276 | else if (cflags == DP_C_LONG) | |
277 | value = (long)va_arg (args, unsigned long int); | |
278 | else if (cflags == DP_C_LLONG) | |
279 | value = (long)va_arg (args, unsigned LLONG); | |
280 | else | |
281 | value = (long)va_arg (args, unsigned int); | |
282 | fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); | |
283 | break; | |
284 | case 'u': | |
285 | flags |= DP_F_UNSIGNED; | |
286 | if (cflags == DP_C_SHORT) | |
287 | value = va_arg (args, unsigned int); | |
288 | else if (cflags == DP_C_LONG) | |
289 | value = (long)va_arg (args, unsigned long int); | |
290 | else if (cflags == DP_C_LLONG) | |
291 | value = (LLONG)va_arg (args, unsigned LLONG); | |
292 | else | |
293 | value = (long)va_arg (args, unsigned int); | |
294 | fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); | |
295 | break; | |
296 | case 'X': | |
297 | flags |= DP_F_UP; | |
298 | case 'x': | |
299 | flags |= DP_F_UNSIGNED; | |
300 | if (cflags == DP_C_SHORT) | |
301 | value = va_arg (args, unsigned int); | |
302 | else if (cflags == DP_C_LONG) | |
303 | value = (long)va_arg (args, unsigned long int); | |
304 | else if (cflags == DP_C_LLONG) | |
305 | value = (LLONG)va_arg (args, unsigned LLONG); | |
306 | else | |
307 | value = (long)va_arg (args, unsigned int); | |
308 | fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); | |
309 | break; | |
310 | case 'f': | |
311 | if (cflags == DP_C_LDOUBLE) | |
312 | fvalue = va_arg (args, LDOUBLE); | |
313 | else | |
314 | fvalue = va_arg (args, double); | |
315 | /* um, floating point? */ | |
316 | fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); | |
317 | break; | |
318 | case 'E': | |
319 | flags |= DP_F_UP; | |
320 | case 'e': | |
321 | if (cflags == DP_C_LDOUBLE) | |
322 | fvalue = va_arg (args, LDOUBLE); | |
323 | else | |
324 | fvalue = va_arg (args, double); | |
325 | break; | |
326 | case 'G': | |
327 | flags |= DP_F_UP; | |
328 | case 'g': | |
329 | if (cflags == DP_C_LDOUBLE) | |
330 | fvalue = va_arg (args, LDOUBLE); | |
331 | else | |
332 | fvalue = va_arg (args, double); | |
333 | break; | |
334 | case 'c': | |
335 | dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); | |
336 | break; | |
337 | case 's': | |
338 | strvalue = va_arg (args, char *); | |
339 | if (max == -1) { | |
340 | max = strlen(strvalue); | |
341 | } | |
342 | if (min > 0 && max >= 0 && min > max) max = min; | |
343 | fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); | |
344 | break; | |
345 | case 'p': | |
346 | strvalue = va_arg (args, void *); | |
347 | fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); | |
348 | break; | |
349 | case 'n': | |
350 | if (cflags == DP_C_SHORT) { | |
351 | short int *num; | |
352 | num = va_arg (args, short int *); | |
353 | *num = currlen; | |
354 | } else if (cflags == DP_C_LONG) { | |
355 | long int *num; | |
356 | num = va_arg (args, long int *); | |
357 | *num = (long int)currlen; | |
358 | } else if (cflags == DP_C_LLONG) { | |
359 | LLONG *num; | |
360 | num = va_arg (args, LLONG *); | |
361 | *num = (LLONG)currlen; | |
362 | } else { | |
363 | int *num; | |
364 | num = va_arg (args, int *); | |
365 | *num = currlen; | |
366 | } | |
367 | break; | |
368 | case '%': | |
369 | dopr_outch (buffer, &currlen, maxlen, ch); | |
370 | break; | |
371 | case 'w': | |
372 | /* not supported yet, treat as next char */ | |
373 | ch = *format++; | |
374 | break; | |
375 | default: | |
376 | /* Unknown, skip */ | |
377 | break; | |
378 | } | |
379 | ch = *format++; | |
380 | state = DP_S_DEFAULT; | |
381 | flags = cflags = min = 0; | |
382 | max = -1; | |
383 | break; | |
384 | case DP_S_DONE: | |
385 | break; | |
386 | default: | |
387 | /* hmm? */ | |
388 | break; /* some picky compilers need this */ | |
389 | } | |
390 | } | |
391 | if (maxlen != 0) { | |
392 | if (currlen < maxlen - 1) | |
393 | buffer[currlen] = '\0'; | |
394 | else if (maxlen > 0) | |
395 | buffer[maxlen - 1] = '\0'; | |
396 | } | |
397 | ||
398 | return currlen; | |
f8be5ef4 AT |
399 | } |
400 | ||
8950ac03 | 401 | static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, |
f8be5ef4 AT |
402 | char *value, int flags, int min, int max) |
403 | { | |
8950ac03 AT |
404 | int padlen, strln; /* amount to pad */ |
405 | int cnt = 0; | |
406 | ||
407 | #ifdef DEBUG_SNPRINTF | |
408 | printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); | |
409 | #endif | |
410 | if (value == 0) { | |
411 | value = "<NULL>"; | |
412 | } | |
413 | ||
414 | for (strln = 0; value[strln]; ++strln); /* strlen */ | |
415 | padlen = min - strln; | |
416 | if (padlen < 0) | |
417 | padlen = 0; | |
418 | if (flags & DP_F_MINUS) | |
419 | padlen = -padlen; /* Left Justify */ | |
420 | ||
421 | while ((padlen > 0) && (cnt < max)) { | |
422 | dopr_outch (buffer, currlen, maxlen, ' '); | |
423 | --padlen; | |
424 | ++cnt; | |
425 | } | |
426 | while (*value && (cnt < max)) { | |
427 | dopr_outch (buffer, currlen, maxlen, *value++); | |
428 | ++cnt; | |
429 | } | |
430 | while ((padlen < 0) && (cnt < max)) { | |
431 | dopr_outch (buffer, currlen, maxlen, ' '); | |
432 | ++padlen; | |
433 | ++cnt; | |
434 | } | |
f8be5ef4 AT |
435 | } |
436 | ||
437 | /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ | |
438 | ||
8950ac03 | 439 | static void fmtint(char *buffer, size_t *currlen, size_t maxlen, |
f8be5ef4 AT |
440 | long value, int base, int min, int max, int flags) |
441 | { | |
8950ac03 AT |
442 | int signvalue = 0; |
443 | unsigned long uvalue; | |
444 | char convert[20]; | |
445 | int place = 0; | |
446 | int spadlen = 0; /* amount to space pad */ | |
447 | int zpadlen = 0; /* amount to zero pad */ | |
448 | int caps = 0; | |
449 | ||
450 | if (max < 0) | |
451 | max = 0; | |
452 | ||
453 | uvalue = value; | |
454 | ||
455 | if(!(flags & DP_F_UNSIGNED)) { | |
456 | if( value < 0 ) { | |
457 | signvalue = '-'; | |
458 | uvalue = -value; | |
459 | } else { | |
460 | if (flags & DP_F_PLUS) /* Do a sign (+/i) */ | |
461 | signvalue = '+'; | |
462 | else if (flags & DP_F_SPACE) | |
463 | signvalue = ' '; | |
464 | } | |
465 | } | |
f8be5ef4 | 466 | |
8950ac03 AT |
467 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ |
468 | ||
469 | do { | |
470 | convert[place++] = | |
471 | (caps? "0123456789ABCDEF":"0123456789abcdef") | |
472 | [uvalue % (unsigned)base ]; | |
473 | uvalue = (uvalue / (unsigned)base ); | |
474 | } while(uvalue && (place < 20)); | |
475 | if (place == 20) place--; | |
476 | convert[place] = 0; | |
477 | ||
478 | zpadlen = max - place; | |
479 | spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); | |
480 | if (zpadlen < 0) zpadlen = 0; | |
481 | if (spadlen < 0) spadlen = 0; | |
482 | if (flags & DP_F_ZERO) { | |
483 | zpadlen = MAX(zpadlen, spadlen); | |
484 | spadlen = 0; | |
485 | } | |
486 | if (flags & DP_F_MINUS) | |
487 | spadlen = -spadlen; /* Left Justifty */ | |
f8be5ef4 AT |
488 | |
489 | #ifdef DEBUG_SNPRINTF | |
8950ac03 AT |
490 | printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", |
491 | zpadlen, spadlen, min, max, place); | |
f8be5ef4 AT |
492 | #endif |
493 | ||
8950ac03 AT |
494 | /* Spaces */ |
495 | while (spadlen > 0) { | |
496 | dopr_outch (buffer, currlen, maxlen, ' '); | |
497 | --spadlen; | |
498 | } | |
499 | ||
500 | /* Sign */ | |
501 | if (signvalue) | |
502 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
503 | ||
504 | /* Zeros */ | |
505 | if (zpadlen > 0) { | |
506 | while (zpadlen > 0) { | |
507 | dopr_outch (buffer, currlen, maxlen, '0'); | |
508 | --zpadlen; | |
509 | } | |
510 | } | |
511 | ||
512 | /* Digits */ | |
513 | while (place > 0) | |
514 | dopr_outch (buffer, currlen, maxlen, convert[--place]); | |
f8be5ef4 | 515 | |
8950ac03 AT |
516 | /* Left Justified spaces */ |
517 | while (spadlen < 0) { | |
518 | dopr_outch (buffer, currlen, maxlen, ' '); | |
519 | ++spadlen; | |
520 | } | |
f8be5ef4 AT |
521 | } |
522 | ||
8950ac03 | 523 | static LDOUBLE abs_val(LDOUBLE value) |
f8be5ef4 | 524 | { |
8950ac03 | 525 | LDOUBLE result = value; |
f8be5ef4 | 526 | |
8950ac03 AT |
527 | if (value < 0) |
528 | result = -value; | |
529 | ||
530 | return result; | |
f8be5ef4 AT |
531 | } |
532 | ||
8950ac03 | 533 | static LDOUBLE POW10(int exp) |
f8be5ef4 | 534 | { |
8950ac03 AT |
535 | LDOUBLE result = 1; |
536 | ||
537 | while (exp) { | |
538 | result *= 10; | |
539 | exp--; | |
540 | } | |
f8be5ef4 | 541 | |
8950ac03 | 542 | return result; |
f8be5ef4 AT |
543 | } |
544 | ||
8950ac03 | 545 | static LLONG ROUND(LDOUBLE value) |
f8be5ef4 | 546 | { |
8950ac03 | 547 | LLONG intpart; |
f8be5ef4 | 548 | |
8950ac03 AT |
549 | intpart = (LLONG)value; |
550 | value = value - intpart; | |
551 | if (value >= 0.5) intpart++; | |
552 | ||
553 | return intpart; | |
554 | } | |
f8be5ef4 | 555 | |
8950ac03 AT |
556 | /* a replacement for modf that doesn't need the math library. Should |
557 | be portable, but slow */ | |
558 | static double my_modf(double x0, double *iptr) | |
559 | { | |
560 | int i; | |
561 | long l; | |
562 | double x = x0; | |
563 | double f = 1.0; | |
564 | ||
565 | for (i=0;i<100;i++) { | |
566 | l = (long)x; | |
567 | if (l <= (x+1) && l >= (x-1)) break; | |
568 | x *= 0.1; | |
569 | f *= 10.0; | |
570 | } | |
571 | ||
572 | if (i == 100) { | |
573 | /* yikes! the number is beyond what we can handle. What do we do? */ | |
574 | (*iptr) = 0; | |
575 | return 0; | |
576 | } | |
577 | ||
578 | if (i != 0) { | |
579 | double i2; | |
580 | double ret; | |
581 | ||
582 | ret = my_modf(x0-l*f, &i2); | |
583 | (*iptr) = l*f + i2; | |
584 | return ret; | |
585 | } | |
586 | ||
587 | (*iptr) = l; | |
588 | return x - (*iptr); | |
f8be5ef4 AT |
589 | } |
590 | ||
8950ac03 | 591 | |
f8be5ef4 | 592 | static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, |
10600500 | 593 | LDOUBLE fvalue, int min, int max, int flags) |
f8be5ef4 | 594 | { |
8950ac03 AT |
595 | int signvalue = 0; |
596 | double ufvalue; | |
597 | char iconvert[311]; | |
598 | char fconvert[311]; | |
599 | int iplace = 0; | |
600 | int fplace = 0; | |
601 | int padlen = 0; /* amount to pad */ | |
602 | int zpadlen = 0; | |
603 | int caps = 0; | |
604 | int index; | |
605 | double intpart; | |
606 | double fracpart; | |
607 | double temp; | |
f8be5ef4 | 608 | |
8950ac03 AT |
609 | /* |
610 | * AIX manpage says the default is 0, but Solaris says the default | |
611 | * is 6, and sprintf on AIX defaults to 6 | |
612 | */ | |
613 | if (max < 0) | |
614 | max = 6; | |
f8be5ef4 | 615 | |
8950ac03 | 616 | ufvalue = abs_val (fvalue); |
f8be5ef4 | 617 | |
8950ac03 AT |
618 | if (fvalue < 0) { |
619 | signvalue = '-'; | |
620 | } else { | |
621 | if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ | |
622 | signvalue = '+'; | |
623 | } else { | |
624 | if (flags & DP_F_SPACE) | |
625 | signvalue = ' '; | |
626 | } | |
627 | } | |
f8be5ef4 | 628 | |
8950ac03 AT |
629 | #if 0 |
630 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | |
f8be5ef4 AT |
631 | #endif |
632 | ||
8950ac03 AT |
633 | #if 0 |
634 | if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ | |
635 | #endif | |
f8be5ef4 | 636 | |
8950ac03 AT |
637 | /* |
638 | * Sorry, we only support 16 digits past the decimal because of our | |
639 | * conversion method | |
640 | */ | |
641 | if (max > 16) | |
642 | max = 16; | |
643 | ||
644 | /* We "cheat" by converting the fractional part to integer by | |
645 | * multiplying by a factor of 10 | |
646 | */ | |
647 | ||
648 | temp = ufvalue; | |
649 | my_modf(temp, &intpart); | |
650 | ||
651 | fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); | |
652 | ||
653 | if (fracpart >= POW10(max)) { | |
654 | intpart++; | |
655 | fracpart -= POW10(max); | |
656 | } | |
657 | ||
658 | ||
659 | /* Convert integer part */ | |
660 | do { | |
661 | temp = intpart; | |
662 | my_modf(intpart*0.1, &intpart); | |
663 | temp = temp*0.1; | |
664 | index = (int) ((temp -intpart +0.05)* 10.0); | |
665 | /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ | |
666 | /* printf ("%llf, %f, %x\n", temp, intpart, index); */ | |
667 | iconvert[iplace++] = | |
668 | (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; | |
669 | } while (intpart && (iplace < 311)); | |
670 | if (iplace == 311) iplace--; | |
671 | iconvert[iplace] = 0; | |
672 | ||
673 | /* Convert fractional part */ | |
674 | if (fracpart) | |
675 | { | |
676 | do { | |
677 | temp = fracpart; | |
678 | my_modf(fracpart*0.1, &fracpart); | |
679 | temp = temp*0.1; | |
680 | index = (int) ((temp -fracpart +0.05)* 10.0); | |
681 | /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */ | |
682 | /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */ | |
683 | fconvert[fplace++] = | |
684 | (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; | |
685 | } while(fracpart && (fplace < 311)); | |
686 | if (fplace == 311) fplace--; | |
687 | } | |
688 | fconvert[fplace] = 0; | |
689 | ||
690 | /* -1 for decimal point, another -1 if we are printing a sign */ | |
691 | padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); | |
692 | zpadlen = max - fplace; | |
693 | if (zpadlen < 0) zpadlen = 0; | |
694 | if (padlen < 0) | |
695 | padlen = 0; | |
696 | if (flags & DP_F_MINUS) | |
697 | padlen = -padlen; /* Left Justifty */ | |
698 | ||
699 | if ((flags & DP_F_ZERO) && (padlen > 0)) { | |
700 | if (signvalue) { | |
701 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
702 | --padlen; | |
703 | signvalue = 0; | |
704 | } | |
705 | while (padlen > 0) { | |
706 | dopr_outch (buffer, currlen, maxlen, '0'); | |
707 | --padlen; | |
708 | } | |
709 | } | |
710 | while (padlen > 0) { | |
711 | dopr_outch (buffer, currlen, maxlen, ' '); | |
712 | --padlen; | |
713 | } | |
714 | if (signvalue) | |
715 | dopr_outch (buffer, currlen, maxlen, signvalue); | |
716 | ||
717 | while (iplace > 0) | |
718 | dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); | |
10600500 AT |
719 | |
720 | #ifdef DEBUG_SNPRINTF | |
8950ac03 | 721 | printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); |
10600500 AT |
722 | #endif |
723 | ||
8950ac03 AT |
724 | /* |
725 | * Decimal point. This should probably use locale to find the correct | |
726 | * char to print out. | |
727 | */ | |
728 | if (max > 0) { | |
729 | dopr_outch (buffer, currlen, maxlen, '.'); | |
730 | ||
990ff150 PG |
731 | while (zpadlen > 0) { |
732 | dopr_outch (buffer, currlen, maxlen, '0'); | |
733 | --zpadlen; | |
734 | } | |
735 | ||
8950ac03 AT |
736 | while (fplace > 0) |
737 | dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); | |
738 | } | |
739 | ||
8950ac03 AT |
740 | while (padlen < 0) { |
741 | dopr_outch (buffer, currlen, maxlen, ' '); | |
742 | ++padlen; | |
743 | } | |
f8be5ef4 AT |
744 | } |
745 | ||
8950ac03 | 746 | static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) |
f8be5ef4 | 747 | { |
8950ac03 AT |
748 | if (*currlen < maxlen) { |
749 | buffer[(*currlen)] = c; | |
750 | } | |
751 | (*currlen)++; | |
f8be5ef4 | 752 | } |
f8be5ef4 | 753 | |
8950ac03 | 754 | #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) |
6813fa7e WD |
755 | #define vsnprintf rsync_vsnprintf |
756 | int vsnprintf(char *str, size_t count, const char *fmt, va_list args) | |
f8be5ef4 | 757 | { |
8950ac03 | 758 | return dopr(str, count, fmt, args); |
f8be5ef4 | 759 | } |
f8be5ef4 | 760 | #endif |
8950ac03 AT |
761 | |
762 | #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF) | |
6813fa7e | 763 | #define snprintf rsync_snprintf |
8950ac03 | 764 | int snprintf(char *str,size_t count,const char *fmt,...) |
f8be5ef4 | 765 | { |
8950ac03 AT |
766 | size_t ret; |
767 | va_list ap; | |
f8be5ef4 | 768 | |
8950ac03 AT |
769 | va_start(ap, fmt); |
770 | ret = vsnprintf(str, count, fmt, ap); | |
771 | va_end(ap); | |
772 | return ret; | |
f8be5ef4 | 773 | } |
8950ac03 | 774 | #endif |
f8be5ef4 | 775 | |
8950ac03 | 776 | #endif |
10600500 | 777 | |
8950ac03 AT |
778 | #ifndef HAVE_VASPRINTF |
779 | int vasprintf(char **ptr, const char *format, va_list ap) | |
780 | { | |
781 | int ret; | |
782 | ||
783 | ret = vsnprintf(NULL, 0, format, ap); | |
784 | if (ret <= 0) return ret; | |
10600500 | 785 | |
8950ac03 AT |
786 | (*ptr) = (char *)malloc(ret+1); |
787 | if (!*ptr) return -1; | |
788 | ret = vsnprintf(*ptr, ret+1, format, ap); | |
789 | ||
790 | return ret; | |
791 | } | |
792 | #endif | |
793 | ||
794 | ||
795 | #ifndef HAVE_ASPRINTF | |
796 | int asprintf(char **ptr, const char *format, ...) | |
797 | { | |
798 | va_list ap; | |
799 | int ret; | |
800 | ||
801 | va_start(ap, format); | |
802 | ret = vasprintf(ptr, format, ap); | |
803 | va_end(ap); | |
804 | ||
805 | return ret; | |
806 | } | |
f8be5ef4 | 807 | #endif |
8950ac03 AT |
808 | |
809 | #ifdef TEST_SNPRINTF | |
810 | ||
811 | int sprintf(char *str,const char *fmt,...); | |
812 | ||
2f098547 | 813 | int main (void) |
f8be5ef4 | 814 | { |
8950ac03 AT |
815 | char buf1[1024]; |
816 | char buf2[1024]; | |
817 | char *fp_fmt[] = { | |
818 | "%1.1f", | |
819 | "%-1.5f", | |
820 | "%1.5f", | |
821 | "%123.9f", | |
822 | "%10.5f", | |
823 | "% 10.5f", | |
824 | "%+22.9f", | |
825 | "%+4.9f", | |
826 | "%01.3f", | |
827 | "%4f", | |
828 | "%3.1f", | |
829 | "%3.2f", | |
830 | "%.0f", | |
831 | "%f", | |
832 | "-16.16f", | |
833 | NULL | |
834 | }; | |
835 | double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, | |
836 | 0.9996, 1.996, 4.136, 0}; | |
837 | char *int_fmt[] = { | |
838 | "%-1.5d", | |
839 | "%1.5d", | |
840 | "%123.9d", | |
841 | "%5.5d", | |
842 | "%10.5d", | |
843 | "% 10.5d", | |
844 | "%+22.33d", | |
845 | "%01.3d", | |
846 | "%4d", | |
847 | "%d", | |
848 | NULL | |
849 | }; | |
850 | long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; | |
851 | char *str_fmt[] = { | |
852 | "10.5s", | |
853 | "5.10s", | |
854 | "10.1s", | |
855 | "0.10s", | |
856 | "10.0s", | |
857 | "1.10s", | |
858 | "%s", | |
859 | "%.1s", | |
860 | "%.10s", | |
861 | "%10s", | |
862 | NULL | |
863 | }; | |
864 | char *str_vals[] = {"hello", "a", "", "a longer string", NULL}; | |
865 | int x, y; | |
866 | int fail = 0; | |
867 | int num = 0; | |
868 | ||
869 | printf ("Testing snprintf format codes against system sprintf...\n"); | |
870 | ||
871 | for (x = 0; fp_fmt[x] ; x++) { | |
872 | for (y = 0; fp_nums[y] != 0 ; y++) { | |
873 | int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]); | |
874 | int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); | |
875 | sprintf (buf2, fp_fmt[x], fp_nums[y]); | |
876 | if (strcmp (buf1, buf2)) { | |
877 | printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", | |
878 | fp_fmt[x], buf1, buf2); | |
879 | fail++; | |
880 | } | |
881 | if (l1 != l2) { | |
882 | printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]); | |
883 | fail++; | |
884 | } | |
885 | num++; | |
886 | } | |
887 | } | |
888 | ||
889 | for (x = 0; int_fmt[x] ; x++) { | |
890 | for (y = 0; int_nums[y] != 0 ; y++) { | |
891 | int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]); | |
892 | int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); | |
893 | sprintf (buf2, int_fmt[x], int_nums[y]); | |
894 | if (strcmp (buf1, buf2)) { | |
895 | printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", | |
896 | int_fmt[x], buf1, buf2); | |
897 | fail++; | |
898 | } | |
899 | if (l1 != l2) { | |
900 | printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]); | |
901 | fail++; | |
902 | } | |
903 | num++; | |
904 | } | |
905 | } | |
906 | ||
907 | for (x = 0; str_fmt[x] ; x++) { | |
908 | for (y = 0; str_vals[y] != 0 ; y++) { | |
909 | int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]); | |
910 | int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]); | |
911 | sprintf (buf2, str_fmt[x], str_vals[y]); | |
912 | if (strcmp (buf1, buf2)) { | |
913 | printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", | |
914 | str_fmt[x], buf1, buf2); | |
915 | fail++; | |
916 | } | |
917 | if (l1 != l2) { | |
918 | printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]); | |
919 | fail++; | |
920 | } | |
921 | num++; | |
922 | } | |
923 | } | |
924 | ||
925 | printf ("%d tests failed out of %d.\n", fail, num); | |
926 | ||
927 | printf("seeing how many digits we support\n"); | |
928 | { | |
929 | double v0 = 0.12345678901234567890123456789012345678901; | |
930 | for (x=0; x<100; x++) { | |
931 | snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x)); | |
932 | sprintf(buf2, "%1.1f", v0*pow(10, x)); | |
933 | if (strcmp(buf1, buf2)) { | |
934 | printf("we seem to support %d digits\n", x-1); | |
935 | break; | |
936 | } | |
937 | } | |
938 | } | |
939 | ||
940 | return 0; | |
f8be5ef4 AT |
941 | } |
942 | #endif /* SNPRINTF_TEST */ |