Check if hb is NULL.
[elinks.git] / src / util / snprintf.c
blob7e9b286e3985f03ef6696ddaf956ea3c7f4fe0d2
1 /* Own portable snprintf() implementation */
3 /* These sources aren't the officially distributed version, they are modified
4 * by us (ELinks coders) and some other third-party hackers. See ELinks
5 * ChangeLog for details about changes we made here, comments bellow may give
6 * you some indices as well. --pasky */
8 /*
9 * Copyright Patrick Powell 1995
10 * This code is based on code written by Patrick Powell (papowell@astart.com)
11 * It may be used for any purpose as long as this notice remains intact
12 * on all source code distributions
15 /**************************************************************
16 * Original:
17 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
18 * A bombproof version of doprnt (dopr) included.
19 * Sigh. This sort of thing is always nasty do deal with. Note that
20 * the version here does not include floating point...
22 * snprintf() is used instead of sprintf() as it does limit checks
23 * for string length. This covers a nasty loophole.
25 * The other functions are there to prevent NULL pointers from
26 * causing nast effects.
28 * More Recently:
29 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
30 * This was ugly. It is still ugly. I opted out of floating point
31 * numbers, but the formatter understands just about everything
32 * from the normal C string format, at least as far as I can tell from
33 * the Solaris 2.5 printf(3S) man page.
35 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
36 * Ok, added some minimal floating point support, which means this
37 * probably requires libm on most operating systems. Don't yet
38 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
39 * was pretty badly broken, it just wasn't being exercised in ways
40 * which showed it, so that's been fixed. Also, formated the code
41 * to mutt conventions, and removed dead code left over from the
42 * original. Also, there is now a builtin-test, just compile with:
43 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
44 * and run snprintf for results.
46 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
47 * The PGP code was using unsigned hexadecimal formats.
48 * Unfortunately, unsigned formats simply didn't work.
50 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
51 * The original code assumed that both snprintf() and vsnprintf() were
52 * missing. Some systems only have snprintf() but not vsnprintf(), so
53 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
55 * === Samba hacking:
57 * Andrew Tridgell (tridge@samba.org) Oct 1998
58 * fixed handling of %.0f
59 * added test for HAVE_LONG_DOUBLE
61 * tridge@samba.org, idra@samba.org, April 2001
62 * got rid of fcvt code (twas buggy and made testing harder)
63 * added C99 semantics
65 * === About ELinks changes see ELinks' ChangeLog.
67 **************************************************************/
69 #ifdef HAVE_CONFIG_H /* for some tests */
70 #include "config.h"
71 #endif
73 #include <ctype.h>
74 #include <stdarg.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <sys/types.h>
79 #include "elinks.h"
81 #include "util/snprintf.h"
84 /* #define TEST_SNPRINTF */ /* For compiling a standlone test binary. */
87 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
89 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
90 #include <stdio.h>
91 /* make the compiler happy with an empty file */
93 void dummy_snprintf(void);
94 void dummy_snprintf(void) {}
96 #else /* defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) */
98 /* TODO: Don't do all this if we just need to get own snprintf() wrapper to
99 * system-provided vsnprintf(). (What an unlikely case, isn't it?) */
101 #ifdef HAVE_LONG_DOUBLE
102 #define LDOUBLE long double
103 #else
104 #define LDOUBLE double
105 #endif
107 #ifdef HAVE_LONG_LONG
108 #define LLONG long long
109 #else
110 #define LLONG long
111 #endif
113 static size_t dopr(char *buffer, size_t maxlen, const char *format,
114 va_list args_in);
115 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
116 char *value, int flags, int min, int max);
117 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
118 long value, int base, int min, int max, int flags);
119 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
120 LDOUBLE fvalue, int min, int max, int flags);
121 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
124 * dopr(): poor man's version of doprintf
127 /* format read states */
128 #define DP_S_DEFAULT 0
129 #define DP_S_FLAGS 1
130 #define DP_S_MIN 2
131 #define DP_S_DOT 3
132 #define DP_S_MAX 4
133 #define DP_S_MOD 5
134 #define DP_S_CONV 6
135 #define DP_S_DONE 7
137 /* format flags - Bits */
138 #define DP_F_MINUS (1 << 0)
139 #define DP_F_PLUS (1 << 1)
140 #define DP_F_SPACE (1 << 2)
141 #define DP_F_NUM (1 << 3)
142 #define DP_F_ZERO (1 << 4)
143 #define DP_F_UP (1 << 5)
144 #define DP_F_UNSIGNED (1 << 6)
146 /* Conversion Flags */
147 #define DP_C_SHORT 1
148 #define DP_C_LONG 2
149 #define DP_C_LDOUBLE 3
150 #define DP_C_LLONG 4
152 #define char_to_int(p) ((p)- '0')
154 static char hexnumbers[] = "0123456789abcdef";
155 static char HEXnumbers[] = "0123456789ABCDEF";
157 static inline void
158 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
160 if (*currlen < maxlen)
161 buffer[(*currlen)] = c;
163 (*currlen)++;
166 static size_t
167 dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
169 char ch;
170 LLONG value;
171 LDOUBLE fvalue;
172 char *strvalue;
173 int min;
174 int max;
175 int state;
176 int flags;
177 int cflags;
178 size_t currlen;
179 va_list args;
181 VA_COPY(args, args_in);
183 state = DP_S_DEFAULT;
184 currlen = 0;
185 flags = cflags = min = 0;
186 max = -1;
187 ch = *format++;
189 while (state != DP_S_DONE) {
190 if (ch == '\0')
191 state = DP_S_DONE;
193 switch (state) {
194 case DP_S_DEFAULT:
195 if (ch == '%')
196 state = DP_S_FLAGS;
197 else
198 dopr_outch(buffer, &currlen, maxlen, ch);
199 ch = *format++;
200 break;
201 case DP_S_FLAGS:
202 switch (ch) {
203 case '-':
204 flags |= DP_F_MINUS;
205 ch = *format++;
206 break;
207 case '+':
208 flags |= DP_F_PLUS;
209 ch = *format++;
210 break;
211 case ' ':
212 flags |= DP_F_SPACE;
213 ch = *format++;
214 break;
215 case '#':
216 flags |= DP_F_NUM;
217 ch = *format++;
218 break;
219 case '0':
220 flags |= DP_F_ZERO;
221 ch = *format++;
222 break;
223 default:
224 state = DP_S_MIN;
225 break;
227 break;
228 case DP_S_MIN:
229 if (isdigit((unsigned char) ch)) {
230 min = 10 * min + char_to_int(ch);
231 ch = *format++;
232 } else if (ch == '*') {
233 min = va_arg(args, int);
234 ch = *format++;
235 state = DP_S_DOT;
236 } else {
237 state = DP_S_DOT;
239 break;
240 case DP_S_DOT:
241 if (ch == '.') {
242 state = DP_S_MAX;
243 ch = *format++;
244 } else {
245 state = DP_S_MOD;
247 break;
248 case DP_S_MAX:
249 if (isdigit((unsigned char) ch)) {
250 if (max < 0)
251 max = 0;
252 max = 10 * max + char_to_int(ch);
253 ch = *format++;
254 } else if (ch == '*') {
255 max = va_arg(args, int);
256 ch = *format++;
257 state = DP_S_MOD;
258 } else {
259 state = DP_S_MOD;
261 break;
262 case DP_S_MOD:
263 switch (ch) {
264 case 'h':
265 cflags = DP_C_SHORT;
266 ch = *format++;
267 break;
268 case 'l':
269 cflags = DP_C_LONG;
270 ch = *format++;
271 if (ch == 'l') { /* It's a long long */
272 cflags = DP_C_LLONG;
273 ch = *format++;
275 break;
276 case 'L':
277 cflags = DP_C_LDOUBLE;
278 ch = *format++;
279 break;
280 default:
281 break;
283 state = DP_S_CONV;
284 break;
285 case DP_S_CONV:
286 switch (ch) {
287 case 'd':
288 case 'i':
289 if (cflags == DP_C_SHORT)
290 value = va_arg(args, int);
291 else if (cflags == DP_C_LONG)
292 value = va_arg(args, long);
293 else if (cflags == DP_C_LLONG)
294 value = va_arg(args, LLONG);
295 else
296 value = va_arg(args, int);
297 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
298 break;
299 case 'o':
300 flags |= DP_F_UNSIGNED;
301 if (cflags == DP_C_SHORT)
302 value = va_arg(args, unsigned int);
303 else if (cflags == DP_C_LONG)
304 value = (long) va_arg(args, unsigned long);
305 else if (cflags == DP_C_LLONG)
306 value = (long) va_arg(args, unsigned LLONG);
307 else
308 value = (long) va_arg(args, unsigned int);
309 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
310 break;
311 case 'u':
312 flags |= DP_F_UNSIGNED;
313 if (cflags == DP_C_SHORT)
314 value = va_arg(args, unsigned int);
315 else if (cflags == DP_C_LONG)
316 value = (long) va_arg(args, unsigned long);
317 else if (cflags == DP_C_LLONG)
318 value = (LLONG) va_arg(args, unsigned LLONG);
319 else
320 value = (long) va_arg(args, unsigned int);
321 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
322 break;
323 case 'X':
324 flags |= DP_F_UP;
325 case 'x':
326 flags |= DP_F_UNSIGNED;
327 if (cflags == DP_C_SHORT)
328 value = va_arg(args, unsigned int);
329 else if (cflags == DP_C_LONG)
330 value = (long) va_arg(args, unsigned long);
331 else if (cflags == DP_C_LLONG)
332 value = (LLONG) va_arg(args, unsigned LLONG);
333 else
334 value = (long) va_arg(args, unsigned int);
335 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
336 break;
337 case 'f':
338 if (cflags == DP_C_LDOUBLE)
339 fvalue = va_arg(args, LDOUBLE);
340 else
341 fvalue = va_arg(args, double);
342 /* um, floating point? */
343 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
344 break;
345 case 'E':
346 flags |= DP_F_UP;
347 case 'e':
348 if (cflags == DP_C_LDOUBLE)
349 fvalue = va_arg(args, LDOUBLE);
350 else
351 fvalue = va_arg(args, double);
352 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
353 break;
354 case 'G':
355 flags |= DP_F_UP;
356 case 'g':
357 if (cflags == DP_C_LDOUBLE)
358 fvalue = va_arg(args, LDOUBLE);
359 else
360 fvalue = va_arg(args, double);
361 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
362 break;
363 case 'c':
364 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
365 break;
366 case 's':
367 strvalue = va_arg(args, char *);
368 if (!strvalue) strvalue = "(NULL)";
369 if (max == -1) {
370 max = strlen(strvalue);
372 if (min > 0 && max >= 0 && min > max) max = min;
373 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
374 break;
375 case 'p':
376 strvalue = va_arg(args, void *);
377 fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
378 break;
379 case 'n':
380 if (cflags == DP_C_SHORT) {
381 short *num;
383 num = va_arg(args, short *);
384 *num = currlen;
385 } else if (cflags == DP_C_LONG) {
386 long *num;
388 num = va_arg(args, long *);
389 *num = (long) currlen;
390 } else if (cflags == DP_C_LLONG) {
391 LLONG *num;
393 num = va_arg(args, LLONG *);
394 *num = (LLONG) currlen;
395 } else {
396 int *num;
398 num = va_arg(args, int *);
399 *num = currlen;
401 break;
402 case '%':
403 dopr_outch(buffer, &currlen, maxlen, ch);
404 break;
405 case 'w':
406 /* not supported yet, treat as next char */
407 ch = *format++;
408 break;
409 default:
410 /* Unknown, skip */
411 break;
413 ch = *format++;
414 state = DP_S_DEFAULT;
415 flags = cflags = min = 0;
416 max = -1;
417 break;
418 case DP_S_DONE:
419 break;
420 default:
421 /* hmm? */
422 break; /* some picky compilers need this */
425 if (maxlen != 0) {
426 if (currlen < maxlen - 1)
427 buffer[currlen] = '\0';
428 else if (maxlen > 0)
429 buffer[maxlen - 1] = '\0';
432 return currlen;
435 static void
436 fmtstr(char *buffer, size_t *currlen, size_t maxlen,
437 char *value, int flags, int min, int max)
439 int padlen, strln; /* amount to pad */
440 int cnt = 0;
442 #ifdef DEBUG_SNPRINTF
443 printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
444 #endif
445 if (value == 0) value = "<NULL>";
447 for (strln = 0; value[strln]; ++strln); /* strlen */
448 padlen = min - strln;
449 if (padlen < 0)
450 padlen = 0;
451 if (flags & DP_F_MINUS)
452 padlen = -padlen; /* Left Justify */
454 while ((padlen > 0) && (cnt < max)) {
455 dopr_outch(buffer, currlen, maxlen, ' ');
456 --padlen;
457 ++cnt;
459 while (*value && (cnt < max)) {
460 dopr_outch(buffer, currlen, maxlen, *value++);
461 ++cnt;
463 while ((padlen < 0) && (cnt < max)) {
464 dopr_outch(buffer, currlen, maxlen, ' ');
465 ++padlen;
466 ++cnt;
470 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
471 static void
472 fmtint(char *buffer, size_t *currlen, size_t maxlen,
473 long value, int base, int min, int max, int flags)
475 int signvalue = 0;
476 unsigned long uvalue;
477 char convert[20];
478 char *numbers = (char *) &hexnumbers;
479 int place = 0;
480 int spadlen = 0; /* amount to space pad */
481 int zpadlen = 0; /* amount to zero pad */
483 if (max < 0)
484 max = 0;
486 uvalue = value;
488 if (!(flags & DP_F_UNSIGNED)) {
489 if (value < 0) {
490 signvalue = '-';
491 uvalue = -value;
492 } else {
493 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
494 signvalue = '+';
495 else if (flags & DP_F_SPACE)
496 signvalue = ' ';
500 if (flags & DP_F_UP) {
501 /* Should characters be upper case? */
502 numbers = (char *) &HEXnumbers;
505 do {
506 convert[place++] = numbers[uvalue % (unsigned) base];
507 uvalue = (uvalue / (unsigned) base);
508 } while (uvalue && (place < 20));
509 if (place == 20) place--;
510 convert[place] = 0;
512 zpadlen = max - place;
513 spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
514 if (zpadlen < 0) zpadlen = 0;
515 if (spadlen < 0) spadlen = 0;
516 if (flags & DP_F_ZERO) {
517 zpadlen = MAX(zpadlen, spadlen);
518 spadlen = 0;
520 if (flags & DP_F_MINUS)
521 spadlen = -spadlen; /* Left Justifty */
523 #ifdef DEBUG_SNPRINTF
524 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
525 zpadlen, spadlen, min, max, place);
526 #endif
528 /* Spaces */
529 while (spadlen > 0) {
530 dopr_outch(buffer, currlen, maxlen, ' ');
531 --spadlen;
534 /* Sign */
535 if (signvalue)
536 dopr_outch(buffer, currlen, maxlen, signvalue);
538 /* Zeros */
539 while (zpadlen > 0) {
540 dopr_outch(buffer, currlen, maxlen, '0');
541 --zpadlen;
544 /* Digits */
545 while (place > 0)
546 dopr_outch(buffer, currlen, maxlen, convert[--place]);
548 /* Left Justified spaces */
549 while (spadlen < 0) {
550 dopr_outch(buffer, currlen, maxlen, ' ');
551 ++spadlen;
555 static LDOUBLE
556 my_abs(LDOUBLE value)
558 LDOUBLE result = value;
560 if (value < 0)
561 result = -value;
563 return result;
566 static LDOUBLE
567 my_pow10(int exp)
569 LDOUBLE result = 1;
571 while (exp) {
572 result *= 10;
573 exp--;
576 return result;
579 static LLONG
580 my_round(LDOUBLE value)
582 LLONG intpart;
584 intpart = (LLONG) value;
585 value = value - intpart;
586 if (value >= 0.5) intpart++;
588 return intpart;
591 /* a replacement for modf that doesn't need the math library. Should
592 be portable, but slow */
593 static double
594 my_modf(double x0, double *iptr)
596 int i;
597 long l;
598 double x = x0;
599 double f = 1.0;
601 for (i = 0; i < 100; i++) {
602 l = (long) x;
603 if (l <= (x + 1) && l >= (x - 1)) break;
604 x *= 0.1;
605 f *= 10.0;
608 if (i == 100) {
609 /* yikes! the number is beyond what we can handle. What do we do? */
610 (*iptr) = 0;
611 return 0;
614 if (i != 0) {
615 double i2;
616 double ret;
618 ret = my_modf(x0 - l * f, &i2);
619 (*iptr) = l * f + i2;
620 return ret;
623 (*iptr) = l;
624 return x - (*iptr);
627 static void
628 fmtfp(char *buffer, size_t *currlen, size_t maxlen,
629 LDOUBLE fvalue, int min, int max, int flags)
631 int signvalue = 0;
632 double ufvalue;
633 char iconvert[311];
634 char fconvert[311];
635 char *numbers = (char *) &hexnumbers;
636 int iplace = 0;
637 int fplace = 0;
638 int padlen = 0; /* amount to pad */
639 int zpadlen = 0;
640 int index;
641 double intpart;
642 double fracpart;
643 double temp;
644 LDOUBLE pow;
647 * AIX manpage says the default is 0, but Solaris says the default
648 * is 6, and sprintf on AIX defaults to 6
650 if (max < 0)
651 max = 6;
653 ufvalue = my_abs(fvalue);
655 if (fvalue < 0) {
656 signvalue = '-';
657 } else {
658 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
659 signvalue = '+';
660 } else {
661 if (flags & DP_F_SPACE)
662 signvalue = ' ';
666 #if 0
667 if (flags & DP_F_UP) {
668 caps = 1; /* Should characters be upper case? */
669 numbers = (char *) &HEXnumbers;
671 #endif
673 #if 0
674 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
675 #endif
678 * Sorry, we only support 16 digits past the decimal because of our
679 * conversion method
681 if (max > 16)
682 max = 16;
684 pow = my_pow10(max);
686 /* We "cheat" by converting the fractional part to integer by
687 * multiplying by a factor of 10
690 temp = ufvalue;
691 my_modf(temp, &intpart);
693 fracpart = my_round(pow * (ufvalue - intpart));
695 if (fracpart >= pow) {
696 intpart++;
697 fracpart -= pow;
700 /* Convert integer part */
701 do {
702 temp = intpart * 0.1;
703 my_modf(temp, &intpart);
704 index = (int) ((temp - intpart + 0.05) * 10.0);
705 #if 0
706 index = (int) (((double) (temp*0.1) -intpart +0.05) *10.0);
707 printf ("%llf, %f, %x\n", temp, intpart, index);
708 #endif
709 iconvert[iplace++] = numbers[index];
710 } while (intpart && (iplace < 311));
711 if (iplace == 311) iplace--;
712 iconvert[iplace] = 0;
714 /* Convert fractional part */
715 if (fracpart) {
716 do {
717 temp = fracpart * 0.1;
718 my_modf(temp, &fracpart);
719 index = (int) ((temp - fracpart + 0.05) * 10.0);
720 #if 0
721 index = (int) ((((temp/10) -fracpart) +0.05) *10);
722 printf ("%lf, %lf, %ld\n", temp, fracpart, index);
723 #endif
724 fconvert[fplace++] = numbers[index];
725 } while (fracpart && (fplace < 311));
726 if (fplace == 311) fplace--;
728 fconvert[fplace] = 0;
730 /* -1 for decimal point, another -1 if we are printing a sign */
731 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
732 zpadlen = max - fplace;
733 if (zpadlen < 0) zpadlen = 0;
734 if (padlen < 0)
735 padlen = 0;
736 if (flags & DP_F_MINUS)
737 padlen = -padlen; /* Left Justifty */
739 if ((flags & DP_F_ZERO) && (padlen > 0)) {
740 if (signvalue) {
741 dopr_outch(buffer, currlen, maxlen, signvalue);
742 --padlen;
743 signvalue = 0;
745 while (padlen > 0) {
746 dopr_outch(buffer, currlen, maxlen, '0');
747 --padlen;
750 while (padlen > 0) {
751 dopr_outch(buffer, currlen, maxlen, ' ');
752 --padlen;
754 if (signvalue)
755 dopr_outch(buffer, currlen, maxlen, signvalue);
757 while (iplace > 0)
758 dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
760 #ifdef DEBUG_SNPRINTF
761 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
762 #endif
765 * Decimal point. This should probably use locale to find the correct
766 * char to print out.
768 if (max > 0) {
769 dopr_outch(buffer, currlen, maxlen, '.');
771 while (zpadlen > 0) {
772 dopr_outch(buffer, currlen, maxlen, '0');
773 --zpadlen;
776 while (fplace > 0)
777 dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
780 while (padlen < 0) {
781 dopr_outch(buffer, currlen, maxlen, ' ');
782 ++padlen;
787 /* Yes the following preprocessor conditions really must be a ||. Don't muck
788 * with this. -- tridge
790 * The logic for these is that we need our own definition if the OS *either*
791 * has no definition of *sprintf, or if it does have one that doesn't work
792 * properly according to the autoconf test. -- mbp */
794 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
796 elinks_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
798 return dopr(str, count, fmt, args);
800 #endif
802 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
804 elinks_snprintf(char *str, size_t count, const char *fmt, ...)
806 size_t ret;
807 va_list ap;
809 va_start(ap, fmt);
810 /* snprintf.h makes this to expand to the right thing. */
811 ret = vsnprintf(str, count, fmt, ap);
812 va_end(ap);
814 return ret;
816 #endif
818 #endif /* defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) */
821 #ifndef HAVE_VASPRINTF
823 elinks_vasprintf(char **ptr, const char *format, va_list ap)
825 int ret;
826 va_list ap2;
828 VA_COPY(ap2, ap);
830 ret = vsnprintf(NULL, 0, format, ap2);
831 if (ret <= 0) return ret;
833 /* Should we use mem_alloc() here instead of malloc() ??? --Zas */
834 /* No: This is an alternative to the system vasprintf.
835 * When the system provides vasprintf, it will use malloc(),
836 * which means that the string will need to be freed with free().
837 * If we did use mem_alloc here in our alternative to the system
838 * vasprintf, we would need to change all users of the memory
839 * allocated by vasprintf to take into account whether the system
840 * vasprintf (and therefore malloc) or our own vasprintf
841 * (and therefore mem_alloc) were used. -- Miciah */
842 (*ptr) = (char *) malloc(ret + 1);
843 if (!*ptr) return -1;
845 VA_COPY(ap2, ap);
847 return vsnprintf(*ptr, ret + 1, format, ap2);
849 #endif
852 #ifndef HAVE_ASPRINTF
854 elinks_asprintf(char **ptr, const char *format, ...)
856 va_list ap;
857 int ret;
859 *ptr = NULL;
860 va_start(ap, format);
861 ret = vasprintf(ptr, format, ap);
862 va_end(ap);
864 return ret;
866 #endif
868 unsigned char *
869 asprintfa(const char *fmt, ...)
871 unsigned char *str;
872 va_list ap;
874 va_start(ap, fmt);
875 str = vasprintfa(fmt, ap);
876 va_end(ap);
878 return str;
881 /* Test program */
882 #ifdef TEST_SNPRINTF
884 #include <math.h>
886 int sprintf(char *str,const char *fmt,...);
889 main(void)
891 char buf1[1024];
892 char buf2[1024];
893 char *fp_fmt[] = {
894 "%1.1f",
895 "%-1.5f",
896 "%1.5f",
897 "%123.9f",
898 "%10.5f",
899 "% 10.5f",
900 "%+22.9f",
901 "%+4.9f",
902 "%01.3f",
903 "%4f",
904 "%3.1f",
905 "%3.2f",
906 "%.0f",
907 "%f",
908 "-16.16f",
909 NULL
911 double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2,
912 341.1234, 0203.9, 0.96, 0.996,
913 0.9996, 1.996, 4.136, 5.030201, 0};
914 char *int_fmt[] = {
915 "%-1.5d",
916 "%1.5d",
917 "%123.9d",
918 "%5.5d",
919 "%10.5d",
920 "% 10.5d",
921 "%+22.33d",
922 "%01.3d",
923 "%4d",
924 "%d",
925 NULL
927 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
928 char *str_fmt[] = {
929 "10.5s",
930 "5.10s",
931 "10.1s",
932 "0.10s",
933 "10.0s",
934 "1.10s",
935 "%s",
936 "%.1s",
937 "%.10s",
938 "%10s",
939 NULL
941 char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
942 int x, y;
943 int fail = 0;
944 int num = 0;
946 printf ("Testing snprintf format codes against system sprintf...\n");
948 for (x = 0; fp_fmt[x] ; x++) {
949 for (y = 0; fp_nums[y] != 0 ; y++) {
950 int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
951 int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
953 sprintf(buf2, fp_fmt[x], fp_nums[y]);
954 if (strcmp(buf1, buf2)) {
955 printf("snprintf doesn't match Format: %s\n\t"
956 "snprintf = [%s]\n\t sprintf = [%s]\n",
957 fp_fmt[x], buf1, buf2);
958 fail++;
960 if (l1 != l2) {
961 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
962 fail++;
964 num++;
968 for (x = 0; int_fmt[x] ; x++) {
969 for (y = 0; int_nums[y] != 0 ; y++) {
970 int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
971 int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
973 sprintf(buf2, int_fmt[x], int_nums[y]);
974 if (strcmp(buf1, buf2)) {
975 printf("snprintf doesn't match Format: %s\n\t"
976 "snprintf = [%s]\n\t sprintf = [%s]\n",
977 int_fmt[x], buf1, buf2);
978 fail++;
980 if (l1 != l2) {
981 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
982 fail++;
984 num++;
988 for (x = 0; str_fmt[x] ; x++) {
989 for (y = 0; str_vals[y] != 0 ; y++) {
990 int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
991 int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
993 sprintf(buf2, str_fmt[x], str_vals[y]);
994 if (strcmp(buf1, buf2)) {
995 printf("snprintf doesn't match Format: %s\n\t"
996 "snprintf = [%s]\n\t sprintf = [%s]\n",
997 str_fmt[x], buf1, buf2);
998 fail++;
1000 if (l1 != l2) {
1001 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
1002 fail++;
1004 num++;
1008 printf ("%d tests failed out of %d.\n", fail, num);
1010 printf("seeing how many digits we support\n");
1012 double v0 = 0.12345678901234567890123456789012345678901;
1014 for ( x = 0; x < 100; x++) {
1015 double p = pow(10, x);
1016 double r = v0*p;
1018 snprintf(buf1, sizeof(buf1), "%1.1f", r);
1019 sprintf(buf2,"%1.1f", r);
1020 if (strcmp(buf1, buf2)) {
1021 printf("we seem to support %d digits\n", x-1);
1022 break;
1027 return 0;
1029 #endif /* SNPRINTF_TEST */