1/* Like vsprintf but provides a pointer to malloc'd storage, which must 2 be freed by the caller. 3 Copyright (C) 1994, 1998-1999, 2000-2003, 2006-2007 Free Software Foundation, Inc. 4 5This program is free software: you can redistribute it and/or modify 6it under the terms of the GNU General Public License as published by 7the Free Software Foundation; either version 3 of the License, or 8(at your option) any later version. 9 10This program is distributed in the hope that it will be useful, 11but WITHOUT ANY WARRANTY; without even the implied warranty of 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18#include <config.h> 19 20/* Specification. */ 21#include <stdio.h> 22 23#include <stdio.h> 24#include <string.h> 25#include <stdlib.h> 26#include <stdarg.h> 27#include <math.h> 28 29#ifdef TEST 30size_t global_total_width; 31#endif 32 33static int 34int_vasprintf (char **result, const char *format, va_list args) 35{ 36 const char *p = format; 37 /* Add one to make sure that it is never zero, which might cause malloc 38 to return NULL. */ 39 size_t total_width = strlen (format) + 1; 40 va_list ap; 41 42 va_copy (ap, args); 43 while (*p != '\0') 44 { 45 if (*p++ == '%') 46 { 47 while (strchr ("-+ #0", *p)) 48 ++p; 49 if (*p == '*') 50 { 51 ++p; 52 total_width += abs (va_arg (ap, int)); 53 } 54 else 55 total_width += strtoul (p, (char **) &p, 10); 56 if (*p == '.') 57 { 58 ++p; 59 if (*p == '*') 60 { 61 ++p; 62 total_width += abs (va_arg (ap, int)); 63 } 64 else 65 total_width += strtoul (p, (char **) &p, 10); 66 } 67 while (strchr ("hlLjtz", *p)) 68 ++p; 69 /* Should be big enough for any format specifier except %s 70 and floats. */ 71 total_width += 30; 72 switch (*p) 73 { 74 case 'd': 75 case 'i': 76 case 'o': 77 case 'u': 78 case 'x': 79 case 'X': 80 case 'c': 81 (void) va_arg (ap, int); 82 break; 83 case 'f': 84 case 'F': 85 { 86 double arg = va_arg (ap, double); 87 if (arg >= 1.0 || arg <= -1.0) 88 /* Since an ieee double can have an exponent of 307, we'll 89 make the buffer wide enough to cover the gross case. */ 90 total_width += 307; 91 } 92 break; 93 case 'e': 94 case 'E': 95 case 'g': 96 case 'G': 97 (void) va_arg (ap, double); 98 break; 99 case 's': 100 total_width += strlen (va_arg (ap, char *)); 101 break; 102 case 'p': 103 case 'n': 104 (void) va_arg (ap, char *); 105 break; 106 } 107 p++; 108 } 109 } 110 va_end (ap); 111 112#ifdef TEST 113 global_total_width = total_width; 114#endif 115 *result = malloc (total_width); 116 if (*result != NULL) 117 return vsprintf (*result, format, args); 118 else 119 return -1; 120} 121 122int 123vasprintf (char **result, const char *format, va_list args) 124{ 125 return int_vasprintf (result, format, args); 126} 127 128int 129asprintf (char **result, const char *format, ...) 130{ 131 va_list args; 132 int done; 133 134 va_start (args, format); 135 done = vasprintf (result, format, args); 136 va_end (args); 137 138 return done; 139} 140 141/* ========================= test program ========================= */ 142 143#ifdef TEST 144 145#include <float.h> 146 147void 148checkit (const char* format, ...) 149{ 150 va_list args; 151 char *result; 152 153 va_start (args, format); 154 vasprintf (&result, format, args); 155 va_end (args); 156 if (strlen (result) < global_total_width) 157 printf ("PASS: "); 158 else 159 printf ("FAIL: "); 160 printf ("%lu %s\n", (unsigned long) global_total_width, result); 161} 162 163int 164main () 165{ 166 checkit ("%d", 0x12345678); 167 checkit ("%200d", 5); 168 checkit ("%.300d", 6); 169 checkit ("%100.150d", 7); 170 checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\ 171777777777777777777333333333333366666666666622222222222777777777777733333"); 172 checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx"); 173 checkit ("%e", DBL_MIN); 174 checkit ("%e", DBL_MAX); 175 checkit ("%.400f", DBL_MIN); 176 checkit ("%f", DBL_MAX); 177 checkit ("%g", DBL_MIN); 178 checkit ("%g", DBL_MAX); 179 return 0; 180} 181 182#endif /* TEST */ 183