Annotation of /trunk/mkinitrd-magellan/busybox/coreutils/printf.c
Parent Directory | Revision Log
Revision 816 -
(hide annotations)
(download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 9642 byte(s)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 9642 byte(s)
-updated to busybox-1.13.4
1 | niro | 532 | /* vi: set sw=4 ts=4: */ |
2 | /* printf - format and print data | ||
3 | |||
4 | Copyright 1999 Dave Cinege | ||
5 | Portions copyright (C) 1990-1996 Free Software Foundation, Inc. | ||
6 | |||
7 | Licensed under GPL v2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* Usage: printf format [argument...] | ||
11 | |||
12 | A front end to the printf function that lets it be used from the shell. | ||
13 | |||
14 | Backslash escapes: | ||
15 | |||
16 | \" = double quote | ||
17 | \\ = backslash | ||
18 | \a = alert (bell) | ||
19 | \b = backspace | ||
20 | \c = produce no further output | ||
21 | \f = form feed | ||
22 | \n = new line | ||
23 | \r = carriage return | ||
24 | \t = horizontal tab | ||
25 | \v = vertical tab | ||
26 | \0ooo = octal number (ooo is 0 to 3 digits) | ||
27 | \xhhh = hexadecimal number (hhh is 1 to 3 digits) | ||
28 | |||
29 | Additional directive: | ||
30 | |||
31 | %b = print an argument string, interpreting backslash escapes | ||
32 | |||
33 | The 'format' argument is re-used as many times as necessary | ||
34 | to convert all of the given arguments. | ||
35 | |||
36 | niro | 816 | David MacKenzie <djm@gnu.ai.mit.edu> |
37 | */ | ||
38 | niro | 532 | |
39 | // 19990508 Busy Boxed! Dave Cinege | ||
40 | |||
41 | niro | 816 | #include "libbb.h" |
42 | niro | 532 | |
43 | niro | 816 | /* 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. We are not compatible here: | ||
57 | * - we do not accept -NUM for %u | ||
58 | * - exit code is 0 even if "invalid number" was seen (FIXME) | ||
59 | * See "if (errno)" checks in the code below. | ||
60 | */ | ||
61 | niro | 532 | |
62 | niro | 816 | typedef void FAST_FUNC (*converter)(const char *arg, void *result); |
63 | niro | 532 | |
64 | niro | 816 | static int multiconvert(const char *arg, void *result, converter convert) |
65 | niro | 532 | { |
66 | if (*arg == '"' || *arg == '\'') { | ||
67 | niro | 816 | arg = utoa((unsigned char)arg[1]); |
68 | niro | 532 | } |
69 | niro | 816 | errno = 0; |
70 | niro | 532 | convert(arg, result); |
71 | niro | 816 | if (errno) { |
72 | bb_error_msg("%s: invalid number", arg); | ||
73 | return 1; | ||
74 | } | ||
75 | return 0; | ||
76 | niro | 532 | } |
77 | |||
78 | niro | 816 | static void FAST_FUNC conv_strtoul(const char *arg, void *result) |
79 | niro | 532 | { |
80 | niro | 816 | *(unsigned long*)result = bb_strtoul(arg, NULL, 0); |
81 | niro | 532 | } |
82 | niro | 816 | static void FAST_FUNC conv_strtol(const char *arg, void *result) |
83 | niro | 532 | { |
84 | niro | 816 | *(long*)result = bb_strtol(arg, NULL, 0); |
85 | niro | 532 | } |
86 | niro | 816 | static void FAST_FUNC conv_strtod(const char *arg, void *result) |
87 | niro | 532 | { |
88 | char *end; | ||
89 | niro | 816 | /* Well, this one allows leading whitespace... so what? */ |
90 | /* What I like much less is that "-" accepted too! :( */ | ||
91 | niro | 532 | *(double*)result = strtod(arg, &end); |
92 | niro | 816 | if (end[0]) { |
93 | errno = ERANGE; | ||
94 | *(double*)result = 0; | ||
95 | } | ||
96 | niro | 532 | } |
97 | |||
98 | niro | 816 | /* Callers should check errno to detect errors */ |
99 | static unsigned long my_xstrtoul(const char *arg) | ||
100 | niro | 532 | { |
101 | unsigned long result; | ||
102 | niro | 816 | if (multiconvert(arg, &result, conv_strtoul)) |
103 | result = 0; | ||
104 | niro | 532 | return result; |
105 | } | ||
106 | niro | 816 | static long my_xstrtol(const char *arg) |
107 | niro | 532 | { |
108 | long result; | ||
109 | niro | 816 | if (multiconvert(arg, &result, conv_strtol)) |
110 | result = 0; | ||
111 | niro | 532 | return result; |
112 | } | ||
113 | niro | 816 | static double my_xstrtod(const char *arg) |
114 | niro | 532 | { |
115 | double result; | ||
116 | multiconvert(arg, &result, conv_strtod); | ||
117 | return result; | ||
118 | } | ||
119 | |||
120 | static void print_esc_string(char *str) | ||
121 | { | ||
122 | niro | 816 | while (*str) { |
123 | niro | 532 | if (*str == '\\') { |
124 | str++; | ||
125 | niro | 816 | bb_putchar(bb_process_escape_sequence((const char **)&str)); |
126 | niro | 532 | } else { |
127 | niro | 816 | bb_putchar(*str); |
128 | str++; | ||
129 | niro | 532 | } |
130 | } | ||
131 | } | ||
132 | |||
133 | niro | 816 | static void print_direc(char *format, unsigned fmt_length, |
134 | int field_width, int precision, | ||
135 | const char *argument) | ||
136 | niro | 532 | { |
137 | niro | 816 | long lv; |
138 | double dv; | ||
139 | char saved; | ||
140 | char *have_prec, *have_width; | ||
141 | niro | 532 | |
142 | niro | 816 | saved = format[fmt_length]; |
143 | format[fmt_length] = '\0'; | ||
144 | niro | 532 | |
145 | niro | 816 | have_prec = strstr(format, ".*"); |
146 | have_width = strchr(format, '*'); | ||
147 | if (have_width - 1 == have_prec) | ||
148 | have_width = NULL; | ||
149 | niro | 532 | |
150 | niro | 816 | switch (format[fmt_length - 1]) { |
151 | case 'c': | ||
152 | printf(format, *argument); | ||
153 | break; | ||
154 | case 'd': | ||
155 | case 'i': | ||
156 | lv = my_xstrtol(argument); | ||
157 | print_long: | ||
158 | /* if (errno) return; - see comment at the top */ | ||
159 | if (!have_width) { | ||
160 | if (!have_prec) | ||
161 | printf(format, lv); | ||
162 | else | ||
163 | printf(format, precision, lv); | ||
164 | } else { | ||
165 | if (!have_prec) | ||
166 | printf(format, field_width, lv); | ||
167 | else | ||
168 | printf(format, field_width, precision, lv); | ||
169 | } | ||
170 | break; | ||
171 | case 'o': | ||
172 | case 'u': | ||
173 | case 'x': | ||
174 | case 'X': | ||
175 | lv = my_xstrtoul(argument); | ||
176 | /* cheat: unsigned long and long have same width, so... */ | ||
177 | goto print_long; | ||
178 | case 's': | ||
179 | /* Are char* and long the same? (true for most arches) */ | ||
180 | if (sizeof(argument) == sizeof(lv)) { | ||
181 | lv = (long)(ptrdiff_t)argument; | ||
182 | goto print_long; | ||
183 | } else { /* Hope compiler will optimize it out */ | ||
184 | if (!have_width) { | ||
185 | if (!have_prec) | ||
186 | printf(format, argument); | ||
187 | else | ||
188 | printf(format, precision, argument); | ||
189 | } else { | ||
190 | if (!have_prec) | ||
191 | printf(format, field_width, argument); | ||
192 | else | ||
193 | printf(format, field_width, precision, argument); | ||
194 | } | ||
195 | break; | ||
196 | } | ||
197 | case 'f': | ||
198 | case 'e': | ||
199 | case 'E': | ||
200 | case 'g': | ||
201 | case 'G': | ||
202 | dv = my_xstrtod(argument); | ||
203 | /* if (errno) return; */ | ||
204 | if (!have_width) { | ||
205 | if (!have_prec) | ||
206 | printf(format, dv); | ||
207 | else | ||
208 | printf(format, precision, dv); | ||
209 | } else { | ||
210 | if (!have_prec) | ||
211 | printf(format, field_width, dv); | ||
212 | else | ||
213 | printf(format, field_width, precision, dv); | ||
214 | } | ||
215 | break; | ||
216 | } /* switch */ | ||
217 | niro | 532 | |
218 | niro | 816 | format[fmt_length] = saved; |
219 | } | ||
220 | niro | 532 | |
221 | niro | 816 | /* Handle params for "%*.*f". Negative numbers are ok (compat). */ |
222 | static int get_width_prec(const char *str) | ||
223 | { | ||
224 | int v = bb_strtoi(str, NULL, 10); | ||
225 | if (errno) { | ||
226 | bb_error_msg("%s: invalid number", str); | ||
227 | v = 0; | ||
228 | } | ||
229 | return v; | ||
230 | niro | 532 | } |
231 | |||
232 | niro | 816 | /* Print the text in FORMAT, using ARGV for arguments to any '%' directives. |
233 | Return advanced ARGV. */ | ||
234 | static char **print_formatted(char *f, char **argv) | ||
235 | niro | 532 | { |
236 | niro | 816 | char *direc_start; /* Start of % directive. */ |
237 | unsigned direc_length; /* Length of % directive. */ | ||
238 | int field_width; /* Arg to first '*' */ | ||
239 | int precision; /* Arg to second '*' */ | ||
240 | char **saved_argv = argv; | ||
241 | niro | 532 | |
242 | niro | 816 | for (; *f; ++f) { |
243 | niro | 532 | switch (*f) { |
244 | case '%': | ||
245 | direc_start = f++; | ||
246 | direc_length = 1; | ||
247 | niro | 816 | field_width = precision = 0; |
248 | niro | 532 | if (*f == '%') { |
249 | niro | 816 | bb_putchar('%'); |
250 | niro | 532 | break; |
251 | } | ||
252 | if (*f == 'b') { | ||
253 | niro | 816 | if (*argv) { |
254 | niro | 532 | print_esc_string(*argv); |
255 | ++argv; | ||
256 | } | ||
257 | break; | ||
258 | } | ||
259 | if (strchr("-+ #", *f)) { | ||
260 | ++f; | ||
261 | ++direc_length; | ||
262 | } | ||
263 | if (*f == '*') { | ||
264 | ++f; | ||
265 | ++direc_length; | ||
266 | niro | 816 | if (*argv) |
267 | field_width = get_width_prec(*argv++); | ||
268 | } else { | ||
269 | niro | 532 | while (isdigit(*f)) { |
270 | ++f; | ||
271 | ++direc_length; | ||
272 | } | ||
273 | niro | 816 | } |
274 | niro | 532 | if (*f == '.') { |
275 | ++f; | ||
276 | ++direc_length; | ||
277 | if (*f == '*') { | ||
278 | ++f; | ||
279 | ++direc_length; | ||
280 | niro | 816 | if (*argv) |
281 | precision = get_width_prec(*argv++); | ||
282 | } else { | ||
283 | niro | 532 | while (isdigit(*f)) { |
284 | ++f; | ||
285 | ++direc_length; | ||
286 | } | ||
287 | niro | 816 | } |
288 | niro | 532 | } |
289 | niro | 816 | /* Remove size modifiers - "%Ld" would try to printf |
290 | * long long, we pass long, and it spews garbage */ | ||
291 | if ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { | ||
292 | overlapping_strcpy(f, f + 1); | ||
293 | niro | 532 | } |
294 | niro | 816 | //FIXME: actually, the same happens with bare "%d": |
295 | //it printfs an int, but we pass long! | ||
296 | //What saves us is that on most arches stack slot | ||
297 | //is pointer-sized -> long-sized -> ints are promoted to longs | ||
298 | // for variadic functions -> printf("%d", int_v) is in reality | ||
299 | // indistinqushable from printf("%d", long_v) -> | ||
300 | // since printf("%d", int_v) works, printf("%d", long_v) has to work. | ||
301 | //But "clean" solution would be to add "l" to d,i,o,x,X. | ||
302 | //Probably makes sense to go all the way to "ll" then. | ||
303 | //Coreutils support long long-sized arguments. | ||
304 | |||
305 | /* needed - try "printf %" without it */ | ||
306 | if (!strchr("diouxXfeEgGcs", *f)) { | ||
307 | bb_error_msg("%s: invalid format", direc_start); | ||
308 | /* causes main() to exit with error */ | ||
309 | return saved_argv - 1; | ||
310 | } | ||
311 | niro | 532 | ++direc_length; |
312 | niro | 816 | if (*argv) { |
313 | niro | 532 | print_direc(direc_start, direc_length, field_width, |
314 | precision, *argv); | ||
315 | ++argv; | ||
316 | niro | 816 | } else { |
317 | niro | 532 | print_direc(direc_start, direc_length, field_width, |
318 | precision, ""); | ||
319 | niro | 816 | } |
320 | /* if (errno) return saved_argv - 1; */ | ||
321 | niro | 532 | break; |
322 | case '\\': | ||
323 | niro | 816 | if (*++f == 'c') { |
324 | return saved_argv; /* causes main() to exit */ | ||
325 | } | ||
326 | bb_putchar(bb_process_escape_sequence((const char **)&f)); | ||
327 | niro | 532 | f--; |
328 | break; | ||
329 | default: | ||
330 | niro | 816 | bb_putchar(*f); |
331 | niro | 532 | } |
332 | } | ||
333 | |||
334 | niro | 816 | return argv; |
335 | niro | 532 | } |
336 | |||
337 | niro | 816 | int printf_main(int argc UNUSED_PARAM, char **argv) |
338 | niro | 532 | { |
339 | niro | 816 | char *format; |
340 | char **argv2; | ||
341 | niro | 532 | |
342 | niro | 816 | /* We must check that stdout is not closed. |
343 | * The reason for this is highly non-obvious. | ||
344 | * printf_main is used from shell. | ||
345 | * Shell must correctly handle 'printf "%s" foo' | ||
346 | * if stdout is closed. With stdio, output gets shoveled into | ||
347 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
348 | * even if libc receives EBADF on write attempts, it feels determined | ||
349 | * to output data no matter what. So it will try later, | ||
350 | * and possibly will clobber future output. Not good. */ | ||
351 | // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? | ||
352 | if (fcntl(1, F_GETFL) == -1) | ||
353 | return 1; /* match coreutils 6.10 (sans error msg to stderr) */ | ||
354 | //if (dup2(1, 1) != 1) - old way | ||
355 | // return 1; | ||
356 | niro | 532 | |
357 | niro | 816 | /* bash builtin errors out on "printf '-%s-\n' foo", |
358 | * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". | ||
359 | * We will mimic coreutils. */ | ||
360 | if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) | ||
361 | argv++; | ||
362 | if (!argv[1]) { | ||
363 | if (ENABLE_ASH_BUILTIN_PRINTF | ||
364 | && applet_name[0] != 'p' | ||
365 | ) { | ||
366 | bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); | ||
367 | return 2; /* bash compat */ | ||
368 | niro | 532 | } |
369 | niro | 816 | bb_show_usage(); |
370 | } | ||
371 | niro | 532 | |
372 | niro | 816 | format = argv[1]; |
373 | argv2 = argv + 2; | ||
374 | niro | 532 | |
375 | niro | 816 | do { |
376 | argv = argv2; | ||
377 | argv2 = print_formatted(format, argv); | ||
378 | } while (argv2 > argv && *argv2); | ||
379 | niro | 532 | |
380 | niro | 816 | /* coreutils compat (bash doesn't do this): |
381 | if (*argv) | ||
382 | fprintf(stderr, "excess args ignored"); | ||
383 | */ | ||
384 | niro | 532 | |
385 | niro | 816 | return (argv2 < argv); /* if true, print_formatted errored out */ |
386 | niro | 532 | } |