From 2fff0a4f281523e3d62018f169585ad88aa5b12b Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 30 May 2011 10:24:57 -0700 Subject: [PATCH] Merge latest samba version to get va_end() fixes, etc. --- lib/snprintf.c | 910 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 699 insertions(+), 211 deletions(-) diff --git a/lib/snprintf.c b/lib/snprintf.c index d2072fb3..f7ab908c 100644 --- a/lib/snprintf.c +++ b/lib/snprintf.c @@ -89,13 +89,21 @@ * * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even * if the C library has some snprintf functions already. + * + * Darren Tucker (dtucker@zip.com.au) 2005 + * Fix bug allowing read overruns of the source string with "%.*s" + * Usually harmless unless the read runs outside the process' allocation + * (eg if your malloc does guard pages) in which case it will segfault. + * From OpenSSH. Also added test for same. + * + * Simo Sorce (idra@samba.org) Jan 2006 + * + * Add support for position independent parameters + * fix fmtstr now it conforms to sprintf wrt min.max + * **************************************************************/ -#ifndef NO_CONFIG_H -#include "config.h" -#else -#define NULL 0 -#endif +#include "../config.h" #ifdef TEST_SNPRINTF /* need math library headers for testing */ @@ -133,13 +141,19 @@ void dummy_snprintf(void) {} #endif /* HAVE_SNPRINTF, etc */ +/* yes this really must be a ||. Don't muck with this (tridge) */ +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) + #ifdef HAVE_LONG_DOUBLE #define LDOUBLE long double #else #define LDOUBLE double #endif -#if SIZEOF_LONG_LONG +#if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG +#define HAVE_LONG_LONG 1 +#endif +#ifdef HAVE_LONG_LONG #define LLONG long long #else #define LLONG long @@ -180,82 +194,145 @@ #define DP_F_UNSIGNED (1 << 6) /* Conversion Flags */ -#define DP_C_SHORT 1 -#define DP_C_LONG 2 -#define DP_C_LDOUBLE 3 -#define DP_C_LLONG 4 +#define DP_C_CHAR 1 +#define DP_C_SHORT 2 +#define DP_C_LONG 3 +#define DP_C_LDOUBLE 4 +#define DP_C_LLONG 5 +#define DP_C_SIZET 6 + +/* Chunk types */ +#define CNK_FMT_STR 0 +#define CNK_INT 1 +#define CNK_OCTAL 2 +#define CNK_UINT 3 +#define CNK_HEX 4 +#define CNK_FLOAT 5 +#define CNK_CHAR 6 +#define CNK_STRING 7 +#define CNK_PTR 8 +#define CNK_NUM 9 +#define CNK_PRCNT 10 #define char_to_int(p) ((p)- '0') #ifndef MAX #define MAX(p,q) (((p) >= (q)) ? (p) : (q)) #endif -/* yes this really must be a ||. Don't muck with this (tridge) */ -#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) - -static size_t dopr(char *buffer, size_t maxlen, const char *format, +struct pr_chunk { + int type; /* chunk type */ + int num; /* parameter number */ + int min; + int max; + int flags; + int cflags; + int start; + int len; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + void *pnum; + struct pr_chunk *min_star; + struct pr_chunk *max_star; + struct pr_chunk *next; +}; + +struct pr_chunk_x { + struct pr_chunk **chunks; + int num; +}; + +static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in); static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, int min, int max); static void fmtint(char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags); + LLONG value, int base, int min, int max, int flags); static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags); static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); +static struct pr_chunk *new_chunk(void); +static int add_cnk_list_entry(struct pr_chunk_x **list, + int max_num, struct pr_chunk *chunk); -static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) +static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) { char ch; - LLONG value; - LDOUBLE fvalue; - char *strvalue; - int min; - int max; int state; - int flags; - int cflags; + int pflag; + int pnum; + int pfirst; size_t currlen; va_list args; + const char *base; + struct pr_chunk *chunks = NULL; + struct pr_chunk *cnk = NULL; + struct pr_chunk_x *clist = NULL; + int max_pos; + int ret = -1; VA_COPY(args, args_in); - + state = DP_S_DEFAULT; - currlen = flags = cflags = min = 0; - max = -1; + pfirst = 1; + pflag = 0; + pnum = 0; + + max_pos = 0; + base = format; ch = *format++; + /* retrieve the string structure as chunks */ while (state != DP_S_DONE) { if (ch == '\0') state = DP_S_DONE; switch(state) { case DP_S_DEFAULT: - if (ch == '%') + + if (cnk) { + cnk->next = new_chunk(); + cnk = cnk->next; + } else { + cnk = new_chunk(); + } + if (!cnk) goto done; + if (!chunks) chunks = cnk; + + if (ch == '%') { state = DP_S_FLAGS; - else - dopr_outch (buffer, &currlen, maxlen, ch); - ch = *format++; + ch = *format++; + } else { + cnk->type = CNK_FMT_STR; + cnk->start = format - base -1; + while ((ch != '\0') && (ch != '%')) ch = *format++; + cnk->len = format - base - cnk->start -1; + } break; case DP_S_FLAGS: switch (ch) { case '-': - flags |= DP_F_MINUS; + cnk->flags |= DP_F_MINUS; ch = *format++; break; case '+': - flags |= DP_F_PLUS; + cnk->flags |= DP_F_PLUS; ch = *format++; break; case ' ': - flags |= DP_F_SPACE; + cnk->flags |= DP_F_SPACE; ch = *format++; break; case '#': - flags |= DP_F_NUM; + cnk->flags |= DP_F_NUM; ch = *format++; break; case '0': - flags |= DP_F_ZERO; + cnk->flags |= DP_F_ZERO; + ch = *format++; + break; + case 'I': + /* internationalization not supported yet */ ch = *format++; break; default: @@ -265,13 +342,51 @@ static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args break; case DP_S_MIN: if (isdigit((unsigned char)ch)) { - min = 10*min + char_to_int (ch); + cnk->min = 10 * cnk->min + char_to_int (ch); + ch = *format++; + } else if (ch == '$') { + if (!pfirst && !pflag) { + /* parameters must be all positioned or none */ + goto done; + } + if (pfirst) { + pfirst = 0; + pflag = 1; + } + if (cnk->min == 0) /* what ?? */ + goto done; + cnk->num = cnk->min; + cnk->min = 0; ch = *format++; } else if (ch == '*') { - min = va_arg (args, int); + if (pfirst) pfirst = 0; + cnk->min_star = new_chunk(); + if (!cnk->min_star) /* out of memory :-( */ + goto done; + cnk->min_star->type = CNK_INT; + if (pflag) { + int num; + ch = *format++; + if (!isdigit((unsigned char)ch)) { + /* parameters must be all positioned or none */ + goto done; + } + for (num = 0; isdigit((unsigned char)ch); ch = *format++) { + num = 10 * num + char_to_int(ch); + } + cnk->min_star->num = num; + if (ch != '$') /* what ?? */ + goto done; + } else { + cnk->min_star->num = ++pnum; + } + max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star); + if (max_pos == 0) /* out of memory :-( */ + goto done; ch = *format++; state = DP_S_DOT; } else { + if (pfirst) pfirst = 0; state = DP_S_DOT; } break; @@ -285,12 +400,45 @@ static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args break; case DP_S_MAX: if (isdigit((unsigned char)ch)) { - if (max < 0) - max = 0; - max = 10*max + char_to_int (ch); + if (cnk->max < 0) + cnk->max = 0; + cnk->max = 10 * cnk->max + char_to_int (ch); + ch = *format++; + } else if (ch == '$') { + if (!pfirst && !pflag) { + /* parameters must be all positioned or none */ + goto done; + } + if (cnk->max <= 0) /* what ?? */ + goto done; + cnk->num = cnk->max; + cnk->max = -1; ch = *format++; } else if (ch == '*') { - max = va_arg (args, int); + cnk->max_star = new_chunk(); + if (!cnk->max_star) /* out of memory :-( */ + goto done; + cnk->max_star->type = CNK_INT; + if (pflag) { + int num; + ch = *format++; + if (!isdigit((unsigned char)ch)) { + /* parameters must be all positioned or none */ + goto done; + } + for (num = 0; isdigit((unsigned char)ch); ch = *format++) { + num = 10 * num + char_to_int(ch); + } + cnk->max_star->num = num; + if (ch != '$') /* what ?? */ + goto done; + } else { + cnk->max_star->num = ++pnum; + } + max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star); + if (max_pos == 0) /* out of memory :-( */ + goto done; + ch = *format++; state = DP_S_MOD; } else { @@ -300,19 +448,27 @@ static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args case DP_S_MOD: switch (ch) { case 'h': - cflags = DP_C_SHORT; + cnk->cflags = DP_C_SHORT; ch = *format++; + if (ch == 'h') { + cnk->cflags = DP_C_CHAR; + ch = *format++; + } break; case 'l': - cflags = DP_C_LONG; + cnk->cflags = DP_C_LONG; ch = *format++; if (ch == 'l') { /* It's a long long */ - cflags = DP_C_LLONG; + cnk->cflags = DP_C_LLONG; ch = *format++; } break; case 'L': - cflags = DP_C_LDOUBLE; + cnk->cflags = DP_C_LDOUBLE; + ch = *format++; + break; + case 'z': + cnk->cflags = DP_C_SIZET; ch = *format++; break; default: @@ -321,133 +477,65 @@ static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args state = DP_S_CONV; break; case DP_S_CONV: + if (cnk->num == 0) cnk->num = ++pnum; + max_pos = add_cnk_list_entry(&clist, max_pos, cnk); + if (max_pos == 0) /* out of memory :-( */ + goto done; + switch (ch) { case 'd': case 'i': - if (cflags == DP_C_SHORT) - value = va_arg (args, int); - else if (cflags == DP_C_LONG) - value = va_arg (args, long int); - else if (cflags == DP_C_LLONG) - value = va_arg (args, LLONG); - else - value = va_arg (args, int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + cnk->type = CNK_INT; break; case 'o': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); - else if (cflags == DP_C_LLONG) - value = (long)va_arg (args, unsigned LLONG); - else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + cnk->type = CNK_OCTAL; + cnk->flags |= DP_F_UNSIGNED; break; case 'u': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); - else if (cflags == DP_C_LLONG) - value = (LLONG)va_arg (args, unsigned LLONG); - else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + cnk->type = CNK_UINT; + cnk->flags |= DP_F_UNSIGNED; break; case 'X': - flags |= DP_F_UP; + cnk->flags |= DP_F_UP; case 'x': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned int); - else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); - else if (cflags == DP_C_LLONG) - value = (LLONG)va_arg (args, unsigned LLONG); - else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); - break; - case 'f': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - /* um, floating point? */ - fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + cnk->type = CNK_HEX; + cnk->flags |= DP_F_UNSIGNED; break; + case 'A': + /* hex float not supported yet */ case 'E': - flags |= DP_F_UP; - case 'e': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); - break; case 'G': - flags |= DP_F_UP; + case 'F': + cnk->flags |= DP_F_UP; + case 'a': + /* hex float not supported yet */ + case 'e': + case 'f': case 'g': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + cnk->type = CNK_FLOAT; break; case 'c': - dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + cnk->type = CNK_CHAR; break; case 's': - strvalue = va_arg (args, char *); - if (!strvalue) strvalue = "(NULL)"; - if (max == -1) { - max = strlen(strvalue); - } - if (min > 0 && max >= 0 && min > max) max = min; - fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + cnk->type = CNK_STRING; break; case 'p': - strvalue = va_arg (args, void *); - fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + cnk->type = CNK_PTR; + cnk->flags |= DP_F_UNSIGNED; break; case 'n': - if (cflags == DP_C_SHORT) { - short int *num; - num = va_arg (args, short int *); - *num = currlen; - } else if (cflags == DP_C_LONG) { - long int *num; - num = va_arg (args, long int *); - *num = (long int)currlen; - } else if (cflags == DP_C_LLONG) { - LLONG *num; - num = va_arg (args, LLONG *); - *num = (LLONG)currlen; - } else { - int *num; - num = va_arg (args, int *); - *num = currlen; - } + cnk->type = CNK_NUM; break; case '%': - dopr_outch (buffer, &currlen, maxlen, ch); - break; - case 'w': - /* not supported yet, treat as next char */ - ch = *format++; + cnk->type = CNK_PRCNT; break; default: - /* Unknown, skip */ - break; + /* Unknown, bail out*/ + goto done; } ch = *format++; state = DP_S_DEFAULT; - flags = cflags = min = 0; - max = -1; break; case DP_S_DONE: break; @@ -456,14 +544,237 @@ static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args break; /* some picky compilers need this */ } } + + /* retrieve the format arguments */ + for (pnum = 0; pnum < max_pos; pnum++) { + int i; + + if (clist[pnum].num == 0) { + /* ignoring a parameter should not be permitted + * all parameters must be matched at least once + * BUT seem some system ignore this rule ... + * at least my glibc based system does --SSS + */ +#ifdef DEBUG_SNPRINTF + printf("parameter at position %d not used\n", pnum+1); +#endif + /* eat the parameter */ + va_arg (args, int); + continue; + } + for (i = 1; i < clist[pnum].num; i++) { + if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) { + /* nooo noo no! + * all the references to a parameter + * must be of the same type + */ + goto done; + } + } + cnk = clist[pnum].chunks[0]; + switch (cnk->type) { + case CNK_INT: + if (cnk->cflags == DP_C_SHORT) + cnk->value = va_arg (args, int); + else if (cnk->cflags == DP_C_LONG) + cnk->value = va_arg (args, long int); + else if (cnk->cflags == DP_C_LLONG) + cnk->value = va_arg (args, LLONG); + else if (cnk->cflags == DP_C_SIZET) + cnk->value = va_arg (args, ssize_t); + else + cnk->value = va_arg (args, int); + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->value = cnk->value; + } + break; + + case CNK_OCTAL: + case CNK_UINT: + case CNK_HEX: + if (cnk->cflags == DP_C_SHORT) + cnk->value = va_arg (args, unsigned int); + else if (cnk->cflags == DP_C_LONG) + cnk->value = (unsigned long int)va_arg (args, unsigned long int); + else if (cnk->cflags == DP_C_LLONG) + cnk->value = (LLONG)va_arg (args, unsigned LLONG); + else if (cnk->cflags == DP_C_SIZET) + cnk->value = (size_t)va_arg (args, size_t); + else + cnk->value = (unsigned int)va_arg (args, unsigned int); + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->value = cnk->value; + } + break; + + case CNK_FLOAT: + if (cnk->cflags == DP_C_LDOUBLE) + cnk->fvalue = va_arg (args, LDOUBLE); + else + cnk->fvalue = va_arg (args, double); + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->fvalue = cnk->fvalue; + } + break; + + case CNK_CHAR: + cnk->value = va_arg (args, int); + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->value = cnk->value; + } + break; + + case CNK_STRING: + cnk->strvalue = va_arg (args, char *); + if (!cnk->strvalue) cnk->strvalue = "(NULL)"; + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->strvalue = cnk->strvalue; + } + break; + + case CNK_PTR: + cnk->strvalue = va_arg (args, void *); + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->strvalue = cnk->strvalue; + } + break; + + case CNK_NUM: + if (cnk->cflags == DP_C_CHAR) + cnk->pnum = va_arg (args, char *); + else if (cnk->cflags == DP_C_SHORT) + cnk->pnum = va_arg (args, short int *); + else if (cnk->cflags == DP_C_LONG) + cnk->pnum = va_arg (args, long int *); + else if (cnk->cflags == DP_C_LLONG) + cnk->pnum = va_arg (args, LLONG *); + else if (cnk->cflags == DP_C_SIZET) + cnk->pnum = va_arg (args, ssize_t *); + else + cnk->pnum = va_arg (args, int *); + + for (i = 1; i < clist[pnum].num; i++) { + clist[pnum].chunks[i]->pnum = cnk->pnum; + } + break; + + case CNK_PRCNT: + break; + + default: + /* what ?? */ + goto done; + } + } + /* print out the actual string from chunks */ + currlen = 0; + cnk = chunks; + while (cnk) { + int len, min, max; + + if (cnk->min_star) min = cnk->min_star->value; + else min = cnk->min; + if (cnk->max_star) max = cnk->max_star->value; + else max = cnk->max; + + switch (cnk->type) { + + case CNK_FMT_STR: + if (maxlen != 0 && maxlen > currlen) { + if (maxlen > (currlen + cnk->len)) len = cnk->len; + else len = maxlen - currlen; + + memcpy(&(buffer[currlen]), &(base[cnk->start]), len); + } + currlen += cnk->len; + + break; + + case CNK_INT: + case CNK_UINT: + fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags); + break; + + case CNK_OCTAL: + fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags); + break; + + case CNK_HEX: + fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags); + break; + + case CNK_FLOAT: + fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags); + break; + + case CNK_CHAR: + dopr_outch (buffer, &currlen, maxlen, cnk->value); + break; + + case CNK_STRING: + if (max == -1) { + max = strlen(cnk->strvalue); + } + fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max); + break; + + case CNK_PTR: + fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags); + break; + + case CNK_NUM: + if (cnk->cflags == DP_C_CHAR) + *((char *)(cnk->pnum)) = (char)currlen; + else if (cnk->cflags == DP_C_SHORT) + *((short int *)(cnk->pnum)) = (short int)currlen; + else if (cnk->cflags == DP_C_LONG) + *((long int *)(cnk->pnum)) = (long int)currlen; + else if (cnk->cflags == DP_C_LLONG) + *((LLONG *)(cnk->pnum)) = (LLONG)currlen; + else if (cnk->cflags == DP_C_SIZET) + *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen; + else + *((int *)(cnk->pnum)) = (int)currlen; + break; + + case CNK_PRCNT: + dopr_outch (buffer, &currlen, maxlen, '%'); + break; + + default: + /* what ?? */ + goto done; + } + cnk = cnk->next; + } if (maxlen != 0) { if (currlen < maxlen - 1) buffer[currlen] = '\0'; else if (maxlen > 0) buffer[maxlen - 1] = '\0'; } - - return currlen; + ret = currlen; + +done: + va_end(args); + + while (chunks) { + cnk = chunks->next; + free(chunks); + chunks = cnk; + } + if (clist) { + for (pnum = 0; pnum < max_pos; pnum++) { + if (clist[pnum].chunks) free(clist[pnum].chunks); + } + free(clist); + } + return ret; } static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, @@ -479,36 +790,34 @@ static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, value = ""; } - for (strln = 0; value[strln]; ++strln); /* strlen */ + for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ padlen = min - strln; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ - while ((padlen > 0) && (cnt < max)) { + while (padlen > 0) { dopr_outch (buffer, currlen, maxlen, ' '); --padlen; - ++cnt; } while (*value && (cnt < max)) { dopr_outch (buffer, currlen, maxlen, *value++); ++cnt; } - while ((padlen < 0) && (cnt < max)) { + while (padlen < 0) { dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; - ++cnt; } } /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ static void fmtint(char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) + LLONG value, int base, int min, int max, int flags) { int signvalue = 0; - unsigned long uvalue; + unsigned LLONG uvalue; char convert[20]; int place = 0; int spadlen = 0; /* amount to space pad */ @@ -626,32 +935,34 @@ static LLONG ROUND(LDOUBLE value) static double my_modf(double x0, double *iptr) { int i; - long l; + LLONG l=0; double x = x0; double f = 1.0; for (i=0;i<100;i++) { l = (long)x; - if (l <= (x+1) && l >= (x-1)) { - if (i != 0) { - double i2; - double ret; - - ret = my_modf(x0-l*f, &i2); - (*iptr) = l*f + i2; - return ret; - } - - (*iptr) = l; - return x - (*iptr); - } + if (l <= (x+1) && l >= (x-1)) break; x *= 0.1; f *= 10.0; } - /* yikes! the number is beyond what we can handle. What do we do? */ - (*iptr) = 0; - return 0; + if (i == 100) { + /* yikes! the number is beyond what we can handle. What do we do? */ + (*iptr) = 0; + return 0; + } + + if (i != 0) { + double i2; + double ret; + + ret = my_modf(x0-l*f, &i2); + (*iptr) = l*f + i2; + return ret; + } + + (*iptr) = l; + return x - (*iptr); } @@ -701,11 +1012,11 @@ static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, #endif /* - * Sorry, we only support 16 digits past the decimal because of our + * Sorry, we only support 9 digits past the decimal because of our * conversion method */ - if (max > 16) - max = 16; + if (max > 9) + max = 9; /* We "cheat" by converting the fractional part to integer by * multiplying by a factor of 10 @@ -815,6 +1126,85 @@ static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) (*currlen)++; } +static struct pr_chunk *new_chunk(void) { + struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk)); + + if (!new_c) + return NULL; + + new_c->type = 0; + new_c->num = 0; + new_c->min = 0; + new_c->min_star = NULL; + new_c->max = -1; + new_c->max_star = NULL; + new_c->flags = 0; + new_c->cflags = 0; + new_c->start = 0; + new_c->len = 0; + new_c->value = 0; + new_c->fvalue = 0; + new_c->strvalue = NULL; + new_c->pnum = NULL; + new_c->next = NULL; + + return new_c; +} + +static int add_cnk_list_entry(struct pr_chunk_x **list, + int max_num, struct pr_chunk *chunk) { + struct pr_chunk_x *l; + struct pr_chunk **c; + int max; + int cnum; + int i, pos; + + if (chunk->num > max_num) { + max = chunk->num; + + if (*list == NULL) { + l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max); + pos = 0; + } else { + l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max); + pos = max_num; + } + if (l == NULL) { + for (i = 0; i < max; i++) { + if ((*list)[i].chunks) free((*list)[i].chunks); + } + return 0; + } + for (i = pos; i < max; i++) { + l[i].chunks = NULL; + l[i].num = 0; + } + } else { + l = *list; + max = max_num; + } + + i = chunk->num - 1; + cnum = l[i].num + 1; + if (l[i].chunks == NULL) { + c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum); + } else { + c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum); + } + if (c == NULL) { + for (i = 0; i < max; i++) { + if (l[i].chunks) free(l[i].chunks); + } + return 0; + } + c[l[i].num] = chunk; + l[i].chunks = c; + l[i].num = cnum; + + *list = l; + return max; +} + int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args) { return dopr(str, count, fmt, args); @@ -851,16 +1241,16 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) va_list ap2; VA_COPY(ap2, ap); - ret = vsnprintf(NULL, 0, format, ap2); - if (ret <= 0) return ret; + va_end(ap2); + if (ret < 0) return ret; (*ptr) = (char *)malloc(ret+1); if (!*ptr) return -1; VA_COPY(ap2, ap); - ret = vsnprintf(*ptr, ret+1, format, ap2); + va_end(ap2); return ret; } @@ -885,11 +1275,13 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) #ifdef TEST_SNPRINTF int sprintf(char *str,const char *fmt,...); + int printf(const char *fmt,...); int main (void) { char buf1[1024]; char buf2[1024]; + char *buf3; char *fp_fmt[] = { "%1.1f", "%-1.5f", @@ -905,7 +1297,8 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) "%3.2f", "%.0f", "%f", - "-16.16f", + "%-8.8f", + "%-9.9f", NULL }; double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, @@ -924,14 +1317,16 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) "%d", NULL }; - long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0}; char *str_fmt[] = { - "10.5s", - "5.10s", - "10.1s", - "0.10s", - "10.0s", - "1.10s", + "%10.5s", + "%-10.5s", + "%5.10s", + "%-5.10s", + "%10.1s", + "%0.10s", + "%10.0s", + "%1.10s", "%s", "%.1s", "%.10s", @@ -939,24 +1334,35 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) NULL }; char *str_vals[] = {"hello", "a", "", "a longer string", NULL}; +#ifdef HAVE_LONG_LONG + char *ll_fmt[] = { + "%llu", + NULL + }; + LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0}; +#endif int x, y; int fail = 0; int num = 0; + int l1, l2; + char *ss_fmt[] = { + "%zd", + "%zu", + NULL + }; + size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0}; printf ("Testing snprintf format codes against system sprintf...\n"); for (x = 0; fp_fmt[x] ; x++) { for (y = 0; fp_nums[y] != 0 ; y++) { - int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]); - int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); - sprintf (buf2, fp_fmt[x], fp_nums[y]); - if (strcmp (buf1, buf2)) { - printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", - fp_fmt[x], buf1, buf2); - fail++; - } - if (l1 != l2) { - printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]); + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); + l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp (buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + fp_fmt[x], l1, buf1, l2, buf2); fail++; } num++; @@ -965,16 +1371,13 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) for (x = 0; int_fmt[x] ; x++) { for (y = 0; int_nums[y] != 0 ; y++) { - int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]); - int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); - sprintf (buf2, int_fmt[x], int_nums[y]); - if (strcmp (buf1, buf2)) { - printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", - int_fmt[x], buf1, buf2); - fail++; - } - if (l1 != l2) { - printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]); + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); + l2 = sprintf (buf2, int_fmt[x], int_nums[y]); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp (buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + int_fmt[x], l1, buf1, l2, buf2); fail++; } num++; @@ -983,22 +1386,107 @@ int rsync_snprintf(char *str,size_t count,const char *fmt,...) for (x = 0; str_fmt[x] ; x++) { for (y = 0; str_vals[y] != 0 ; y++) { - int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]); - int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]); - sprintf (buf2, str_fmt[x], str_vals[y]); - if (strcmp (buf1, buf2)) { - printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", - str_fmt[x], buf1, buf2); + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]); + l2 = sprintf (buf2, str_fmt[x], str_vals[y]); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp (buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + str_fmt[x], l1, buf1, l2, buf2); + fail++; + } + num++; + } + } + +#ifdef HAVE_LONG_LONG + for (x = 0; ll_fmt[x] ; x++) { + for (y = 0; ll_nums[y] != 0 ; y++) { + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]); + l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp (buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + ll_fmt[x], l1, buf1, l2, buf2); fail++; } - if (l1 != l2) { - printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]); + num++; + } + } +#endif + +#define BUFSZ 2048 + + buf1[0] = buf2[0] = '\0'; + if ((buf3 = malloc(BUFSZ)) == NULL) { + fail++; + } else { + num++; + memset(buf3, 'a', BUFSZ); + snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3); + buf1[1023] = '\0'; + if (strcmp(buf1, "a") != 0) { + printf("length limit buf1 '%s' expected 'a'\n", buf1); + fail++; + } + } + + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9); + l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp(buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); + fail++; + } + + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9); + l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp(buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); + fail++; + } + + for (x = 0; ss_fmt[x] ; x++) { + for (y = 0; ss_nums[y] != 0 ; y++) { + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]); + l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp (buf1, buf2) || (l1 != l2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + ss_fmt[x], l1, buf1, l2, buf2); fail++; } num++; } } +#if 0 + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890); + l2 = sprintf(buf2, "%lld", (LLONG)1234567890); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp(buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + "%lld", l1, buf1, l2, buf2); + fail++; + } + buf1[0] = buf2[0] = '\0'; + l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123); + l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123); + buf1[1023] = buf2[1023] = '\0'; + if (strcmp(buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", + "%Lf", l1, buf1, l2, buf2); + fail++; + } +#endif printf ("%d tests failed out of %d.\n", fail, num); printf("seeing how many digits we support\n"); -- 2.34.1