summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1997-01-23 03:38:23 +0000
committerJim Meyering <jim@meyering.net>1997-01-23 03:38:23 +0000
commit2611844743b1d9a0fa1ca3ce02de6295b2be18b9 (patch)
treeb3bc60487c6a28f173a34fbb696cc550f1e7c92e /src
parentc04fc60484b0b3db599ba94b4684b3231b8173f4 (diff)
downloadcoreutils-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.
Diffstat (limited to 'src')
-rw-r--r--src/tail.c271
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'. */