Initial revision
[platform/upstream/busybox.git] / coreutils / printf.c
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