diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-18 11:10:51 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-18 11:10:51 +0000 |
commit | a48656b441224a53d2bb3face920ba5487eaae09 (patch) | |
tree | 5120d90a6cd45463e5a7f358d6e01ab6d6e34c61 /coreutils | |
parent | 1a715e487dc6b37a996ff3041c332765dbba45a7 (diff) | |
download | busybox-w32-a48656b441224a53d2bb3face920ba5487eaae09.tar.gz busybox-w32-a48656b441224a53d2bb3face920ba5487eaae09.tar.bz2 busybox-w32-a48656b441224a53d2bb3face920ba5487eaae09.zip |
printf: fix %b, fix several bugs in %*.*, fix compat issues with
aborting too early, support %zd; expand testsuite
function old new delta
get_width_prec - 46 +46
multiconvert 82 99 +17
conv_strtod 44 54 +10
print_direc 382 391 +9
printf_main 629 633 +4
conv_strtoul 20 16 -4
conv_strtol 20 16 -4
my_xstrtoul 20 - -20
my_xstrtol 20 - -20
my_xstrtod 21 - -21
------------------------------------------------------------------------------
(add/remove: 1/3 grow/shrink: 4/2 up/down: 86/-69) Total: 17 bytes
Diffstat (limited to 'coreutils')
-rw-r--r-- | coreutils/printf.c | 199 |
1 files changed, 121 insertions, 78 deletions
diff --git a/coreutils/printf.c b/coreutils/printf.c index d877e0581..ca035cc48 100644 --- a/coreutils/printf.c +++ b/coreutils/printf.c | |||
@@ -36,14 +36,32 @@ | |||
36 | David MacKenzie <djm@gnu.ai.mit.edu> | 36 | David MacKenzie <djm@gnu.ai.mit.edu> |
37 | */ | 37 | */ |
38 | 38 | ||
39 | |||
40 | // 19990508 Busy Boxed! Dave Cinege | 39 | // 19990508 Busy Boxed! Dave Cinege |
41 | 40 | ||
42 | #include "libbb.h" | 41 | #include "libbb.h" |
43 | 42 | ||
44 | typedef void (*converter)(const char *arg, void *result); | 43 | /* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. |
45 | 44 | * They report it: | |
46 | static void multiconvert(const char *arg, void *result, converter convert) | 45 | * bash: printf: XXX: invalid number |
46 | * printf: XXX: expected a numeric value | ||
47 | * bash: printf: 123XXX: invalid number | ||
48 | * printf: 123XXX: value not completely converted | ||
49 | * but then they use 0 (or partially converted numeric prefix) as a value | ||
50 | * and continue. They exit with 1 in this case. | ||
51 | * Both accept insane field width/precision (e.g. %9999999999.9999999999d). | ||
52 | * Both print error message and assume 0 if %*.*f width/precision is "bad" | ||
53 | * (but negative numbers are not "bad"). | ||
54 | * Both accept negative numbers for %u specifier. | ||
55 | * | ||
56 | * We try to be compatible. We are not compatible here: | ||
57 | * - we do not accept -NUM for %u | ||
58 | * - exit code is 0 even if "invalid number" was seen (FIXME) | ||
59 | * See "if (errno)" checks in the code below. | ||
60 | */ | ||
61 | |||
62 | typedef void FAST_FUNC (*converter)(const char *arg, void *result); | ||
63 | |||
64 | static int multiconvert(const char *arg, void *result, converter convert) | ||
47 | { | 65 | { |
48 | char s[sizeof(int)*3 + 2]; | 66 | char s[sizeof(int)*3 + 2]; |
49 | 67 | ||
@@ -51,43 +69,50 @@ static void multiconvert(const char *arg, void *result, converter convert) | |||
51 | sprintf(s, "%d", (unsigned char)arg[1]); | 69 | sprintf(s, "%d", (unsigned char)arg[1]); |
52 | arg = s; | 70 | arg = s; |
53 | } | 71 | } |
72 | errno = 0; | ||
54 | convert(arg, result); | 73 | convert(arg, result); |
55 | /* if there was conversion error, print unconverted string */ | 74 | if (errno) { |
56 | if (errno) | 75 | bb_error_msg("%s: invalid number", arg); |
57 | fputs(arg, stderr); | 76 | return 1; |
77 | } | ||
78 | return 0; | ||
58 | } | 79 | } |
59 | 80 | ||
60 | static void conv_strtoul(const char *arg, void *result) | 81 | static void FAST_FUNC conv_strtoul(const char *arg, void *result) |
61 | { | 82 | { |
62 | *(unsigned long*)result = bb_strtoul(arg, NULL, 0); | 83 | *(unsigned long*)result = bb_strtoul(arg, NULL, 0); |
63 | } | 84 | } |
64 | static void conv_strtol(const char *arg, void *result) | 85 | static void FAST_FUNC conv_strtol(const char *arg, void *result) |
65 | { | 86 | { |
66 | *(long*)result = bb_strtol(arg, NULL, 0); | 87 | *(long*)result = bb_strtol(arg, NULL, 0); |
67 | } | 88 | } |
68 | static void conv_strtod(const char *arg, void *result) | 89 | static void FAST_FUNC conv_strtod(const char *arg, void *result) |
69 | { | 90 | { |
70 | char *end; | 91 | char *end; |
71 | /* Well, this one allows leading whitespace... so what */ | 92 | /* Well, this one allows leading whitespace... so what? */ |
72 | /* What I like much less is that "-" is accepted too! :( */ | 93 | /* What I like much less is that "-" accepted too! :( */ |
73 | *(double*)result = strtod(arg, &end); | 94 | *(double*)result = strtod(arg, &end); |
74 | if (end[0]) errno = ERANGE; | 95 | if (end[0]) { |
96 | errno = ERANGE; | ||
97 | *(double*)result = 0; | ||
98 | } | ||
75 | } | 99 | } |
76 | 100 | ||
101 | /* Callers should check errno to detect errors */ | ||
77 | static unsigned long my_xstrtoul(const char *arg) | 102 | static unsigned long my_xstrtoul(const char *arg) |
78 | { | 103 | { |
79 | unsigned long result; | 104 | unsigned long result; |
80 | multiconvert(arg, &result, conv_strtoul); | 105 | if (multiconvert(arg, &result, conv_strtoul)) |
106 | result = 0; | ||
81 | return result; | 107 | return result; |
82 | } | 108 | } |
83 | |||
84 | static long my_xstrtol(const char *arg) | 109 | static long my_xstrtol(const char *arg) |
85 | { | 110 | { |
86 | long result; | 111 | long result; |
87 | multiconvert(arg, &result, conv_strtol); | 112 | if (multiconvert(arg, &result, conv_strtol)) |
113 | result = 0; | ||
88 | return result; | 114 | return result; |
89 | } | 115 | } |
90 | |||
91 | static double my_xstrtod(const char *arg) | 116 | static double my_xstrtod(const char *arg) |
92 | { | 117 | { |
93 | double result; | 118 | double result; |
@@ -97,14 +122,14 @@ static double my_xstrtod(const char *arg) | |||
97 | 122 | ||
98 | static void print_esc_string(char *str) | 123 | static void print_esc_string(char *str) |
99 | { | 124 | { |
100 | for (; *str; str++) { | 125 | while (*str) { |
101 | if (*str == '\\') { | 126 | if (*str == '\\') { |
102 | str++; | 127 | str++; |
103 | bb_putchar(bb_process_escape_sequence((const char **)&str)); | 128 | bb_putchar(bb_process_escape_sequence((const char **)&str)); |
104 | } else { | 129 | } else { |
105 | bb_putchar(*str); | 130 | bb_putchar(*str); |
131 | str++; | ||
106 | } | 132 | } |
107 | |||
108 | } | 133 | } |
109 | } | 134 | } |
110 | 135 | ||
@@ -112,88 +137,109 @@ static void print_direc(char *format, unsigned fmt_length, | |||
112 | int field_width, int precision, | 137 | int field_width, int precision, |
113 | const char *argument) | 138 | const char *argument) |
114 | { | 139 | { |
140 | long lv; | ||
141 | double dv; | ||
115 | char saved; | 142 | char saved; |
143 | char *have_prec, *have_width; | ||
144 | |||
145 | have_prec = strstr(format, ".*"); | ||
146 | have_width = strchr(format, '*'); | ||
147 | if (have_width - 1 == have_prec) | ||
148 | have_width = NULL; | ||
116 | 149 | ||
117 | saved = format[fmt_length]; | 150 | saved = format[fmt_length]; |
118 | format[fmt_length] = '\0'; | 151 | format[fmt_length] = '\0'; |
119 | 152 | ||
120 | switch (format[fmt_length - 1]) { | 153 | switch (format[fmt_length - 1]) { |
154 | case 'c': | ||
155 | printf(format, *argument); | ||
156 | break; | ||
121 | case 'd': | 157 | case 'd': |
122 | case 'i': | 158 | case 'i': |
123 | if (field_width < 0) { | 159 | lv = my_xstrtol(argument); |
124 | if (precision < 0) | 160 | print_long: |
125 | printf(format, my_xstrtol(argument)); | 161 | /* if (errno) return; - see comment at the top */ |
162 | if (!have_width) { | ||
163 | if (!have_prec) | ||
164 | printf(format, lv); | ||
126 | else | 165 | else |
127 | printf(format, precision, my_xstrtol(argument)); | 166 | printf(format, precision, lv); |
128 | } else { | 167 | } else { |
129 | if (precision < 0) | 168 | if (!have_prec) |
130 | printf(format, field_width, my_xstrtol(argument)); | 169 | printf(format, field_width, lv); |
131 | else | 170 | else |
132 | printf(format, field_width, precision, my_xstrtol(argument)); | 171 | printf(format, field_width, precision, lv); |
133 | } | 172 | } |
134 | break; | 173 | break; |
135 | case 'o': | 174 | case 'o': |
136 | case 'u': | 175 | case 'u': |
137 | case 'x': | 176 | case 'x': |
138 | case 'X': | 177 | case 'X': |
139 | if (field_width < 0) { | 178 | lv = my_xstrtoul(argument); |
140 | if (precision < 0) | 179 | /* cheat: unsigned long and long have same width, so... */ |
141 | printf(format, my_xstrtoul(argument)); | 180 | goto print_long; |
142 | else | 181 | case 's': |
143 | printf(format, precision, my_xstrtoul(argument)); | 182 | /* Are char* and long the same? (true for most arches) */ |
144 | } else { | 183 | if (sizeof(argument) == sizeof(lv)) { |
145 | if (precision < 0) | 184 | lv = (long)(ptrdiff_t)argument; |
146 | printf(format, field_width, my_xstrtoul(argument)); | 185 | goto print_long; |
147 | else | 186 | } else { /* Hope compiler will optimize it out */ |
148 | printf(format, field_width, precision, my_xstrtoul(argument)); | 187 | if (!have_width) { |
188 | if (!have_prec) | ||
189 | printf(format, argument); | ||
190 | else | ||
191 | printf(format, precision, argument); | ||
192 | } else { | ||
193 | if (!have_prec) | ||
194 | printf(format, field_width, argument); | ||
195 | else | ||
196 | printf(format, field_width, precision, argument); | ||
197 | } | ||
198 | break; | ||
149 | } | 199 | } |
150 | break; | ||
151 | case 'f': | 200 | case 'f': |
152 | case 'e': | 201 | case 'e': |
153 | case 'E': | 202 | case 'E': |
154 | case 'g': | 203 | case 'g': |
155 | case 'G': | 204 | case 'G': |
156 | if (field_width < 0) { | 205 | dv = my_xstrtod(argument); |
157 | if (precision < 0) | 206 | /* if (errno) return; */ |
158 | printf(format, my_xstrtod(argument)); | 207 | if (!have_width) { |
208 | if (!have_prec) | ||
209 | printf(format, dv); | ||
159 | else | 210 | else |
160 | printf(format, precision, my_xstrtod(argument)); | 211 | printf(format, precision, dv); |
161 | } else { | 212 | } else { |
162 | if (precision < 0) | 213 | if (!have_prec) |
163 | printf(format, field_width, my_xstrtod(argument)); | 214 | printf(format, field_width, dv); |
164 | else | 215 | else |
165 | printf(format, field_width, precision, my_xstrtod(argument)); | 216 | printf(format, field_width, precision, dv); |
166 | } | 217 | } |
167 | break; | 218 | break; |
168 | case 'c': | 219 | } /* switch */ |
169 | printf(format, *argument); | ||
170 | break; | ||
171 | case 's': | ||
172 | if (field_width < 0) { | ||
173 | if (precision < 0) | ||
174 | printf(format, argument); | ||
175 | else | ||
176 | printf(format, precision, argument); | ||
177 | } else { | ||
178 | if (precision < 0) | ||
179 | printf(format, field_width, argument); | ||
180 | else | ||
181 | printf(format, field_width, precision, argument); | ||
182 | } | ||
183 | break; | ||
184 | } | ||
185 | 220 | ||
186 | format[fmt_length] = saved; | 221 | format[fmt_length] = saved; |
187 | } | 222 | } |
188 | 223 | ||
224 | /* Handle params for "%*.*f". Negative numbers are ok (compat). */ | ||
225 | static int get_width_prec(const char *str) | ||
226 | { | ||
227 | int v = bb_strtoi(str, NULL, 10); | ||
228 | if (errno) { | ||
229 | bb_error_msg("%s: invalid number", str); | ||
230 | v = 0; | ||
231 | } | ||
232 | return v; | ||
233 | } | ||
234 | |||
189 | /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. | 235 | /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. |
190 | Return advanced ARGV. */ | 236 | Return advanced ARGV. */ |
191 | static char **print_formatted(char *f, char **argv) | 237 | static char **print_formatted(char *f, char **argv) |
192 | { | 238 | { |
193 | char *direc_start; /* Start of % directive. */ | 239 | char *direc_start; /* Start of % directive. */ |
194 | unsigned direc_length; /* Length of % directive. */ | 240 | unsigned direc_length; /* Length of % directive. */ |
195 | int field_width; /* Arg to first '*', or -1 if none. */ | 241 | int field_width; /* Arg to first '*' */ |
196 | int precision; /* Arg to second '*', or -1 if none. */ | 242 | int precision; /* Arg to second '*' */ |
197 | char **saved_argv = argv; | 243 | char **saved_argv = argv; |
198 | 244 | ||
199 | for (; *f; ++f) { | 245 | for (; *f; ++f) { |
@@ -201,7 +247,7 @@ static char **print_formatted(char *f, char **argv) | |||
201 | case '%': | 247 | case '%': |
202 | direc_start = f++; | 248 | direc_start = f++; |
203 | direc_length = 1; | 249 | direc_length = 1; |
204 | field_width = precision = -1; | 250 | field_width = precision = 0; |
205 | if (*f == '%') { | 251 | if (*f == '%') { |
206 | bb_putchar('%'); | 252 | bb_putchar('%'); |
207 | break; | 253 | break; |
@@ -220,11 +266,8 @@ static char **print_formatted(char *f, char **argv) | |||
220 | if (*f == '*') { | 266 | if (*f == '*') { |
221 | ++f; | 267 | ++f; |
222 | ++direc_length; | 268 | ++direc_length; |
223 | if (*argv) { | 269 | if (*argv) |
224 | field_width = my_xstrtoul(*argv); | 270 | field_width = get_width_prec(*argv++); |
225 | ++argv; | ||
226 | } else | ||
227 | field_width = 0; | ||
228 | } else { | 271 | } else { |
229 | while (isdigit(*f)) { | 272 | while (isdigit(*f)) { |
230 | ++f; | 273 | ++f; |
@@ -237,24 +280,22 @@ static char **print_formatted(char *f, char **argv) | |||
237 | if (*f == '*') { | 280 | if (*f == '*') { |
238 | ++f; | 281 | ++f; |
239 | ++direc_length; | 282 | ++direc_length; |
240 | if (*argv) { | 283 | if (*argv) |
241 | precision = my_xstrtoul(*argv); | 284 | precision = get_width_prec(*argv++); |
242 | ++argv; | 285 | } else { |
243 | } else | ||
244 | precision = 0; | ||
245 | } else | ||
246 | while (isdigit(*f)) { | 286 | while (isdigit(*f)) { |
247 | ++f; | 287 | ++f; |
248 | ++direc_length; | 288 | ++direc_length; |
249 | } | 289 | } |
290 | } | ||
250 | } | 291 | } |
251 | if (*f == 'l' || *f == 'L' || *f == 'h') { | 292 | if ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { |
252 | ++f; | 293 | ++f; |
253 | ++direc_length; | 294 | ++direc_length; |
254 | } | 295 | } |
255 | /* needed - try "printf %" without it */ | 296 | /* needed - try "printf %" without it */ |
256 | if (!strchr("diouxXfeEgGcs", *f)) { | 297 | if (!strchr("diouxXfeEgGcs", *f)) { |
257 | bb_error_msg("invalid directive '%s'", direc_start); | 298 | bb_error_msg("%s: invalid format", direc_start); |
258 | /* causes main() to exit with error */ | 299 | /* causes main() to exit with error */ |
259 | return saved_argv - 1; | 300 | return saved_argv - 1; |
260 | } | 301 | } |
@@ -263,9 +304,11 @@ static char **print_formatted(char *f, char **argv) | |||
263 | print_direc(direc_start, direc_length, field_width, | 304 | print_direc(direc_start, direc_length, field_width, |
264 | precision, *argv); | 305 | precision, *argv); |
265 | ++argv; | 306 | ++argv; |
266 | } else | 307 | } else { |
267 | print_direc(direc_start, direc_length, field_width, | 308 | print_direc(direc_start, direc_length, field_width, |
268 | precision, ""); | 309 | precision, ""); |
310 | } | ||
311 | /* if (errno) return saved_argv - 1; */ | ||
269 | break; | 312 | break; |
270 | case '\\': | 313 | case '\\': |
271 | if (*++f == 'c') { | 314 | if (*++f == 'c') { |