(invalid-j): New partial test for the above fix.
[coreutils.git] / src / printf.c
blobf812212f5f6faa767ec2f1573c6ce33800b49d6c
1 /* printf - format and print data
2 Copyright (C) 1990-2004 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 \ooo = octal number (ooo is 1 to 3 digits)
35 \xhh = hexadecimal number (hhh is 1 to 2 digits)
36 \uhhhh = 16-bit Unicode character (hhhh is 4 digits)
37 \Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits)
39 Additional directive:
41 %b = print an argument string, interpreting backslash escapes,
42 except that octal escapes are of the form \0 or \0ooo.
44 The `format' argument is re-used as many times as necessary
45 to convert all of the given arguments.
47 David MacKenzie <djm@gnu.ai.mit.edu> */
49 #include <config.h>
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <getopt.h>
54 #include "system.h"
55 #include "c-strtod.h"
56 #include "long-options.h"
57 #include "error.h"
58 #include "unicodeio.h"
60 /* The official name of this program (e.g., no `g' prefix). */
61 #define PROGRAM_NAME "printf"
63 #define AUTHORS "David MacKenzie"
65 #define isodigit(c) ((c) >= '0' && (c) <= '7')
66 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
67 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
68 #define octtobin(c) ((c) - '0')
70 /* A value for field_width or precision that indicates it was not specified. */
71 #define UNSPECIFIED INT_MIN
73 /* The value to return to the calling program. */
74 static int exit_status;
76 /* Non-zero if the POSIXLY_CORRECT environment variable is set. */
77 static int posixly_correct;
79 /* This message appears in N_() here rather than just in _() below because
80 the sole use would have been in a #define. */
81 static char *const cfcc_msg =
82 N_("warning: %s: character(s) following character constant have been ignored");
84 /* The name this program was run with. */
85 char *program_name;
87 void
88 usage (int status)
90 if (status != EXIT_SUCCESS)
91 fprintf (stderr, _("Try `%s --help' for more information.\n"),
92 program_name);
93 else
95 printf (_("\
96 Usage: %s FORMAT [ARGUMENT]...\n\
97 or: %s OPTION\n\
98 "),
99 program_name, program_name);
100 fputs (_("\
101 Print ARGUMENT(s) according to FORMAT.\n\
103 "), stdout);
104 fputs (HELP_OPTION_DESCRIPTION, stdout);
105 fputs (VERSION_OPTION_DESCRIPTION, stdout);
106 fputs (_("\
108 FORMAT controls the output as in C printf. Interpreted sequences are:\n\
110 \\\" double quote\n\
111 \\NNN character with octal value NNN (1 to 3 digits)\n\
112 \\\\ backslash\n\
113 "), stdout);
114 fputs (_("\
115 \\a alert (BEL)\n\
116 \\b backspace\n\
117 \\c produce no further output\n\
118 \\f form feed\n\
119 "), stdout);
120 fputs (_("\
121 \\n new line\n\
122 \\r carriage return\n\
123 \\t horizontal tab\n\
124 \\v vertical tab\n\
125 "), stdout);
126 fputs (_("\
127 \\xNN byte with hexadecimal value NN (1 to 2 digits)\n\
129 \\uNNNN character with hexadecimal value NNNN (4 digits)\n\
130 \\UNNNNNNNN character with hexadecimal value NNNNNNNN (8 digits)\n\
131 "), stdout);
132 fputs (_("\
133 %% a single %\n\
134 %b ARGUMENT as a string with `\\' escapes interpreted,\n\
135 except that octal escapes are of the form \\0 or \\0NNN\n\
137 and all C format specifications ending with one of diouxXfeEgGcs, with\n\
138 ARGUMENTs converted to proper type first. Variable widths are handled.\n\
139 "), stdout);
140 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
142 exit (status);
145 static void
146 verify (const char *s, const char *end)
148 if (errno)
150 error (0, errno, "%s", s);
151 exit_status = 1;
153 else if (*end)
155 if (s == end)
156 error (0, 0, _("%s: expected a numeric value"), s);
157 else
158 error (0, 0, _("%s: value not completely converted"), s);
159 exit_status = 1;
163 #define STRTOX(TYPE, FUNC_NAME, LIB_FUNC_EXPR) \
164 static TYPE \
165 FUNC_NAME (s) \
166 const char *s; \
168 char *end; \
169 TYPE val; \
171 if (*s == '\"' || *s == '\'') \
173 val = *(unsigned char *) ++s; \
174 /* If POSIXLY_CORRECT is not set, then give a warning that there \
175 are characters following the character constant and that GNU \
176 printf is ignoring those characters. If POSIXLY_CORRECT *is* \
177 set, then don't give the warning. */ \
178 if (*++s != 0 && !posixly_correct) \
179 error (0, 0, _(cfcc_msg), s); \
181 else \
183 errno = 0; \
184 val = LIB_FUNC_EXPR; \
185 verify (s, end); \
187 return val; \
190 STRTOX (unsigned long int, xstrtoul, (strtoul (s, &end, 0)))
191 STRTOX (long int, xstrtol, (strtol (s, &end, 0)))
192 STRTOX (double, xstrtod, (c_strtod (s, &end)))
194 /* Output a single-character \ escape. */
196 static void
197 print_esc_char (int c)
199 switch (c)
201 case 'a': /* Alert. */
202 putchar (7);
203 break;
204 case 'b': /* Backspace. */
205 putchar (8);
206 break;
207 case 'c': /* Cancel the rest of the output. */
208 exit (EXIT_SUCCESS);
209 break;
210 case 'f': /* Form feed. */
211 putchar (12);
212 break;
213 case 'n': /* New line. */
214 putchar (10);
215 break;
216 case 'r': /* Carriage return. */
217 putchar (13);
218 break;
219 case 't': /* Horizontal tab. */
220 putchar (9);
221 break;
222 case 'v': /* Vertical tab. */
223 putchar (11);
224 break;
225 default:
226 putchar (c);
227 break;
231 /* Print a \ escape sequence starting at ESCSTART.
232 Return the number of characters in the escape sequence
233 besides the backslash.
234 If OCTAL_0 is nonzero, octal escapes are of the form \0ooo, where o
235 is an octal digit; otherwise they are of the form \ooo. */
237 static int
238 print_esc (const char *escstart, bool octal_0)
240 register const char *p = escstart + 1;
241 int esc_value = 0; /* Value of \nnn escape. */
242 int esc_length; /* Length of \nnn escape. */
244 if (!posixly_correct && *p == 'x')
246 /* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */
247 for (esc_length = 0, ++p;
248 esc_length < 2 && ISXDIGIT (*p);
249 ++esc_length, ++p)
250 esc_value = esc_value * 16 + hextobin (*p);
251 if (esc_length == 0)
252 error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
253 putchar (esc_value);
255 else if (isodigit (*p))
257 /* Parse \0ooo (if octal_0 && *p == '0') or \ooo (otherwise).
258 Allow \ooo if octal_0 && *p != '0'; this is an undocumented
259 extension to POSIX that is compatible with Bash 2.05b. */
260 for (esc_length = 0, p += octal_0 && *p == '0';
261 esc_length < 3 && isodigit (*p);
262 ++esc_length, ++p)
263 esc_value = esc_value * 8 + octtobin (*p);
264 putchar (esc_value);
266 else if (*p && strchr ("\"\\abcfnrtv", *p))
267 print_esc_char (*p++);
268 else if (!posixly_correct && (*p == 'u' || *p == 'U'))
270 char esc_char = *p;
271 unsigned int uni_value;
273 uni_value = 0;
274 for (esc_length = (esc_char == 'u' ? 4 : 8), ++p;
275 esc_length > 0;
276 --esc_length, ++p)
278 if (!ISXDIGIT (*p))
279 error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
280 uni_value = uni_value * 16 + hextobin (*p);
283 /* A universal character name shall not specify a character short
284 identifier in the range 00000000 through 00000020, 0000007F through
285 0000009F, or 0000D800 through 0000DFFF inclusive. A universal
286 character name shall not designate a character in the required
287 character set. */
288 if ((uni_value <= 0x9f
289 && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60)
290 || (uni_value >= 0xd800 && uni_value <= 0xdfff))
291 error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"),
292 esc_char, (esc_char == 'u' ? 4 : 8), uni_value);
294 print_unicode_char (stdout, uni_value, 0);
296 else
298 putchar ('\\');
299 if (*p)
301 putchar (*p);
302 p++;
305 return p - escstart - 1;
308 /* Print string STR, evaluating \ escapes. */
310 static void
311 print_esc_string (const char *str)
313 for (; *str; str++)
314 if (*str == '\\')
315 str += print_esc (str, true);
316 else
317 putchar (*str);
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 UNSPECIFIED, they are args for
323 '*' values in those fields. */
325 static void
326 print_direc (const char *start, size_t length, int field_width,
327 int precision, const char *argument)
329 char *p; /* Null-terminated copy of % directive. */
331 p = xmalloc ((unsigned) (length + 1));
332 strncpy (p, start, length);
333 p[length] = 0;
335 switch (p[length - 1])
337 case 'd':
338 case 'i':
339 if (field_width == UNSPECIFIED)
341 if (precision == UNSPECIFIED)
342 printf (p, xstrtol (argument));
343 else
344 printf (p, precision, xstrtol (argument));
346 else
348 if (precision == UNSPECIFIED)
349 printf (p, field_width, xstrtol (argument));
350 else
351 printf (p, field_width, precision, xstrtol (argument));
353 break;
355 case 'o':
356 case 'u':
357 case 'x':
358 case 'X':
359 if (field_width == UNSPECIFIED)
361 if (precision == UNSPECIFIED)
362 printf (p, xstrtoul (argument));
363 else
364 printf (p, precision, xstrtoul (argument));
366 else
368 if (precision == UNSPECIFIED)
369 printf (p, field_width, xstrtoul (argument));
370 else
371 printf (p, field_width, precision, xstrtoul (argument));
373 break;
375 case 'f':
376 case 'e':
377 case 'E':
378 case 'g':
379 case 'G':
380 if (field_width == UNSPECIFIED)
382 if (precision == UNSPECIFIED)
383 printf (p, xstrtod (argument));
384 else
385 printf (p, precision, xstrtod (argument));
387 else
389 if (precision == UNSPECIFIED)
390 printf (p, field_width, xstrtod (argument));
391 else
392 printf (p, field_width, precision, xstrtod (argument));
394 break;
396 case 'c':
397 printf (p, *argument);
398 break;
400 case 's':
401 if (field_width == UNSPECIFIED)
403 if (precision == UNSPECIFIED)
404 printf (p, argument);
405 else
406 printf (p, precision, argument);
408 else
410 if (precision == UNSPECIFIED)
411 printf (p, field_width, argument);
412 else
413 printf (p, field_width, precision, argument);
415 break;
418 free (p);
421 /* Print the text in FORMAT, using ARGV (with ARGC elements) for
422 arguments to any `%' directives.
423 Return the number of elements of ARGV used. */
425 static int
426 print_formatted (const char *format, int argc, char **argv)
428 int save_argc = argc; /* Preserve original value. */
429 const char *f; /* Pointer into `format'. */
430 const char *direc_start; /* Start of % directive. */
431 size_t direc_length; /* Length of % directive. */
432 int field_width; /* Arg to first '*', or UNSPECIFIED if none. */
433 int precision; /* Arg to second '*', or UNSPECIFIED if none. */
435 for (f = format; *f; ++f)
437 switch (*f)
439 case '%':
440 direc_start = f++;
441 direc_length = 1;
442 field_width = precision = UNSPECIFIED;
443 if (*f == '%')
445 putchar ('%');
446 break;
448 if (*f == 'b')
450 if (argc > 0)
452 print_esc_string (*argv);
453 ++argv;
454 --argc;
456 break;
458 while (*f == ' ' || *f == '#' || *f == '+' || *f == '-')
460 ++f;
461 ++direc_length;
463 if (*f == '*')
465 ++f;
466 ++direc_length;
467 if (argc > 0)
469 field_width = xstrtoul (*argv);
470 if (field_width == UNSPECIFIED)
471 error (EXIT_FAILURE, 0, _("invalid field width: %s"),
472 *argv);
473 ++argv;
474 --argc;
476 else
477 field_width = 0;
479 else
480 while (ISDIGIT (*f))
482 ++f;
483 ++direc_length;
485 if (*f == '.')
487 ++f;
488 ++direc_length;
489 if (*f == '*')
491 ++f;
492 ++direc_length;
493 if (argc > 0)
495 precision = xstrtoul (*argv);
496 if (precision == UNSPECIFIED)
497 error (EXIT_FAILURE, 0, _("invalid precision: %s"),
498 *argv);
499 ++argv;
500 --argc;
502 else
503 precision = 0;
505 else
506 while (ISDIGIT (*f))
508 ++f;
509 ++direc_length;
512 if (*f == 'l' || *f == 'L' || *f == 'h')
514 ++f;
515 ++direc_length;
517 if (! (*f && strchr ("diouxXfeEgGcs", *f)))
518 error (EXIT_FAILURE, 0, _("%%%c: invalid directive"), *f);
519 ++direc_length;
520 if (argc > 0)
522 print_direc (direc_start, direc_length, field_width,
523 precision, *argv);
524 ++argv;
525 --argc;
527 else
528 print_direc (direc_start, direc_length, field_width,
529 precision, "");
530 break;
532 case '\\':
533 f += print_esc (f, false);
534 break;
536 default:
537 putchar (*f);
541 return save_argc - argc;
545 main (int argc, char **argv)
547 char *format;
548 int args_used;
550 initialize_main (&argc, &argv);
551 program_name = argv[0];
552 setlocale (LC_ALL, "");
553 bindtextdomain (PACKAGE, LOCALEDIR);
554 textdomain (PACKAGE);
556 atexit (close_stdout);
558 exit_status = 0;
560 /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
561 posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
562 if (!posixly_correct)
563 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
564 usage, AUTHORS, (char const *) NULL);
566 /* The above handles --help and --version.
567 Since there is no other invocation of getopt, handle `--' here. */
568 if (1 < argc && STREQ (argv[1], "--"))
570 --argc;
571 ++argv;
574 if (argc <= 1)
576 fprintf (stderr, _("Usage: %s format [argument...]\n"), program_name);
577 exit (EXIT_FAILURE);
580 format = argv[1];
581 argc -= 2;
582 argv += 2;
586 args_used = print_formatted (format, argc, argv);
587 argc -= args_used;
588 argv += args_used;
590 while (args_used > 0 && argc > 0);
592 if (argc > 0)
593 error (0, 0,
594 _("warning: ignoring excess arguments, starting with `%s'"),
595 argv[0]);
597 exit (exit_status);