53 |
* (but negative numbers are not "bad"). |
* (but negative numbers are not "bad"). |
54 |
* Both accept negative numbers for %u specifier. |
* Both accept negative numbers for %u specifier. |
55 |
* |
* |
56 |
* We try to be compatible. We are not compatible here: |
* We try to be compatible. |
|
* - we do not accept -NUM for %u |
|
|
* - exit code is 0 even if "invalid number" was seen (FIXME) |
|
|
* See "if (errno)" checks in the code below. |
|
57 |
*/ |
*/ |
58 |
|
|
59 |
typedef void FAST_FUNC (*converter)(const char *arg, void *result); |
typedef void FAST_FUNC (*converter)(const char *arg, void *result); |
72 |
return 0; |
return 0; |
73 |
} |
} |
74 |
|
|
75 |
static void FAST_FUNC conv_strtoul(const char *arg, void *result) |
static void FAST_FUNC conv_strtoull(const char *arg, void *result) |
76 |
{ |
{ |
77 |
*(unsigned long*)result = bb_strtoul(arg, NULL, 0); |
*(unsigned long long*)result = bb_strtoull(arg, NULL, 0); |
78 |
|
/* both coreutils 6.10 and bash 3.2: |
79 |
|
* $ printf '%x\n' -2 |
80 |
|
* fffffffffffffffe |
81 |
|
* Mimic that: |
82 |
|
*/ |
83 |
|
if (errno) { |
84 |
|
*(unsigned long long*)result = bb_strtoll(arg, NULL, 0); |
85 |
|
} |
86 |
} |
} |
87 |
static void FAST_FUNC conv_strtol(const char *arg, void *result) |
static void FAST_FUNC conv_strtoll(const char *arg, void *result) |
88 |
{ |
{ |
89 |
*(long*)result = bb_strtol(arg, NULL, 0); |
*(long long*)result = bb_strtoll(arg, NULL, 0); |
90 |
} |
} |
91 |
static void FAST_FUNC conv_strtod(const char *arg, void *result) |
static void FAST_FUNC conv_strtod(const char *arg, void *result) |
92 |
{ |
{ |
101 |
} |
} |
102 |
|
|
103 |
/* Callers should check errno to detect errors */ |
/* Callers should check errno to detect errors */ |
104 |
static unsigned long my_xstrtoul(const char *arg) |
static unsigned long long my_xstrtoull(const char *arg) |
105 |
{ |
{ |
106 |
unsigned long result; |
unsigned long long result; |
107 |
if (multiconvert(arg, &result, conv_strtoul)) |
if (multiconvert(arg, &result, conv_strtoull)) |
108 |
result = 0; |
result = 0; |
109 |
return result; |
return result; |
110 |
} |
} |
111 |
static long my_xstrtol(const char *arg) |
static long long my_xstrtoll(const char *arg) |
112 |
{ |
{ |
113 |
long result; |
long long result; |
114 |
if (multiconvert(arg, &result, conv_strtol)) |
if (multiconvert(arg, &result, conv_strtoll)) |
115 |
result = 0; |
result = 0; |
116 |
return result; |
return result; |
117 |
} |
} |
139 |
int field_width, int precision, |
int field_width, int precision, |
140 |
const char *argument) |
const char *argument) |
141 |
{ |
{ |
142 |
long lv; |
long long llv; |
143 |
double dv; |
double dv; |
144 |
char saved; |
char saved; |
145 |
char *have_prec, *have_width; |
char *have_prec, *have_width; |
152 |
if (have_width - 1 == have_prec) |
if (have_width - 1 == have_prec) |
153 |
have_width = NULL; |
have_width = NULL; |
154 |
|
|
155 |
|
errno = 0; |
156 |
|
|
157 |
switch (format[fmt_length - 1]) { |
switch (format[fmt_length - 1]) { |
158 |
case 'c': |
case 'c': |
159 |
printf(format, *argument); |
printf(format, *argument); |
160 |
break; |
break; |
161 |
case 'd': |
case 'd': |
162 |
case 'i': |
case 'i': |
163 |
lv = my_xstrtol(argument); |
llv = my_xstrtoll(argument); |
164 |
print_long: |
print_long: |
|
/* if (errno) return; - see comment at the top */ |
|
165 |
if (!have_width) { |
if (!have_width) { |
166 |
if (!have_prec) |
if (!have_prec) |
167 |
printf(format, lv); |
printf(format, llv); |
168 |
else |
else |
169 |
printf(format, precision, lv); |
printf(format, precision, llv); |
170 |
} else { |
} else { |
171 |
if (!have_prec) |
if (!have_prec) |
172 |
printf(format, field_width, lv); |
printf(format, field_width, llv); |
173 |
else |
else |
174 |
printf(format, field_width, precision, lv); |
printf(format, field_width, precision, llv); |
175 |
} |
} |
176 |
break; |
break; |
177 |
case 'o': |
case 'o': |
178 |
case 'u': |
case 'u': |
179 |
case 'x': |
case 'x': |
180 |
case 'X': |
case 'X': |
181 |
lv = my_xstrtoul(argument); |
llv = my_xstrtoull(argument); |
182 |
/* cheat: unsigned long and long have same width, so... */ |
/* cheat: unsigned long and long have same width, so... */ |
183 |
goto print_long; |
goto print_long; |
184 |
case 's': |
case 's': |
185 |
/* Are char* and long the same? (true for most arches) */ |
/* Are char* and long long the same? */ |
186 |
if (sizeof(argument) == sizeof(lv)) { |
if (sizeof(argument) == sizeof(llv)) { |
187 |
lv = (long)(ptrdiff_t)argument; |
llv = (long long)(ptrdiff_t)argument; |
188 |
goto print_long; |
goto print_long; |
189 |
} else { /* Hope compiler will optimize it out */ |
} else { |
190 |
|
/* Hope compiler will optimize it out by moving call |
191 |
|
* instruction after the ifs... */ |
192 |
if (!have_width) { |
if (!have_width) { |
193 |
if (!have_prec) |
if (!have_prec) |
194 |
printf(format, argument); |
printf(format, argument, /*unused:*/ argument, argument); |
195 |
else |
else |
196 |
printf(format, precision, argument); |
printf(format, precision, argument, /*unused:*/ argument); |
197 |
} else { |
} else { |
198 |
if (!have_prec) |
if (!have_prec) |
199 |
printf(format, field_width, argument); |
printf(format, field_width, argument, /*unused:*/ argument); |
200 |
else |
else |
201 |
printf(format, field_width, precision, argument); |
printf(format, field_width, precision, argument); |
202 |
} |
} |
208 |
case 'g': |
case 'g': |
209 |
case 'G': |
case 'G': |
210 |
dv = my_xstrtod(argument); |
dv = my_xstrtod(argument); |
|
/* if (errno) return; */ |
|
211 |
if (!have_width) { |
if (!have_width) { |
212 |
if (!have_prec) |
if (!have_prec) |
213 |
printf(format, dv); |
printf(format, dv); |
238 |
|
|
239 |
/* Print the text in FORMAT, using ARGV for arguments to any '%' directives. |
/* Print the text in FORMAT, using ARGV for arguments to any '%' directives. |
240 |
Return advanced ARGV. */ |
Return advanced ARGV. */ |
241 |
static char **print_formatted(char *f, char **argv) |
static char **print_formatted(char *f, char **argv, int *conv_err) |
242 |
{ |
{ |
243 |
char *direc_start; /* Start of % directive. */ |
char *direc_start; /* Start of % directive. */ |
244 |
unsigned direc_length; /* Length of % directive. */ |
unsigned direc_length; /* Length of % directive. */ |
293 |
} |
} |
294 |
} |
} |
295 |
} |
} |
296 |
/* Remove size modifiers - "%Ld" would try to printf |
|
297 |
* long long, we pass long, and it spews garbage */ |
/* Remove "lLhz" size modifiers, repeatedly. |
298 |
if ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { |
* bash does not like "%lld", but coreutils |
299 |
|
* happily takes even "%Llllhhzhhzd"! |
300 |
|
* We are permissive like coreutils */ |
301 |
|
while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { |
302 |
overlapping_strcpy(f, f + 1); |
overlapping_strcpy(f, f + 1); |
303 |
} |
} |
304 |
//FIXME: actually, the same happens with bare "%d": |
/* Add "ll" if integer modifier, then print */ |
305 |
//it printfs an int, but we pass long! |
{ |
306 |
//What saves us is that on most arches stack slot |
static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; |
307 |
//is pointer-sized -> long-sized -> ints are promoted to longs |
char *p = strchr(format_chars, *f); |
308 |
// for variadic functions -> printf("%d", int_v) is in reality |
/* needed - try "printf %" without it */ |
309 |
// indistinqushable from printf("%d", long_v) -> |
if (p == NULL) { |
310 |
// since printf("%d", int_v) works, printf("%d", long_v) has to work. |
bb_error_msg("%s: invalid format", direc_start); |
311 |
//But "clean" solution would be to add "l" to d,i,o,x,X. |
/* causes main() to exit with error */ |
312 |
//Probably makes sense to go all the way to "ll" then. |
return saved_argv - 1; |
313 |
//Coreutils support long long-sized arguments. |
} |
314 |
|
++direc_length; |
315 |
/* needed - try "printf %" without it */ |
if (p - format_chars <= 5) { |
316 |
if (!strchr("diouxXfeEgGcs", *f)) { |
/* it is one of "diouxX" */ |
317 |
bb_error_msg("%s: invalid format", direc_start); |
p = xmalloc(direc_length + 3); |
318 |
/* causes main() to exit with error */ |
memcpy(p, direc_start, direc_length); |
319 |
return saved_argv - 1; |
p[direc_length + 1] = p[direc_length - 1]; |
320 |
} |
p[direc_length - 1] = 'l'; |
321 |
++direc_length; |
p[direc_length] = 'l'; |
322 |
if (*argv) { |
//bb_error_msg("<%s>", p); |
323 |
print_direc(direc_start, direc_length, field_width, |
direc_length += 2; |
324 |
precision, *argv); |
direc_start = p; |
325 |
++argv; |
} else { |
326 |
} else { |
p = NULL; |
327 |
print_direc(direc_start, direc_length, field_width, |
} |
328 |
precision, ""); |
if (*argv) { |
329 |
|
print_direc(direc_start, direc_length, field_width, |
330 |
|
precision, *argv++); |
331 |
|
} else { |
332 |
|
print_direc(direc_start, direc_length, field_width, |
333 |
|
precision, ""); |
334 |
|
} |
335 |
|
*conv_err |= errno; |
336 |
|
free(p); |
337 |
} |
} |
|
/* if (errno) return saved_argv - 1; */ |
|
338 |
break; |
break; |
339 |
case '\\': |
case '\\': |
340 |
if (*++f == 'c') { |
if (*++f == 'c') { |
353 |
|
|
354 |
int printf_main(int argc UNUSED_PARAM, char **argv) |
int printf_main(int argc UNUSED_PARAM, char **argv) |
355 |
{ |
{ |
356 |
|
int conv_err; |
357 |
char *format; |
char *format; |
358 |
char **argv2; |
char **argv2; |
359 |
|
|
390 |
format = argv[1]; |
format = argv[1]; |
391 |
argv2 = argv + 2; |
argv2 = argv + 2; |
392 |
|
|
393 |
|
conv_err = 0; |
394 |
do { |
do { |
395 |
argv = argv2; |
argv = argv2; |
396 |
argv2 = print_formatted(format, argv); |
argv2 = print_formatted(format, argv, &conv_err); |
397 |
} while (argv2 > argv && *argv2); |
} while (argv2 > argv && *argv2); |
398 |
|
|
399 |
/* coreutils compat (bash doesn't do this): |
/* coreutils compat (bash doesn't do this): |
401 |
fprintf(stderr, "excess args ignored"); |
fprintf(stderr, "excess args ignored"); |
402 |
*/ |
*/ |
403 |
|
|
404 |
return (argv2 < argv); /* if true, print_formatted errored out */ |
return (argv2 < argv) /* if true, print_formatted errored out */ |
405 |
|
|| conv_err; /* print_formatted saw invalid number */ |
406 |
} |
} |