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