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