aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-18 11:10:51 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-18 11:10:51 +0000
commita48656b441224a53d2bb3face920ba5487eaae09 (patch)
tree5120d90a6cd45463e5a7f358d6e01ab6d6e34c61 /coreutils
parent1a715e487dc6b37a996ff3041c332765dbba45a7 (diff)
downloadbusybox-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.c199
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
44typedef 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:
46static 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
62typedef void FAST_FUNC (*converter)(const char *arg, void *result);
63
64static 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
60static void conv_strtoul(const char *arg, void *result) 81static 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}
64static void conv_strtol(const char *arg, void *result) 85static 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}
68static void conv_strtod(const char *arg, void *result) 89static 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 */
77static unsigned long my_xstrtoul(const char *arg) 102static 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
84static long my_xstrtol(const char *arg) 109static 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
91static double my_xstrtod(const char *arg) 116static 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
98static void print_esc_string(char *str) 123static 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). */
225static 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. */
191static char **print_formatted(char *f, char **argv) 237static 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') {