From ba24c42e139095888dbf83255e6481fb4b1503a6 Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Fri, 17 Jul 2015 23:58:31 -0400 Subject: 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 --- bootstrap.conf | 2 - src/local.mk | 1 + src/numfmt.c | 179 +++++---------------------------------------------- tests/misc/numfmt.pl | 70 +++++++++++++++++++- 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 @@ -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}], -- cgit v1.2.3-54-g00ecf