From 783df3ff47c64afd6afb978be0a800d119355853 Mon Sep 17 00:00:00 2001 From: Bo Borgerson Date: Fri, 4 Apr 2008 20:40:58 -0400 Subject: [PATCH] Add support for --output-delimiter=STR and --null-output-delimiter to comm src/comm.c: (static char *delimiter) points to the delimiter string (static void delimit) new function responsible for outputting delimiter tests/misc/comm: Add new test file for comm. tests/misc/Makefile.am: Run comm tests. doc/coreutils.texi: Document new options. NEWS: Advertise new delimiter. TODO: Remove associated item. Signed-off-by: Bo Borgerson --- NEWS | 4 ++ TODO | 5 -- doc/coreutils.texi | 17 +++++++- src/comm.c | 63 ++++++++++++++++++++++++--- tests/misc/Makefile.am | 1 + tests/misc/comm | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 12 deletions(-) create mode 100755 tests/misc/comm diff --git a/NEWS b/NEWS index e208b30..e90ee1a 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,10 @@ GNU coreutils NEWS -*- outline -*- options --general-numeric-sort/-g, --month-sort/-M, --numeric-sort/-n and --random-sort/-R, resp. + comm accepts two new options, --output-delimiter=STR and + --null-output-delimiter, that allow specification of a delimiter other + than the default single TAB. + ** Improvements id and groups work around an AFS-related bug whereby those programs diff --git a/TODO b/TODO index 86320b9..ffbdccf 100644 --- a/TODO +++ b/TODO @@ -14,11 +14,6 @@ document the following in coreutils.texi: uptime Also document the SELinux changes. -comm: add an option, --output-delimiter=STR - Files to change: src/comm.c, ChangeLog, NEWS, doc/coreutils.texi, - Add a new file, tests/misc/comm (use another file in that directory as - a template), to exercise the new option. Suggestion from Dan Jacobson. - printf: Now that gnulib supports *printf("%a"), import one of the *printf-posix modules so that printf(1) will support %a even on diff --git a/doc/coreutils.texi b/doc/coreutils.texi index ee7dbb2..cc577b1 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -4332,12 +4332,27 @@ Columns are separated by a single TAB character. The options @option{-1}, @option{-2}, and @option{-3} suppress printing of the corresponding columns. Also see @ref{Common options}. +Other options are: + address@hidden @samp + address@hidden address@hidden +Columns will be delimited by @var{str} in output, rather than the default +single TAB character. + +The delimiter @var{str} may not be empty. + address@hidden --null-output-delimiter +Columns will be delimited by a null character in output, rather than the +default single TAB character. + address@hidden table + Unlike some other comparison utilities, @command{comm} has an exit status that does not depend on the result of the comparison. Upon normal completion @command{comm} produces an exit code of zero. If there is an error it exits with nonzero status. - @node tsort invocation @section @command{tsort}: Topological sort diff --git a/src/comm.c b/src/comm.c index cbda362..95c8413 100644 --- a/src/comm.c +++ b/src/comm.c @@ -52,8 +52,23 @@ static bool only_file_2; /* If true, print lines that are found in both files. */ static bool both; +/* If pointing to string of > 0 length, delimit with string. + If pointing to empty string, delimit with a null byte. */ +static char *delimiter; + +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + OUTPUT_DELIMITER_OPTION = CHAR_MAX + 1, + NULL_DELIMITER_OPTION +}; + + static struct option const long_options[] = { + {"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION}, + {"null-output-delimiter", no_argument, NULL, NULL_DELIMITER_OPTION}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -87,6 +102,9 @@ and column three contains lines common to both files.\n\ -1 suppress lines unique to FILE1\n\ -2 suppress lines unique to FILE2\n\ -3 suppress lines that appear in both files\n\ +\n\ + --output-delimiter=STR separate columns with STR\n\ + --null-output-delimiter separate columns with a NULL byte\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -95,6 +113,17 @@ and column three contains lines common to both files.\n\ exit (status); } +/* Output the appropriate delimiter. */ + +static void +delimit (FILE *stream) +{ + if (*delimiter) + fputs (delimiter, stream); + else + putc ('\0', stream); +} + /* Output the line in linebuffer LINE to stream STREAM provided the switches say it should be output. CLASS is 1 for a line found only in file 1, @@ -113,20 +142,20 @@ writeline (const struct linebuffer *line, FILE *stream, int class) case 2: if (!only_file_2) return; - /* Print a TAB if we are printing lines from file 1. */ + /* Print a delimiter if we are printing lines from file 1. */ if (only_file_1) - putc ('\t', stream); + delimit (stream); break; case 3: if (!both) return; - /* Print a TAB if we are printing lines from file 1. */ + /* Print a delimiter if we are printing lines from file 1. */ if (only_file_1) - putc ('\t', stream); - /* Print a TAB if we are printing lines from file 2. */ + delimit (stream); + /* Print a delimiter if we are printing lines from file 2. */ if (only_file_2) - putc ('\t', stream); + delimit (stream); break; } @@ -255,6 +284,24 @@ main (int argc, char **argv) both = false; break; + case OUTPUT_DELIMITER_OPTION: + if (delimiter) + error (EXIT_FAILURE, 0, _("multiple delimiters specified")); + delimiter = optarg; + if (!*delimiter) + { + error (0, 0, _("empty delimiter not allowed")); + error (EXIT_FAILURE, 0, _("use %s for null delimiter"), + quote ("--null-delimiter")); + } + break; + + case NULL_DELIMITER_OPTION: + if (delimiter) + error (EXIT_FAILURE, 0, _("multiple delimiters specified")); + delimiter = "\0"; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -278,6 +325,10 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + /* The default delimiter is a TAB. */ + if (!delimiter) + delimiter = "\t"; + compare_files (argv + optind); exit (EXIT_SUCCESS); diff --git a/tests/misc/Makefile.am b/tests/misc/Makefile.am index 17a0ec0..83e0262 100644 --- a/tests/misc/Makefile.am +++ b/tests/misc/Makefile.am @@ -49,6 +49,7 @@ TESTS = \ chcon \ chcon-fail \ selinux \ + comm \ cut \ wc-files0-from \ wc-files0 \ diff --git a/tests/misc/comm b/tests/misc/comm new file mode 100755 index 0000000..21be8a5 --- /dev/null +++ b/tests/misc/comm @@ -0,0 +1,112 @@ +#!/bin/sh +# -*- perl -*- +# Test comm + +# Copyright (C) 2008 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 . + +: ${srcdir=.} +. $srcdir/../require-perl + +me=`echo $0|sed 's,.*/,,'` +exec $PERL -w -I$srcdir/.. -MCoreutils -M"CuTmpdir qw($me)" -- - <<\EOF +#/ +require 5.003; +use strict; + +(my $program_name = $0) =~ s|.*/||; + +my $prog = 'comm'; + +# Turn off localization of executable's ouput. address@hidden(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @inputs = ({IN=>{a=>"1\n2\n3"}}, {IN=>{b=>"1\n2\n4"}}); + +my @Tests = + ( + # basic operation + ['basic', @inputs, {OUT=>"\t\t1\n\t\t2\n3\n\t4\n"} ], + + # supress lines unique to file 1 + ['opt-1', '-1', @inputs, {OUT=>"\t1\n\t2\n4\n"} ], + + # supress lines unique to file 2 + ['opt-2', '-2', @inputs, {OUT=>"\t1\n\t2\n3\n"} ], + + # supress lines that appear in both files + ['opt-3', '-3', @inputs, {OUT=>"3\n\t4\n"} ], + + # supress lines unique to file 1 and lines unique to file 2 + ['opt-12', '-1', '-2', @inputs, {OUT=>"1\n2\n"} ], + + # supress lines unique to file 1 and those that appear in both files + ['opt-13', '-1', '-3', @inputs, {OUT=>"4\n"} ], + + # supress lines unique to file 2 and those that appear in both files + ['opt-23', '-2', '-3', @inputs, {OUT=>"3\n"} ], + + # supress all output (really?) + ['opt-123', '-1', '-2', '-3', @inputs, {OUT=>""} ], + + # invalid missing command line argument (1) + ['missing-arg1', $inputs[0], {EXIT=>1}, + {ERR => "$prog: missing operand after `a'\n" + . "Try `$prog --help' for more information.\n"}], + + # invalid missing command line argument (both) + ['missing-arg2', {EXIT=>1}, + {ERR => "$prog: missing operand\n" + . "Try `$prog --help' for more information.\n"}], + + # invalid extra command line argument + ['extra-arg', @inputs, 'no-such', {EXIT=>1}, + {ERR => "$prog: extra operand `no-such'\n" + . "Try `$prog --help' for more information.\n"}], + + # alternate delimiter: ',' + ['delim-comma', '--output-delimiter=,', @inputs, + {OUT=>",,1\n,,2\n3\n,4\n"} ], + + # two-character alternate delimiter: '++' + ['delim-2char', '--output-delimiter=++', @inputs, + {OUT=>"++++1\n++++2\n3\n++4\n"} ], + +# FIXME: Non null-terminated comparison of results +# # null delimiter +# ['delim-null', '--null-output-delimiter', @inputs, +# {OUT=>"\0\01\n\0\02\n3\n\04\n"} ], + + # invalid empty delimiter + ['delim-empty', '--output-delimiter=', @inputs, {EXIT=>1}, + {ERR => "$prog: empty delimiter not allowed\n" + . "$prog: use `--null-delimiter' for null delimiter\n"}], + + # invalid dual delimiter + ['delim-dual', '--output-delimiter=,', '--null-output-delimiter', + @inputs, {EXIT=>1}, {ERR => "$prog: multiple delimiters specified\n"}], + + # invalid dual delimiter (the other way) + ['delim-dual2', '--output-delimiter=,', '--output-delimiter=+', + @inputs, {EXIT=>1}, {ERR => "$prog: multiple delimiters specified\n"}], + + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, address@hidden, $save_temps, $verbose); +exit $fail; +EOF -- 1.5.2.5