printf: make integer format strings print long long-sized values.

function                                             old     new   delta
printf_main                                          668     834    +166
bb_strtoll                                             -      84     +84
print_direc                                          391     431     +40
conv_strtoull                                          -      19     +19
conv_strtoll                                           -      19     +19
conv_strtoul                                          16       -     -16
conv_strtol                                           16       -     -16
------------------------------------------------------------------------------
(add/remove: 4/2 grow/shrink: 2/0 up/down: 342/-32)           Total: 296 bytes
This commit is contained in:
Denis Vlasenko 2009-01-04 02:58:58 +00:00
parent 0416e3dde1
commit cb39a7ca6d

View File

@ -75,13 +75,13 @@ static int multiconvert(const char *arg, void *result, converter convert)
return 0; return 0;
} }
static void FAST_FUNC conv_strtoul(const char *arg, void *result) static void FAST_FUNC conv_strtoull(const char *arg, void *result)
{ {
*(unsigned long*)result = bb_strtoul(arg, NULL, 0); *(unsigned long long*)result = bb_strtoull(arg, NULL, 0);
} }
static void FAST_FUNC conv_strtol(const char *arg, void *result) static void FAST_FUNC conv_strtoll(const char *arg, void *result)
{ {
*(long*)result = bb_strtol(arg, NULL, 0); *(long long*)result = bb_strtoll(arg, NULL, 0);
} }
static void FAST_FUNC conv_strtod(const char *arg, void *result) static void FAST_FUNC conv_strtod(const char *arg, void *result)
{ {
@ -96,17 +96,17 @@ static void FAST_FUNC conv_strtod(const char *arg, void *result)
} }
/* Callers should check errno to detect errors */ /* Callers should check errno to detect errors */
static unsigned long my_xstrtoul(const char *arg) static unsigned long long my_xstrtoull(const char *arg)
{ {
unsigned long result; unsigned long long result;
if (multiconvert(arg, &result, conv_strtoul)) if (multiconvert(arg, &result, conv_strtoull))
result = 0; result = 0;
return result; return result;
} }
static long my_xstrtol(const char *arg) static long long my_xstrtoll(const char *arg)
{ {
long result; long long result;
if (multiconvert(arg, &result, conv_strtol)) if (multiconvert(arg, &result, conv_strtoll))
result = 0; result = 0;
return result; return result;
} }
@ -134,7 +134,7 @@ static void print_direc(char *format, unsigned fmt_length,
int field_width, int precision, int field_width, int precision,
const char *argument) const char *argument)
{ {
long lv; long long llv;
double dv; double dv;
char saved; char saved;
char *have_prec, *have_width; char *have_prec, *have_width;
@ -153,42 +153,44 @@ static void print_direc(char *format, unsigned fmt_length,
break; break;
case 'd': case 'd':
case 'i': case 'i':
lv = my_xstrtol(argument); llv = my_xstrtoll(argument);
print_long: print_long:
/* if (errno) return; - see comment at the top */ /* if (errno) return; - see comment at the top */
if (!have_width) { if (!have_width) {
if (!have_prec) if (!have_prec)
printf(format, lv); printf(format, llv);
else else
printf(format, precision, lv); printf(format, precision, llv);
} else { } else {
if (!have_prec) if (!have_prec)
printf(format, field_width, lv); printf(format, field_width, llv);
else else
printf(format, field_width, precision, lv); printf(format, field_width, precision, llv);
} }
break; break;
case 'o': case 'o':
case 'u': case 'u':
case 'x': case 'x':
case 'X': case 'X':
lv = my_xstrtoul(argument); llv = my_xstrtoull(argument);
/* cheat: unsigned long and long have same width, so... */ /* cheat: unsigned long and long have same width, so... */
goto print_long; goto print_long;
case 's': case 's':
/* Are char* and long the same? (true for most arches) */ /* Are char* and long long the same? */
if (sizeof(argument) == sizeof(lv)) { if (sizeof(argument) == sizeof(llv)) {
lv = (long)(ptrdiff_t)argument; llv = (long long)(ptrdiff_t)argument;
goto print_long; goto print_long;
} else { /* Hope compiler will optimize it out */ } else {
/* Hope compiler will optimize it out by moving call
* instruction after the ifs... */
if (!have_width) { if (!have_width) {
if (!have_prec) if (!have_prec)
printf(format, argument); printf(format, argument, /*unused:*/ argument, argument);
else else
printf(format, precision, argument); printf(format, precision, argument, /*unused:*/ argument);
} else { } else {
if (!have_prec) if (!have_prec)
printf(format, field_width, argument); printf(format, field_width, argument, /*unused:*/ argument);
else else
printf(format, field_width, precision, argument); printf(format, field_width, precision, argument);
} }
@ -286,29 +288,38 @@ static char **print_formatted(char *f, char **argv)
} }
} }
} }
/* Remove size modifiers - "%Ld" would try to printf
* long long, we pass long, and it spews garbage */ /* Remove "lLhz" size modifiers, repeatedly.
if ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { * bash does not like "%lld", but coreutils
* would happily take even "%Llllhhzhhzd"!
* We will be permissive like coreutils */
while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') {
overlapping_strcpy(f, f + 1); overlapping_strcpy(f, f + 1);
} }
//FIXME: actually, the same happens with bare "%d": /* Add "ll" if integer modifier, then print */
//it printfs an int, but we pass long! {
//What saves us is that on most arches stack slot static const char format_chars[] ALIGN1 = "diouxXfeEgGcs";
//is pointer-sized -> long-sized -> ints are promoted to longs char *p = strchr(format_chars, *f);
// for variadic functions -> printf("%d", int_v) is in reality
// indistinqushable from printf("%d", long_v) ->
// since printf("%d", int_v) works, printf("%d", long_v) has to work.
//But "clean" solution would be to add "l" to d,i,o,x,X.
//Probably makes sense to go all the way to "ll" then.
//Coreutils support long long-sized arguments.
/* needed - try "printf %" without it */ /* needed - try "printf %" without it */
if (!strchr("diouxXfeEgGcs", *f)) { if (p == NULL) {
bb_error_msg("%s: invalid format", direc_start); bb_error_msg("%s: invalid format", direc_start);
/* causes main() to exit with error */ /* causes main() to exit with error */
return saved_argv - 1; return saved_argv - 1;
} }
++direc_length; ++direc_length;
if (p - format_chars <= 5) {
/* it is one of "diouxX" */
p = xmalloc(direc_length + 3);
memcpy(p, direc_start, direc_length);
p[direc_length + 1] = p[direc_length - 1];
p[direc_length - 1] = 'l';
p[direc_length] = 'l';
//bb_error_msg("<%s>", p);
direc_length += 2;
direc_start = p;
} else {
p = NULL;
}
if (*argv) { if (*argv) {
print_direc(direc_start, direc_length, field_width, print_direc(direc_start, direc_length, field_width,
precision, *argv); precision, *argv);
@ -317,7 +328,8 @@ static char **print_formatted(char *f, char **argv)
print_direc(direc_start, direc_length, field_width, print_direc(direc_start, direc_length, field_width,
precision, ""); precision, "");
} }
/* if (errno) return saved_argv - 1; */ free(p);
}
break; break;
case '\\': case '\\':
if (*++f == 'c') { if (*++f == 'c') {