99f5f6b9a9d0ae4dea5758ff5877da3081bc0fe8
[platform/upstream/coreutils.git] / src / printf.c
1 /* printf - format and print data
2    Copyright (C) 1990, 1991 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
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 #include <stdio.h>
47 #include <ctype.h>
48 #include <sys/types.h>
49 #include "system.h"
50
51 #ifndef STDC_HEADERS
52 double strtod ();
53 long strtol ();
54 unsigned long strtoul ();
55 #endif
56
57 #define isodigit(c) ((c) >= '0' && (c) <= '7')
58 #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
59 #define octtobin(c) ((c) - '0')
60
61 char *xmalloc ();
62 double xstrtod ();
63 int print_esc ();
64 int print_formatted ();
65 long xstrtol ();
66 unsigned long xstrtoul ();
67 void error ();
68 void print_direc ();
69 void print_esc_char ();
70 void print_esc_string ();
71 void verify ();
72
73 /* The name this program was run with. */
74 char *program_name;
75
76 /* The value to return to the calling program.  */
77 static int exit_status;
78
79 void
80 main (argc, argv)
81      int argc;
82      char **argv;
83 {
84   char *format;
85   int args_used;
86
87   program_name = argv[0];
88   exit_status = 0;
89
90   if (argc == 1)
91     {
92       fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
93       exit (1);
94     }
95
96   format = argv[1];
97   argc -= 2;
98   argv += 2;
99
100   do
101     {
102       args_used = print_formatted (format, argc, argv);
103       argc -= args_used;
104       argv += args_used;
105     }
106   while (args_used > 0 && argc > 0);
107
108   exit (exit_status);
109 }
110
111 /* Print the text in FORMAT, using ARGV (with ARGC elements) for
112    arguments to any `%' directives.
113    Return the number of elements of ARGV used.  */
114
115 int
116 print_formatted (format, argc, argv)
117      char *format;
118      int argc;
119      char **argv;
120 {
121   int save_argc = argc;         /* Preserve original value.  */
122   char *f;                      /* Pointer into `format'.  */
123   char *direc_start;            /* Start of % directive.  */
124   int direc_length;             /* Length of % directive.  */
125   int field_width;              /* Arg to first '*', or -1 if none.  */
126   int precision;                /* Arg to second '*', or -1 if none.  */
127
128   for (f = format; *f; ++f)
129     {
130       switch (*f)
131         {
132         case '%':
133           direc_start = f++;
134           direc_length = 1;
135           field_width = precision = -1;
136           if (*f == '%')
137             {
138               putchar ('%');
139               break;
140             }
141           if (*f == 'b')
142             {
143               if (argc > 0)
144                 {
145                   print_esc_string (*argv);
146                   ++argv;
147                   --argc;
148                 }
149               break;
150             }
151           if (index ("-+ #", *f))
152             {
153               ++f;
154               ++direc_length;
155             }
156           if (*f == '*')
157             {
158               ++f;
159               ++direc_length;
160               if (argc > 0)
161                 {
162                   field_width = xstrtoul (*argv);
163                   ++argv;
164                   --argc;
165                 }
166               else
167                 field_width = 0;
168             }
169           else
170             while (isdigit (*f))
171               {
172                 ++f;
173                 ++direc_length;
174               }
175           if (*f == '.')
176             {
177               ++f;
178               ++direc_length;
179               if (*f == '*')
180                 {
181                   ++f;
182                   ++direc_length;
183                   if (argc > 0)
184                     {
185                       precision = xstrtoul (*argv);
186                       ++argv;
187                       --argc;
188                     }
189                   else
190                     precision = 0;
191                 }
192               else
193                 while (isdigit (*f))
194                   {
195                     ++f;
196                     ++direc_length;
197                   }
198             }
199           if (*f == 'l' || *f == 'L' || *f == 'h')
200             {
201               ++f;
202               ++direc_length;
203             }
204           if (!index ("diouxXfeEgGcs", *f))
205             error (1, 0, "%%%c: invalid directive", *f);
206           ++direc_length;
207           if (argc > 0)
208             {
209               print_direc (direc_start, direc_length, field_width,
210                            precision, *argv);
211               ++argv;
212               --argc;
213             }
214           else
215             print_direc (direc_start, direc_length, field_width,
216                          precision, "");
217           break;
218
219         case '\\':
220           f += print_esc (f);
221           break;
222
223         default:
224           putchar (*f);
225         }
226     }
227
228   return save_argc - argc;
229 }
230
231 /* Print a \ escape sequence starting at ESCSTART.
232    Return the number of characters in the escape sequence
233    besides the backslash. */
234
235 int
236 print_esc (escstart)
237      char *escstart;
238 {
239   register char *p = escstart + 1;
240   int esc_value = 0;            /* Value of \nnn escape. */
241   int esc_length;               /* Length of \nnn escape. */
242
243   /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
244   if (*p == 'x')
245     {
246       for (esc_length = 0, ++p;
247            esc_length < 3 && isxdigit (*p);
248            ++esc_length, ++p)
249         esc_value = esc_value * 16 + hextobin (*p);
250       if (esc_length == 0)
251         error (1, 0, "missing hexadecimal number in escape");
252       putchar (esc_value);
253     }
254   else if (*p == '0')
255     {
256       for (esc_length = 0, ++p;
257            esc_length < 3 && isodigit (*p);
258            ++esc_length, ++p)
259         esc_value = esc_value * 8 + octtobin (*p);
260       putchar (esc_value);
261     }
262   else if (index ("\"\\abcfnrtv", *p))
263     print_esc_char (*p++);
264   else
265     error (1, 0, "\\%c: invalid escape", *p);
266   return p - escstart - 1;
267 }
268
269 /* Output a single-character \ escape.  */
270
271 void
272 print_esc_char (c)
273      char c;
274 {
275   switch (c)
276     {
277     case 'a':                   /* Alert. */
278       putchar (7);
279       break;
280     case 'b':                   /* Backspace. */
281       putchar (8);
282       break;
283     case 'c':                   /* Cancel the rest of the output. */
284       exit (0);
285       break;
286     case 'f':                   /* Form feed. */
287       putchar (12);
288       break;
289     case 'n':                   /* New line. */
290       putchar (10);
291       break;
292     case 'r':                   /* Carriage return. */
293       putchar (13);
294       break;
295     case 't':                   /* Horizontal tab. */
296       putchar (9);
297       break;
298     case 'v':                   /* Vertical tab. */
299       putchar (11);
300       break;
301     default:
302       putchar (c);
303       break;
304     }
305 }
306
307 /* Print string STR, evaluating \ escapes. */
308
309 void
310 print_esc_string (str)
311      char *str;
312 {
313   for (; *str; str++)
314     if (*str == '\\')
315       str += print_esc (str);
316     else
317       putchar (*str);
318 }
319
320 /* Output a % directive.  START is the start of the directive,
321    LENGTH is its length, and ARGUMENT is its argument.
322    If FIELD_WIDTH or PRECISION is non-negative, they are args for
323    '*' values in those fields. */
324
325 void
326 print_direc (start, length, field_width, precision, argument)
327      char *start;
328      int length;
329      int field_width;
330      int precision;
331      char *argument;
332 {
333   char *p;              /* Null-terminated copy of % directive. */
334
335   p = xmalloc ((unsigned) (length + 1));
336   strncpy (p, start, length);
337   p[length] = 0;
338
339   switch (p[length - 1])
340     {
341     case 'd':
342     case 'i':
343       if (field_width < 0)
344         {
345           if (precision < 0)
346             printf (p, xstrtol (argument));
347           else
348             printf (p, precision, xstrtol (argument));
349         }
350       else
351         {
352           if (precision < 0)
353             printf (p, field_width, xstrtol (argument));
354           else
355             printf (p, field_width, precision, xstrtol (argument));
356         }
357       break;
358
359     case 'o':
360     case 'u':
361     case 'x':
362     case 'X':
363       if (field_width < 0)
364         {
365           if (precision < 0)
366             printf (p, xstrtoul (argument));
367           else
368             printf (p, precision, xstrtoul (argument));
369         }
370       else
371         {
372           if (precision < 0)
373             printf (p, field_width, xstrtoul (argument));
374           else
375             printf (p, field_width, precision, xstrtoul (argument));
376         }
377       break;
378
379     case 'f':
380     case 'e':
381     case 'E':
382     case 'g':
383     case 'G':
384       if (field_width < 0)
385         {
386           if (precision < 0)
387             printf (p, xstrtod (argument));
388           else
389             printf (p, precision, xstrtod (argument));
390         }
391       else
392         {
393           if (precision < 0)
394             printf (p, field_width, xstrtod (argument));
395           else
396             printf (p, field_width, precision, xstrtod (argument));
397         }
398       break;
399
400     case 'c':
401       printf (p, *argument);
402       break;
403
404     case 's':
405       if (field_width < 0)
406         {
407           if (precision < 0)
408             printf (p, argument);
409           else
410             printf (p, precision, argument);
411         }
412       else
413         {
414           if (precision < 0)
415             printf (p, field_width, argument);
416           else
417             printf (p, field_width, precision, argument);
418         }
419       break;
420     }
421
422   free (p);
423 }
424
425 unsigned long
426 xstrtoul (s)
427      char *s;
428 {
429   char *end;
430   unsigned long val;
431
432   errno = 0;
433   val = strtoul (s, &end, 0);
434   verify (s, end);
435   return val;
436 }
437
438 long
439 xstrtol (s)
440      char *s;
441 {
442   char *end;
443   long val;
444
445   errno = 0;
446   val = strtol (s, &end, 0);
447   verify (s, end);
448   return val;
449 }
450
451 double
452 xstrtod (s)
453      char *s;
454 {
455   char *end;
456   double val;
457
458   errno = 0;
459   val = strtod (s, &end);
460   verify (s, end);
461   return val;
462 }
463
464 void
465 verify (s, end)
466      char *s, *end;
467 {
468   if (errno)
469     {
470       error (0, errno, "%s", s);
471       exit_status = 1;
472     }
473   else if (*end)
474     {
475       if (s == end)
476         error (0, 0, "%s: expected a numeric value", s);
477       else
478         error (0, 0, "%s: value not completely converted", s);
479       exit_status = 1;
480     }
481 }