diff options
author | Jim Meyering <jim@meyering.net> | 1997-01-23 03:38:23 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1997-01-23 03:38:23 +0000 |
commit | 2611844743b1d9a0fa1ca3ce02de6295b2be18b9 (patch) | |
tree | b3bc60487c6a28f173a34fbb696cc550f1e7c92e | |
parent | c04fc60484b0b3db599ba94b4684b3231b8173f4 (diff) | |
download | coreutils-2611844743b1d9a0fa1ca3ce02de6295b2be18b9.tar.xz |
(parse_options): New function.
(parse_obsolescent_option): New function.
(main): Use the new functions instead of open-coding them.
This better fixes the bug in handling obsolescent `+Nc' options.
General cleanup.
-rw-r--r-- | src/tail.c | 271 |
1 files changed, 181 insertions, 90 deletions
diff --git a/src/tail.c b/src/tail.c index d79435f4b..153ce7048 100644 --- a/src/tail.c +++ b/src/tail.c @@ -49,11 +49,27 @@ #include <assert.h> #include <getopt.h> #include <sys/types.h> +#if HAVE_LIMITS_H +# include <limits.h> +#endif #include "system.h" -#include "xstrtol.h" +#include "xstrtoul.h" #include "error.h" +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#ifndef OFF_T_MIN +# define OFF_T_MIN (0 < (off_t) -1 ? (off_t) 0 \ + : ~ (off_t) 0 << (sizeof (off_t) * CHAR_BIT - 1)) +#endif + +#ifndef OFF_T_MAX +# define OFF_T_MAX (~ (off_t) 0 - OFF_T_MIN) +#endif + /* Disable assertions. Some systems have broken assert macros. */ #define NDEBUG 1 @@ -837,93 +853,123 @@ tail_file (const char *filename, off_t n_units, int filenum) return errors; } -int -main (int argc, char **argv) +static int +parse_obsolescent_option (int argc, const char *const *argv, + off_t *n_units, int *fail) { - enum header_mode header_mode = multiple_files; - int exit_status = 0; - /* If from_start, the number of items to skip before printing; otherwise, - the number of items at the end of the file to print. Initially, -1 - means the value has not been set. */ - off_t n_units = -1; - long int tmp_long; - int c; /* Option character. */ - int n_files; - char **file; + const char *p = argv[1]; + const char *n_string = NULL; + const char *n_string_end; - program_name = argv[0]; - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + int t_from_start; + int t_count_lines; + int t_forever; - have_read_stdin = 0; - count_lines = 1; - forever = forever_multiple = from_start = print_headers = 0; + /* FIXME: comment. */ + if (argc < 2 || argc > 3) + return 0; - if (argc > 1 - && ((argv[1][0] == '-' && ISDIGIT (argv[1][1])) - || (argv[1][0] == '+' && (ISDIGIT (argv[1][1]) - || argv[1][1] == 0)))) - { - /* Old option syntax: a dash or plus, one or more digits (zero digits - are acceptable with a plus), and one or more option letters. */ - if (argv[1][0] == '+') - from_start = 1; - if (argv[1][1] != '\0') - { - strtol_error s_err; - char *p; + /* If I were implementing this in Perl, the rest of this function + would be essentially this single statement: + return $p ne '-' && $p ne '-c' && $p =~ /^[+-]\d*[cl]?f?$/; */ - s_err = xstrtol (++argv[1], &p, 0, &tmp_long, "cbkm"); - n_units = tmp_long; - if (s_err == LONGINT_OVERFLOW) - { - STRTOL_FATAL_ERROR (argv[1], _("argument"), s_err); - } + /* Test this: + if (STREQ (p, "-") || STREQ (p, "-c")) + but without using strcmp. */ + if (p[0] == '-' && (p[1] == 0 || (p[1] == 'c' && p[2] == 0))) + return 0; - /* If a [bckm] suffix was given then count bytes, not lines. */ - if (p[-1] == 'b' || p[-1] == 'c' || p[-1] == 'k' || p[-1] == 'm') - count_lines = 0; + if (*p == '+') + t_from_start = 1; + else if (*p == '-') + t_from_start = 0; + else + return 0; - /* Parse any appended option letters. */ - while (*p) - { - switch (*p) - { - case 'f': - forever = 1; - break; + ++p; + if (ISDIGIT (*p)) + { + n_string = p; + do + { + ++p; + } + while (ISDIGIT (*p)); + } + n_string_end = p; - case 'l': - count_lines = 1; - break; + t_count_lines = 1; + if (*p == 'c') + { + t_count_lines = 0; + ++p; + } + else if (*p == 'l') + { + ++p; + } - case 'q': - header_mode = never; - break; + t_forever = 0; + if (*p == 'f') + { + t_forever = 1; + ++p; + } - case 'v': - header_mode = always; - break; + if (*p != '\0') + return 0; - default: - error (0, 0, _("unrecognized option `-%c'"), *p); - usage (1); - } - ++p; - } + *fail = 0; + if (n_string == NULL) + *n_units = DEFAULT_N_LINES; + else + { + strtol_error s_err; + unsigned long int tmp_ulong; + char *end; + s_err = xstrtoul (n_string, &end, 0, &tmp_ulong, NULL); + if (s_err == LONGINT_OK && tmp_ulong <= OFF_T_MAX) + *n_units = (off_t) tmp_ulong; + else + { + /* Extract a NUL-terminated string for the error message. */ + size_t len = n_string_end - n_string; + char *n_string_tmp = xmalloc (len + 1); + + strncpy (n_string_tmp, n_string, len); + n_string_tmp[len] = '\0'; + + error (0, 0, + _("%s, `%s' is so large that it is not representable"), + count_lines ? _("number of lines") : _("number of bytes"), + n_string_tmp); + free (n_string_tmp); + *fail = 1; } - /* Make the options we just parsed invisible to getopt. */ - argv[1] = argv[0]; - argv++; - argc--; } + if (!*fail) + { + from_start = t_from_start; + count_lines = t_count_lines; + forever = t_forever; + } + + return 1; +} + +static void +parse_options (int argc, char **argv, + off_t *n_units, enum header_mode *header_mode) +{ + int c; + + count_lines = 1; + forever = forever_multiple = from_start = print_headers = 0; + while ((c = getopt_long (argc, argv, "c:n:fqv", long_options, (int *) 0)) != EOF) { - strtol_error s_err; - switch (c) { case 0: @@ -937,20 +983,30 @@ main (int argc, char **argv) count_lines = 1; getnum: if (*optarg == '+') - { - from_start = 1; - } - - s_err = xstrtol (optarg, NULL, 0, &tmp_long, "bkm"); - if (tmp_long < 0) - tmp_long = -tmp_long; - n_units = tmp_long; - if (s_err != LONGINT_OK) - { - STRTOL_FATAL_ERROR (optarg, (c == 'n' - ? _("number of lines") - : _("number of bytes")), s_err); - } + from_start = 1; + else if (*optarg == '-') + ++optarg; + + { + strtol_error s_err; + unsigned long int tmp_ulong; + s_err = xstrtoul (optarg, NULL, 0, &tmp_ulong, "bkm"); + if (s_err == LONGINT_INVALID) + { + error (EXIT_FAILURE, 0, "%s: %s", optarg, + (c == 'n' + ? _("invalid number of lines") + : _("invalid number of bytes"))); + } + if (s_err != LONGINT_OK || tmp_ulong > OFF_T_MAX) + { + error (EXIT_FAILURE, 0, + _("%s: %s is so large that it is not representable"), + optarg, + c == 'n' ? _("number of lines") : _("number of bytes")); + } + *n_units = (off_t) tmp_ulong; + } break; case 'f': @@ -958,17 +1014,55 @@ main (int argc, char **argv) break; case 'q': - header_mode = never; + *header_mode = never; break; case 'v': - header_mode = always; + *header_mode = always; break; default: usage (1); } } +} + +int +main (int argc, char **argv) +{ + enum header_mode header_mode = multiple_files; + int exit_status = 0; + /* If from_start, the number of items to skip before printing; otherwise, + the number of items at the end of the file to print. Although the type + is signed, the value is never negative. */ + off_t n_units = DEFAULT_N_LINES; + int n_files; + char **file; + + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + have_read_stdin = 0; + + { + int found_obsolescent; + int fail; + found_obsolescent = parse_obsolescent_option (argc, + (const char *const *) argv, + &n_units, &fail); + if (found_obsolescent) + { + if (fail) + exit (EXIT_FAILURE); + optind = 2; + } + else + { + parse_options (argc, argv, &n_units, &header_mode); + } + } if (show_version) { @@ -979,9 +1073,6 @@ main (int argc, char **argv) if (show_help) usage (0); - if (n_units == -1) - n_units = DEFAULT_N_LINES; - /* To start printing with item N_UNITS from the start of the file, skip N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix compatibility it's treated the same as `tail +1'. */ |