]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cut: refactor into set-fields module
authorAssaf Gordon <assafgordon@gmail.com>
Sat, 18 Jul 2015 03:30:30 +0000 (23:30 -0400)
committerAssaf Gordon <assafgordon@gmail.com>
Sat, 12 Sep 2015 02:27:32 +0000 (02:27 +0000)
Extract the functionality of parsing --field=LIST into a separate
module, to be used by other programs.

* src/cut.c: move field parsing code from here ...
* src/set-fields.{c,h}: ... to here.
  (set_fields): generalize by supporting multiple parsing/reporting
  options.
  (struct range_pair): rename to field_range_pair.
* src/local.mk: link cut with set-field.
* po/POTFILES.in: add set-field.c
* tests/misc/cut.pl: update wording of error messages

po/POTFILES.in
src/cut.c
src/local.mk
src/set-fields.c [new file with mode: 0644]
src/set-fields.h [new file with mode: 0644]
tests/misc/cut.pl

index abec109613e5bff9acd914bc21790ef80dfd3f35..b3fe6686666a8740d7c3c90b1bf234842c7126d3 100644 (file)
@@ -109,6 +109,7 @@ src/rmdir.c
 src/runcon.c
 src/selinux.c
 src/seq.c
+src/set-fields.c
 src/shred.c
 src/shuf.c
 src/sleep.c
index 4f80ebd4593351a25f07d503c014a4ffb42fefd4..aac91ef5ec90f3c4e5df20fffd7cd2e8ad50ff43 100644 (file)
--- a/src/cut.c
+++ b/src/cut.c
 #include "fadvise.h"
 #include "getndelim2.h"
 #include "hash.h"
-#include "quote.h"
 #include "xstrndup.h"
 
+#include "set-fields.h"
+
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "cut"
 
   while (0)
 
 
-struct range_pair
-  {
-    size_t lo;
-    size_t hi;
-  };
-
-/* Array of `struct range_pair' holding all the finite ranges. */
-static struct range_pair *rp;
-
 /* Pointer inside RP.  When checking if a byte or field is selected
    by a finite range, we check if it is between CURRENT_RP.LO
    and CURRENT_RP.HI.  If the byte or field index is greater than
    CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */
-static struct range_pair *current_rp;
-
-/* Number of finite ranges specified by the user. */
-static size_t n_rp;
-
-/* Number of `struct range_pair's allocated. */
-static size_t n_rp_allocated;
-
-
-/* Append LOW, HIGH to the list RP of range pairs, allocating additional
-   space if necessary.  Update global variable N_RP.  When allocating,
-   update global variable N_RP_ALLOCATED.  */
-
-static void
-add_range_pair (size_t lo, size_t hi)
-{
-  if (n_rp == n_rp_allocated)
-    rp = X2NREALLOC (rp, &n_rp_allocated);
-  rp[n_rp].lo = lo;
-  rp[n_rp].hi = hi;
-  ++n_rp;
-}
+static struct field_range_pair *current_rp;
 
 /* This buffer is used to support the semantics of the -s option
    (or lack of same) when the specified field list includes (does
@@ -221,208 +192,6 @@ Each range is one of:\n\
   exit (status);
 }
 
-/* Comparison function for qsort to order the list of
-   struct range_pairs.  */
-static int
-compare_ranges (const void *a, const void *b)
-{
-  int a_start = ((const struct range_pair *) a)->lo;
-  int b_start = ((const struct range_pair *) b)->lo;
-  return a_start < b_start ? -1 : a_start > b_start;
-}
-
-/* Reallocate Range Pair entries, with corresponding
-   entries outside the range of each specified entry.  */
-
-static void
-complement_rp (void)
-{
-  if (complement)
-    {
-      struct range_pair *c = rp;
-      size_t n = n_rp;
-      size_t i;
-
-      rp = NULL;
-      n_rp = 0;
-      n_rp_allocated = 0;
-
-      if (c[0].lo > 1)
-        add_range_pair (1, c[0].lo - 1);
-
-      for (i = 1; i < n; ++i)
-        {
-          if (c[i-1].hi + 1 == c[i].lo)
-            continue;
-
-          add_range_pair (c[i-1].hi + 1, c[i].lo - 1);
-        }
-
-      if (c[n-1].hi < SIZE_MAX)
-        add_range_pair (c[n-1].hi + 1, SIZE_MAX);
-
-      free (c);
-    }
-}
-
-/* Given the list of field or byte range specifications FIELDSTR,
-   allocate and initialize the RP array. FIELDSTR should
-   be composed of one or more numbers or ranges of numbers, separated
-   by blanks or commas.  Incomplete ranges may be given: '-m' means '1-m';
-   'n-' means 'n' through end of line.
-   Return true if FIELDSTR contains at least one field specification,
-   false otherwise.  */
-
-static bool
-set_fields (const char *fieldstr)
-{
-  size_t initial = 1;          /* Value of first number in a range.  */
-  size_t value = 0;            /* If nonzero, a number being accumulated.  */
-  bool lhs_specified = false;
-  bool rhs_specified = false;
-  bool dash_found = false;     /* True if a '-' is found in this field.  */
-  bool field_found = false;    /* True if at least one field spec
-                                   has been processed.  */
-
-  size_t i;
-  bool in_digits = false;
-
-  /* Collect and store in RP the range end points. */
-
-  while (true)
-    {
-      if (*fieldstr == '-')
-        {
-          in_digits = false;
-          /* Starting a range. */
-          if (dash_found)
-            FATAL_ERROR (_("invalid byte, character or field list"));
-          dash_found = true;
-          fieldstr++;
-
-          if (lhs_specified && !value)
-            FATAL_ERROR (_("fields and positions are numbered from 1"));
-
-          initial = (lhs_specified ? value : 1);
-          value = 0;
-        }
-      else if (*fieldstr == ','
-               || isblank (to_uchar (*fieldstr)) || *fieldstr == '\0')
-        {
-          in_digits = false;
-          /* Ending the string, or this field/byte sublist. */
-          if (dash_found)
-            {
-              dash_found = false;
-
-              if (!lhs_specified && !rhs_specified)
-                FATAL_ERROR (_("invalid range with no endpoint: -"));
-
-              /* A range.  Possibilities: -n, m-n, n-.
-                 In any case, 'initial' contains the start of the range. */
-              if (!rhs_specified)
-                {
-                  /* 'n-'.  From 'initial' to end of line. */
-                  add_range_pair (initial, SIZE_MAX);
-                  field_found = true;
-                }
-              else
-                {
-                  /* 'm-n' or '-n' (1-n). */
-                  if (value < initial)
-                    FATAL_ERROR (_("invalid decreasing range"));
-
-                  add_range_pair (initial, value);
-                  field_found = true;
-                }
-              value = 0;
-            }
-          else
-            {
-              /* A simple field number, not a range. */
-              if (value == 0)
-                FATAL_ERROR (_("fields and positions are numbered from 1"));
-              add_range_pair (value, value);
-              value = 0;
-              field_found = true;
-            }
-
-          if (*fieldstr == '\0')
-            break;
-
-          fieldstr++;
-          lhs_specified = false;
-          rhs_specified = false;
-        }
-      else if (ISDIGIT (*fieldstr))
-        {
-          /* Record beginning of digit string, in case we have to
-             complain about it.  */
-          static char const *num_start;
-          if (!in_digits || !num_start)
-            num_start = fieldstr;
-          in_digits = true;
-
-          if (dash_found)
-            rhs_specified = 1;
-          else
-            lhs_specified = 1;
-
-          /* Detect overflow.  */
-          if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t)
-              || value == SIZE_MAX)
-            {
-              /* In case the user specified -c$(echo 2^64|bc),22,
-                 complain only about the first number.  */
-              /* Determine the length of the offending number.  */
-              size_t len = strspn (num_start, "0123456789");
-              char *bad_num = xstrndup (num_start, len);
-              if (operating_mode == byte_mode)
-                error (0, 0,
-                       _("byte offset %s is too large"), quote (bad_num));
-              else
-                error (0, 0,
-                       _("field number %s is too large"), quote (bad_num));
-              free (bad_num);
-              exit (EXIT_FAILURE);
-            }
-
-          fieldstr++;
-        }
-      else
-        FATAL_ERROR (_("invalid byte, character or field list"));
-    }
-
-  qsort (rp, n_rp, sizeof (rp[0]), compare_ranges);
-
-  /* Merge range pairs (e.g. `2-5,3-4' becomes `2-5'). */
-  for (i = 0; i < n_rp; ++i)
-    {
-      for (size_t j = i + 1; j < n_rp; ++j)
-        {
-          if (rp[j].lo <= rp[i].hi)
-            {
-              rp[i].hi = MAX (rp[j].hi, rp[i].hi);
-              memmove (rp + j, rp + j + 1, (n_rp - j - 1) * sizeof *rp);
-              n_rp--;
-              j--;
-            }
-          else
-            break;
-        }
-    }
-
-  complement_rp ();
-
-  /* After merging, reallocate RP so we release memory to the system.
-     Also add a sentinel at the end of RP, to avoid out of bounds access
-     and for performance reasons.  */
-  ++n_rp;
-  rp = xrealloc (rp, n_rp * sizeof (struct range_pair));
-  rp[n_rp - 1].lo = rp[n_rp - 1].hi = SIZE_MAX;
-
-  return field_found;
-}
 
 /* Increment *ITEM_IDX (i.e., a field or byte index),
    and if required CURRENT_RP.  */
@@ -463,7 +232,7 @@ cut_bytes (FILE *stream)
 
   byte_idx = 0;
   print_delimiter = false;
-  current_rp = rp;
+  current_rp = frp;
   while (true)
     {
       int c;           /* Each character from the file. */
@@ -475,7 +244,7 @@ cut_bytes (FILE *stream)
           putchar ('\n');
           byte_idx = 0;
           print_delimiter = false;
-          current_rp = rp;
+          current_rp = frp;
         }
       else if (c == EOF)
         {
@@ -514,7 +283,7 @@ cut_fields (FILE *stream)
   bool found_any_selected_field = false;
   bool buffer_first_field;
 
-  current_rp = rp;
+  current_rp = frp;
 
   c = getc (stream);
   if (c == EOF)
@@ -642,7 +411,7 @@ cut_fields (FILE *stream)
           if (c == EOF)
             break;
           field_idx = 1;
-          current_rp = rp;
+          current_rp = frp;
           found_any_selected_field = false;
         }
     }
@@ -793,13 +562,9 @@ main (int argc, char **argv)
     FATAL_ERROR (_("suppressing non-delimited lines makes sense\n\
 \tonly when operating on fields"));
 
-  if (! set_fields (spec_list_string))
-    {
-      if (operating_mode == field_mode)
-        FATAL_ERROR (_("missing list of fields"));
-      else
-        FATAL_ERROR (_("missing list of positions"));
-    }
+  set_fields (spec_list_string,
+              ( (operating_mode == field_mode) ? 0 : SETFLD_ERRMSG_USE_POS)
+              | (complement ? SETFLD_COMPLEMENT : 0) );
 
   if (!delim_specified)
     delim = '\t';
@@ -826,5 +591,7 @@ main (int argc, char **argv)
       ok = false;
     }
 
+  IF_LINT (reset_fields ());
+
   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
 }
index d0ceae4bde5a6f1636413687781e1d3ade1b00a8..5fb4d373304125f9e4378ca2dca42f715d0c55f7 100644 (file)
@@ -55,6 +55,7 @@ noinst_HEADERS =              \
   src/operand2sig.h            \
   src/prog-fprintf.h           \
   src/remove.h                 \
+  src/set-fields.h             \
   src/system.h                 \
   src/uname.h
 
@@ -387,6 +388,8 @@ src_stat_SOURCES = src/stat.c src/find-mount-point.c
 src_uname_SOURCES = src/uname.c src/uname-uname.c
 src_arch_SOURCES = src/uname.c src/uname-arch.c
 
+src_cut_SOURCES = src/cut.c src/set-fields.c
+
 src_md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
 src_sha1sum_SOURCES = src/md5sum.c
 src_sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
diff --git a/src/set-fields.c b/src/set-fields.c
new file mode 100644 (file)
index 0000000..847c812
--- /dev/null
@@ -0,0 +1,322 @@
+/* set-fields.c -- common functions for parsing field list
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   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 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, see <http://www.gnu.org/licenses/>.  */
+
+/* Extracted from cut.c by Assaf Gordon */
+
+#include <config.h>
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+#include "xstrndup.h"
+#include "set-fields.h"
+
+/* Array of `struct field_range_pair' holding all the finite ranges. */
+struct field_range_pair *frp;
+
+/* Number of finite ranges specified by the user. */
+size_t n_frp;
+
+/* Number of `struct field_range_pair's allocated. */
+static size_t n_frp_allocated;
+
+#define FATAL_ERROR(Message)                                            \
+  do                                                                    \
+    {                                                                   \
+      error (0, 0, (Message));                                          \
+      usage (EXIT_FAILURE);                                             \
+    }                                                                   \
+  while (0)
+
+/* Append LOW, HIGH to the list RP of range pairs, allocating additional
+   space if necessary.  Update global variable N_FRP.  When allocating,
+   update global variable N_FRP_ALLOCATED.  */
+static void
+add_range_pair (size_t lo, size_t hi)
+{
+  if (n_frp == n_frp_allocated)
+    frp = X2NREALLOC (frp, &n_frp_allocated);
+  frp[n_frp].lo = lo;
+  frp[n_frp].hi = hi;
+  ++n_frp;
+}
+
+
+/* Comparison function for qsort to order the list of
+   struct range_pairs.  */
+static int
+compare_ranges (const void *a, const void *b)
+{
+  int a_start = ((const struct field_range_pair *) a)->lo;
+  int b_start = ((const struct field_range_pair *) b)->lo;
+  return a_start < b_start ? -1 : a_start > b_start;
+}
+
+/* Reallocate Range Pair entries, with corresponding
+   entries outside the range of each specified entry.  */
+
+static void
+complement_rp (void)
+{
+  struct field_range_pair *c = frp;
+  size_t n = n_frp;
+  size_t i;
+
+  frp = NULL;
+  n_frp = 0;
+  n_frp_allocated = 0;
+
+  if (c[0].lo > 1)
+    add_range_pair (1, c[0].lo - 1);
+
+  for (i = 1; i < n; ++i)
+    {
+      if (c[i-1].hi + 1 == c[i].lo)
+        continue;
+
+      add_range_pair (c[i-1].hi + 1, c[i].lo - 1);
+    }
+
+  if (c[n-1].hi < SIZE_MAX)
+    add_range_pair (c[n-1].hi + 1, SIZE_MAX);
+
+  free (c);
+}
+
+/* Given the list of field or byte range specifications FIELDSTR,
+   allocate and initialize the FRP array. FIELDSTR should
+   be composed of one or more numbers or ranges of numbers, separated
+   by blanks or commas.  Incomplete ranges may be given: '-m' means '1-m';
+   'n-' means 'n' through end of line.
+   n=0 and n>=SIZE_MAX values will trigger an error.
+
+   if SETFLD_ALLOW_DASH option is used, a single '-' means all fields
+   (otherwise a single dash triggers an error).
+
+   if SETFLD_COMPLEMENT option is used, the specified field list
+   is complemented (e.g. '1-3' will result in fields '4-').
+
+   if SETFLD_ERRMSG_USE_POS option is used, error messages
+   will say 'position' (or 'byte/character positions')
+   instead of fields (used with cut -b/-c).
+
+   The function terminates on failure.
+
+   Upon return, the FRP array is initialized to contain
+   a non-overlapping, increasing list of field ranges.
+
+   N_FRP holds the number of field ranges in the FRP array.
+
+   The first field is stored as 1 (zero is not used).
+   An open-ended range (i.e., until the last field of the input line)
+   is indicated with hi = SIZE_MAX.
+
+   A sentinel of SIZE_MAX/SIZE_MAX is always added as the last
+   field range pair.
+
+   Examples:
+   given '1-2,4', frp = [ { .lo = 1,        .hi = 2 },
+                          { .lo = 4,        .hi = 4 },
+                          { .lo = SIZE_MAX, .hi = SIZE_MAX } ];
+
+   given '3-',    frp = [ { .lo = 3,        .hi = SIZE_MAX },
+                          { .lo = SIZE_MAX, .hi = SIZE_MAX } ];
+*/
+void
+set_fields (const char *fieldstr, unsigned int options)
+{
+  size_t initial = 1;          /* Value of first number in a range.  */
+  size_t value = 0;            /* If nonzero, a number being accumulated.  */
+  bool lhs_specified = false;
+  bool rhs_specified = false;
+  bool dash_found = false;     /* True if a '-' is found in this field.  */
+
+  size_t i;
+  bool in_digits = false;
+
+  /* Collect and store in RP the range end points. */
+
+  /* Special case: '--field=-' means all fields, emulate '--field=1-' . */
+  if ((options & SETFLD_ALLOW_DASH) && STREQ (fieldstr,"-"))
+    {
+      value = 1;
+      lhs_specified = true;
+      dash_found = true;
+      fieldstr++;
+    }
+
+  while (true)
+    {
+      if (*fieldstr == '-')
+        {
+          in_digits = false;
+          /* Starting a range. */
+          if (dash_found)
+            FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+                          ?_("invalid byte or character range")
+                          :_("invalid field range"));
+
+          dash_found = true;
+          fieldstr++;
+
+          if (lhs_specified && !value)
+            FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+                          ?_("byte/character positions are numbered from 1")
+                          :_("fields are numbered from 1"));
+
+          initial = (lhs_specified ? value : 1);
+          value = 0;
+        }
+      else if (*fieldstr == ','
+               || isblank (to_uchar (*fieldstr)) || *fieldstr == '\0')
+        {
+          in_digits = false;
+          /* Ending the string, or this field/byte sublist. */
+          if (dash_found)
+            {
+              dash_found = false;
+
+              if (!lhs_specified && !rhs_specified)
+                {
+                  /* if a lone dash is allowed, emulate '1-' for all fields */
+                  if (options & SETFLD_ALLOW_DASH)
+                    initial = 1;
+                  else
+                    FATAL_ERROR (_("invalid range with no endpoint: -"));
+                }
+
+              /* A range.  Possibilities: -n, m-n, n-.
+                 In any case, 'initial' contains the start of the range. */
+              if (!rhs_specified)
+                {
+                  /* 'n-'.  From 'initial' to end of line. */
+                  add_range_pair (initial, SIZE_MAX);
+                }
+              else
+                {
+                  /* 'm-n' or '-n' (1-n). */
+                  if (value < initial)
+                    FATAL_ERROR (_("invalid decreasing range"));
+
+                  add_range_pair (initial, value);
+                }
+              value = 0;
+            }
+          else
+            {
+              /* A simple field number, not a range. */
+              if (value == 0)
+                FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+                              ?_("byte/character positions are numbered from 1")
+                              :_("fields are numbered from 1"));
+
+              add_range_pair (value, value);
+              value = 0;
+            }
+
+          if (*fieldstr == '\0')
+            break;
+
+          fieldstr++;
+          lhs_specified = false;
+          rhs_specified = false;
+        }
+      else if (ISDIGIT (*fieldstr))
+        {
+          /* Record beginning of digit string, in case we have to
+             complain about it.  */
+          static char const *num_start;
+          if (!in_digits || !num_start)
+            num_start = fieldstr;
+          in_digits = true;
+
+          if (dash_found)
+            rhs_specified = 1;
+          else
+            lhs_specified = 1;
+
+          /* Detect overflow.  */
+          if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t)
+              || value == SIZE_MAX)
+            {
+              /* In case the user specified -c$(echo 2^64|bc),22,
+                 complain only about the first number.  */
+              /* Determine the length of the offending number.  */
+              size_t len = strspn (num_start, "0123456789");
+              char *bad_num = xstrndup (num_start, len);
+              error (0, 0, (options & SETFLD_ERRMSG_USE_POS)
+                           ?_("byte/character offset %s is too large")
+                           :_("field number %s is too large"),
+                           quote (bad_num));
+              free (bad_num);
+              usage (EXIT_FAILURE);
+            }
+
+          fieldstr++;
+        }
+      else
+        {
+          error (0, 0, (options & SETFLD_ERRMSG_USE_POS)
+                       ?_("invalid byte/character position %s")
+                       :_("invalid field value %s"),
+                       quote (fieldstr));
+          usage (EXIT_FAILURE);
+        }
+    }
+
+  if (!n_frp)
+    FATAL_ERROR ( (options&SETFLD_ERRMSG_USE_POS)
+                  ?_("missing list of byte/character positions")
+                  :_("missing list of fields"));
+
+  qsort (frp, n_frp, sizeof (frp[0]), compare_ranges);
+
+  /* Merge range pairs (e.g. `2-5,3-4' becomes `2-5'). */
+  for (i = 0; i < n_frp; ++i)
+    {
+      for (size_t j = i + 1; j < n_frp; ++j)
+        {
+          if (frp[j].lo <= frp[i].hi)
+            {
+              frp[i].hi = MAX (frp[j].hi, frp[i].hi);
+              memmove (frp + j, frp + j + 1, (n_frp - j - 1) * sizeof *frp);
+              n_frp--;
+              j--;
+            }
+          else
+            break;
+        }
+    }
+
+  if (options & SETFLD_COMPLEMENT)
+    complement_rp ();
+
+  /* After merging, reallocate RP so we release memory to the system.
+     Also add a sentinel at the end of RP, to avoid out of bounds access
+     and for performance reasons.  */
+  ++n_frp;
+  frp = xrealloc (frp, n_frp * sizeof (struct field_range_pair));
+  frp[n_frp - 1].lo = frp[n_frp - 1].hi = SIZE_MAX;
+}
+
+void
+reset_fields (void)
+{
+  n_frp = 0 ;
+  n_frp_allocated = 0;
+  free (frp);
+  frp = NULL;
+}
diff --git a/src/set-fields.h b/src/set-fields.h
new file mode 100644 (file)
index 0000000..2c55c29
--- /dev/null
@@ -0,0 +1,49 @@
+/* set-fields.h -- parse field list argument
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   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 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, see <http://www.gnu.org/licenses/>.  */
+#ifndef SET_FIELDS_H
+# define SET_FIELDS_H
+
+struct field_range_pair
+  {
+    size_t lo;
+    size_t hi;
+  };
+
+/* Array of `struct range_pair' holding all the finite ranges. */
+extern struct field_range_pair *frp;
+
+/* Number of finite ranges specified by the user. */
+extern size_t n_frp;
+
+/* field list parsing options */
+enum
+{
+  SETFLD_ALLOW_DASH = 0x01,     /* allow single dash meaning 'all fields' */
+  SETFLD_COMPLEMENT = 0x02,     /* complement the field list */
+  SETFLD_ERRMSG_USE_POS = 0x04  /* when reporting errors, say 'position' instead
+                                   of 'field' (used with cut -b/-c) */
+};
+
+/* allocates and initializes the FRP array and N_FRP count */
+void
+set_fields (const char *fieldstr, unsigned int options);
+
+/* frees memory allocated by set_fields() */
+void
+reset_fields (void);
+
+#endif
index 23e9ce36f817d3248dc1f0d4f1ab5b7f7862360f..a6239d6295f20950e60a846d08a72c0c62e2b3da 100755 (executable)
@@ -29,8 +29,10 @@ my $mb_locale = $ENV{LOCALE_FR_UTF8};
 
 my $prog = 'cut';
 my $try = "Try '$prog --help' for more information.\n";
-my $from_1 = "$prog: fields and positions are numbered from 1\n$try";
-my $inval = "$prog: invalid byte, character or field list\n$try";
+my $from_field1 = "$prog: fields are numbered from 1\n$try";
+my $from_pos1 =   "$prog: byte/character positions are numbered from 1\n$try";
+my $inval_fld = "$prog: invalid field range\n$try";
+my $inval_pos = "$prog: invalid byte or character range\n$try";
 my $no_endpoint = "$prog: invalid range with no endpoint: -\n$try";
 my $nofield = "$prog: an input delimiter may be specified only when " .
               "operating on fields\n$try";
@@ -42,16 +44,16 @@ my @Tests =
 
   # This failed (as it should) even before coreutils-6.9.90,
   # but cut from 6.9.90 produces a more useful diagnostic.
-  ['zero-1', '-b0',   {ERR=>$from_1}, {EXIT => 1} ],
+  ['zero-1', '-b0',   {ERR=>$from_pos1}, {EXIT => 1} ],
 
   # Up to coreutils-6.9, specifying a range of 0-2 was not an error.
   # It was treated just like "-2".
-  ['zero-2', '-f0-2', {ERR=>$from_1}, {EXIT => 1} ],
+  ['zero-2', '-f0-2', {ERR=>$from_field1}, {EXIT => 1} ],
 
   # Up to coreutils-8.20, specifying a range of 0- was not an error.
-  ['zero-3b', '-b0-', {ERR=>$from_1}, {EXIT => 1} ],
-  ['zero-3c', '-c0-', {ERR=>$from_1}, {EXIT => 1} ],
-  ['zero-3f', '-f0-', {ERR=>$from_1}, {EXIT => 1} ],
+  ['zero-3b', '-b0-', {ERR=>$from_pos1}, {EXIT => 1} ],
+  ['zero-3c', '-c0-', {ERR=>$from_pos1}, {EXIT => 1} ],
+  ['zero-3f', '-f0-', {ERR=>$from_field1}, {EXIT => 1} ],
 
   ['1', '-d:', '-f1,3-', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}],
   ['2', '-d:', '-f1,3-', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}],
@@ -101,13 +103,16 @@ my @Tests =
    {ERR=>"$prog: you must specify a list of bytes, characters, or fields\n$try"}
   ],
   # Empty field list
-  ['empty-fl', qw(-f ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$from_1}],
+  ['empty-fl', qw(-f ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1},
+   {ERR=>$from_field1}],
   # Missing field list
-  ['missing-fl', qw(-f --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$inval}],
+  ['missing-fl', qw(-f --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1},
+   {ERR=>$inval_fld}],
   # Empty byte list
-  ['empty-bl', qw(-b ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$from_1}],
+  ['empty-bl', qw(-b ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$from_pos1}],
   # Missing byte list
-  ['missing-bl', qw(-b --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$inval}],
+  ['missing-bl', qw(-b --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1},
+   {ERR=>$inval_pos}],
 
   # This test fails with cut from textutils-1.22.
   ['empty-f1', '-f1', {IN=>""}, {OUT=>""}],