Got rid of a bunch of cruft.
[rsync/rsync.git] / lib / snprintf.c
CommitLineData
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
100static size_t dopr(char *buffer, size_t maxlen, const char *format,
101 va_list args);
102static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
f8be5ef4 103 char *value, int flags, int min, int max);
8950ac03 104static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
f8be5ef4 105 long value, int base, int min, int max, int flags);
8950ac03 106static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
10600500 107 LDOUBLE fvalue, int min, int max, int flags);
8950ac03 108static 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 144static 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 401static 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 439static 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 523static 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 533static 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 545static 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 */
558static 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 592static 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 746static 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 */