]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
numfmt: use new set-fields module to parse --field
authorAssaf Gordon <assafgordon@gmail.com>
Sat, 18 Jul 2015 03:58:31 +0000 (23:58 -0400)
committerAssaf Gordon <assafgordon@gmail.com>
Sat, 12 Sep 2015 02:27:32 +0000 (02:27 +0000)
numfmt --field=LIST can accept the same options as cut.

* bootstrap.conf: remove xlist, linked-list
* src/local.mk: link numfmt with set-fields
* src/numfmt.c: use set-fields.c instead of custom field parsing code.
  (include_field): adapt to new code.
* tests/misc/numfmt.pl: add new tests, adapt current tests to new
  error message wording from set-fields.c

bootstrap.conf
src/local.mk
src/numfmt.c
tests/misc/numfmt.pl

index d0017f995c2f8b716b6e96ad03c6141ca0e3f885..165e1c153c2e86f78570eb706d20474bfb428ed6 100644 (file)
@@ -34,7 +34,6 @@ gnulib_modules="
   argv-iter
   assert
   autobuild
-  linked-list
   backupfile
   base64
   base32
@@ -274,7 +273,6 @@ gnulib_modules="
   xgetcwd
   xgetgroups
   xgethostname
-  xlist
   xmemcoll
   xnanosleep
   xprintf
index 5fb4d373304125f9e4378ca2dca42f715d0c55f7..536b7cc37d3f123a90db05ed785d4c743058fd02 100644 (file)
@@ -389,6 +389,7 @@ 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_numfmt_SOURCES = src/numfmt.c src/set-fields.c
 
 src_md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
 src_sha1sum_SOURCES = src/md5sum.c
index 35c5c5b97d34b7eadc3ce6320804c88ec9da5d12..24bf45b3778c61113cd63b0aca3dda96a221c970 100644 (file)
@@ -29,8 +29,8 @@
 #include "system.h"
 #include "xstrtol.h"
 #include "xstrndup.h"
-#include "gl_linked_list.h"
-#include "gl_xlist.h"
+
+#include "set-fields.h"
 
 #if HAVE_FPSETPREC
 # include <ieeefp.h>
@@ -189,10 +189,6 @@ static int conv_exit_code = EXIT_CONVERSION_WARNINGS;
 /* auto-pad each line based on skipped whitespace.  */
 static int auto_padding = 0;
 static mbs_align_t padding_alignment = MBS_ALIGN_RIGHT;
-static bool all_fields = false;
-static size_t all_fields_after = 0;
-static size_t all_fields_before = 0;
-static gl_list_t field_list;
 static int delimiter = DELIMITER_DEFAULT;
 
 /* if non-zero, the first 'header' lines from STDIN are skipped.  */
@@ -1313,141 +1309,6 @@ process_suffixed_number (char *text, long double *result,
   return (e == SSE_OK || e == SSE_OK_PRECISION_LOSS);
 }
 
-typedef struct range_pair
-{
-  size_t lo;
-  size_t hi;
-} range_pair_t;
-
-static int
-sort_field (const void *elt1, const void *elt2)
-{
-  range_pair_t* rp1 = (range_pair_t*) elt1;
-  range_pair_t* rp2 = (range_pair_t*) elt2;
-
-  if (rp1->lo < rp2->lo)
-    return -1;
-
-  return rp1->lo > rp2->lo;
-}
-
-static int
-match_field (const void *elt1, const void *elt2)
-{
-  range_pair_t* rp = (range_pair_t*) elt1;
-  size_t field = *(size_t*) elt2;
-
-  if (rp->lo <= field && field <= rp->hi)
-    return 0;
-
-  if (rp->lo < field)
-    return -1;
-
-  return 1;
-}
-
-static void
-free_field (const void *elt)
-{
-  void *p = (void *)elt;
-  free (p);
-}
-
-/* Add the specified fields to field_list.
-   The format recognized is similar to cut.
-   TODO: Refactor the more performant cut implementation
-   for use by both utilities.  */
-static void
-parse_field_arg (char *arg)
-{
-
-  char *start, *end;
-  range_pair_t *rp;
-  size_t field_val;
-  size_t range_val = 0;
-
-  start = end = arg;
-
-  if (STREQ (arg, "-"))
-    {
-      all_fields = true;
-
-      return;
-    }
-
-  if (*start == '-')
-    {
-      /* range -M */
-      ++start;
-
-      all_fields_before = strtol (start, &end, 10);
-
-      if (start == end || all_fields_before <=0)
-        error (EXIT_FAILURE, 0, _("invalid field value %s"),
-               quote (start));
-
-      return;
-    }
-
-  field_list = gl_list_create_empty (GL_LINKED_LIST,
-                                     NULL, NULL, free_field, false);
-
-  while (*end != '\0') {
-    field_val = strtol (start, &end, 10);
-
-    if (start == end || field_val <=0)
-      error (EXIT_FAILURE, 0, _("invalid field value %s"),
-             quote (start));
-
-    if (! range_val)
-      {
-        /* field N */
-        rp = xmalloc (sizeof (*rp));
-        rp->lo = rp->hi = field_val;
-        gl_sortedlist_add (field_list, sort_field, rp);
-      }
-    else
-      {
-        /* range N-M
-           The last field was the start of the field range. The current
-           field is the end of the field range.  We already added the
-           start field, so increment and add all the fields through
-           range end. */
-        if (field_val < range_val)
-          error (EXIT_FAILURE, 0, _("invalid decreasing range"));
-        rp = xmalloc (sizeof (*rp));
-        rp->lo = range_val + 1;
-        rp->hi = field_val;
-        gl_sortedlist_add (field_list, sort_field, rp);
-
-        range_val = 0;
-      }
-
-    switch (*end) {
-      case ',':
-        /* discrete field separator */
-        ++end;
-        start = end;
-        break;
-
-      case '-':
-        /* field range separator */
-        ++end;
-        start = end;
-        range_val = field_val;
-        break;
-    }
-  }
-
-  if (range_val)
-    {
-      /* range N-
-         range_val was not reset indicating ARG
-         ended with a trailing '-' */
-      all_fields_after = range_val;
-    }
-}
-
 /* Return a pointer to the beginning of the next field in line.
    The line pointer is moved to the end of the next field. */
 static char*
@@ -1479,23 +1340,20 @@ next_field (char **line)
   return field_start;
 }
 
-static bool
+static bool _GL_ATTRIBUTE_PURE
 include_field (size_t field)
 {
-  if (all_fields)
-    return true;
-
-  if (all_fields_after && all_fields_after <= field)
-    return true;
-
-  if (all_fields_before && field <= all_fields_before)
-    return true;
-
-  /* default to field 1 */
-  if (! field_list)
+  struct field_range_pair *p = frp;
+  if (!p)
     return field == 1;
 
-  return gl_sortedlist_search (field_list, match_field, &field);
+  while (p->lo != SIZE_MAX)
+    {
+      if (p->lo <= field && p->hi >= field)
+        return true;
+      ++p;
+    }
+  return false;
 }
 
 /* Convert and output the given field. If it is not included in the set
@@ -1640,12 +1498,9 @@ main (int argc, char **argv)
           break;
 
         case FIELD_OPTION:
-          if (all_fields || all_fields_before || all_fields_after || field_list)
-            {
-              error (EXIT_FAILURE, 0,
-                     _("multiple field specifications"));
-            }
-          parse_field_arg (optarg);
+          if (n_frp)
+            error (EXIT_FAILURE, 0, _("multiple field specifications"));
+          set_fields (optarg, SETFLD_ALLOW_DASH);
           break;
 
         case 'd':
@@ -1761,9 +1616,7 @@ main (int argc, char **argv)
   free (padding_buffer);
   free (format_str_prefix);
   free (format_str_suffix);
-
-  if (field_list)
-    gl_list_free (field_list);
+  reset_fields ();
 #endif
 
   if (debug && !valid_numbers)
index 0e4dc79c4eac3c24641dd8e7a697fd16ac9981be..ddd8c0f4c151461acd3e1e4c116d5c55b97297f8 100755 (executable)
@@ -31,6 +31,8 @@ my $locale = $ENV{LOCALE_FR_UTF8};
 ! defined $locale || $locale eq 'none'
   and $locale = 'C';
 
+my $try = "Try '$prog --help' for more information.\n";
+
 my @Tests =
     (
      ['1', '1234',             {OUT => "1234"}],
@@ -197,10 +199,12 @@ my @Tests =
      ['delim-4', '--delimiter=: --from=auto 40M:60M',  {OUT=>'40000000:60M'}],
      ['delim-5', '-d: --field=2 --from=auto :40M:60M',  {OUT=>':40000000:60M'}],
      ['delim-6', '-d: --field 3 --from=auto 40M:60M', {OUT=>"40M:60M"}],
+     ['delim-err-1', '-d,, --to=si 1', {EXIT=>1},
+             {ERR => "$prog: the delimiter must be a single character\n"}],
 
      #Fields
      ['field-1', '--field A',
-             {ERR => "$prog: invalid field value 'A'\n"},
+             {ERR => "$prog: invalid field value 'A'\n$try"},
              {EXIT => '1'}],
      ['field-2', '--field 2 --from=auto "Hello 40M World 90G"',
              {OUT=>'Hello 40000000 World 90G'}],
@@ -244,9 +248,70 @@ my @Tests =
      ['field-range-7', '--field -3 --to=si "1000 2000 3000 4000 5000"',
              {OUT=>"1.0K 2.0K 3.0K 4000 5000"}],
 
+     ['field-range-8', '--field 1-2,4-5 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}],
+     ['field-range-9', '--field 4-5,1-2 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}],
+
+     ['field-range-10','--field 1-3,2-4 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}],
+     ['field-range-11','--field 2-4,1-3 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}],
+
+     ['field-range-12','--field 1-1,3-3 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2000 3.0K 4000 5000"}],
+
+     ['field-range-13', '--field 1,-2 --to=si "1000 2000 3000"',
+             {OUT=>"1.0K 2.0K 3000"}],
+
+     ['field-range-14', '--field -2,4- --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}],
+     ['field-range-15', '--field -2,-4 --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}],
+     ['field-range-16', '--field 2-,4- --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1000 2.0K 3.0K 4.0K 5.0K"}],
+     ['field-range-17', '--field 4-,2- --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1000 2.0K 3.0K 4.0K 5.0K"}],
+
+     # white space are valid field separators
+     # (undocumented? but works in cut as well).
+     ['field-range-18', '--field "1,2 4" --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3000 4.0K 5000"}],
+
+     # Unlike 'cut', a lone '-' means 'all fields', even as part of a list
+     # of fields.
+     ['field-range-19','--field 3,- --to=si "1000 2000 3000 4000 5000"',
+             {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}],
+
      ['all-fields-1', '--field=- --to=si "1000 2000 3000 4000 5000"',
              {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}],
 
+     ['field-range-err-1', '--field -foo --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: invalid field value 'foo'\n$try"}],
+     ['field-range-err-2', '--field --3 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}],
+     ['field-range-err-3', '--field 0 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}],
+     ['field-range-err-4', '--field 3-2 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: invalid decreasing range\n$try"}],
+     ['field-range-err-6', '--field - --field 1- --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
+     ['field-range-err-7', '--field -1 --field 1- --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
+     ['field-range-err-8', '--field -1 --field 1,2,3 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
+     ['field-range-err-9', '--field 1- --field 1,2,3 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
+     ['field-range-err-10','--field 1,2,3 --field 1- --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
+     ['field-range-err-11','--field 1-2-3 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}],
+     ['field-range-err-12','--field 0-1 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}],
+     ['field-range-err-13','--field '.$limits->{SIZE_MAX}.',22 --to=si 10',
+             {EXIT=>1}, {ERR=>"$prog: field number " .
+                              "'".$limits->{SIZE_MAX}."' is too large\n$try"}],
+
      # Auto-consume white-space, setup auto-padding
      ['whitespace-1', '--to=si --field 2 "A    500 B"', {OUT=>"A    500 B"}],
      ['whitespace-2', '--to=si --field 2 "A   5000 B"', {OUT=>"A   5.0K B"}],
@@ -582,8 +647,7 @@ my @Tests =
 
      # Invalid parameters
      ['help-1', '--foobar',
-             {ERR=>"$prog: unrecognized option\n" .
-                   "Try '$prog --help' for more information.\n"},
+             {ERR=>"$prog: unrecognized option\n$try"},
              {ERR_SUBST=>"s/option.*/option/; s/unknown/unrecognized/"},
              {EXIT=>1}],