source: trunk/essentials/net-misc/wget/src/snprintf.c

Last change on this file was 3440, checked in by bird, 18 years ago

wget 1.10.2

File size: 24.6 KB
Line 
1/* This file is NOT part of Wget, but is used by Wget on the systems
2 where vsnprintf() is not defined. It has been written by Patrick
3 Powell and modified by other people. All the copyright and other
4 notices have been left intact.
5
6 My changes are documented at the bottom, along with other changes.
7 I hereby place my modifications to this file under the public
8 domain. */
9
10/*
11 * Copyright Patrick Powell 1995
12 * This code is based on code written by Patrick Powell (papowell@astart.com)
13 * It may be used for any purpose as long as this notice remains intact
14 * on all source code distributions
15 */
16
17/**************************************************************
18 * Original:
19 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
20 * A bombproof version of doprnt (dopr) included.
21 * Sigh. This sort of thing is always nasty do deal with. Note that
22 * the version here does not include floating point...
23 *
24 * snprintf() is used instead of sprintf() as it does limit checks
25 * for string length. This covers a nasty loophole.
26 *
27 * The other functions are there to prevent NULL pointers from
28 * causing nast effects.
29 *
30 * More Recently:
31 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
32 * This was ugly. It is still ugly. I opted out of floating point
33 * numbers, but the formatter understands just about everything
34 * from the normal C string format, at least as far as I can tell from
35 * the Solaris 2.5 printf(3S) man page.
36 *
37 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
38 * Ok, added some minimal floating point support, which means this
39 * probably requires libm on most operating systems. Don't yet
40 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
41 * was pretty badly broken, it just wasn't being exercised in ways
42 * which showed it, so that's been fixed. Also, formated the code
43 * to mutt conventions, and removed dead code left over from the
44 * original. Also, there is now a builtin-test, just compile with:
45 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
46 * and run snprintf for results.
47 *
48 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
49 * The PGP code was using unsigned hexadecimal formats.
50 * Unfortunately, unsigned formats simply didn't work.
51 *
52 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
53 * The original code assumed that both snprintf() and vsnprintf() were
54 * missing. Some systems only have snprintf() but not vsnprintf(), so
55 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
56 *
57 * Andrew Tridgell (tridge@samba.org) Oct 1998
58 * fixed handling of %.0f
59 * added test for HAVE_LONG_DOUBLE
60 *
61 * Russ Allbery <rra@stanford.edu> 2000-08-26
62 * fixed return value to comply with C99
63 * fixed handling of snprintf(NULL, ...)
64 *
65 * Hrvoje Niksic <hniksic@xemacs.org> 2000-11-04
66 * include <config.h> instead of "config.h".
67 * moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef.
68 * include <stdio.h> for NULL.
69 * added support and test cases for long long.
70 * don't declare argument types to (v)snprintf if stdarg is not used.
71 * use int instead of short int as 2nd arg to va_arg.
72 *
73 * alexk (INN) 2002-08-21
74 * use LLONG in fmtfp to handle more characters during floating
75 * point conversion.
76 *
77 * herb (Samba) 2002-12-19
78 * actually print args for %g and %e
79 *
80 * Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
81 * use the PARAMS macro to handle prototypes.
82 * write function definitions in the ansi2knr-friendly way.
83 * if string precision is specified, don't read VALUE past it.
84 * fix bug in fmtfp that caused 0.01 to be printed as 0.1.
85 * don't include <ctype.h> because none of it is used.
86 * interpret precision as number of significant digits with %g
87 * omit trailing decimal zeros with %g
88 *
89 **************************************************************/
90
91#ifdef HAVE_CONFIG_H
92# include <config.h>
93#endif
94
95/* For testing purposes, always compile in the code. */
96#ifdef TEST_SNPRINTF
97# undef HAVE_SNPRINTF
98# undef HAVE_VSNPRINTF
99# ifndef SIZEOF_LONG_LONG
100# ifdef __GNUC__
101# define SIZEOF_LONG_LONG 8
102# endif
103# endif
104#endif
105
106#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
107
108#ifdef HAVE_STRING_H
109# include <string.h>
110#else
111# include <strings.h>
112#endif
113#include <sys/types.h>
114#include <stdio.h> /* for NULL */
115
116/* varargs declarations: */
117
118#if defined(HAVE_STDARG_H)
119# include <stdarg.h>
120# define HAVE_STDARGS /* let's hope that works everywhere (mj) */
121# define VA_LOCAL_DECL va_list ap
122# define VA_START(f) va_start(ap, f)
123# define VA_SHIFT(v,t) ; /* no-op for ANSI */
124# define VA_END va_end(ap)
125#else
126# include <varargs.h>
127# undef HAVE_STDARGS
128# define VA_LOCAL_DECL va_list ap
129# define VA_START(f) va_start(ap) /* f is ignored! */
130# define VA_SHIFT(v,t) v = va_arg(ap,t)
131# define VA_END va_end(ap)
132#endif
133
134#ifdef HAVE_LONG_DOUBLE
135#define LDOUBLE long double
136#else
137#define LDOUBLE double
138#endif
139
140#if SIZEOF_LONG_LONG != 0
141# define LLONG long long
142#else
143# define LLONG long
144#endif
145
146/* If we're running the test suite, rename snprintf and vsnprintf to
147 avoid conflicts with the system version. */
148#ifdef TEST_SNPRINTF
149# define snprintf test_snprintf
150# define vsnprintf test_vsnprintf
151#endif
152
153#ifdef HAVE_STDARGS
154int snprintf (char *str, size_t count, const char *fmt, ...);
155int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
156#else
157int snprintf ();
158int vsnprintf ();
159#endif
160
161#ifndef PARAMS
162# define PARAMS(x) x
163#endif
164
165static int dopr PARAMS ((char *buffer, size_t maxlen, const char *format,
166 va_list args));
167static int fmtstr PARAMS ((char *buffer, size_t *currlen, size_t maxlen,
168 const char *value, int flags, int min, int max));
169static int fmtint PARAMS ((char *buffer, size_t *currlen, size_t maxlen,
170 LLONG value, int base, int min, int max, int flags));
171static int fmtfp PARAMS ((char *buffer, size_t *currlen, size_t maxlen,
172 LDOUBLE fvalue, int min, int max, int flags));
173static int dopr_outch PARAMS ((char *buffer, size_t *currlen, size_t maxlen,
174 char c));
175
176/*
177 * dopr(): poor man's version of doprintf
178 */
179
180/* format read states */
181#define DP_S_DEFAULT 0
182#define DP_S_FLAGS 1
183#define DP_S_MIN 2
184#define DP_S_DOT 3
185#define DP_S_MAX 4
186#define DP_S_MOD 5
187#define DP_S_MOD_L 6
188#define DP_S_CONV 7
189#define DP_S_DONE 8
190
191/* format flags - Bits */
192#define DP_F_MINUS (1 << 0)
193#define DP_F_PLUS (1 << 1)
194#define DP_F_SPACE (1 << 2)
195#define DP_F_NUM (1 << 3)
196#define DP_F_ZERO (1 << 4)
197#define DP_F_UP (1 << 5)
198#define DP_F_UNSIGNED (1 << 6)
199#define DP_F_FP_G (1 << 7)
200
201/* Conversion Flags */
202#define DP_C_SHORT 1
203#define DP_C_LONG 2
204#define DP_C_LLONG 3
205#define DP_C_LDOUBLE 4
206
207#define char_to_int(p) (p - '0')
208#define MAX(p,q) ((p >= q) ? p : q)
209#define MIN(p,q) ((p <= q) ? p : q)
210
211static int
212dopr (char *buffer, size_t maxlen, const char *format, va_list args)
213{
214 char ch;
215 LLONG value;
216 LDOUBLE fvalue;
217 char *strvalue;
218 int min;
219 int max;
220 int state;
221 int flags;
222 int cflags;
223 int total;
224 size_t currlen;
225
226 state = DP_S_DEFAULT;
227 currlen = flags = cflags = min = 0;
228 max = -1;
229 ch = *format++;
230 total = 0;
231
232 while (state != DP_S_DONE)
233 {
234 if (ch == '\0')
235 state = DP_S_DONE;
236
237 switch(state)
238 {
239 case DP_S_DEFAULT:
240 if (ch == '%')
241 state = DP_S_FLAGS;
242 else
243 total += dopr_outch (buffer, &currlen, maxlen, ch);
244 ch = *format++;
245 break;
246 case DP_S_FLAGS:
247 switch (ch)
248 {
249 case '-':
250 flags |= DP_F_MINUS;
251 ch = *format++;
252 break;
253 case '+':
254 flags |= DP_F_PLUS;
255 ch = *format++;
256 break;
257 case ' ':
258 flags |= DP_F_SPACE;
259 ch = *format++;
260 break;
261 case '#':
262 flags |= DP_F_NUM;
263 ch = *format++;
264 break;
265 case '0':
266 flags |= DP_F_ZERO;
267 ch = *format++;
268 break;
269 default:
270 state = DP_S_MIN;
271 break;
272 }
273 break;
274 case DP_S_MIN:
275 if ('0' <= ch && ch <= '9')
276 {
277 min = 10*min + char_to_int (ch);
278 ch = *format++;
279 }
280 else if (ch == '*')
281 {
282 min = va_arg (args, int);
283 ch = *format++;
284 state = DP_S_DOT;
285 }
286 else
287 state = DP_S_DOT;
288 break;
289 case DP_S_DOT:
290 if (ch == '.')
291 {
292 state = DP_S_MAX;
293 ch = *format++;
294 }
295 else
296 state = DP_S_MOD;
297 break;
298 case DP_S_MAX:
299 if ('0' <= ch && ch <= '9')
300 {
301 if (max < 0)
302 max = 0;
303 max = 10*max + char_to_int (ch);
304 ch = *format++;
305 }
306 else if (ch == '*')
307 {
308 max = va_arg (args, int);
309 ch = *format++;
310 state = DP_S_MOD;
311 }
312 else
313 state = DP_S_MOD;
314 break;
315 case DP_S_MOD:
316 switch (ch)
317 {
318 case 'h':
319 cflags = DP_C_SHORT;
320 ch = *format++;
321 break;
322 case 'l':
323 cflags = DP_C_LONG;
324 ch = *format++;
325 break;
326 case 'L':
327 cflags = DP_C_LDOUBLE;
328 ch = *format++;
329 break;
330 default:
331 break;
332 }
333 if (cflags != DP_C_LONG)
334 state = DP_S_CONV;
335 else
336 state = DP_S_MOD_L;
337 break;
338 case DP_S_MOD_L:
339 switch (ch)
340 {
341 case 'l':
342 cflags = DP_C_LLONG;
343 ch = *format++;
344 break;
345 default:
346 break;
347 }
348 state = DP_S_CONV;
349 break;
350 case DP_S_CONV:
351 switch (ch)
352 {
353 case 'd':
354 case 'i':
355 if (cflags == DP_C_SHORT)
356 value = (short int) va_arg (args, int);
357 else if (cflags == DP_C_LONG)
358 value = va_arg (args, long int);
359 else if (cflags == DP_C_LLONG)
360 value = va_arg (args, LLONG);
361 else
362 value = va_arg (args, int);
363 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
364 break;
365 case 'o':
366 flags |= DP_F_UNSIGNED;
367 if (cflags == DP_C_SHORT)
368 value = (unsigned short int) va_arg (args, unsigned int);
369 else if (cflags == DP_C_LONG)
370 value = va_arg (args, unsigned long int);
371 else if (cflags == DP_C_LLONG)
372 value = va_arg (args, unsigned LLONG);
373 else
374 value = va_arg (args, unsigned int);
375 total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
376 break;
377 case 'u':
378 flags |= DP_F_UNSIGNED;
379 if (cflags == DP_C_SHORT)
380 value = (unsigned short int) va_arg (args, unsigned int);
381 else if (cflags == DP_C_LONG)
382 value = va_arg (args, unsigned long int);
383 else if (cflags == DP_C_LLONG)
384 value = va_arg (args, unsigned LLONG);
385 else
386 value = va_arg (args, unsigned int);
387 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
388 break;
389 case 'X':
390 flags |= DP_F_UP;
391 case 'x':
392 flags |= DP_F_UNSIGNED;
393 if (cflags == DP_C_SHORT)
394 value = (unsigned short int) va_arg (args, unsigned int);
395 else if (cflags == DP_C_LONG)
396 value = va_arg (args, unsigned long int);
397 else if (cflags == DP_C_LLONG)
398 value = va_arg (args, unsigned LLONG);
399 else
400 value = va_arg (args, unsigned int);
401 total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
402 break;
403 case 'f':
404 if (cflags == DP_C_LDOUBLE)
405 fvalue = va_arg (args, LDOUBLE);
406 else
407 fvalue = va_arg (args, double);
408 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
409 break;
410 case 'E':
411 flags |= DP_F_UP;
412 case 'e':
413 if (cflags == DP_C_LDOUBLE)
414 fvalue = va_arg (args, LDOUBLE);
415 else
416 fvalue = va_arg (args, double);
417 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
418 break;
419 case 'G':
420 flags |= DP_F_UP;
421 case 'g':
422 flags |= DP_F_FP_G;
423 if (cflags == DP_C_LDOUBLE)
424 fvalue = va_arg (args, LDOUBLE);
425 else
426 fvalue = va_arg (args, double);
427 if (max == 0)
428 /* C99 says: if precision [for %g] is zero, it is taken as one */
429 max = 1;
430 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
431 break;
432 case 'c':
433 total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
434 break;
435 case 's':
436 strvalue = va_arg (args, char *);
437 total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
438 break;
439 case 'p':
440 strvalue = va_arg (args, void *);
441 total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
442 max, flags);
443 break;
444 case 'n':
445 if (cflags == DP_C_SHORT)
446 {
447 short int *num;
448 num = va_arg (args, short int *);
449 *num = currlen;
450 }
451 else if (cflags == DP_C_LONG)
452 {
453 long int *num;
454 num = va_arg (args, long int *);
455 *num = currlen;
456 }
457 else if (cflags == DP_C_LLONG)
458 {
459 LLONG *num;
460 num = va_arg (args, LLONG *);
461 *num = currlen;
462 }
463 else
464 {
465 int *num;
466 num = va_arg (args, int *);
467 *num = currlen;
468 }
469 break;
470 case '%':
471 total += dopr_outch (buffer, &currlen, maxlen, ch);
472 break;
473 case 'w':
474 /* not supported yet, treat as next char */
475 ch = *format++;
476 break;
477 default:
478 /* Unknown, skip */
479 break;
480 }
481 ch = *format++;
482 state = DP_S_DEFAULT;
483 flags = cflags = min = 0;
484 max = -1;
485 break;
486 case DP_S_DONE:
487 break;
488 default:
489 /* hmm? */
490 break; /* some picky compilers need this */
491 }
492 }
493 if (buffer != NULL)
494 {
495 if (currlen < maxlen - 1)
496 buffer[currlen] = '\0';
497 else
498 buffer[maxlen - 1] = '\0';
499 }
500 return total;
501}
502
503static int
504fmtstr (char *buffer, size_t *currlen, size_t maxlen,
505 const char *value, int flags, int min, int max)
506{
507 int padlen, strln; /* amount to pad */
508 int cnt = 0;
509 int total = 0;
510
511 if (value == 0)
512 {
513 value = "(null)";
514 }
515
516 if (max < 0)
517 strln = strlen (value);
518 else
519 /* When precision is specified, don't read VALUE past precision. */
520 /*strln = strnlen (value, max);*/
521 for (strln = 0; strln < max && value[strln]; ++strln);
522 padlen = min - strln;
523 if (padlen < 0)
524 padlen = 0;
525 if (flags & DP_F_MINUS)
526 padlen = -padlen; /* Left Justify */
527
528 while (padlen > 0)
529 {
530 total += dopr_outch (buffer, currlen, maxlen, ' ');
531 --padlen;
532 }
533 while (*value && ((max < 0) || (cnt < max)))
534 {
535 total += dopr_outch (buffer, currlen, maxlen, *value++);
536 ++cnt;
537 }
538 while (padlen < 0)
539 {
540 total += dopr_outch (buffer, currlen, maxlen, ' ');
541 ++padlen;
542 }
543 return total;
544}
545
546/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
547
548static int
549fmtint (char *buffer, size_t *currlen, size_t maxlen,
550 LLONG value, int base, int min, int max, int flags)
551{
552 int signvalue = 0;
553 unsigned LLONG uvalue;
554 char convert[24];
555 int place = 0;
556 int spadlen = 0; /* amount to space pad */
557 int zpadlen = 0; /* amount to zero pad */
558 const char *digits;
559 int total = 0;
560
561 if (max < 0)
562 max = 0;
563
564 uvalue = value;
565
566 if(!(flags & DP_F_UNSIGNED))
567 {
568 if( value < 0 ) {
569 signvalue = '-';
570 uvalue = -value;
571 }
572 else
573 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
574 signvalue = '+';
575 else
576 if (flags & DP_F_SPACE)
577 signvalue = ' ';
578 }
579
580 if (flags & DP_F_UP)
581 /* Should characters be upper case? */
582 digits = "0123456789ABCDEF";
583 else
584 digits = "0123456789abcdef";
585
586 do {
587 convert[place++] = digits[uvalue % (unsigned)base];
588 uvalue = (uvalue / (unsigned)base );
589 } while(uvalue && (place < sizeof (convert)));
590 if (place == sizeof (convert)) place--;
591 convert[place] = 0;
592
593 zpadlen = max - place;
594 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
595 if (zpadlen < 0) zpadlen = 0;
596 if (spadlen < 0) spadlen = 0;
597 if (flags & DP_F_ZERO)
598 {
599 zpadlen = MAX(zpadlen, spadlen);
600 spadlen = 0;
601 }
602 if (flags & DP_F_MINUS)
603 spadlen = -spadlen; /* Left Justifty */
604
605#ifdef DEBUG_SNPRINTF
606 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
607 zpadlen, spadlen, min, max, place));
608#endif
609
610 /* Spaces */
611 while (spadlen > 0)
612 {
613 total += dopr_outch (buffer, currlen, maxlen, ' ');
614 --spadlen;
615 }
616
617 /* Sign */
618 if (signvalue)
619 total += dopr_outch (buffer, currlen, maxlen, signvalue);
620
621 /* Zeros */
622 if (zpadlen > 0)
623 {
624 while (zpadlen > 0)
625 {
626 total += dopr_outch (buffer, currlen, maxlen, '0');
627 --zpadlen;
628 }
629 }
630
631 /* Digits */
632 while (place > 0)
633 total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
634
635 /* Left Justified spaces */
636 while (spadlen < 0) {
637 total += dopr_outch (buffer, currlen, maxlen, ' ');
638 ++spadlen;
639 }
640
641 return total;
642}
643
644static LDOUBLE
645abs_val (LDOUBLE value)
646{
647 LDOUBLE result = value;
648
649 if (value < 0)
650 result = -value;
651
652 return result;
653}
654
655static LDOUBLE
656pow10 (int exp)
657{
658 LDOUBLE result = 1;
659
660 while (exp)
661 {
662 result *= 10;
663 exp--;
664 }
665
666 return result;
667}
668
669static LLONG
670round (LDOUBLE value)
671{
672 LLONG intpart;
673
674 intpart = value;
675 value = value - intpart;
676 if (value >= 0.5)
677 intpart++;
678
679 return intpart;
680}
681
682static int
683fmtfp (char *buffer, size_t *currlen, size_t maxlen,
684 LDOUBLE fvalue, int min, int max, int flags)
685{
686 int signvalue = 0;
687 LDOUBLE ufvalue;
688 char iconvert[24];
689 char fconvert[24];
690 int iplace = 0;
691 int fplace = 0;
692 int padlen = 0; /* amount to pad */
693 int zpadlen = 0;
694 int total = 0;
695 LLONG intpart;
696 LLONG fracpart;
697 LLONG mask10;
698 int leadingfrac0s = 0; /* zeros at the start of fractional part */
699 int omitzeros = 0;
700 int omitcount = 0;
701
702 /*
703 * AIX manpage says the default is 0, but Solaris says the default
704 * is 6, and sprintf on AIX defaults to 6
705 */
706 if (max < 0)
707 max = 6;
708
709 ufvalue = abs_val (fvalue);
710
711 if (fvalue < 0)
712 signvalue = '-';
713 else
714 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
715 signvalue = '+';
716 else
717 if (flags & DP_F_SPACE)
718 signvalue = ' ';
719
720#if 0
721 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
722#endif
723
724 intpart = ufvalue;
725
726 /* With %g precision is the number of significant digits, which
727 includes the digits in intpart. */
728 if (flags & DP_F_FP_G)
729 {
730 if (intpart != 0)
731 {
732 /* For each digit of INTPART, print one less fractional digit. */
733 LLONG temp = intpart;
734 for (temp = intpart; temp != 0; temp /= 10)
735 --max;
736 if (max < 0)
737 max = 0;
738 }
739 else
740 {
741 /* For each leading 0 in fractional part, print one more
742 fractional digit. */
743 LDOUBLE temp;
744 if (ufvalue != 0)
745 for (temp = ufvalue; temp < 0.1; temp *= 10)
746 ++max;
747 }
748 }
749
750 /* C99: trailing zeros are removed from the fractional portion of the
751 result unless the # flag is specified */
752 if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
753 omitzeros = 1;
754
755#if SIZEOF_LONG_LONG > 0
756# define MAX_DIGITS 18 /* grok more digits with long long */
757#else
758# define MAX_DIGITS 9 /* just long */
759#endif
760
761 /*
762 * Sorry, we only support several digits past the decimal because of
763 * our conversion method
764 */
765 if (max > MAX_DIGITS)
766 max = MAX_DIGITS;
767
768 /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
769 mask10 = pow10 (max);
770
771 /* We "cheat" by converting the fractional part to integer by
772 * multiplying by a factor of 10
773 */
774 fracpart = round (mask10 * (ufvalue - intpart));
775
776 if (fracpart >= mask10)
777 {
778 intpart++;
779 fracpart -= mask10;
780 }
781 else if (fracpart != 0)
782 /* If fracpart has less digits than the 10* mask, we need to
783 manually insert leading 0s. For example 2.01's fractional part
784 requires one leading zero to distinguish it from 2.1. */
785 while (fracpart < mask10 / 10)
786 {
787 ++leadingfrac0s;
788 mask10 /= 10;
789 }
790
791#ifdef DEBUG_SNPRINTF
792 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
793#endif
794
795 /* Convert integer part */
796 do {
797 iconvert[iplace++] = '0' + intpart % 10;
798 intpart = (intpart / 10);
799 } while(intpart && (iplace < sizeof(iconvert)));
800 if (iplace == sizeof(iconvert)) iplace--;
801 iconvert[iplace] = 0;
802
803 /* Convert fractional part */
804 do {
805 fconvert[fplace++] = '0' + fracpart % 10;
806 fracpart = (fracpart / 10);
807 } while(fracpart && (fplace < sizeof(fconvert)));
808 while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
809 fconvert[fplace++] = '0';
810 if (fplace == sizeof(fconvert)) fplace--;
811 fconvert[fplace] = 0;
812 if (omitzeros)
813 while (omitcount < fplace && fconvert[omitcount] == '0')
814 ++omitcount;
815
816 /* -1 for decimal point, another -1 if we are printing a sign */
817 padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
818 if (!omitzeros)
819 zpadlen = max - fplace;
820 if (zpadlen < 0)
821 zpadlen = 0;
822 if (padlen < 0)
823 padlen = 0;
824 if (flags & DP_F_MINUS)
825 padlen = -padlen; /* Left Justifty */
826
827 if ((flags & DP_F_ZERO) && (padlen > 0))
828 {
829 if (signvalue)
830 {
831 total += dopr_outch (buffer, currlen, maxlen, signvalue);
832 --padlen;
833 signvalue = 0;
834 }
835 while (padlen > 0)
836 {
837 total += dopr_outch (buffer, currlen, maxlen, '0');
838 --padlen;
839 }
840 }
841 while (padlen > 0)
842 {
843 total += dopr_outch (buffer, currlen, maxlen, ' ');
844 --padlen;
845 }
846 if (signvalue)
847 total += dopr_outch (buffer, currlen, maxlen, signvalue);
848
849 while (iplace > 0)
850 total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
851
852 /*
853 * Decimal point. This should probably use locale to find the correct
854 * char to print out.
855 */
856 if (max > 0 && (fplace > omitcount || zpadlen > 0))
857 {
858 total += dopr_outch (buffer, currlen, maxlen, '.');
859
860 while (fplace > omitcount)
861 total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
862 }
863
864 while (zpadlen > 0)
865 {
866 total += dopr_outch (buffer, currlen, maxlen, '0');
867 --zpadlen;
868 }
869
870 while (padlen < 0)
871 {
872 total += dopr_outch (buffer, currlen, maxlen, ' ');
873 ++padlen;
874 }
875
876 return total;
877}
878
879static int
880dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
881{
882 if (*currlen + 1 < maxlen)
883 buffer[(*currlen)++] = c;
884 return 1;
885}
886
887#ifndef HAVE_VSNPRINTF
888int
889vsnprintf (char *str, size_t count, const char *fmt, va_list args)
890{
891 if (str != NULL)
892 str[0] = 0;
893 return dopr(str, count, fmt, args);
894}
895#endif /* !HAVE_VSNPRINTF */
896
897#ifndef HAVE_SNPRINTF
898int
899snprintf (char *str, size_t count,const char *fmt,...)
900{
901#ifndef HAVE_STDARGS
902 char *str;
903 size_t count;
904 char *fmt;
905#endif
906 VA_LOCAL_DECL;
907 int total;
908
909 VA_START (fmt);
910 VA_SHIFT (str, char *);
911 VA_SHIFT (count, size_t );
912 VA_SHIFT (fmt, char *);
913 total = vsnprintf(str, count, fmt, ap);
914 VA_END;
915 return total;
916}
917#endif /* !HAVE_SNPRINTF */
918#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF */
919
920#ifdef TEST_SNPRINTF
921
922# ifndef LONG_STRING
923# define LONG_STRING 1024
924# endif
925
926int
927main (void)
928{
929 char buf1[LONG_STRING];
930 char buf2[LONG_STRING];
931 char *fp_fmt[] = {
932 /* %f formats */
933 "%f",
934 "%-1.5f",
935 "%1.5f",
936 "%123.9f",
937 "%10.5f",
938 "% 10.5f",
939 "%+22.9f",
940 "%+4.9f",
941 "%01.3f",
942 "%4f",
943 "%3.1f",
944 "%3.2f",
945 "%.0f",
946 "%.1f",
947 "%#10.1f",
948#if SIZEOF_LONG_LONG != 0
949 "%.16f",
950 "%18.16f",
951 "%-16.16f",
952#endif
953 /* %g formats */
954 "%g",
955 "%1.5g",
956 "%-1.5g",
957 "%.9g",
958 "%123.9g",
959 "%#123.9g",
960#if SIZEOF_LONG_LONG != 0
961 "%.16g",
962 "%20.16g",
963#endif
964 NULL
965 };
966 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
967 0.9996, 1.996, 4.136, 0.00205, 0.0001, 321.000009,
968 0};
969 char *int_fmt[] = {
970 "%-1.5d",
971 "%1.5d",
972 "%123.9d",
973 "%5.5d",
974 "%10.5d",
975 "% 10.5d",
976 "%+22.33d",
977 "%01.3d",
978 "%4d",
979 NULL
980 };
981 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
982#if SIZEOF_LONG_LONG != 0
983 char *llong_fmt[] = {
984 "%lld", "%llu",
985 "%-1.5lld", "%-1.5llu",
986 "%1.5lld", "%1.5llu",
987 "%123.9lld", "%123.9llu",
988 "%5.5lld", "%5.5llu",
989 "%10.5lld", "%10.5llu",
990 "% 10.5lld", "% 10.5llu",
991 "%+22.33lld", "%+22.33llu",
992 "%01.3lld", "%01.3llu",
993 "%4lld", "%4llu",
994 NULL
995 };
996 long long llong_nums[] = {
997 ~(long long)0, /* all-1 bit pattern */
998 (~(unsigned long long)0) >> 1, /* largest signed long long */
999 /* random... */
1000 -150, 134, 91340, 341,
1001 0
1002 };
1003#endif
1004 int x, y;
1005 int fail = 0;
1006 int num = 0;
1007
1008 printf ("Testing snprintf format codes against system sprintf...\n");
1009
1010 for (x = 0; fp_fmt[x] != NULL ; x++)
1011 for (y = 0; fp_nums[y] != 0 ; y++)
1012 {
1013 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1014 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1015 if (strcmp (buf1, buf2))
1016 {
1017 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1018 fp_fmt[x], buf1, buf2);
1019 fail++;
1020 }
1021 num++;
1022 }
1023
1024 for (x = 0; int_fmt[x] != NULL ; x++)
1025 for (y = 0; int_nums[y] != 0 ; y++)
1026 {
1027 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1028 sprintf (buf2, int_fmt[x], int_nums[y]);
1029 if (strcmp (buf1, buf2))
1030 {
1031 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1032 int_fmt[x], buf1, buf2);
1033 fail++;
1034 }
1035 num++;
1036 }
1037
1038#if SIZEOF_LONG_LONG != 0
1039 for (x = 0; llong_fmt[x] != NULL ; x++)
1040 for (y = 0; llong_nums[y] != 0 ; y++)
1041 {
1042 snprintf (buf1, sizeof (buf1), llong_fmt[x], llong_nums[y]);
1043 sprintf (buf2, llong_fmt[x], llong_nums[y]);
1044 if (strcmp (buf1, buf2))
1045 {
1046 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1047 llong_fmt[x], buf1, buf2);
1048 fail++;
1049 }
1050 num++;
1051 }
1052#endif
1053
1054 printf ("%d tests failed out of %d.\n", fail, num);
1055 return 0;
1056}
1057#endif /* TEST_SNPRINTF */
Note: See TracBrowser for help on using the repository browser.