- make append_option and multiconvert static.
[platform/upstream/busybox.git] / coreutils / printf.c
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 <unistd.h>
42 #include <stdio.h>
43 #include <sys/types.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <ctype.h>
49 #include <assert.h>
50 #include "busybox.h"
51
52 static int print_formatted (char *format, int argc, char **argv);
53 static void print_direc (char *start, size_t length,
54                         int field_width, int precision, char *argument);
55
56 typedef int (*converter)(char *arg, void *result);
57 static void multiconvert(char *arg, void *result, converter convert)
58 {
59         char s[16];
60         if (*arg == '"' || *arg == '\'') {
61                 sprintf(s,"%d",(unsigned)*(++arg));
62                 arg=s;
63         }
64         if(convert(arg,result)) fprintf(stderr, "%s", arg);
65 }
66
67 static unsigned long xstrtoul(char *arg)
68 {
69         unsigned long result;
70
71         multiconvert(arg,&result, (converter)safe_strtoul);
72         return result;
73 }
74
75 static long xstrtol(char *arg)
76 {
77         long result;
78         multiconvert(arg, &result, (converter)safe_strtol);
79         return result;
80 }
81
82 static double xstrtod(char *arg)
83 {
84         double result;
85         multiconvert(arg, &result, (converter)safe_strtod);
86         return result;
87 }
88
89 static void print_esc_string(char *str)
90 {
91         for (; *str; str++) {
92                 if (*str == '\\') {
93                         str++;
94                         putchar(bb_process_escape_sequence((const char **)&str));
95                 } else {
96                         putchar(*str);
97                 }
98
99         }
100 }
101
102 int printf_main(int argc, char **argv)
103 {
104         char *format;
105         int args_used;
106
107         if (argc <= 1 || **(argv + 1) == '-') {
108                 bb_show_usage();
109         }
110
111         format = argv[1];
112         argc -= 2;
113         argv += 2;
114
115         do {
116                 args_used = print_formatted(format, argc, argv);
117                 argc -= args_used;
118                 argv += args_used;
119         }
120         while (args_used > 0 && argc > 0);
121
122 /*
123   if (argc > 0)
124     fprintf(stderr, "excess args ignored");
125 */
126
127         return EXIT_SUCCESS;
128 }
129
130 /* Print the text in FORMAT, using ARGV (with ARGC elements) for
131    arguments to any `%' directives.
132    Return the number of elements of ARGV used.  */
133
134 static int print_formatted(char *format, int argc, char **argv)
135 {
136         int save_argc = argc;           /* Preserve original value.  */
137         char *f;                                        /* Pointer into `format'.  */
138         char *direc_start;                      /* Start of % directive.  */
139         size_t direc_length;            /* Length of % directive.  */
140         int field_width;                        /* Arg to first '*', or -1 if none.  */
141         int precision;                          /* Arg to second '*', or -1 if none.  */
142
143         for (f = format; *f; ++f) {
144                 switch (*f) {
145                 case '%':
146                         direc_start = f++;
147                         direc_length = 1;
148                         field_width = precision = -1;
149                         if (*f == '%') {
150                                 putchar('%');
151                                 break;
152                         }
153                         if (*f == 'b') {
154                                 if (argc > 0) {
155                                         print_esc_string(*argv);
156                                         ++argv;
157                                         --argc;
158                                 }
159                                 break;
160                         }
161                         if (strchr("-+ #", *f)) {
162                                 ++f;
163                                 ++direc_length;
164                         }
165                         if (*f == '*') {
166                                 ++f;
167                                 ++direc_length;
168                                 if (argc > 0) {
169                                         field_width = xstrtoul(*argv);
170                                         ++argv;
171                                         --argc;
172                                 } else
173                                         field_width = 0;
174                         } else
175                                 while (isdigit(*f)) {
176                                         ++f;
177                                         ++direc_length;
178                                 }
179                         if (*f == '.') {
180                                 ++f;
181                                 ++direc_length;
182                                 if (*f == '*') {
183                                         ++f;
184                                         ++direc_length;
185                                         if (argc > 0) {
186                                                 precision = xstrtoul(*argv);
187                                                 ++argv;
188                                                 --argc;
189                                         } else
190                                                 precision = 0;
191                                 } else
192                                         while (isdigit(*f)) {
193                                                 ++f;
194                                                 ++direc_length;
195                                         }
196                         }
197                         if (*f == 'l' || *f == 'L' || *f == 'h') {
198                                 ++f;
199                                 ++direc_length;
200                         }
201                         /*
202                            if (!strchr ("diouxXfeEgGcs", *f))
203                            fprintf(stderr, "%%%c: invalid directive", *f);
204                          */
205                         ++direc_length;
206                         if (argc > 0) {
207                                 print_direc(direc_start, direc_length, field_width,
208                                                         precision, *argv);
209                                 ++argv;
210                                 --argc;
211                         } else
212                                 print_direc(direc_start, direc_length, field_width,
213                                                         precision, "");
214                         break;
215
216                 case '\\':
217                         if (*++f == 'c')
218                                 exit(0);
219                         putchar(bb_process_escape_sequence((const char **)&f));
220                         f--;
221                         break;
222
223                 default:
224                         putchar(*f);
225                 }
226         }
227
228         return save_argc - argc;
229 }
230
231 static void
232 print_direc(char *start, size_t length, int field_width, int precision,
233                         char *argument)
234 {
235         char *p;                                        /* Null-terminated copy of % directive. */
236
237         p = xmalloc((unsigned) (length + 1));
238         strncpy(p, start, length);
239         p[length] = 0;
240
241         switch (p[length - 1]) {
242         case 'd':
243         case 'i':
244                 if (field_width < 0) {
245                         if (precision < 0)
246                                 printf(p, xstrtol(argument));
247                         else
248                                 printf(p, precision, xstrtol(argument));
249                 } else {
250                         if (precision < 0)
251                                 printf(p, field_width, xstrtol(argument));
252                         else
253                                 printf(p, field_width, precision, xstrtol(argument));
254                 }
255                 break;
256
257         case 'o':
258         case 'u':
259         case 'x':
260         case 'X':
261                 if (field_width < 0) {
262                         if (precision < 0)
263                                 printf(p, xstrtoul(argument));
264                         else
265                                 printf(p, precision, xstrtoul(argument));
266                 } else {
267                         if (precision < 0)
268                                 printf(p, field_width, xstrtoul(argument));
269                         else
270                                 printf(p, field_width, precision, xstrtoul(argument));
271                 }
272                 break;
273
274         case 'f':
275         case 'e':
276         case 'E':
277         case 'g':
278         case 'G':
279                 if (field_width < 0) {
280                         if (precision < 0)
281                                 printf(p, xstrtod(argument));
282                         else
283                                 printf(p, precision, xstrtod(argument));
284                 } else {
285                         if (precision < 0)
286                                 printf(p, field_width, xstrtod(argument));
287                         else
288                                 printf(p, field_width, precision, xstrtod(argument));
289                 }
290                 break;
291
292         case 'c':
293                 printf(p, *argument);
294                 break;
295
296         case 's':
297                 if (field_width < 0) {
298                         if (precision < 0)
299                                 printf(p, argument);
300                         else
301                                 printf(p, precision, argument);
302                 } else {
303                         if (precision < 0)
304                                 printf(p, field_width, argument);
305                         else
306                                 printf(p, field_width, precision, argument);
307                 }
308                 break;
309         }
310
311         free(p);
312 }