[PATCH] printf: fix exit code on conversion failure

Colin Watson cjwatson at ubuntu.com
Thu Jun 18 15:52:57 UTC 2009


This resolves a FIXME left over from commit
a48656b441224a53d2bb3face920ba5487eaae09. SUSv3 says:

  If an argument operand cannot be completely converted into an internal
  value appropriate to the corresponding conversion specification, a
  diagnostic message shall be written to standard error and the utility
  shall not exit with a zero exit status, but shall continue processing
  any remaining operands and shall write the value accumulated at the
  time the error was detected to standard output.

... so we make sure to accumulate error statuses rather than just
returning straight away.

Signed-off-by: Colin Watson <cjwatson at ubuntu.com>
---
 coreutils/printf.c     |   23 +++++++++++++----------
 testsuite/printf.tests |    3 +--
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/coreutils/printf.c b/coreutils/printf.c
index 2beea71..1b2b2ec 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -55,8 +55,6 @@
  *
  * 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.
  */
 
 typedef void FAST_FUNC (*converter)(const char *arg, void *result);
@@ -69,7 +67,9 @@ static int multiconvert(const char *arg, void *result, converter convert)
 	errno = 0;
 	convert(arg, result);
 	if (errno) {
+		int save_errno = errno;
 		bb_error_msg("%s: invalid number", arg);
+		errno = save_errno;
 		return 1;
 	}
 	return 0;
@@ -140,7 +140,7 @@ static void print_esc_string(char *str)
 
 static void print_direc(char *format, unsigned fmt_length,
 		int field_width, int precision,
-		const char *argument)
+		const char *argument, int *status)
 {
 	long long llv;
 	double dv;
@@ -163,7 +163,8 @@ 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 (errno)
+			*status = EXIT_FAILURE;
 		if (!have_width) {
 			if (!have_prec)
 				printf(format, llv);
@@ -210,7 +211,8 @@ static void print_direc(char *format, unsigned fmt_length,
 	case 'g':
 	case 'G':
 		dv = my_xstrtod(argument);
-		/* if (errno) return; */
+		if (errno)
+			*status = EXIT_FAILURE;
 		if (!have_width) {
 			if (!have_prec)
 				printf(format, dv);
@@ -241,7 +243,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 *status)
 {
 	char *direc_start;      /* Start of % directive.  */
 	unsigned direc_length;  /* Length of % directive.  */
@@ -330,11 +332,11 @@ static char **print_formatted(char *f, char **argv)
 				}
 				if (*argv) {
 					print_direc(direc_start, direc_length, field_width,
-								precision, *argv);
+								precision, *argv, status);
 					++argv;
 				} else {
 					print_direc(direc_start, direc_length, field_width,
-								precision, "");
+								precision, "", status);
 				}
 				free(p);
 			}
@@ -358,6 +360,7 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
 {
 	char *format;
 	char **argv2;
+	int status = EXIT_SUCCESS;
 
 	/* We must check that stdout is not closed.
 	 * The reason for this is highly non-obvious.
@@ -394,7 +397,7 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
 
 	do {
 		argv = argv2;
-		argv2 = print_formatted(format, argv);
+		argv2 = print_formatted(format, argv, &status);
 	} while (argv2 > argv && *argv2);
 
 	/* coreutils compat (bash doesn't do this):
@@ -402,5 +405,5 @@ 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 || status); /* if true, print_formatted errored out */
 }
diff --git a/testsuite/printf.tests b/testsuite/printf.tests
index 558ecb1..0a63f0f 100755
--- a/testsuite/printf.tests
+++ b/testsuite/printf.tests
@@ -87,13 +87,12 @@ testing "printf understands %Ld" \
 #	"1\n""printf: -2: invalid number\n""0\n""3\n""0\n" \
 #	"" ""
 
-# Actually, we are wrong here: exit code should be 1
 testing "printf handles %d bad_input" \
 	"${bb}printf '%d\n' 1 - 2 bad 3 123bad 4 2>&1; echo \$?" \
 "1\n""printf: -: invalid number\n""0\n"\
 "2\n""printf: bad: invalid number\n""0\n"\
 "3\n""printf: 123bad: invalid number\n""0\n"\
-"4\n""0\n" \
+"4\n""1\n" \
 	"" ""
 
 testing "printf aborts on bare %" \
-- 
1.6.3.1


More information about the busybox mailing list