dd: speed measurement. optional.
[oweals/busybox.git] / coreutils / printf.c
index 0b004eaeb192f4b49df8d026924df727cbc64554..eb53fa490690f4264c76c50045b606bca662bb74 100644 (file)
  *  (but negative numbers are not "bad").
  * Both accept negative numbers for %u specifier.
  *
- * We try to be compatible. We are not compatible here:
- * - 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.
+ * We try to be compatible.
  */
 
 typedef void FAST_FUNC (*converter)(const char *arg, void *result);
@@ -78,6 +75,14 @@ static int multiconvert(const char *arg, void *result, converter convert)
 static void FAST_FUNC conv_strtoull(const char *arg, void *result)
 {
        *(unsigned long long*)result = bb_strtoull(arg, NULL, 0);
+       /* both coreutils 6.10 and bash 3.2:
+        * $ printf '%x\n' -2
+        * fffffffffffffffe
+        * Mimic that:
+        */
+       if (errno) {
+               *(unsigned long long*)result = bb_strtoll(arg, NULL, 0);
+       }
 }
 static void FAST_FUNC conv_strtoll(const char *arg, void *result)
 {
@@ -147,6 +152,8 @@ static void print_direc(char *format, unsigned fmt_length,
        if (have_width - 1 == have_prec)
                have_width = NULL;
 
+       errno = 0;
+
        switch (format[fmt_length - 1]) {
        case 'c':
                printf(format, *argument);
@@ -155,7 +162,6 @@ static void print_direc(char *format, unsigned fmt_length,
        case 'i':
                llv = my_xstrtoll(argument);
  print_long:
-               /* if (errno) return; - see comment at the top */
                if (!have_width) {
                        if (!have_prec)
                                printf(format, llv);
@@ -202,7 +208,6 @@ static void print_direc(char *format, unsigned fmt_length,
        case 'g':
        case 'G':
                dv = my_xstrtod(argument);
-               /* if (errno) return; */
                if (!have_width) {
                        if (!have_prec)
                                printf(format, dv);
@@ -233,7 +238,7 @@ static int get_width_prec(const char *str)
 
 /* Print the text in FORMAT, using ARGV for arguments to any '%' directives.
    Return advanced ARGV.  */
-static char **print_formatted(char *f, char **argv)
+static char **print_formatted(char *f, char **argv, int *conv_err)
 {
        char *direc_start;      /* Start of % directive.  */
        unsigned direc_length;  /* Length of % directive.  */
@@ -291,8 +296,8 @@ static char **print_formatted(char *f, char **argv)
 
                        /* Remove "lLhz" size modifiers, repeatedly.
                         * bash does not like "%lld", but coreutils
-                        * would happily take even "%Llllhhzhhzd"!
-                        * We will be permissive like coreutils */
+                        * happily takes even "%Llllhhzhhzd"!
+                        * We are permissive like coreutils */
                        while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') {
                                overlapping_strcpy(f, f + 1);
                        }
@@ -322,12 +327,12 @@ static char **print_formatted(char *f, char **argv)
                                }
                                if (*argv) {
                                        print_direc(direc_start, direc_length, field_width,
-                                                               precision, *argv);
-                                       ++argv;
+                                                               precision, *argv++);
                                } else {
                                        print_direc(direc_start, direc_length, field_width,
                                                                precision, "");
                                }
+                               *conv_err |= errno;
                                free(p);
                        }
                        break;
@@ -348,6 +353,7 @@ static char **print_formatted(char *f, char **argv)
 
 int printf_main(int argc UNUSED_PARAM, char **argv)
 {
+       int conv_err;
        char *format;
        char **argv2;
 
@@ -384,9 +390,10 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
        format = argv[1];
        argv2 = argv + 2;
 
+       conv_err = 0;
        do {
                argv = argv2;
-               argv2 = print_formatted(format, argv);
+               argv2 = print_formatted(format, argv, &conv_err);
        } while (argv2 > argv && *argv2);
 
        /* coreutils compat (bash doesn't do this):
@@ -394,5 +401,6 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
                fprintf(stderr, "excess args ignored");
        */
 
-       return (argv2 < argv); /* if true, print_formatted errored out */
+       return (argv2 < argv) /* if true, print_formatted errored out */
+               || conv_err; /* print_formatted saw invalid number */
 }