Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/coreutils/printf.c

    r1765 r2725  
    55   Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
    66
    7    Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
     7   Licensed under GPLv2 or later, see file LICENSE in this source tree.
    88*/
    99
     
    3434   to convert all of the given arguments.
    3535
    36    David MacKenzie <djm@gnu.ai.mit.edu> */
    37 
     36   David MacKenzie <djm@gnu.ai.mit.edu>
     37*/
    3838
    3939//   19990508 Busy Boxed! Dave Cinege
     
    4141#include "libbb.h"
    4242
    43 typedef void (*converter)(const char *arg, void *result);
    44 
    45 static void multiconvert(const char *arg, void *result, converter convert)
    46 {
    47     char s[sizeof(int)*3 + 2];
    48 
     43/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it.
     44 * They report it:
     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.
     57 */
     58
     59typedef void FAST_FUNC (*converter)(const char *arg, void *result);
     60
     61static int multiconvert(const char *arg, void *result, converter convert)
     62{
    4963    if (*arg == '"' || *arg == '\'') {
    50         sprintf(s, "%d", (unsigned char)arg[1]);
    51         arg = s;
    52     }
     64        arg = utoa((unsigned char)arg[1]);
     65    }
     66    errno = 0;
    5367    convert(arg, result);
    54     /* if there was conversion error, print unconverted string */
    55     if (errno)
    56         fputs(arg, stderr);
    57 }
    58 
    59 static void conv_strtoul(const char *arg, void *result)
    60 {
    61     *(unsigned long*)result = bb_strtoul(arg, NULL, 0);
    62 }
    63 static void conv_strtol(const char *arg, void *result)
    64 {
    65     *(long*)result = bb_strtol(arg, NULL, 0);
    66 }
    67 static void conv_strtod(const char *arg, void *result)
     68    if (errno) {
     69        bb_error_msg("invalid number '%s'", arg);
     70        return 1;
     71    }
     72    return 0;
     73}
     74
     75static void FAST_FUNC conv_strtoull(const char *arg, void *result)
     76{
     77    *(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}
     87static void FAST_FUNC conv_strtoll(const char *arg, void *result)
     88{
     89    *(long long*)result = bb_strtoll(arg, NULL, 0);
     90}
     91static void FAST_FUNC conv_strtod(const char *arg, void *result)
    6892{
    6993    char *end;
    70     /* Well, this one allows leading whitespace... so what */
    71     /* What I like much less is that "-" is accepted too! :( */
     94    /* Well, this one allows leading whitespace... so what? */
     95    /* What I like much less is that "-" accepted too! :( */
    7296    *(double*)result = strtod(arg, &end);
    73     if (end[0]) errno = ERANGE;
    74 }
    75 
    76 static unsigned long my_xstrtoul(const char *arg)
    77 {
    78     unsigned long result;
    79     multiconvert(arg, &result, conv_strtoul);
     97    if (end[0]) {
     98        errno = ERANGE;
     99        *(double*)result = 0;
     100    }
     101}
     102
     103/* Callers should check errno to detect errors */
     104static unsigned long long my_xstrtoull(const char *arg)
     105{
     106    unsigned long long result;
     107    if (multiconvert(arg, &result, conv_strtoull))
     108        result = 0;
    80109    return result;
    81110}
    82 
    83 static long my_xstrtol(const char *arg)
    84 {
    85     long result;
    86     multiconvert(arg, &result, conv_strtol);
     111static long long my_xstrtoll(const char *arg)
     112{
     113    long long result;
     114    if (multiconvert(arg, &result, conv_strtoll))
     115        result = 0;
    87116    return result;
    88117}
    89 
    90118static double my_xstrtod(const char *arg)
    91119{
     
    95123}
    96124
    97 static void print_esc_string(char *str)
    98 {
    99     for (; *str; str++) {
    100         if (*str == '\\') {
    101             str++;
    102             putchar(bb_process_escape_sequence((const char **)&str));
    103         } else {
    104             putchar(*str);
    105         }
    106 
    107     }
    108 }
    109 
    110 static void print_direc(char *start, size_t length, int field_width, int precision,
     125static void print_esc_string(const char *str)
     126{
     127    char c;
     128    while ((c = *str) != '\0') {
     129        str++;
     130        if (c == '\\')
     131            c = bb_process_escape_sequence(&str);
     132        putchar(c);
     133    }
     134}
     135
     136static void print_direc(char *format, unsigned fmt_length,
     137        int field_width, int precision,
    111138        const char *argument)
    112139{
    113     char *p;        /* Null-terminated copy of % directive. */
    114 
    115     p = xmalloc((unsigned) (length + 1));
    116     strncpy(p, start, length);
    117     p[length] = 0;
    118 
    119     switch (p[length - 1]) {
     140    long long llv;
     141    double dv;
     142    char saved;
     143    char *have_prec, *have_width;
     144
     145    saved = format[fmt_length];
     146    format[fmt_length] = '\0';
     147
     148    have_prec = strstr(format, ".*");
     149    have_width = strchr(format, '*');
     150    if (have_width - 1 == have_prec)
     151        have_width = NULL;
     152
     153    errno = 0;
     154
     155    switch (format[fmt_length - 1]) {
     156    case 'c':
     157        printf(format, *argument);
     158        break;
    120159    case 'd':
    121160    case 'i':
    122         if (field_width < 0) {
    123             if (precision < 0)
    124                 printf(p, my_xstrtol(argument));
     161        llv = my_xstrtoll(argument);
     162 print_long:
     163        if (!have_width) {
     164            if (!have_prec)
     165                printf(format, llv);
    125166            else
    126                 printf(p, precision, my_xstrtol(argument));
     167                printf(format, precision, llv);
    127168        } else {
    128             if (precision < 0)
    129                 printf(p, field_width, my_xstrtol(argument));
     169            if (!have_prec)
     170                printf(format, field_width, llv);
    130171            else
    131                 printf(p, field_width, precision, my_xstrtol(argument));
     172                printf(format, field_width, precision, llv);
    132173        }
    133174        break;
     
    136177    case 'x':
    137178    case 'X':
    138         if (field_width < 0) {
    139             if (precision < 0)
    140                 printf(p, my_xstrtoul(argument));
    141             else
    142                 printf(p, precision, my_xstrtoul(argument));
     179        llv = my_xstrtoull(argument);
     180        /* cheat: unsigned long and long have same width, so... */
     181        goto print_long;
     182    case 's':
     183        /* Are char* and long long the same? */
     184        if (sizeof(argument) == sizeof(llv)) {
     185            llv = (long long)(ptrdiff_t)argument;
     186            goto print_long;
    143187        } else {
    144             if (precision < 0)
    145                 printf(p, field_width, my_xstrtoul(argument));
    146             else
    147                 printf(p, field_width, precision, my_xstrtoul(argument));
     188            /* Hope compiler will optimize it out by moving call
     189             * instruction after the ifs... */
     190            if (!have_width) {
     191                if (!have_prec)
     192                    printf(format, argument, /*unused:*/ argument, argument);
     193                else
     194                    printf(format, precision, argument, /*unused:*/ argument);
     195            } else {
     196                if (!have_prec)
     197                    printf(format, field_width, argument, /*unused:*/ argument);
     198                else
     199                    printf(format, field_width, precision, argument);
     200            }
     201            break;
    148202        }
    149         break;
    150203    case 'f':
    151204    case 'e':
     
    153206    case 'g':
    154207    case 'G':
    155         if (field_width < 0) {
    156             if (precision < 0)
    157                 printf(p, my_xstrtod(argument));
     208        dv = my_xstrtod(argument);
     209        if (!have_width) {
     210            if (!have_prec)
     211                printf(format, dv);
    158212            else
    159                 printf(p, precision, my_xstrtod(argument));
     213                printf(format, precision, dv);
    160214        } else {
    161             if (precision < 0)
    162                 printf(p, field_width, my_xstrtod(argument));
     215            if (!have_prec)
     216                printf(format, field_width, dv);
    163217            else
    164                 printf(p, field_width, precision, my_xstrtod(argument));
     218                printf(format, field_width, precision, dv);
    165219        }
    166220        break;
    167     case 'c':
    168         printf(p, *argument);
    169         break;
    170     case 's':
    171         if (field_width < 0) {
    172             if (precision < 0)
    173                 printf(p, argument);
    174             else
    175                 printf(p, precision, argument);
    176         } else {
    177             if (precision < 0)
    178                 printf(p, field_width, argument);
    179             else
    180                 printf(p, field_width, precision, argument);
    181         }
    182         break;
    183     }
    184 
    185     free(p);
    186 }
    187 
    188 /* Print the text in FORMAT, using ARGV (with ARGC elements) for
    189    arguments to any '%' directives.
    190    Return the number of elements of ARGV used.  */
    191 
    192 static int print_formatted(char *format, int argc, char **argv)
    193 {
    194     int save_argc = argc;   /* Preserve original value.  */
    195     char *f;                /* Pointer into 'format'.  */
     221    } /* switch */
     222
     223    format[fmt_length] = saved;
     224}
     225
     226/* Handle params for "%*.*f". Negative numbers are ok (compat). */
     227static int get_width_prec(const char *str)
     228{
     229    int v = bb_strtoi(str, NULL, 10);
     230    if (errno) {
     231        bb_error_msg("invalid number '%s'", str);
     232        v = 0;
     233    }
     234    return v;
     235}
     236
     237/* Print the text in FORMAT, using ARGV for arguments to any '%' directives.
     238   Return advanced ARGV.  */
     239static char **print_formatted(char *f, char **argv, int *conv_err)
     240{
    196241    char *direc_start;      /* Start of % directive.  */
    197     size_t direc_length;    /* Length of % directive.  */
    198     int field_width;        /* Arg to first '*', or -1 if none.  */
    199     int precision;          /* Arg to second '*', or -1 if none.  */
    200 
    201     for (f = format; *f; ++f) {
     242    unsigned direc_length;  /* Length of % directive.  */
     243    int field_width;        /* Arg to first '*' */
     244    int precision;          /* Arg to second '*' */
     245    char **saved_argv = argv;
     246
     247    for (; *f; ++f) {
    202248        switch (*f) {
    203249        case '%':
    204250            direc_start = f++;
    205251            direc_length = 1;
    206             field_width = precision = -1;
     252            field_width = precision = 0;
    207253            if (*f == '%') {
    208                 putchar('%');
     254                bb_putchar('%');
    209255                break;
    210256            }
    211257            if (*f == 'b') {
    212                 if (argc > 0) {
     258                if (*argv) {
    213259                    print_esc_string(*argv);
    214260                    ++argv;
    215                     --argc;
    216261                }
    217262                break;
     
    224269                ++f;
    225270                ++direc_length;
    226                 if (argc > 0) {
    227                     field_width = my_xstrtoul(*argv);
    228                     ++argv;
    229                     --argc;
    230                 } else
    231                     field_width = 0;
     271                if (*argv)
     272                    field_width = get_width_prec(*argv++);
    232273            } else {
    233274                while (isdigit(*f)) {
     
    242283                    ++f;
    243284                    ++direc_length;
    244                     if (argc > 0) {
    245                         precision = my_xstrtoul(*argv);
    246                         ++argv;
    247                         --argc;
    248                     } else
    249                         precision = 0;
    250                 } else
     285                    if (*argv)
     286                        precision = get_width_prec(*argv++);
     287                } else {
    251288                    while (isdigit(*f)) {
    252289                        ++f;
    253290                        ++direc_length;
    254291                    }
    255             }
    256             if (*f == 'l' || *f == 'L' || *f == 'h') {
    257                 ++f;
     292                }
     293            }
     294
     295            /* Remove "lLhz" size modifiers, repeatedly.
     296             * bash does not like "%lld", but coreutils
     297             * happily takes even "%Llllhhzhhzd"!
     298             * We are permissive like coreutils */
     299            while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') {
     300                overlapping_strcpy(f, f + 1);
     301            }
     302            /* Add "ll" if integer modifier, then print */
     303            {
     304                static const char format_chars[] ALIGN1 = "diouxXfeEgGcs";
     305                char *p = strchr(format_chars, *f);
     306                /* needed - try "printf %" without it */
     307                if (p == NULL) {
     308                    bb_error_msg("%s: invalid format", direc_start);
     309                    /* causes main() to exit with error */
     310                    return saved_argv - 1;
     311                }
    258312                ++direc_length;
    259             }
    260             /*
    261             if (!strchr ("diouxXfeEgGcs", *f))
    262             fprintf(stderr, "%%%c: invalid directive", *f);
    263             */
    264             ++direc_length;
    265             if (argc > 0) {
    266                 print_direc(direc_start, direc_length, field_width,
    267                             precision, *argv);
    268                 ++argv;
    269                 --argc;
    270             } else
    271                 print_direc(direc_start, direc_length, field_width,
    272                             precision, "");
     313                if (p - format_chars <= 5) {
     314                    /* it is one of "diouxX" */
     315                    p = xmalloc(direc_length + 3);
     316                    memcpy(p, direc_start, direc_length);
     317                    p[direc_length + 1] = p[direc_length - 1];
     318                    p[direc_length - 1] = 'l';
     319                    p[direc_length] = 'l';
     320                    //bb_error_msg("<%s>", p);
     321                    direc_length += 2;
     322                    direc_start = p;
     323                } else {
     324                    p = NULL;
     325                }
     326                if (*argv) {
     327                    print_direc(direc_start, direc_length, field_width,
     328                                precision, *argv++);
     329                } else {
     330                    print_direc(direc_start, direc_length, field_width,
     331                                precision, "");
     332                }
     333                *conv_err |= errno;
     334                free(p);
     335            }
    273336            break;
    274337        case '\\':
    275             if (*++f == 'c')
    276                 exit(0);
    277             putchar(bb_process_escape_sequence((const char **)&f));
     338            if (*++f == 'c') {
     339                return saved_argv; /* causes main() to exit */
     340            }
     341            bb_putchar(bb_process_escape_sequence((const char **)&f));
    278342            f--;
    279343            break;
     
    283347    }
    284348
    285     return save_argc - argc;
    286 }
    287 
    288 int printf_main(int argc, char **argv);
    289 int printf_main(int argc, char **argv)
    290 {
     349    return argv;
     350}
     351
     352int printf_main(int argc UNUSED_PARAM, char **argv)
     353{
     354    int conv_err;
    291355    char *format;
    292     int args_used;
    293 
    294     if (argc <= 1 || argv[1][0] == '-') {
     356    char **argv2;
     357
     358    /* We must check that stdout is not closed.
     359     * The reason for this is highly non-obvious.
     360     * printf_main is used from shell.
     361     * Shell must correctly handle 'printf "%s" foo'
     362     * if stdout is closed. With stdio, output gets shoveled into
     363     * stdout buffer, and even fflush cannot clear it out. It seems that
     364     * even if libc receives EBADF on write attempts, it feels determined
     365     * to output data no matter what. So it will try later,
     366     * and possibly will clobber future output. Not good. */
     367// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
     368    if (fcntl(1, F_GETFL) == -1)
     369        return 1; /* match coreutils 6.10 (sans error msg to stderr) */
     370    //if (dup2(1, 1) != 1) - old way
     371    //  return 1;
     372
     373    /* bash builtin errors out on "printf '-%s-\n' foo",
     374     * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo".
     375     * We will mimic coreutils. */
     376    if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2])
     377        argv++;
     378    if (!argv[1]) {
     379        if (ENABLE_ASH_BUILTIN_PRINTF
     380         && applet_name[0] != 'p'
     381        ) {
     382            bb_error_msg("usage: printf FORMAT [ARGUMENT...]");
     383            return 2; /* bash compat */
     384        }
    295385        bb_show_usage();
    296386    }
    297387
    298388    format = argv[1];
    299     argc -= 2;
    300     argv += 2;
    301 
     389    argv2 = argv + 2;
     390
     391    conv_err = 0;
    302392    do {
    303         args_used = print_formatted(format, argc, argv);
    304         argc -= args_used;
    305         argv += args_used;
    306     } while (args_used > 0 && argc > 0);
    307 
    308 /*  if (argc > 0)
     393        argv = argv2;
     394        argv2 = print_formatted(format, argv, &conv_err);
     395    } while (argv2 > argv && *argv2);
     396
     397    /* coreutils compat (bash doesn't do this):
     398    if (*argv)
    309399        fprintf(stderr, "excess args ignored");
    310 */
    311 
    312     return EXIT_SUCCESS;
    313 }
     400    */
     401
     402    return (argv2 < argv) /* if true, print_formatted errored out */
     403        || conv_err; /* print_formatted saw invalid number */
     404}
Note: See TracChangeset for help on using the changeset viewer.