src/comm.c - coreutils-8.23
Global variables defined
Functions defined
Macros defined
Source code
#include <config.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "linebuffer.h"
#include "error.h"
#include "fadvise.h"
#include "hard-locale.h"
#include "quote.h"
#include "stdio--.h"
#include "memcmp2.h"
#include "xmemcoll.h"
#define PROGRAM_NAME "comm"
#define AUTHORS \
proper_name ("Richard M. Stallman"), \
proper_name ("David MacKenzie")
#undef min
#define min(x, y) ((x) < (y) ? (x) : (y))
static bool hard_LC_COLLATE;
static bool only_file_1;
static bool only_file_2;
static bool both;
static bool seen_unpairable;
static bool issued_disorder_warning[2];
static enum
{
CHECK_ORDER_DEFAULT,
CHECK_ORDER_ENABLED,
CHECK_ORDER_DISABLED
} check_input_order;
static char const *delimiter;
enum
{
CHECK_ORDER_OPTION = CHAR_MAX + 1,
NOCHECK_ORDER_OPTION,
OUTPUT_DELIMITER_OPTION
};
static struct option const long_options[] =
{
{"check-order", no_argument, NULL, CHECK_ORDER_OPTION},
{"nocheck-order", no_argument, NULL, NOCHECK_ORDER_OPTION},
{"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... FILE1 FILE2\n\
"),
program_name);
fputs (_("\
Compare sorted files FILE1 and FILE2 line by line.\n\
"), stdout);
fputs (_("\
\n\
With no options, produce three-column output. Column one contains\n\
lines unique to FILE1, column two contains lines unique to FILE2,\n\
and column three contains lines common to both files.\n\
"), stdout);
fputs (_("\
\n\
-1 suppress column 1 (lines unique to FILE1)\n\
-2 suppress column 2 (lines unique to FILE2)\n\
-3 suppress column 3 (lines that appear in both files)\n\
"), stdout);
fputs (_("\
\n\
--check-order check that the input is correctly sorted, even\n\
if all input lines are pairable\n\
--nocheck-order do not check that the input is correctly sorted\n\
"), stdout);
fputs (_("\
--output-delimiter=STR separate columns with STR\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
Note, comparisons honor the rules specified by 'LC_COLLATE'.\n\
"), stdout);
printf (_("\
\n\
Examples:\n\
%s -12 file1 file2 Print only lines present in both file1 and file2.\n\
%s -3 file1 file2 Print lines in file1 not in file2, and vice versa.\n\
"),
program_name, program_name);
emit_ancillary_info ();
}
exit (status);
}
static void
writeline (struct linebuffer const *line, FILE *stream, int class)
{
switch (class)
{
case 1:
if (!only_file_1)
return;
break;
case 2:
if (!only_file_2)
return;
if (only_file_1)
fputs (delimiter, stream);
break;
case 3:
if (!both)
return;
if (only_file_1)
fputs (delimiter, stream);
if (only_file_2)
fputs (delimiter, stream);
break;
}
fwrite (line->buffer, sizeof (char), line->length, stream);
}
static void
check_order (struct linebuffer const *prev,
struct linebuffer const *current,
int whatfile)
{
if (check_input_order != CHECK_ORDER_DISABLED
&& ((check_input_order == CHECK_ORDER_ENABLED) || seen_unpairable))
{
if (!issued_disorder_warning[whatfile - 1])
{
int order;
if (hard_LC_COLLATE)
order = xmemcoll (prev->buffer, prev->length - 1,
current->buffer, current->length - 1);
else
order = memcmp2 (prev->buffer, prev->length - 1,
current->buffer, current->length - 1);
if (0 < order)
{
error ((check_input_order == CHECK_ORDER_ENABLED
? EXIT_FAILURE : 0),
0, _("file %d is not in sorted order"), whatfile);
issued_disorder_warning[whatfile - 1] = true;
}
}
}
}
static void
compare_files (char **infiles)
{
struct linebuffer lba[2][4];
struct linebuffer *thisline[2];
struct linebuffer *all_line[2][4];
int alt[2][3];
FILE *streams[2];
int i, j;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 4; j++)
{
initbuffer (&lba[i][j]);
all_line[i][j] = &lba[i][j];
}
alt[i][0] = 0;
alt[i][1] = 0;
alt[i][2] = 0;
streams[i] = (STREQ (infiles[i], "-") ? stdin : fopen (infiles[i], "r"));
if (!streams[i])
error (EXIT_FAILURE, errno, "%s", infiles[i]);
fadvise (streams[i], FADVISE_SEQUENTIAL);
thisline[i] = readlinebuffer (all_line[i][alt[i][0]], streams[i]);
if (ferror (streams[i]))
error (EXIT_FAILURE, errno, "%s", infiles[i]);
}
while (thisline[0] || thisline[1])
{
int order;
bool fill_up[2] = { false, false };
if (!thisline[0])
order = 1;
else if (!thisline[1])
order = -1;
else
{
if (hard_LC_COLLATE)
order = xmemcoll (thisline[0]->buffer, thisline[0]->length - 1,
thisline[1]->buffer, thisline[1]->length - 1);
else
{
size_t len = min (thisline[0]->length, thisline[1]->length) - 1;
order = memcmp (thisline[0]->buffer, thisline[1]->buffer, len);
if (order == 0)
order = (thisline[0]->length < thisline[1]->length
? -1
: thisline[0]->length != thisline[1]->length);
}
}
if (order == 0)
writeline (thisline[1], stdout, 3);
else
{
seen_unpairable = true;
if (order <= 0)
writeline (thisline[0], stdout, 1);
else
writeline (thisline[1], stdout, 2);
}
if (0 <= order)
fill_up[1] = true;
if (order <= 0)
fill_up[0] = true;
for (i = 0; i < 2; i++)
if (fill_up[i])
{
alt[i][2] = alt[i][1];
alt[i][1] = alt[i][0];
alt[i][0] = (alt[i][0] + 1) & 0x03;
thisline[i] = readlinebuffer (all_line[i][alt[i][0]], streams[i]);
if (thisline[i])
check_order (all_line[i][alt[i][1]], thisline[i], i + 1);
else if (all_line[i][alt[i][2]]->buffer)
check_order (all_line[i][alt[i][2]],
all_line[i][alt[i][1]], i + 1);
if (ferror (streams[i]))
error (EXIT_FAILURE, errno, "%s", infiles[i]);
fill_up[i] = false;
}
}
for (i = 0; i < 2; i++)
if (fclose (streams[i]) != 0)
error (EXIT_FAILURE, errno, "%s", infiles[i]);
}
int
main (int argc, char **argv)
{
int c;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
hard_LC_COLLATE = hard_locale (LC_COLLATE);
atexit (close_stdout);
only_file_1 = true;
only_file_2 = true;
both = true;
seen_unpairable = false;
issued_disorder_warning[0] = issued_disorder_warning[1] = false;
check_input_order = CHECK_ORDER_DEFAULT;
while ((c = getopt_long (argc, argv, "123", long_options, NULL)) != -1)
switch (c)
{
case '1':
only_file_1 = false;
break;
case '2':
only_file_2 = false;
break;
case '3':
both = false;
break;
case NOCHECK_ORDER_OPTION:
check_input_order = CHECK_ORDER_DISABLED;
break;
case CHECK_ORDER_OPTION:
check_input_order = CHECK_ORDER_ENABLED;
break;
case OUTPUT_DELIMITER_OPTION:
if (delimiter && !STREQ (delimiter, optarg))
error (EXIT_FAILURE, 0, _("multiple delimiters specified"));
delimiter = optarg;
if (!*delimiter)
{
error (EXIT_FAILURE, 0, _("empty %s not allowed"),
quote ("--output-delimiter"));
}
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
if (argc - optind < 2)
{
if (argc <= optind)
error (0, 0, _("missing operand"));
else
error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
if (2 < argc - optind)
{
error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
usage (EXIT_FAILURE);
}
if (!delimiter)
delimiter = "\t";
compare_files (argv + optind);
if (issued_disorder_warning[0] || issued_disorder_warning[1])
exit (EXIT_FAILURE);
else
exit (EXIT_SUCCESS);
}