summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAssaf Gordon <assafgordon@gmail.com>2015-07-17 23:58:31 -0400
committerAssaf Gordon <assafgordon@gmail.com>2015-09-12 02:27:32 +0000
commitba24c42e139095888dbf83255e6481fb4b1503a6 (patch)
tree4f03d3ba8be4de4f2404735ea469180cc7972846
parentbd1bc2b3fe1c87744cc251334cc1c804075b027c (diff)
downloadcoreutils-ba24c42e139095888dbf83255e6481fb4b1503a6.tar.xz
numfmt: use new set-fields module to parse --field
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
-rw-r--r--bootstrap.conf2
-rw-r--r--src/local.mk1
-rw-r--r--src/numfmt.c179
-rwxr-xr-xtests/misc/numfmt.pl70
4 files changed, 84 insertions, 168 deletions
diff --git a/bootstrap.conf b/bootstrap.conf
index d0017f995..165e1c153 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -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
diff --git a/src/local.mk b/src/local.mk
index 5fb4d3733..536b7cc37 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -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
diff --git a/src/numfmt.c b/src/numfmt.c
index 35c5c5b97..24bf45b37 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -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)
diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl
index 0e4dc79c4..ddd8c0f4c 100755
--- a/tests/misc/numfmt.pl
+++ b/tests/misc/numfmt.pl
@@ -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}],