Contents of /trunk/mkinitrd-magellan/busybox/coreutils/printf.c
Parent Directory | Revision Log
Revision 816 -
(show annotations)
(download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 9642 byte(s)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 9642 byte(s)
-updated to busybox-1.13.4
1 | /* 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 | David MacKenzie <djm@gnu.ai.mit.edu> |
37 | */ |
38 | |
39 | // 19990508 Busy Boxed! Dave Cinege |
40 | |
41 | #include "libbb.h" |
42 | |
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. 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 | |
62 | typedef void FAST_FUNC (*converter)(const char *arg, void *result); |
63 | |
64 | static int multiconvert(const char *arg, void *result, converter convert) |
65 | { |
66 | if (*arg == '"' || *arg == '\'') { |
67 | arg = utoa((unsigned char)arg[1]); |
68 | } |
69 | errno = 0; |
70 | convert(arg, result); |
71 | if (errno) { |
72 | bb_error_msg("%s: invalid number", arg); |
73 | return 1; |
74 | } |
75 | return 0; |
76 | } |
77 | |
78 | static void FAST_FUNC conv_strtoul(const char *arg, void *result) |
79 | { |
80 | *(unsigned long*)result = bb_strtoul(arg, NULL, 0); |
81 | } |
82 | static void FAST_FUNC conv_strtol(const char *arg, void *result) |
83 | { |
84 | *(long*)result = bb_strtol(arg, NULL, 0); |
85 | } |
86 | static void FAST_FUNC conv_strtod(const char *arg, void *result) |
87 | { |
88 | char *end; |
89 | /* Well, this one allows leading whitespace... so what? */ |
90 | /* What I like much less is that "-" accepted too! :( */ |
91 | *(double*)result = strtod(arg, &end); |
92 | if (end[0]) { |
93 | errno = ERANGE; |
94 | *(double*)result = 0; |
95 | } |
96 | } |
97 | |
98 | /* Callers should check errno to detect errors */ |
99 | static unsigned long my_xstrtoul(const char *arg) |
100 | { |
101 | unsigned long result; |
102 | if (multiconvert(arg, &result, conv_strtoul)) |
103 | result = 0; |
104 | return result; |
105 | } |
106 | static long my_xstrtol(const char *arg) |
107 | { |
108 | long result; |
109 | if (multiconvert(arg, &result, conv_strtol)) |
110 | result = 0; |
111 | return result; |
112 | } |
113 | static double my_xstrtod(const char *arg) |
114 | { |
115 | double result; |
116 | multiconvert(arg, &result, conv_strtod); |
117 | return result; |
118 | } |
119 | |
120 | static void print_esc_string(char *str) |
121 | { |
122 | while (*str) { |
123 | if (*str == '\\') { |
124 | str++; |
125 | bb_putchar(bb_process_escape_sequence((const char **)&str)); |
126 | } else { |
127 | bb_putchar(*str); |
128 | str++; |
129 | } |
130 | } |
131 | } |
132 | |
133 | static void print_direc(char *format, unsigned fmt_length, |
134 | int field_width, int precision, |
135 | const char *argument) |
136 | { |
137 | long lv; |
138 | double dv; |
139 | char saved; |
140 | char *have_prec, *have_width; |
141 | |
142 | saved = format[fmt_length]; |
143 | format[fmt_length] = '\0'; |
144 | |
145 | have_prec = strstr(format, ".*"); |
146 | have_width = strchr(format, '*'); |
147 | if (have_width - 1 == have_prec) |
148 | have_width = NULL; |
149 | |
150 | 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 | |
218 | format[fmt_length] = saved; |
219 | } |
220 | |
221 | /* 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 | } |
231 | |
232 | /* 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 | { |
236 | 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 | |
242 | for (; *f; ++f) { |
243 | switch (*f) { |
244 | case '%': |
245 | direc_start = f++; |
246 | direc_length = 1; |
247 | field_width = precision = 0; |
248 | if (*f == '%') { |
249 | bb_putchar('%'); |
250 | break; |
251 | } |
252 | if (*f == 'b') { |
253 | if (*argv) { |
254 | 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 | if (*argv) |
267 | field_width = get_width_prec(*argv++); |
268 | } else { |
269 | while (isdigit(*f)) { |
270 | ++f; |
271 | ++direc_length; |
272 | } |
273 | } |
274 | if (*f == '.') { |
275 | ++f; |
276 | ++direc_length; |
277 | if (*f == '*') { |
278 | ++f; |
279 | ++direc_length; |
280 | if (*argv) |
281 | precision = get_width_prec(*argv++); |
282 | } else { |
283 | while (isdigit(*f)) { |
284 | ++f; |
285 | ++direc_length; |
286 | } |
287 | } |
288 | } |
289 | /* 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 | } |
294 | //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 | ++direc_length; |
312 | if (*argv) { |
313 | print_direc(direc_start, direc_length, field_width, |
314 | precision, *argv); |
315 | ++argv; |
316 | } else { |
317 | print_direc(direc_start, direc_length, field_width, |
318 | precision, ""); |
319 | } |
320 | /* if (errno) return saved_argv - 1; */ |
321 | break; |
322 | case '\\': |
323 | if (*++f == 'c') { |
324 | return saved_argv; /* causes main() to exit */ |
325 | } |
326 | bb_putchar(bb_process_escape_sequence((const char **)&f)); |
327 | f--; |
328 | break; |
329 | default: |
330 | bb_putchar(*f); |
331 | } |
332 | } |
333 | |
334 | return argv; |
335 | } |
336 | |
337 | int printf_main(int argc UNUSED_PARAM, char **argv) |
338 | { |
339 | char *format; |
340 | char **argv2; |
341 | |
342 | /* 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 | |
357 | /* 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 | } |
369 | bb_show_usage(); |
370 | } |
371 | |
372 | format = argv[1]; |
373 | argv2 = argv + 2; |
374 | |
375 | do { |
376 | argv = argv2; |
377 | argv2 = print_formatted(format, argv); |
378 | } while (argv2 > argv && *argv2); |
379 | |
380 | /* coreutils compat (bash doesn't do this): |
381 | if (*argv) |
382 | fprintf(stderr, "excess args ignored"); |
383 | */ |
384 | |
385 | return (argv2 < argv); /* if true, print_formatted errored out */ |
386 | } |