1 /* vi: set sw=4 ts=4: */
3 * printf - format and print data
5 * Copyright 1999 Dave Cinege
6 * Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 /* Usage: printf format [argument...]
12 * A front end to the printf function that lets it be used from the shell.
20 * \c = produce no further output
23 * \r = carriage return
26 * \0ooo = octal number (ooo is 0 to 3 digits)
27 * \xhhh = hexadecimal number (hhh is 1 to 3 digits)
29 * Additional directive:
31 * %b = print an argument string, interpreting backslash escapes
33 * The 'format' argument is re-used as many times as necessary
34 * to convert all of the given arguments.
36 * David MacKenzie <djm@gnu.ai.mit.edu>
38 /* 19990508 Busy Boxed! Dave Cinege */
40 //config:config PRINTF
41 //config: bool "printf (4.1 kb)"
44 //config: printf is used to format and print specified strings.
45 //config: It's similar to 'echo' except it has more options.
47 //applet:IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
49 //kbuild:lib-$(CONFIG_PRINTF) += printf.o
50 //kbuild:lib-$(CONFIG_ASH_PRINTF) += printf.o
51 //kbuild:lib-$(CONFIG_HUSH_PRINTF) += printf.o
53 //usage:#define printf_trivial_usage
54 //usage: "FORMAT [ARG]..."
55 //usage:#define printf_full_usage "\n\n"
56 //usage: "Format and print ARG(s) according to FORMAT (a-la C printf)"
58 //usage:#define printf_example_usage
59 //usage: "$ printf \"Val=%d\\n\" 5\n"
64 /* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it.
66 * bash: printf: XXX: invalid number
67 * printf: XXX: expected a numeric value
68 * bash: printf: 123XXX: invalid number
69 * printf: 123XXX: value not completely converted
70 * but then they use 0 (or partially converted numeric prefix) as a value
71 * and continue. They exit with 1 in this case.
72 * Both accept insane field width/precision (e.g. %9999999999.9999999999d).
73 * Both print error message and assume 0 if %*.*f width/precision is "bad"
74 * (but negative numbers are not "bad").
75 * Both accept negative numbers for %u specifier.
77 * We try to be compatible.
80 typedef void FAST_FUNC (*converter
)(const char *arg
, void *result
);
82 static int multiconvert(const char *arg
, void *result
, converter convert
)
84 if (*arg
== '"' || *arg
== '\'') {
85 arg
= utoa((unsigned char)arg
[1]);
90 bb_error_msg("invalid number '%s'", arg
);
96 static void FAST_FUNC
conv_strtoull(const char *arg
, void *result
)
98 /* Allow leading '+' - bb_strtoull() by itself does not allow it,
99 * and probably shouldn't (other callers might require purely numeric
100 * inputs to be allowed.
104 *(unsigned long long*)result
= bb_strtoull(arg
, NULL
, 0);
105 /* both coreutils 6.10 and bash 3.2:
111 *(unsigned long long*)result
= bb_strtoll(arg
, NULL
, 0);
114 static void FAST_FUNC
conv_strtoll(const char *arg
, void *result
)
118 *(long long*)result
= bb_strtoll(arg
, NULL
, 0);
120 static void FAST_FUNC
conv_strtod(const char *arg
, void *result
)
123 /* Well, this one allows leading whitespace... so what? */
124 /* What I like much less is that "-" accepted too! :( */
125 //TODO: needs setlocale(LC_NUMERIC, "C")?
126 *(double*)result
= strtod(arg
, &end
);
129 *(double*)result
= 0;
133 /* Callers should check errno to detect errors */
134 static unsigned long long my_xstrtoull(const char *arg
)
136 unsigned long long result
;
137 if (multiconvert(arg
, &result
, conv_strtoull
))
141 static long long my_xstrtoll(const char *arg
)
144 if (multiconvert(arg
, &result
, conv_strtoll
))
148 static double my_xstrtod(const char *arg
)
151 multiconvert(arg
, &result
, conv_strtod
);
155 /* Handles %b; return 1 if output is to be short-circuited by \c */
156 static int print_esc_string(const char *str
)
159 while ((c
= *str
) != '\0') {
162 /* %b also accepts 4-digit octals of the form \0### */
164 if ((unsigned char)(str
[1] - '0') < 8) {
165 /* 2nd char is 0..7: skip leading '0' */
169 else if (*str
== 'c') {
173 /* optimization: don't force arg to be on-stack,
174 * use another variable for that. */
176 c
= bb_process_escape_sequence(&z
);
186 static void print_direc(char *format
, unsigned fmt_length
,
187 int field_width
, int precision
,
188 const char *argument
)
193 char *have_prec
, *have_width
;
195 saved
= format
[fmt_length
];
196 format
[fmt_length
] = '\0';
198 have_prec
= strstr(format
, ".*");
199 have_width
= strchr(format
, '*');
200 if (have_width
- 1 == have_prec
)
203 /* multiconvert sets errno = 0, but %s needs it cleared */
206 switch (format
[fmt_length
- 1]) {
208 printf(format
, *argument
);
212 llv
= my_xstrtoll(skip_whitespace(argument
));
218 printf(format
, precision
, llv
);
221 printf(format
, field_width
, llv
);
223 printf(format
, field_width
, precision
, llv
);
230 llv
= my_xstrtoull(skip_whitespace(argument
));
231 /* cheat: unsigned long and long have same width, so... */
234 /* Are char* and long long the same? */
235 if (sizeof(argument
) == sizeof(llv
)) {
236 llv
= (long long)(ptrdiff_t)argument
;
239 /* Hope compiler will optimize it out by moving call
240 * instruction after the ifs... */
243 printf(format
, argument
, /*unused:*/ argument
, argument
);
245 printf(format
, precision
, argument
, /*unused:*/ argument
);
248 printf(format
, field_width
, argument
, /*unused:*/ argument
);
250 printf(format
, field_width
, precision
, argument
);
259 dv
= my_xstrtod(argument
);
264 printf(format
, precision
, dv
);
267 printf(format
, field_width
, dv
);
269 printf(format
, field_width
, precision
, dv
);
274 format
[fmt_length
] = saved
;
277 /* Handle params for "%*.*f". Negative numbers are ok (compat). */
278 static int get_width_prec(const char *str
)
280 int v
= bb_strtoi(str
, NULL
, 10);
282 bb_error_msg("invalid number '%s'", str
);
288 /* Print the text in FORMAT, using ARGV for arguments to any '%' directives.
289 Return advanced ARGV. */
290 static char **print_formatted(char *f
, char **argv
, int *conv_err
)
292 char *direc_start
; /* Start of % directive. */
293 unsigned direc_length
; /* Length of % directive. */
294 int field_width
; /* Arg to first '*' */
295 int precision
; /* Arg to second '*' */
296 char **saved_argv
= argv
;
303 field_width
= precision
= 0;
310 if (print_esc_string(*argv
))
311 return saved_argv
; /* causes main() to exit */
316 while (*f
&& strchr("-+ #0", *f
)) {
324 field_width
= get_width_prec(*argv
++);
326 while (isdigit(*f
)) {
338 precision
= get_width_prec(*argv
++);
340 while (isdigit(*f
)) {
347 /* Remove "lLhz" size modifiers, repeatedly.
348 * bash does not like "%lld", but coreutils
349 * happily takes even "%Llllhhzhhzd"!
350 * We are permissive like coreutils */
351 while ((*f
| 0x20) == 'l' || *f
== 'h' || *f
== 'z') {
352 overlapping_strcpy(f
, f
+ 1);
354 /* Add "ll" if integer modifier, then print */
356 static const char format_chars
[] ALIGN1
= "diouxXfeEgGcs";
357 char *p
= strchr(format_chars
, *f
);
358 /* needed - try "printf %" without it */
359 if (p
== NULL
|| *f
== '\0') {
360 bb_error_msg("%s: invalid format", direc_start
);
361 /* causes main() to exit with error */
362 return saved_argv
- 1;
365 if (p
- format_chars
<= 5) {
366 /* it is one of "diouxX" */
367 p
= xmalloc(direc_length
+ 3);
368 memcpy(p
, direc_start
, direc_length
);
369 p
[direc_length
+ 1] = p
[direc_length
- 1];
370 p
[direc_length
- 1] = 'l';
371 p
[direc_length
] = 'l';
372 //bb_error_msg("<%s>", p);
379 print_direc(direc_start
, direc_length
, field_width
,
382 print_direc(direc_start
, direc_length
, field_width
,
391 return saved_argv
; /* causes main() to exit */
393 bb_putchar(bb_process_escape_sequence((const char **)&f
));
404 int printf_main(int argc UNUSED_PARAM
, char **argv
)
410 /* We must check that stdout is not closed.
411 * The reason for this is highly non-obvious.
412 * printf_main is used from shell.
413 * Shell must correctly handle 'printf "%s" foo'
414 * if stdout is closed. With stdio, output gets shoveled into
415 * stdout buffer, and even fflush cannot clear it out. It seems that
416 * even if libc receives EBADF on write attempts, it feels determined
417 * to output data no matter what. So it will try later,
418 * and possibly will clobber future output. Not good. */
419 // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
420 if (fcntl(1, F_GETFL
) == -1)
421 return 1; /* match coreutils 6.10 (sans error msg to stderr) */
422 //if (dup2(1, 1) != 1) - old way
425 /* bash builtin errors out on "printf '-%s-\n' foo",
426 * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo".
427 * We will mimic coreutils. */
428 argv
= skip_dash_dash(argv
);
431 if ((ENABLE_ASH_PRINTF
|| ENABLE_HUSH_PRINTF
)
432 && applet_name
[0] != 'p'
434 bb_simple_error_msg("usage: printf FORMAT [ARGUMENT...]");
435 return 2; /* bash compat */
446 argv2
= print_formatted(format
, argv
, &conv_err
);
447 } while (argv2
> argv
&& *argv2
);
449 /* coreutils compat (bash doesn't do this):
451 fprintf(stderr, "excess args ignored");
454 return (argv2
< argv
) /* if true, print_formatted errored out */
455 || conv_err
; /* print_formatted saw invalid number */