diff options
Diffstat (limited to 'busybox/coreutils/printf.c')
-rw-r--r-- | busybox/coreutils/printf.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/busybox/coreutils/printf.c b/busybox/coreutils/printf.c new file mode 100644 index 000000000..da5e46a58 --- /dev/null +++ b/busybox/coreutils/printf.c | |||
@@ -0,0 +1,316 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* printf - format and print data | ||
3 | Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; if not, write to the Free Software Foundation, | ||
17 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | ||
18 | |||
19 | /* Usage: printf format [argument...] | ||
20 | |||
21 | A front end to the printf function that lets it be used from the shell. | ||
22 | |||
23 | Backslash escapes: | ||
24 | |||
25 | \" = double quote | ||
26 | \\ = backslash | ||
27 | \a = alert (bell) | ||
28 | \b = backspace | ||
29 | \c = produce no further output | ||
30 | \f = form feed | ||
31 | \n = new line | ||
32 | \r = carriage return | ||
33 | \t = horizontal tab | ||
34 | \v = vertical tab | ||
35 | \0ooo = octal number (ooo is 0 to 3 digits) | ||
36 | \xhhh = hexadecimal number (hhh is 1 to 3 digits) | ||
37 | |||
38 | Additional directive: | ||
39 | |||
40 | %b = print an argument string, interpreting backslash escapes | ||
41 | |||
42 | The `format' argument is re-used as many times as necessary | ||
43 | to convert all of the given arguments. | ||
44 | |||
45 | David MacKenzie <djm@gnu.ai.mit.edu> */ | ||
46 | |||
47 | |||
48 | // 19990508 Busy Boxed! Dave Cinege | ||
49 | |||
50 | #include <unistd.h> | ||
51 | #include <stdio.h> | ||
52 | #include <sys/types.h> | ||
53 | #include <string.h> | ||
54 | #include <errno.h> | ||
55 | #include <stdlib.h> | ||
56 | #include <fcntl.h> | ||
57 | #include <ctype.h> | ||
58 | #include <assert.h> | ||
59 | #include "busybox.h" | ||
60 | |||
61 | static double xstrtod __P((char *s)); | ||
62 | static long xstrtol __P((char *s)); | ||
63 | static unsigned long xstrtoul __P((char *s)); | ||
64 | static void print_esc_string __P((char *str)); | ||
65 | static int print_formatted __P((char *format, int argc, char **argv)); | ||
66 | static void print_direc __P( (char *start, size_t length, | ||
67 | int field_width, int precision, char *argument)); | ||
68 | |||
69 | int printf_main(int argc, char **argv) | ||
70 | { | ||
71 | char *format; | ||
72 | int args_used; | ||
73 | |||
74 | if (argc <= 1 || **(argv + 1) == '-') { | ||
75 | bb_show_usage(); | ||
76 | } | ||
77 | |||
78 | format = argv[1]; | ||
79 | argc -= 2; | ||
80 | argv += 2; | ||
81 | |||
82 | do { | ||
83 | args_used = print_formatted(format, argc, argv); | ||
84 | argc -= args_used; | ||
85 | argv += args_used; | ||
86 | } | ||
87 | while (args_used > 0 && argc > 0); | ||
88 | |||
89 | /* | ||
90 | if (argc > 0) | ||
91 | fprintf(stderr, "excess args ignored"); | ||
92 | */ | ||
93 | |||
94 | return EXIT_SUCCESS; | ||
95 | } | ||
96 | |||
97 | /* Print the text in FORMAT, using ARGV (with ARGC elements) for | ||
98 | arguments to any `%' directives. | ||
99 | Return the number of elements of ARGV used. */ | ||
100 | |||
101 | static int print_formatted(char *format, int argc, char **argv) | ||
102 | { | ||
103 | int save_argc = argc; /* Preserve original value. */ | ||
104 | char *f; /* Pointer into `format'. */ | ||
105 | char *direc_start; /* Start of % directive. */ | ||
106 | size_t direc_length; /* Length of % directive. */ | ||
107 | int field_width; /* Arg to first '*', or -1 if none. */ | ||
108 | int precision; /* Arg to second '*', or -1 if none. */ | ||
109 | |||
110 | for (f = format; *f; ++f) { | ||
111 | switch (*f) { | ||
112 | case '%': | ||
113 | direc_start = f++; | ||
114 | direc_length = 1; | ||
115 | field_width = precision = -1; | ||
116 | if (*f == '%') { | ||
117 | putchar('%'); | ||
118 | break; | ||
119 | } | ||
120 | if (*f == 'b') { | ||
121 | if (argc > 0) { | ||
122 | print_esc_string(*argv); | ||
123 | ++argv; | ||
124 | --argc; | ||
125 | } | ||
126 | break; | ||
127 | } | ||
128 | if (strchr("-+ #", *f)) { | ||
129 | ++f; | ||
130 | ++direc_length; | ||
131 | } | ||
132 | if (*f == '*') { | ||
133 | ++f; | ||
134 | ++direc_length; | ||
135 | if (argc > 0) { | ||
136 | field_width = xstrtoul(*argv); | ||
137 | ++argv; | ||
138 | --argc; | ||
139 | } else | ||
140 | field_width = 0; | ||
141 | } else | ||
142 | while (isdigit(*f)) { | ||
143 | ++f; | ||
144 | ++direc_length; | ||
145 | } | ||
146 | if (*f == '.') { | ||
147 | ++f; | ||
148 | ++direc_length; | ||
149 | if (*f == '*') { | ||
150 | ++f; | ||
151 | ++direc_length; | ||
152 | if (argc > 0) { | ||
153 | precision = xstrtoul(*argv); | ||
154 | ++argv; | ||
155 | --argc; | ||
156 | } else | ||
157 | precision = 0; | ||
158 | } else | ||
159 | while (isdigit(*f)) { | ||
160 | ++f; | ||
161 | ++direc_length; | ||
162 | } | ||
163 | } | ||
164 | if (*f == 'l' || *f == 'L' || *f == 'h') { | ||
165 | ++f; | ||
166 | ++direc_length; | ||
167 | } | ||
168 | /* | ||
169 | if (!strchr ("diouxXfeEgGcs", *f)) | ||
170 | fprintf(stderr, "%%%c: invalid directive", *f); | ||
171 | */ | ||
172 | ++direc_length; | ||
173 | if (argc > 0) { | ||
174 | print_direc(direc_start, direc_length, field_width, | ||
175 | precision, *argv); | ||
176 | ++argv; | ||
177 | --argc; | ||
178 | } else | ||
179 | print_direc(direc_start, direc_length, field_width, | ||
180 | precision, ""); | ||
181 | break; | ||
182 | |||
183 | case '\\': | ||
184 | if (*++f == 'c') | ||
185 | exit(0); | ||
186 | putchar(bb_process_escape_sequence((const char **)&f)); | ||
187 | f--; | ||
188 | break; | ||
189 | |||
190 | default: | ||
191 | putchar(*f); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return save_argc - argc; | ||
196 | } | ||
197 | |||
198 | static void | ||
199 | print_direc(char *start, size_t length, int field_width, int precision, | ||
200 | char *argument) | ||
201 | { | ||
202 | char *p; /* Null-terminated copy of % directive. */ | ||
203 | |||
204 | p = xmalloc((unsigned) (length + 1)); | ||
205 | strncpy(p, start, length); | ||
206 | p[length] = 0; | ||
207 | |||
208 | switch (p[length - 1]) { | ||
209 | case 'd': | ||
210 | case 'i': | ||
211 | if (field_width < 0) { | ||
212 | if (precision < 0) | ||
213 | printf(p, xstrtol(argument)); | ||
214 | else | ||
215 | printf(p, precision, xstrtol(argument)); | ||
216 | } else { | ||
217 | if (precision < 0) | ||
218 | printf(p, field_width, xstrtol(argument)); | ||
219 | else | ||
220 | printf(p, field_width, precision, xstrtol(argument)); | ||
221 | } | ||
222 | break; | ||
223 | |||
224 | case 'o': | ||
225 | case 'u': | ||
226 | case 'x': | ||
227 | case 'X': | ||
228 | if (field_width < 0) { | ||
229 | if (precision < 0) | ||
230 | printf(p, xstrtoul(argument)); | ||
231 | else | ||
232 | printf(p, precision, xstrtoul(argument)); | ||
233 | } else { | ||
234 | if (precision < 0) | ||
235 | printf(p, field_width, xstrtoul(argument)); | ||
236 | else | ||
237 | printf(p, field_width, precision, xstrtoul(argument)); | ||
238 | } | ||
239 | break; | ||
240 | |||
241 | case 'f': | ||
242 | case 'e': | ||
243 | case 'E': | ||
244 | case 'g': | ||
245 | case 'G': | ||
246 | if (field_width < 0) { | ||
247 | if (precision < 0) | ||
248 | printf(p, xstrtod(argument)); | ||
249 | else | ||
250 | printf(p, precision, xstrtod(argument)); | ||
251 | } else { | ||
252 | if (precision < 0) | ||
253 | printf(p, field_width, xstrtod(argument)); | ||
254 | else | ||
255 | printf(p, field_width, precision, xstrtod(argument)); | ||
256 | } | ||
257 | break; | ||
258 | |||
259 | case 'c': | ||
260 | printf(p, *argument); | ||
261 | break; | ||
262 | |||
263 | case 's': | ||
264 | if (field_width < 0) { | ||
265 | if (precision < 0) | ||
266 | printf(p, argument); | ||
267 | else | ||
268 | printf(p, precision, argument); | ||
269 | } else { | ||
270 | if (precision < 0) | ||
271 | printf(p, field_width, argument); | ||
272 | else | ||
273 | printf(p, field_width, precision, argument); | ||
274 | } | ||
275 | break; | ||
276 | } | ||
277 | |||
278 | free(p); | ||
279 | } | ||
280 | |||
281 | static unsigned long xstrtoul(char *arg) | ||
282 | { | ||
283 | unsigned long result; | ||
284 | if (safe_strtoul(arg, &result)) | ||
285 | fprintf(stderr, "%s", arg); | ||
286 | return result; | ||
287 | } | ||
288 | |||
289 | static long xstrtol(char *arg) | ||
290 | { | ||
291 | long result; | ||
292 | if (safe_strtol(arg, &result)) | ||
293 | fprintf(stderr, "%s", arg); | ||
294 | return result; | ||
295 | } | ||
296 | |||
297 | static double xstrtod(char *arg) | ||
298 | { | ||
299 | double result; | ||
300 | if (safe_strtod(arg, &result)) | ||
301 | fprintf(stderr, "%s", arg); | ||
302 | return result; | ||
303 | } | ||
304 | |||
305 | static void print_esc_string(char *str) | ||
306 | { | ||
307 | for (; *str; str++) { | ||
308 | if (*str == '\\') { | ||
309 | str++; | ||
310 | putchar(bb_process_escape_sequence((const char **)&str)); | ||
311 | } else { | ||
312 | putchar(*str); | ||
313 | } | ||
314 | |||
315 | } | ||
316 | } | ||