diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-01-20 10:55:18 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-01-20 10:55:18 +0000 |
commit | 70e9163c9c18e995515598085cb824e554eb7ae7 (patch) | |
tree | a42dc8b2a6c031354bf31472de888bfc8a060132 /src/printf.c | |
parent | cbf5993c43f49281173f185863577d86bfac6eae (diff) | |
download | coreutils-tarball-master.tar.gz |
coreutils-8.25HEADcoreutils-8.25master
Diffstat (limited to 'src/printf.c')
-rw-r--r-- | src/printf.c | 587 |
1 files changed, 307 insertions, 280 deletions
diff --git a/src/printf.c b/src/printf.c index 6205153..0885f8f 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,10 +1,10 @@ /* printf - format and print data - Copyright (C) 1990-2007 Free Software Foundation, Inc. + Copyright (C) 1990-2016 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,8 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Usage: printf format [argument...] @@ -26,6 +25,7 @@ \a = alert (bell) \b = backspace \c = produce no further output + \e = escape \f = form feed \n = new line \r = carriage return @@ -41,7 +41,11 @@ %b = print an argument string, interpreting backslash escapes, except that octal escapes are of the form \0 or \0ooo. - The `format' argument is re-used as many times as necessary + %q = print an argument string in a format that can be + reused as shell input. Escaped characters used the proposed + POSIX $'' syntax supported by most shells. + + The 'format' argument is re-used as many times as necessary to convert all of the given arguments. David MacKenzie <djm@gnu.ai.mit.edu> */ @@ -49,23 +53,22 @@ #include <config.h> #include <stdio.h> #include <sys/types.h> -#include <getopt.h> #include "system.h" #include "c-strtod.h" #include "error.h" -#include "long-options.h" #include "quote.h" #include "unicodeio.h" +#include "xprintf.h" -/* The official name of this program (e.g., no `g' prefix). */ +/* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "printf" -#define AUTHORS "David MacKenzie" +#define AUTHORS proper_name ("David MacKenzie") #define isodigit(c) ((c) >= '0' && (c) <= '7') #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \ - (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0') + (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0') #define octtobin(c) ((c) - '0') /* The value to return to the calling program. */ @@ -79,24 +82,20 @@ static bool posixly_correct; static char const *const cfcc_msg = N_("warning: %s: character(s) following character constant have been ignored"); -/* The name this program was run with. */ -char *program_name; - void usage (int status) { if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + emit_try_help (); else { printf (_("\ Usage: %s FORMAT [ARGUMENT]...\n\ or: %s OPTION\n\ "), - program_name, program_name); + program_name, program_name); fputs (_("\ -Print ARGUMENT(s) according to FORMAT.\n\ +Print ARGUMENT(s) according to FORMAT, or execute according to OPTION:\n\ \n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); @@ -106,36 +105,37 @@ Print ARGUMENT(s) according to FORMAT.\n\ FORMAT controls the output as in C printf. Interpreted sequences are:\n\ \n\ \\\" double quote\n\ - \\NNN character with octal value NNN (1 to 3 digits)\n\ - \\\\ backslash\n\ "), stdout); fputs (_("\ + \\\\ backslash\n\ \\a alert (BEL)\n\ \\b backspace\n\ \\c produce no further output\n\ + \\e escape\n\ \\f form feed\n\ -"), stdout); - fputs (_("\ \\n new line\n\ \\r carriage return\n\ \\t horizontal tab\n\ \\v vertical tab\n\ "), stdout); fputs (_("\ + \\NNN byte with octal value NNN (1 to 3 digits)\n\ \\xHH byte with hexadecimal value HH (1 to 2 digits)\n\ \\uHHHH Unicode (ISO/IEC 10646) character with hex value HHHH (4 digits)\n\ \\UHHHHHHHH Unicode character with hex value HHHHHHHH (8 digits)\n\ "), stdout); fputs (_("\ %% a single %\n\ - %b ARGUMENT as a string with `\\' escapes interpreted,\n\ + %b ARGUMENT as a string with '\\' escapes interpreted,\n\ except that octal escapes are of the form \\0 or \\0NNN\n\ -\n\ + %q ARGUMENT is printed in a format that can be reused as shell input,\n\ + escaping non-printable characters with the proposed POSIX $'' syntax.\ +\n\n\ and all C format specifications ending with one of diouxXfeEgGcs, with\n\ ARGUMENTs converted to proper type first. Variable widths are handled.\n\ "), stdout); printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + emit_ancillary_info (PROGRAM_NAME); } exit (status); } @@ -145,15 +145,15 @@ verify_numeric (const char *s, const char *end) { if (errno) { - error (0, errno, "%s", s); + error (0, errno, "%s", quote (s)); exit_status = EXIT_FAILURE; } else if (*end) { if (s == end) - error (0, 0, _("%s: expected a numeric value"), s); + error (0, 0, _("%s: expected a numeric value"), quote (s)); else - error (0, 0, _("%s: value not completely converted"), s); + error (0, 0, _("%s: value not completely converted"), quote (s)); exit_status = EXIT_FAILURE; } } @@ -164,17 +164,17 @@ FUNC_NAME (char const *s) \ { \ char *end; \ TYPE val; \ - \ - if (*s == '\"' || *s == '\'') \ + \ + if ((*s == '\"' || *s == '\'') && *(s + 1)) \ { \ unsigned char ch = *++s; \ val = ch; \ /* If POSIXLY_CORRECT is not set, then give a warning that there \ - are characters following the character constant and that GNU \ - printf is ignoring those characters. If POSIXLY_CORRECT *is* \ - set, then don't give the warning. */ \ + are characters following the character constant and that GNU \ + printf is ignoring those characters. If POSIXLY_CORRECT *is* \ + set, then don't give the warning. */ \ if (*++s != 0 && !posixly_correct) \ - error (0, 0, _(cfcc_msg), s); \ + error (0, 0, _(cfcc_msg), s); \ } \ else \ { \ @@ -205,6 +205,9 @@ print_esc_char (char c) case 'c': /* Cancel the rest of the output. */ exit (EXIT_SUCCESS); break; + case 'e': /* Escape. */ + putchar ('\x1B'); + break; case 'f': /* Form feed. */ putchar ('\f'); break; @@ -243,11 +246,11 @@ print_esc (const char *escstart, bool octal_0) { /* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */ for (esc_length = 0, ++p; - esc_length < 2 && isxdigit (to_uchar (*p)); - ++esc_length, ++p) - esc_value = esc_value * 16 + hextobin (*p); + esc_length < 2 && isxdigit (to_uchar (*p)); + ++esc_length, ++p) + esc_value = esc_value * 16 + hextobin (*p); if (esc_length == 0) - error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape")); + error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape")); putchar (esc_value); } else if (isodigit (*p)) @@ -256,12 +259,12 @@ print_esc (const char *escstart, bool octal_0) Allow \ooo if octal_0 && *p != '0'; this is an undocumented extension to POSIX that is compatible with Bash 2.05b. */ for (esc_length = 0, p += octal_0 && *p == '0'; - esc_length < 3 && isodigit (*p); - ++esc_length, ++p) - esc_value = esc_value * 8 + octtobin (*p); + esc_length < 3 && isodigit (*p); + ++esc_length, ++p) + esc_value = esc_value * 8 + octtobin (*p); putchar (esc_value); } - else if (*p && strchr ("\"\\abcfnrtv", *p)) + else if (*p && strchr ("\"\\abcefnrtv", *p)) print_esc_char (*p++); else if (*p == 'u' || *p == 'U') { @@ -270,24 +273,24 @@ print_esc (const char *escstart, bool octal_0) uni_value = 0; for (esc_length = (esc_char == 'u' ? 4 : 8), ++p; - esc_length > 0; - --esc_length, ++p) - { - if (! isxdigit (to_uchar (*p))) - error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape")); - uni_value = uni_value * 16 + hextobin (*p); - } + esc_length > 0; + --esc_length, ++p) + { + if (! isxdigit (to_uchar (*p))) + error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape")); + uni_value = uni_value * 16 + hextobin (*p); + } /* A universal character name shall not specify a character short - identifier in the range 00000000 through 00000020, 0000007F through - 0000009F, or 0000D800 through 0000DFFF inclusive. A universal - character name shall not designate a character in the required - character set. */ + identifier in the range 00000000 through 00000020, 0000007F through + 0000009F, or 0000D800 through 0000DFFF inclusive. A universal + character name shall not designate a character in the required + character set. */ if ((uni_value <= 0x9f - && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60) - || (uni_value >= 0xd800 && uni_value <= 0xdfff)) - error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"), - esc_char, (esc_char == 'u' ? 4 : 8), uni_value); + && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60) + || (uni_value >= 0xd800 && uni_value <= 0xdfff)) + error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"), + esc_char, (esc_char == 'u' ? 4 : 8), uni_value); print_unicode_char (stdout, uni_value, 0); } @@ -295,10 +298,10 @@ print_esc (const char *escstart, bool octal_0) { putchar ('\\'); if (*p) - { - putchar (*p); - p++; - } + { + putchar (*p); + p++; + } } return p - escstart - 1; } @@ -325,9 +328,9 @@ print_esc_string (const char *str) static void print_direc (const char *start, size_t length, char conversion, - bool have_field_width, int field_width, - bool have_precision, int precision, - char const *argument) + bool have_field_width, int field_width, + bool have_precision, int precision, + char const *argument) { char *p; /* Null-terminated copy of % directive. */ @@ -342,20 +345,20 @@ print_direc (const char *start, size_t length, char conversion, switch (conversion) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': - length_modifier = PRIdMAX; - length_modifier_len = sizeof PRIdMAX - 2; - break; + length_modifier = PRIdMAX; + length_modifier_len = sizeof PRIdMAX - 2; + break; case 'a': case 'e': case 'f': case 'g': case 'A': case 'E': case 'F': case 'G': - length_modifier = "L"; - length_modifier_len = 1; - break; + length_modifier = "L"; + length_modifier_len = 1; + break; default: - length_modifier = start; /* Any valid pointer will do. */ - length_modifier_len = 0; - break; + length_modifier = start; /* Any valid pointer will do. */ + length_modifier_len = 0; + break; } p = xmalloc (length + length_modifier_len + 2); @@ -370,21 +373,21 @@ print_direc (const char *start, size_t length, char conversion, case 'd': case 'i': { - intmax_t arg = vstrtoimax (argument); - if (!have_field_width) - { - if (!have_precision) - printf (p, arg); - else - printf (p, precision, arg); - } - else - { - if (!have_precision) - printf (p, field_width, arg); - else - printf (p, field_width, precision, arg); - } + intmax_t arg = vstrtoimax (argument); + if (!have_field_width) + { + if (!have_precision) + xprintf (p, arg); + else + xprintf (p, precision, arg); + } + else + { + if (!have_precision) + xprintf (p, field_width, arg); + else + xprintf (p, field_width, precision, arg); + } } break; @@ -393,21 +396,21 @@ print_direc (const char *start, size_t length, char conversion, case 'x': case 'X': { - uintmax_t arg = vstrtoumax (argument); - if (!have_field_width) - { - if (!have_precision) - printf (p, arg); - else - printf (p, precision, arg); - } - else - { - if (!have_precision) - printf (p, field_width, arg); - else - printf (p, field_width, precision, arg); - } + uintmax_t arg = vstrtoumax (argument); + if (!have_field_width) + { + if (!have_precision) + xprintf (p, arg); + else + xprintf (p, precision, arg); + } + else + { + if (!have_precision) + xprintf (p, field_width, arg); + else + xprintf (p, field_width, precision, arg); + } } break; @@ -420,46 +423,46 @@ print_direc (const char *start, size_t length, char conversion, case 'g': case 'G': { - long double arg = vstrtold (argument); - if (!have_field_width) - { - if (!have_precision) - printf (p, arg); - else - printf (p, precision, arg); - } - else - { - if (!have_precision) - printf (p, field_width, arg); - else - printf (p, field_width, precision, arg); - } + long double arg = vstrtold (argument); + if (!have_field_width) + { + if (!have_precision) + xprintf (p, arg); + else + xprintf (p, precision, arg); + } + else + { + if (!have_precision) + xprintf (p, field_width, arg); + else + xprintf (p, field_width, precision, arg); + } } break; case 'c': if (!have_field_width) - printf (p, *argument); + xprintf (p, *argument); else - printf (p, field_width, *argument); + xprintf (p, field_width, *argument); break; case 's': if (!have_field_width) - { - if (!have_precision) - printf (p, argument); - else - printf (p, precision, argument); - } + { + if (!have_precision) + xprintf (p, argument); + else + xprintf (p, precision, argument); + } else - { - if (!have_precision) - printf (p, field_width, argument); - else - printf (p, field_width, precision, argument); - } + { + if (!have_precision) + xprintf (p, field_width, argument); + else + xprintf (p, field_width, precision, argument); + } break; } @@ -467,14 +470,14 @@ print_direc (const char *start, size_t length, char conversion, } /* Print the text in FORMAT, using ARGV (with ARGC elements) for - arguments to any `%' directives. + arguments to any '%' directives. Return the number of elements of ARGV used. */ static int print_formatted (const char *format, int argc, char **argv) { int save_argc = argc; /* Preserve original value. */ - const char *f; /* Pointer into `format'. */ + const char *f; /* Pointer into 'format'. */ const char *direc_start; /* Start of % directive. */ size_t direc_length; /* Length of % directive. */ bool have_field_width; /* True if FIELD_WIDTH is valid. */ @@ -486,146 +489,158 @@ print_formatted (const char *format, int argc, char **argv) for (f = format; *f; ++f) { switch (*f) - { - case '%': - direc_start = f++; - direc_length = 1; - have_field_width = have_precision = false; - if (*f == '%') - { - putchar ('%'); - break; - } - if (*f == 'b') - { - /* FIXME: Field width and precision are not supported - for %b, even though POSIX requires it. */ - if (argc > 0) - { - print_esc_string (*argv); - ++argv; - --argc; - } - break; - } - - memset (ok, 0, sizeof ok); - ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] = - ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] = - ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1; - - for (;; f++, direc_length++) - switch (*f) - { + { + case '%': + direc_start = f++; + direc_length = 1; + have_field_width = have_precision = false; + if (*f == '%') + { + putchar ('%'); + break; + } + if (*f == 'b') + { + /* FIXME: Field width and precision are not supported + for %b, even though POSIX requires it. */ + if (argc > 0) + { + print_esc_string (*argv); + ++argv; + --argc; + } + break; + } + + if (*f == 'q') + { + if (argc > 0) + { + fputs (quotearg_style (shell_escape_quoting_style, *argv), + stdout); + ++argv; + --argc; + } + break; + } + + memset (ok, 0, sizeof ok); + ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] = + ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] = + ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1; + + for (;; f++, direc_length++) + switch (*f) + { #if (__GLIBC__ == 2 && 2 <= __GLIBC_MINOR__) || 3 <= __GLIBC__ - case 'I': + case 'I': #endif - case '\'': - ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = - ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0; - break; - case '-': case '+': case ' ': - break; - case '#': - ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0; - break; - case '0': - ok['c'] = ok['s'] = 0; - break; - default: - goto no_more_flag_characters; - } - no_more_flag_characters:; - - if (*f == '*') - { - ++f; - ++direc_length; - if (argc > 0) - { - intmax_t width = vstrtoimax (*argv); - if (INT_MIN <= width && width <= INT_MAX) - field_width = width; - else - error (EXIT_FAILURE, 0, _("invalid field width: %s"), - *argv); - ++argv; - --argc; - } - else - field_width = 0; - have_field_width = true; - } - else - while (ISDIGIT (*f)) - { - ++f; - ++direc_length; - } - if (*f == '.') - { - ++f; - ++direc_length; - ok['c'] = 0; - if (*f == '*') - { - ++f; - ++direc_length; - if (argc > 0) - { - intmax_t prec = vstrtoimax (*argv); - if (prec < 0) - { - /* A negative precision is taken as if the - precision were omitted, so -1 is safe - here even if prec < INT_MIN. */ - precision = -1; - } - else if (INT_MAX < prec) - error (EXIT_FAILURE, 0, _("invalid precision: %s"), - *argv); - else - precision = prec; - ++argv; - --argc; - } - else - precision = 0; - have_precision = true; - } - else - while (ISDIGIT (*f)) - { - ++f; - ++direc_length; - } - } - - while (*f == 'l' || *f == 'L' || *f == 'h' - || *f == 'j' || *f == 't' || *f == 'z') - ++f; - - { - unsigned char conversion = *f; - if (! ok[conversion]) - error (EXIT_FAILURE, 0, - _("%.*s: invalid conversion specification"), - (int) (f + 1 - direc_start), direc_start); - } - - print_direc (direc_start, direc_length, *f, - have_field_width, field_width, - have_precision, precision, - (argc <= 0 ? "" : (argc--, *argv++))); - break; - - case '\\': - f += print_esc (f, false); - break; - - default: - putchar (*f); - } + case '\'': + ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = + ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0; + break; + case '-': case '+': case ' ': + break; + case '#': + ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0; + break; + case '0': + ok['c'] = ok['s'] = 0; + break; + default: + goto no_more_flag_characters; + } + no_more_flag_characters: + + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + intmax_t width = vstrtoimax (*argv); + if (INT_MIN <= width && width <= INT_MAX) + field_width = width; + else + error (EXIT_FAILURE, 0, _("invalid field width: %s"), + quote (*argv)); + ++argv; + --argc; + } + else + field_width = 0; + have_field_width = true; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + if (*f == '.') + { + ++f; + ++direc_length; + ok['c'] = 0; + if (*f == '*') + { + ++f; + ++direc_length; + if (argc > 0) + { + intmax_t prec = vstrtoimax (*argv); + if (prec < 0) + { + /* A negative precision is taken as if the + precision were omitted, so -1 is safe + here even if prec < INT_MIN. */ + precision = -1; + } + else if (INT_MAX < prec) + error (EXIT_FAILURE, 0, _("invalid precision: %s"), + quote (*argv)); + else + precision = prec; + ++argv; + --argc; + } + else + precision = 0; + have_precision = true; + } + else + while (ISDIGIT (*f)) + { + ++f; + ++direc_length; + } + } + + while (*f == 'l' || *f == 'L' || *f == 'h' + || *f == 'j' || *f == 't' || *f == 'z') + ++f; + + { + unsigned char conversion = *f; + if (! ok[conversion]) + error (EXIT_FAILURE, 0, + _("%.*s: invalid conversion specification"), + (int) (f + 1 - direc_start), direc_start); + } + + print_direc (direc_start, direc_length, *f, + have_field_width, field_width, + have_precision, precision, + (argc <= 0 ? "" : (argc--, *argv++))); + break; + + case '\\': + f += print_esc (f, false); + break; + + default: + putchar (*f); + } } return save_argc - argc; @@ -638,7 +653,7 @@ main (int argc, char **argv) int args_used; initialize_main (&argc, &argv); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); @@ -649,11 +664,23 @@ main (int argc, char **argv) posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL); - parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION, - usage, AUTHORS, (char const *) NULL); + /* We directly parse options, rather than use parse_long_options, in + order to avoid accepting abbreviations. */ + if (argc == 2) + { + if (STREQ (argv[1], "--help")) + usage (EXIT_SUCCESS); + + if (STREQ (argv[1], "--version")) + { + version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS, + (char *) NULL); + return EXIT_SUCCESS; + } + } /* The above handles --help and --version. - Since there is no other invocation of getopt, handle `--' here. */ + Since there is no other invocation of getopt, handle '--' here. */ if (1 < argc && STREQ (argv[1], "--")) { --argc; @@ -680,8 +707,8 @@ main (int argc, char **argv) if (argc > 0) error (0, 0, - _("warning: ignoring excess arguments, starting with %s"), - quote (argv[0])); + _("warning: ignoring excess arguments, starting with %s"), + quote (argv[0])); - exit (exit_status); + return exit_status; } |