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