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