diff -aru -x *.o textutils-2.0.14/doc/textutils.texi textutils-pb/doc/textutils.texi --- textutils-2.0.14/doc/textutils.texi Sat Mar 24 09:17:51 2001 +++ textutils-pb/doc/textutils.texi Tue May 15 00:57:25 2001 @@ -2641,13 +2641,23 @@ Print only duplicate lines. @item -D -@itemx --all-repeated +@itemx --all-repeated[=delimit-method] @opindex -D @opindex --all-repeated @cindex all duplicate lines, outputting Print all duplicate lines and only duplicate lines. This option is useful mainly in conjunction with other options e.g., to ignore case or to compare only selected fields. +The optional delimit-method is one of {all, minimum, none}, none +being implied if delimit-method is not specified or if -D used. +The "all" delimit method is most useful for automated processing +of uniq output, as it precedes all duplicate groups with a +blank line. The "minimum" delimit-method is very similar except a +blank line is not printed before the first group and hence better +suited for output direct to users. +Note the output will be ambiguous if {all,minimum} is specified and +the input stream contains 2 or more consecutive blank lines. If this +is expected in the input stream it can be removed using tr -s '\n'. This is a @sc{gnu} extension. @c FIXME: give an example showing *how* it's useful diff -aru -x *.o textutils-2.0.14/src/uniq.c textutils-pb/src/uniq.c --- textutils-2.0.14/src/uniq.c Sun Mar 18 07:53:56 2001 +++ textutils-pb/src/uniq.c Tue May 15 01:00:21 2001 @@ -25,6 +25,7 @@ #include "system.h" #include "closeout.h" +#include "argmatch.h" #include "linebuffer.h" #include "error.h" #include "xstrtol.h" @@ -81,11 +82,31 @@ /* If nonzero, ignore case when comparing. */ static int ignore_case; +enum delimit_method +{ + delimit_none, /* --all-repeated[=none] No delimiters output. */ + delimit_all, /* --all-repeated=all Delimiter precedes all groups. */ + delimit_minimum, /* --all-repeated=minimum Delimit all groups. */ +}; + +static char const *const delimit_method_string[] = +{ + "none", "all", "minimum", 0 +}; + +static enum delimit_method const delimit_method_map[] = +{ + delimit_none, delimit_all, delimit_minimum, +}; + +/* If nonzero, delimit groups of duplicate lines with \n */ +static enum delimit_method delimit_groups; + static struct option const longopts[] = { {"count", no_argument, NULL, 'c'}, {"repeated", no_argument, NULL, 'd'}, - {"all-repeated", no_argument, NULL, 'D'}, + {"all-repeated", optional_argument, NULL, 'D'}, {"ignore-case", no_argument, NULL, 'i'}, {"unique", no_argument, NULL, 'u'}, {"skip-fields", required_argument, NULL, 'f'}, @@ -114,7 +135,9 @@ \n\ -c, --count prefix lines by the number of occurrences\n\ -d, --repeated only print duplicate lines\n\ - -D, --all-repeated print all duplicate lines\n\ + -D, --all-repeated[=delimit-method] print all duplicate lines\n\ + delimit-method={all,minimum,none(default)}\n\ + Delimiting is done with blank lines.\n\ -f, --skip-fields=N avoid comparing the first N fields\n\ -i, --ignore-case ignore differences in case when comparing\n\ -s, --skip-chars=N avoid comparing the first N characters\n\ @@ -276,6 +299,7 @@ char *prevfield; size_t prevlen; int match_count = 0; + int first_delimiter = 1; if (readline (prevline, istream) == 0) goto closefiles; @@ -296,6 +320,19 @@ if (match) ++match_count; + if (mode == output_all_repeated && delimit_groups != delimit_none) + if (!match) + { + if (match_count) /* a previous match */ + first_delimiter = 0; /* Only used when delimit_minimum */ + } + else if (match_count == 1) + { + if ((delimit_groups == delimit_all) || + (delimit_groups == delimit_minimum && !first_delimiter)) + putc ('\n', ostream); + } + if (!match || mode == output_all_repeated) { writeline (prevline, ostream, match_count); @@ -341,6 +378,7 @@ check_chars = 0; mode = output_all; countmode = count_none; + delimit_groups = delimit_none; while ((optc = getopt_long (argc, argv, "0123456789cdDf:is:uw:", longopts, NULL)) != -1) @@ -373,6 +411,12 @@ case 'D': mode = output_all_repeated; + if (optarg == NULL) + delimit_groups = delimit_none; + else + delimit_groups = XARGMATCH ("--all-repeated", optarg, + delimit_method_string, + delimit_method_map); break; case 'f': /* Like '-#'. */ diff -aru -x *.o textutils-2.0.14/tests/uniq/Test.pm textutils-pb/tests/uniq/Test.pm --- textutils-2.0.14/tests/uniq/Test.pm Sat Jan 9 16:14:38 1999 +++ textutils-pb/tests/uniq/Test.pm Mon May 14 23:41:42 2001 @@ -70,6 +70,13 @@ ['110', '-D', "a\na\n", "a\na\n", 0], ['111', '-D -w1',"a a\na b\n", "a a\na b\n", 0], ['112', '-D -c', "a a\na b\n", "", 1], +['113', '--all-repeated=minimum', "a\na\n", "a\na\n", 0], +['114', '--all-repeated=minimum',"a\na\nb\nc\nc\n", "a\na\n\nc\nc\n", 0], +['115', '--all-repeated=minimum',"a\na\nb\nb\nc\n", "a\na\n\nb\nb\n", 0], +['116', '--all-repeated=all', "a\na\n", "\na\na\n", 0], +['117', '--all-repeated=all', "a\na\nb\nc\nc\n", "\na\na\n\nc\nc\n", 0], +['118', '--all-repeated=all', "a\nb\n", "", 0], +['119', '--all-repeated=badoption', "a\n", "", 1], ); sub test_vector