diff --git a/src/util/asprintf.c b/src/util/asprintf.c index b7f1f6bad5..cc0d94790f 100644 --- a/src/util/asprintf.c +++ b/src/util/asprintf.c @@ -3,49 +3,276 @@ */ /* - * Simple implementation of asprintf based on vsnprintf. - * - * Assumes that vsnprintf returns the required number of characters when - * supplied with a null buffer (as specified in C99). + * Buffer safe printf functions for portability to archaic platforms. */ +#include "ompi_config.h" + +#include "util/printf.h" + #include -#include #include #include +#include + /* - * Need to implement ompi_vsnprintf for platforms that don't have a - * working vsnprintf. - * - * For now, assume a working vsnprintf... + * Make a good guess about how long a printf-style varargs formatted + * string will be once all the % escapes are filled in. We don't + * handle every % escape here, but we handle enough, and then add a + * fudge factor in at the end. */ +static int guess_strlen(const char *fmt, va_list ap) +{ + char *sarg; + double darg; + float farg; + int i; + int iarg; + int len; + long larg; + + /* Start off with a fudge factor of 128 to handle the % escapes that + we aren't calculating here */ + + len = strlen(fmt) + 128; + for (i = 0; i < strlen(fmt); ++i) { + if ('%' == fmt[i] && i + 1 < strlen(fmt) + && '%' != fmt[i + 1]) { + ++i; + switch (fmt[i]) { + case 's': + sarg = va_arg(ap, char *); + + /* If there's an arg, get the strlen, otherwise we'll + * use (null) */ + + if (NULL != sarg) + len += strlen(sarg); + else + len += 5; + break; + + case 'd': + case 'i': + iarg = va_arg(ap, int); + /* Alloc for minus sign */ + if (iarg < 0) + ++len; + /* Now get the log10 */ + do { + ++len; + iarg /= 10; + } while (0 != iarg); + break; + + case 'x': + case 'X': + iarg = va_arg(ap, int); + /* Now get the log16 */ + do { + ++len; + iarg /= 16; + } while (0 != iarg); + break; + + case 'f': + farg = va_arg(ap, int); + /* Alloc for minus sign */ + if (farg < 0) { + ++len; + farg = -farg; + } + /* Alloc for 3 decimal places + '.' */ + len += 4; + /* Now get the log10 */ + do { + ++len; + farg /= 10.0; + } while (0 != farg); + break; + + case 'g': + darg = va_arg(ap, int); + /* Alloc for minus sign */ + if (darg < 0) { + ++len; + darg = -darg; + } + /* Alloc for 3 decimal places + '.' */ + len += 4; + /* Now get the log10 */ + do { + ++len; + darg /= 10.0; + } while (0 != darg); + break; + + case 'l': + /* Get %ld %lx %lX %lf */ + if (i + 1 < strlen(fmt)) { + ++i; + switch (fmt[i]) { + case 'x': + case 'X': + larg = va_arg(ap, int); + /* Now get the log16 */ + do { + ++len; + larg /= 16; + } while (0 != larg); + break; + + case 'f': + darg = va_arg(ap, int); + /* Alloc for minus sign */ + if (darg < 0) { + ++len; + darg = -darg; + } + /* Alloc for 3 decimal places + '.' */ + len += 4; + /* Now get the log10 */ + do { + ++len; + darg /= 10.0; + } while (0 != darg); + break; + + case 'd': + default: + larg = va_arg(ap, int); + /* Now get the log10 */ + do { + ++len; + larg /= 10; + } while (0 != larg); + break; + } + } + + default: + break; + } + } + } + + return len; +} -#define ompi_vsnprintf vsnprintf int ompi_asprintf(char **ptr, const char *fmt, ...) { int length; - size_t size; va_list ap; - /* find the required size */ va_start(ap, fmt); - length = ompi_vsnprintf(NULL, (size_t) 0, fmt, ap); - size = (size_t) length + 1; + length = ompi_vasprintf(ptr, fmt, ap); va_end(ap); + return length; +} + + +int ompi_vasprintf(char **ptr, const char *fmt, va_list ap) +{ + int length; + + /* guess the size */ + length = guess_strlen(fmt, ap); + /* allocate a buffer */ - *ptr = (char *) malloc(size); - if (*ptr == NULL) { + *ptr = (char *) malloc((size_t) length + 1); + if (NULL == *ptr) { errno = ENOMEM; return -1; } /* fill the buffer */ + length = vsprintf(*ptr, fmt, ap); + + /* realloc */ + *ptr = realloc(*ptr, (size_t) length + 1); + if (NULL == *ptr) { + errno = ENOMEM; + return -1; + } + + return length; +} + + +int ompi_snprintf(char *str, size_t size, const char *fmt, ...) +{ + int length; + va_list ap; + va_start(ap, fmt); - length = ompi_vsnprintf(*ptr, size, fmt, ap); + length = ompi_vsnprintf(str, size, fmt, ap); va_end(ap); return length; } + + +int ompi_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) +{ + int length; + char *buf; + + length = ompi_vasprintf(&buf, fmt, ap); + if (length < 0) { + return length; + } + + /* return the length when given a null buffer (C99) */ + if (str) { + if ((size_t) length < size) { + strcpy(str, buf); + } else { + memcpy(str, buf, size - 1); + str[size] = '\0'; + } + } + + return length; +} + + +#ifdef TEST + +int main(int argc, char *argv[]) +{ + char a[10]; + char b[173]; + char *s; + int length; + + puts("test for NULL buffer in snprintf:"); + length = ompi_snprintf(NULL, 0, "this is a string %d", 1004); + printf("length = %d\n", length); + + puts("test of snprintf to an undersize buffer:"); + length = ompi_snprintf(a, sizeof(a), "this is a string %d", 1004); + printf("string = %s\n", a); + printf("length = %d\n", length); + printf("strlen = %d\n", (int) strlen(a)); + + puts("test of snprintf to an oversize buffer:"); + length = ompi_snprintf(b, sizeof(b), "this is a string %d", 1004); + printf("string = %s\n", b); + printf("length = %d\n", length); + printf("strlen = %d\n", (int) strlen(b)); + + puts("test of asprintf:"); + length = ompi_asprintf(&s, "this is a string %d", 1004); + printf("string = %s\n", s); + printf("length = %d\n", length); + printf("strlen = %d\n", (int) strlen(s)); + + free(s); + + return 0; +} + +#endif