Noticed this in the TODO and figured I'd take a stab at it -- what
better way to spend a Friday night? ;)

Bo
From 783df3ff47c64afd6afb978be0a800d119355853 Mon Sep 17 00:00:00 2001
From: Bo Borgerson <[EMAIL PROTECTED]>
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 <[EMAIL PROTECTED]>
---
 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:
+
[EMAIL PROTECTED] @samp
+
[EMAIL PROTECTED] [EMAIL PROTECTED]
+Columns will be delimited by @var{str} in output, rather than the default
+single TAB character.
+
+The delimiter @var{str} may not be empty.
+
[EMAIL PROTECTED] --null-output-delimiter
+Columns will be delimited by a null character in output, rather than the
+default single TAB character.
+
[EMAIL PROTECTED] 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 <http://www.gnu.org/licenses/>.
+
+: ${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.
[EMAIL PROTECTED](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, [EMAIL PROTECTED], $save_temps, $verbose);
+exit $fail;
+EOF
-- 
1.5.2.5

_______________________________________________
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils

Reply via email to