diff options
Diffstat (limited to 'printf.c')
-rw-r--r-- | printf.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/printf.c b/printf.c new file mode 100644 index 000000000..e79843c80 --- /dev/null +++ b/printf.c | |||
@@ -0,0 +1,531 @@ | |||
1 | // I may still need some more cleaning...fix my error checking | ||
2 | |||
3 | #include "internal.h" | ||
4 | #ifdef BB_PRINTF | ||
5 | |||
6 | /* printf - format and print data | ||
7 | Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. | ||
8 | |||
9 | This program is free software; you can redistribute it and/or modify | ||
10 | it under the terms of the GNU General Public License as published by | ||
11 | the Free Software Foundation; either version 2, or (at your option) | ||
12 | any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software Foundation, | ||
21 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | ||
22 | |||
23 | /* Usage: printf format [argument...] | ||
24 | |||
25 | A front end to the printf function that lets it be used from the shell. | ||
26 | |||
27 | Backslash escapes: | ||
28 | |||
29 | \" = double quote | ||
30 | \\ = backslash | ||
31 | \a = alert (bell) | ||
32 | \b = backspace | ||
33 | \c = produce no further output | ||
34 | \f = form feed | ||
35 | \n = new line | ||
36 | \r = carriage return | ||
37 | \t = horizontal tab | ||
38 | \v = vertical tab | ||
39 | \0ooo = octal number (ooo is 0 to 3 digits) | ||
40 | \xhhh = hexadecimal number (hhh is 1 to 3 digits) | ||
41 | |||
42 | Additional directive: | ||
43 | |||
44 | %b = print an argument string, interpreting backslash escapes | ||
45 | |||
46 | The `format' argument is re-used as many times as necessary | ||
47 | to convert all of the given arguments. | ||
48 | |||
49 | David MacKenzie <djm@gnu.ai.mit.edu> */ | ||
50 | |||
51 | |||
52 | // 19990508 Busy Boxed! Dave Cinege | ||
53 | |||
54 | #include <unistd.h> | ||
55 | #include <stdio.h> | ||
56 | #include <sys/types.h> | ||
57 | #include <getopt.h> | ||
58 | #include <sys/stat.h> | ||
59 | #include <string.h> | ||
60 | #include <errno.h> | ||
61 | #include <stdlib.h> | ||
62 | #include <fcntl.h> | ||
63 | #include <ctype.h> | ||
64 | #include <libintl.h> | ||
65 | |||
66 | |||
67 | #ifndef S_IFMT | ||
68 | # define S_IFMT 0170000 | ||
69 | #endif | ||
70 | #if !defined(S_ISBLK) && defined(S_IFBLK) | ||
71 | # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) | ||
72 | #endif | ||
73 | #if !defined(S_ISCHR) && defined(S_IFCHR) | ||
74 | # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) | ||
75 | #endif | ||
76 | #if !defined(S_ISDIR) && defined(S_IFDIR) | ||
77 | # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) | ||
78 | #endif | ||
79 | #if !defined(S_ISREG) && defined(S_IFREG) | ||
80 | # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | ||
81 | #endif | ||
82 | #if !defined(S_ISFIFO) && defined(S_IFIFO) | ||
83 | # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) | ||
84 | #endif | ||
85 | #if !defined(S_ISLNK) && defined(S_IFLNK) | ||
86 | # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) | ||
87 | #endif | ||
88 | #if !defined(S_ISSOCK) && defined(S_IFSOCK) | ||
89 | # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) | ||
90 | #endif | ||
91 | #if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ | ||
92 | # define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) | ||
93 | # define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) | ||
94 | #endif | ||
95 | #if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ | ||
96 | # define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) | ||
97 | #endif | ||
98 | |||
99 | #define IN_CTYPE_DOMAIN(c) 1 | ||
100 | |||
101 | #ifdef isblank | ||
102 | # define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c)) | ||
103 | #else | ||
104 | # define ISBLANK(c) ((c) == ' ' || (c) == '\t') | ||
105 | #endif | ||
106 | #ifdef isgraph | ||
107 | # define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c)) | ||
108 | #else | ||
109 | # define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c)) | ||
110 | #endif | ||
111 | |||
112 | #define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) | ||
113 | #define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c)) | ||
114 | #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) | ||
115 | #define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c)) | ||
116 | #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) | ||
117 | #define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c)) | ||
118 | #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) | ||
119 | #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) | ||
120 | #define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) | ||
121 | #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) | ||
122 | #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) | ||
123 | |||
124 | #define isodigit(c) ((c) >= '0' && (c) <= '7') | ||
125 | #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0') | ||
126 | #define octtobin(c) ((c) - '0') | ||
127 | |||
128 | char *xmalloc (); | ||
129 | |||
130 | static double xstrtod __P ((char *s)); | ||
131 | static int print_esc __P ((char *escstart)); | ||
132 | static int print_formatted __P ((char *format, int argc, char **argv)); | ||
133 | static long xstrtol __P ((char *s)); | ||
134 | static unsigned long xstrtoul __P ((char *s)); | ||
135 | static void print_direc __P ((char *start, size_t length, int field_width, int precision, char *argument)); | ||
136 | static void print_esc_char __P ((int c)); | ||
137 | static void print_esc_string __P ((char *str)); | ||
138 | static void verify __P ((char *s, char *end)); | ||
139 | |||
140 | /* The value to return to the calling program. */ | ||
141 | static int exit_status; | ||
142 | |||
143 | const char printf_usage[] = "Usage: printf format [argument...]\n"; | ||
144 | |||
145 | int | ||
146 | printf_main(struct FileInfo * i, int argc, char * * argv) | ||
147 | { | ||
148 | char *format; | ||
149 | int args_used; | ||
150 | |||
151 | exit_status = 0; | ||
152 | |||
153 | format = argv[1]; | ||
154 | argc -= 2; | ||
155 | argv += 2; | ||
156 | |||
157 | do | ||
158 | { | ||
159 | args_used = print_formatted (format, argc, argv); | ||
160 | argc -= args_used; | ||
161 | argv += args_used; | ||
162 | } | ||
163 | while (args_used > 0 && argc > 0); | ||
164 | |||
165 | /* | ||
166 | if (argc > 0) | ||
167 | fprintf(stderr, "excess args ignored"); | ||
168 | */ | ||
169 | |||
170 | exit (exit_status); | ||
171 | } | ||
172 | |||
173 | /* Print the text in FORMAT, using ARGV (with ARGC elements) for | ||
174 | arguments to any `%' directives. | ||
175 | Return the number of elements of ARGV used. */ | ||
176 | |||
177 | static int | ||
178 | print_formatted (char *format, int argc, char **argv) | ||
179 | { | ||
180 | int save_argc = argc; /* Preserve original value. */ | ||
181 | char *f; /* Pointer into `format'. */ | ||
182 | char *direc_start; /* Start of % directive. */ | ||
183 | size_t direc_length; /* Length of % directive. */ | ||
184 | int field_width; /* Arg to first '*', or -1 if none. */ | ||
185 | int precision; /* Arg to second '*', or -1 if none. */ | ||
186 | |||
187 | for (f = format; *f; ++f) | ||
188 | { | ||
189 | switch (*f) | ||
190 | { | ||
191 | case '%': | ||
192 | direc_start = f++; | ||
193 | direc_length = 1; | ||
194 | field_width = precision = -1; | ||
195 | if (*f == '%') | ||
196 | { | ||
197 | putchar ('%'); | ||
198 | break; | ||
199 | } | ||
200 | if (*f == 'b') | ||
201 | { | ||
202 | if (argc > 0) | ||
203 | { | ||
204 | print_esc_string (*argv); | ||
205 | ++argv; | ||
206 | --argc; | ||
207 | } | ||
208 | break; | ||
209 | } | ||
210 | if (strchr ("-+ #", *f)) | ||
211 | { | ||
212 | ++f; | ||
213 | ++direc_length; | ||
214 | } | ||
215 | if (*f == '*') | ||
216 | { | ||
217 | ++f; | ||
218 | ++direc_length; | ||
219 | if (argc > 0) | ||
220 | { | ||
221 | field_width = xstrtoul (*argv); | ||
222 | ++argv; | ||
223 | --argc; | ||
224 | } | ||
225 | else | ||
226 | field_width = 0; | ||
227 | } | ||
228 | else | ||
229 | while (ISDIGIT (*f)) | ||
230 | { | ||
231 | ++f; | ||
232 | ++direc_length; | ||
233 | } | ||
234 | if (*f == '.') | ||
235 | { | ||
236 | ++f; | ||
237 | ++direc_length; | ||
238 | if (*f == '*') | ||
239 | { | ||
240 | ++f; | ||
241 | ++direc_length; | ||
242 | if (argc > 0) | ||
243 | { | ||
244 | precision = xstrtoul (*argv); | ||
245 | ++argv; | ||
246 | --argc; | ||
247 | } | ||
248 | else | ||
249 | precision = 0; | ||
250 | } | ||
251 | else | ||
252 | while (ISDIGIT (*f)) | ||
253 | { | ||
254 | ++f; | ||
255 | ++direc_length; | ||
256 | } | ||
257 | } | ||
258 | if (*f == 'l' || *f == 'L' || *f == 'h') | ||
259 | { | ||
260 | ++f; | ||
261 | ++direc_length; | ||
262 | } | ||
263 | /* | ||
264 | if (!strchr ("diouxXfeEgGcs", *f)) | ||
265 | fprintf(stderr, "%%%c: invalid directive", *f); | ||
266 | */ | ||
267 | ++direc_length; | ||
268 | if (argc > 0) | ||
269 | { | ||
270 | print_direc (direc_start, direc_length, field_width, | ||
271 | precision, *argv); | ||
272 | ++argv; | ||
273 | --argc; | ||
274 | } | ||
275 | else | ||
276 | print_direc (direc_start, direc_length, field_width, | ||
277 | precision, ""); | ||
278 | break; | ||
279 | |||
280 | case '\\': | ||
281 | f += print_esc (f); | ||
282 | break; | ||
283 | |||
284 | default: | ||
285 | putchar (*f); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | return save_argc - argc; | ||
290 | } | ||
291 | |||
292 | /* Print a \ escape sequence starting at ESCSTART. | ||
293 | Return the number of characters in the escape sequence | ||
294 | besides the backslash. */ | ||
295 | |||
296 | static int | ||
297 | print_esc (char *escstart) | ||
298 | { | ||
299 | register char *p = escstart + 1; | ||
300 | int esc_value = 0; /* Value of \nnn escape. */ | ||
301 | int esc_length; /* Length of \nnn escape. */ | ||
302 | |||
303 | /* \0ooo and \xhhh escapes have maximum length of 3 chars. */ | ||
304 | if (*p == 'x') | ||
305 | { | ||
306 | for (esc_length = 0, ++p; | ||
307 | esc_length < 3 && ISXDIGIT (*p); | ||
308 | ++esc_length, ++p) | ||
309 | esc_value = esc_value * 16 + hextobin (*p); | ||
310 | /* if (esc_length == 0) | ||
311 | fprintf(stderr, "missing hex in esc"); | ||
312 | */ | ||
313 | putchar (esc_value); | ||
314 | } | ||
315 | else if (*p == '0') | ||
316 | { | ||
317 | for (esc_length = 0, ++p; | ||
318 | esc_length < 3 && isodigit (*p); | ||
319 | ++esc_length, ++p) | ||
320 | esc_value = esc_value * 8 + octtobin (*p); | ||
321 | putchar (esc_value); | ||
322 | } | ||
323 | else if (strchr ("\"\\abcfnrtv", *p)) | ||
324 | print_esc_char (*p++); | ||
325 | /* else | ||
326 | fprintf(stderr, "\\%c: invalid esc", *p); | ||
327 | */ | ||
328 | return p - escstart - 1; | ||
329 | } | ||
330 | |||
331 | /* Output a single-character \ escape. */ | ||
332 | |||
333 | static void | ||
334 | print_esc_char (int c) | ||
335 | { | ||
336 | switch (c) | ||
337 | { | ||
338 | case 'a': /* Alert. */ | ||
339 | putchar (7); | ||
340 | break; | ||
341 | case 'b': /* Backspace. */ | ||
342 | putchar (8); | ||
343 | break; | ||
344 | case 'c': /* Cancel the rest of the output. */ | ||
345 | exit (0); | ||
346 | break; | ||
347 | case 'f': /* Form feed. */ | ||
348 | putchar (12); | ||
349 | break; | ||
350 | case 'n': /* New line. */ | ||
351 | putchar (10); | ||
352 | break; | ||
353 | case 'r': /* Carriage return. */ | ||
354 | putchar (13); | ||
355 | break; | ||
356 | case 't': /* Horizontal tab. */ | ||
357 | putchar (9); | ||
358 | break; | ||
359 | case 'v': /* Vertical tab. */ | ||
360 | putchar (11); | ||
361 | break; | ||
362 | default: | ||
363 | putchar (c); | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* Print string STR, evaluating \ escapes. */ | ||
369 | |||
370 | static void | ||
371 | print_esc_string (char *str) | ||
372 | { | ||
373 | for (; *str; str++) | ||
374 | if (*str == '\\') | ||
375 | str += print_esc (str); | ||
376 | else | ||
377 | putchar (*str); | ||
378 | } | ||
379 | |||
380 | static void | ||
381 | print_direc (char *start, size_t length, int field_width, int precision, char *argument) | ||
382 | { | ||
383 | char *p; /* Null-terminated copy of % directive. */ | ||
384 | |||
385 | p = xmalloc ((unsigned) (length + 1)); | ||
386 | strncpy (p, start, length); | ||
387 | p[length] = 0; | ||
388 | |||
389 | switch (p[length - 1]) | ||
390 | { | ||
391 | case 'd': | ||
392 | case 'i': | ||
393 | if (field_width < 0) | ||
394 | { | ||
395 | if (precision < 0) | ||
396 | printf (p, xstrtol (argument)); | ||
397 | else | ||
398 | printf (p, precision, xstrtol (argument)); | ||
399 | } | ||
400 | else | ||
401 | { | ||
402 | if (precision < 0) | ||
403 | printf (p, field_width, xstrtol (argument)); | ||
404 | else | ||
405 | printf (p, field_width, precision, xstrtol (argument)); | ||
406 | } | ||
407 | break; | ||
408 | |||
409 | case 'o': | ||
410 | case 'u': | ||
411 | case 'x': | ||
412 | case 'X': | ||
413 | if (field_width < 0) | ||
414 | { | ||
415 | if (precision < 0) | ||
416 | printf (p, xstrtoul (argument)); | ||
417 | else | ||
418 | printf (p, precision, xstrtoul (argument)); | ||
419 | } | ||
420 | else | ||
421 | { | ||
422 | if (precision < 0) | ||
423 | printf (p, field_width, xstrtoul (argument)); | ||
424 | else | ||
425 | printf (p, field_width, precision, xstrtoul (argument)); | ||
426 | } | ||
427 | break; | ||
428 | |||
429 | case 'f': | ||
430 | case 'e': | ||
431 | case 'E': | ||
432 | case 'g': | ||
433 | case 'G': | ||
434 | if (field_width < 0) | ||
435 | { | ||
436 | if (precision < 0) | ||
437 | printf (p, xstrtod (argument)); | ||
438 | else | ||
439 | printf (p, precision, xstrtod (argument)); | ||
440 | } | ||
441 | else | ||
442 | { | ||
443 | if (precision < 0) | ||
444 | printf (p, field_width, xstrtod (argument)); | ||
445 | else | ||
446 | printf (p, field_width, precision, xstrtod (argument)); | ||
447 | } | ||
448 | break; | ||
449 | |||
450 | case 'c': | ||
451 | printf (p, *argument); | ||
452 | break; | ||
453 | |||
454 | case 's': | ||
455 | if (field_width < 0) | ||
456 | { | ||
457 | if (precision < 0) | ||
458 | printf (p, argument); | ||
459 | else | ||
460 | printf (p, precision, argument); | ||
461 | } | ||
462 | else | ||
463 | { | ||
464 | if (precision < 0) | ||
465 | printf (p, field_width, argument); | ||
466 | else | ||
467 | printf (p, field_width, precision, argument); | ||
468 | } | ||
469 | break; | ||
470 | } | ||
471 | |||
472 | free (p); | ||
473 | } | ||
474 | |||
475 | static unsigned long | ||
476 | xstrtoul (char *s) | ||
477 | { | ||
478 | char *end; | ||
479 | unsigned long val; | ||
480 | |||
481 | errno = 0; | ||
482 | val = strtoul (s, &end, 0); | ||
483 | verify (s, end); | ||
484 | return val; | ||
485 | } | ||
486 | |||
487 | static long | ||
488 | xstrtol (char *s) | ||
489 | { | ||
490 | char *end; | ||
491 | long val; | ||
492 | |||
493 | errno = 0; | ||
494 | val = strtol (s, &end, 0); | ||
495 | verify (s, end); | ||
496 | return val; | ||
497 | } | ||
498 | |||
499 | static double | ||
500 | xstrtod (char *s) | ||
501 | { | ||
502 | char *end; | ||
503 | double val; | ||
504 | |||
505 | errno = 0; | ||
506 | val = strtod (s, &end); | ||
507 | verify (s, end); | ||
508 | return val; | ||
509 | } | ||
510 | |||
511 | static void | ||
512 | verify (char *s, char *end) | ||
513 | { | ||
514 | if (errno) | ||
515 | { | ||
516 | fprintf(stderr, "%s", s); | ||
517 | exit_status = 1; | ||
518 | } | ||
519 | else if (*end) | ||
520 | { | ||
521 | /* | ||
522 | if (s == end) | ||
523 | fprintf(stderr, "%s: expected numeric", s); | ||
524 | else | ||
525 | fprintf(stderr, "%s: not completely converted", s); | ||
526 | */ | ||
527 | exit_status = 1; | ||
528 | } | ||
529 | } | ||
530 | |||
531 | #endif | ||