.
[coreutils.git] / src / printf.c
blob836937e16ac2527a4dba8026ad9c66bf7839591f
1 /* printf - format and print data
2 Copyright (C) 90,91,92,93,94,95,96,97,1998 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Usage: printf format [argument...]
20 A front end to the printf function that lets it be used from the shell.
22 Backslash escapes:
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)
37 Additional directive:
39 %b = print an argument string, interpreting backslash escapes
41 The `format' argument is re-used as many times as necessary
42 to convert all of the given arguments.
44 David MacKenzie <djm@gnu.ai.mit.edu> */
46 #include <config.h>
47 #include <stdio.h>
48 #include <sys/types.h>
49 #include <getopt.h>
51 #include "system.h"
52 #include "long-options.h"
53 #include "error.h"
55 #ifndef STDC_HEADERS
56 double strtod ();
57 long int strtol ();
58 unsigned long int strtoul ();
59 #endif
61 #define isodigit(c) ((c) >= '0' && (c) <= '7')
62 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
63 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
64 #define octtobin(c) ((c) - '0')
66 /* The value to return to the calling program. */
67 static int exit_status;
69 /* Non-zero if the POSIXLY_CORRECT environment variable is set. */
70 static int posixly_correct;
72 /* This message appears in N_() here rather than just in _() below because
73 the sole use would have been in a #define, and xgettext doesn't look for
74 strings in cpp directives. */
75 static char *const cfcc_msg =
76 N_("warning: %s: character(s) following character constant have been ignored");
78 /* The name this program was run with. */
79 char *program_name;
81 static void
82 usage (int status)
84 if (status != 0)
85 fprintf (stderr, _("Try `%s --help' for more information.\n"),
86 program_name);
87 else
89 printf (_("\
90 Usage: %s FORMAT [ARGUMENT]...\n\
91 or: %s OPTION\n\
92 "),
93 program_name, program_name);
94 printf (_("\
95 Print ARGUMENT(s) according to FORMAT.\n\
96 \n\
97 --help display this help and exit\n\
98 --version output version information and exit\n\
99 \n\
100 FORMAT controls the output as in C printf. Interpreted sequences are:\n\
102 \\\" double quote\n\
103 \\0NNN character with octal value NNN (0 to 3 digits)\n\
104 \\\\ backslash\n\
105 \\a alert (BEL)\n\
106 \\b backspace\n\
107 \\c produce no further output\n\
108 \\f form feed\n\
109 \\n new line\n\
110 \\r carriage return\n\
111 \\t horizontal tab\n\
112 \\v vertical tab\n\
113 \\xNNN character with hexadecimal value NNN (1 to 3 digits)\n\
115 %%%% a single %%\n\
116 %%b ARGUMENT as a string with `\\' escapes interpreted\n\
118 and all C format specifications ending with one of diouxXfeEgGcs, with\n\
119 ARGUMENTs converted to proper type first. Variable widths are handled.\n\
120 "));
121 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
123 exit (status);
126 static void
127 verify (const char *s, const char *end)
129 if (errno)
131 error (0, errno, "%s", s);
132 exit_status = 1;
134 else if (*end)
136 if (s == end)
137 error (0, 0, _("%s: expected a numeric value"), s);
138 else
139 error (0, 0, _("%s: value not completely converted"), s);
140 exit_status = 1;
144 #define STRTOX(TYPE, FUNC_NAME, LIB_FUNC_EXPR) \
145 static TYPE \
146 FUNC_NAME (s) \
147 const char *s; \
149 char *end; \
150 TYPE val; \
152 if (*s == '\"' || *s == '\'') \
154 val = *(unsigned char *) ++s; \
155 /* If POSIXLY_CORRECT is not set, then give a warning that there \
156 are characters following the character constant and that GNU \
157 printf is ignoring those characters. If POSIXLY_CORRECT *is* \
158 set, then don't give the warning. */ \
159 if (*++s != 0 && !posixly_correct) \
160 error (0, 0, _(cfcc_msg), s); \
162 else \
164 errno = 0; \
165 val = LIB_FUNC_EXPR; \
166 verify (s, end); \
168 return val; \
171 STRTOX (unsigned long int, xstrtoul, (strtoul (s, &end, 0)))
172 STRTOX (long int, xstrtol, (strtol (s, &end, 0)))
173 STRTOX (double, xstrtod, (strtod (s, &end)))
175 /* Output a single-character \ escape. */
177 static void
178 print_esc_char (int c)
180 switch (c)
182 case 'a': /* Alert. */
183 putchar (7);
184 break;
185 case 'b': /* Backspace. */
186 putchar (8);
187 break;
188 case 'c': /* Cancel the rest of the output. */
189 exit (0);
190 break;
191 case 'f': /* Form feed. */
192 putchar (12);
193 break;
194 case 'n': /* New line. */
195 putchar (10);
196 break;
197 case 'r': /* Carriage return. */
198 putchar (13);
199 break;
200 case 't': /* Horizontal tab. */
201 putchar (9);
202 break;
203 case 'v': /* Vertical tab. */
204 putchar (11);
205 break;
206 default:
207 putchar (c);
208 break;
212 /* Print a \ escape sequence starting at ESCSTART.
213 Return the number of characters in the escape sequence
214 besides the backslash. */
216 static int
217 print_esc (const char *escstart)
219 register const char *p = escstart + 1;
220 int esc_value = 0; /* Value of \nnn escape. */
221 int esc_length; /* Length of \nnn escape. */
223 /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
224 if (*p == 'x')
226 for (esc_length = 0, ++p;
227 esc_length < 3 && ISXDIGIT (*p);
228 ++esc_length, ++p)
229 esc_value = esc_value * 16 + hextobin (*p);
230 if (esc_length == 0)
231 error (1, 0, _("missing hexadecimal number in escape"));
232 putchar (esc_value);
234 else if (*p == '0')
236 for (esc_length = 0, ++p;
237 esc_length < 3 && isodigit (*p);
238 ++esc_length, ++p)
239 esc_value = esc_value * 8 + octtobin (*p);
240 putchar (esc_value);
242 else if (strchr ("\"\\abcfnrtv", *p))
243 print_esc_char (*p++);
244 else
245 error (1, 0, _("\\%c: invalid escape"), *p);
246 return p - escstart - 1;
249 /* Print string STR, evaluating \ escapes. */
251 static void
252 print_esc_string (const char *str)
254 for (; *str; str++)
255 if (*str == '\\')
256 str += print_esc (str);
257 else
258 putchar (*str);
261 /* Output a % directive. START is the start of the directive,
262 LENGTH is its length, and ARGUMENT is its argument.
263 If FIELD_WIDTH or PRECISION is non-negative, they are args for
264 '*' values in those fields. */
266 static void
267 print_direc (const char *start, size_t length, int field_width, int precision, const char *argument)
269 char *p; /* Null-terminated copy of % directive. */
271 p = xmalloc ((unsigned) (length + 1));
272 strncpy (p, start, length);
273 p[length] = 0;
275 switch (p[length - 1])
277 case 'd':
278 case 'i':
279 if (field_width < 0)
281 if (precision < 0)
282 printf (p, xstrtol (argument));
283 else
284 printf (p, precision, xstrtol (argument));
286 else
288 if (precision < 0)
289 printf (p, field_width, xstrtol (argument));
290 else
291 printf (p, field_width, precision, xstrtol (argument));
293 break;
295 case 'o':
296 case 'u':
297 case 'x':
298 case 'X':
299 if (field_width < 0)
301 if (precision < 0)
302 printf (p, xstrtoul (argument));
303 else
304 printf (p, precision, xstrtoul (argument));
306 else
308 if (precision < 0)
309 printf (p, field_width, xstrtoul (argument));
310 else
311 printf (p, field_width, precision, xstrtoul (argument));
313 break;
315 case 'f':
316 case 'e':
317 case 'E':
318 case 'g':
319 case 'G':
320 if (field_width < 0)
322 if (precision < 0)
323 printf (p, xstrtod (argument));
324 else
325 printf (p, precision, xstrtod (argument));
327 else
329 if (precision < 0)
330 printf (p, field_width, xstrtod (argument));
331 else
332 printf (p, field_width, precision, xstrtod (argument));
334 break;
336 case 'c':
337 printf (p, *argument);
338 break;
340 case 's':
341 if (field_width < 0)
343 if (precision < 0)
344 printf (p, argument);
345 else
346 printf (p, precision, argument);
348 else
350 if (precision < 0)
351 printf (p, field_width, argument);
352 else
353 printf (p, field_width, precision, argument);
355 break;
358 free (p);
361 /* Print the text in FORMAT, using ARGV (with ARGC elements) for
362 arguments to any `%' directives.
363 Return the number of elements of ARGV used. */
365 static int
366 print_formatted (const char *format, int argc, char **argv)
368 int save_argc = argc; /* Preserve original value. */
369 const char *f; /* Pointer into `format'. */
370 const char *direc_start; /* Start of % directive. */
371 size_t direc_length; /* Length of % directive. */
372 int field_width; /* Arg to first '*', or -1 if none. */
373 int precision; /* Arg to second '*', or -1 if none. */
375 for (f = format; *f; ++f)
377 switch (*f)
379 case '%':
380 direc_start = f++;
381 direc_length = 1;
382 field_width = precision = -1;
383 if (*f == '%')
385 putchar ('%');
386 break;
388 if (*f == 'b')
390 if (argc > 0)
392 print_esc_string (*argv);
393 ++argv;
394 --argc;
396 break;
398 if (strchr ("-+ #", *f))
400 ++f;
401 ++direc_length;
403 if (*f == '*')
405 ++f;
406 ++direc_length;
407 if (argc > 0)
409 field_width = xstrtoul (*argv);
410 ++argv;
411 --argc;
413 else
414 field_width = 0;
416 else
417 while (ISDIGIT (*f))
419 ++f;
420 ++direc_length;
422 if (*f == '.')
424 ++f;
425 ++direc_length;
426 if (*f == '*')
428 ++f;
429 ++direc_length;
430 if (argc > 0)
432 precision = xstrtoul (*argv);
433 ++argv;
434 --argc;
436 else
437 precision = 0;
439 else
440 while (ISDIGIT (*f))
442 ++f;
443 ++direc_length;
446 if (*f == 'l' || *f == 'L' || *f == 'h')
448 ++f;
449 ++direc_length;
451 if (!strchr ("diouxXfeEgGcs", *f))
452 error (1, 0, _("%%%c: invalid directive"), *f);
453 ++direc_length;
454 if (argc > 0)
456 print_direc (direc_start, direc_length, field_width,
457 precision, *argv);
458 ++argv;
459 --argc;
461 else
462 print_direc (direc_start, direc_length, field_width,
463 precision, "");
464 break;
466 case '\\':
467 f += print_esc (f);
468 break;
470 default:
471 putchar (*f);
475 return save_argc - argc;
479 main (int argc, char **argv)
481 char *format;
482 int args_used;
484 program_name = argv[0];
485 setlocale (LC_ALL, "");
486 bindtextdomain (PACKAGE, LOCALEDIR);
487 textdomain (PACKAGE);
489 exit_status = 0;
491 /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
492 posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
493 if (!posixly_correct)
494 parse_long_options (argc, argv, "printf", GNU_PACKAGE, VERSION, usage);
496 if (argc == 1)
498 fprintf (stderr, _("Usage: %s format [argument...]\n"), program_name);
499 exit (1);
502 format = argv[1];
503 argc -= 2;
504 argv += 2;
508 args_used = print_formatted (format, argc, argv);
509 argc -= args_used;
510 argv += args_used;
512 while (args_used > 0 && argc > 0);
514 if (argc > 0)
515 error (0, 0, _("warning: excess arguments have been ignored"));
517 exit (exit_status);