home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / textutils-1.19-src.tgz / tar.out / fsf / textutils / src / comm.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  6KB  |  265 lines

  1. /* comm -- compare two sorted files line by line.
  2.    Copyright (C) 1986, 1990, 1991, 1995, 1996 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* Written by Richard Stallman and David MacKenzie. */
  19.  
  20. #include <config.h>
  21.  
  22. #include <stdio.h>
  23. #include <getopt.h>
  24. #include <sys/types.h>
  25. #include "system.h"
  26. #include "linebuffer.h"
  27. #include "error.h"
  28.  
  29. /* Undefine, to avoid warning about redefinition on some systems.  */
  30. #undef min
  31. #define min(x, y) ((x) < (y) ? (x) : (y))
  32.  
  33. /* The name this program was run with. */
  34. char *program_name;
  35.  
  36. /* If nonzero, print lines that are found only in file 1. */
  37. static int only_file_1;
  38.  
  39. /* If nonzero, print lines that are found only in file 2. */
  40. static int only_file_2;
  41.  
  42. /* If nonzero, print lines that are found in both files. */
  43. static int both;
  44.  
  45. /* If nonzero, display usage information and exit.  */
  46. static int show_help;
  47.  
  48. /* If nonzero, print the version on standard output then exit.  */
  49. static int show_version;
  50.  
  51. static struct option const long_options[] =
  52. {
  53.   {"help", no_argument, &show_help, 1},
  54.   {"version", no_argument, &show_version, 1},
  55.   {0, 0, 0, 0}
  56. };
  57.  
  58.  
  59.  
  60. static void
  61. usage (int status)
  62. {
  63.   if (status != 0)
  64.     fprintf (stderr, _("Try `%s --help' for more information.\n"),
  65.          program_name);
  66.   else
  67.     {
  68.       printf (_("\
  69. Usage: %s [OPTION]... LEFT_FILE RIGHT_FILE\n\
  70. "),
  71.           program_name);
  72.       printf (_("\
  73. Compare sorted files LEFT_FILE and RIGHT_FILE line by line.\n\
  74. \n\
  75.   -1              suppress lines unique to left file\n\
  76.   -2              suppress lines unique to right file\n\
  77.   -3              suppress lines unique to both files\n\
  78.       --help      display this help and exit\n\
  79.       --version   output version information and exit\n\
  80. "));
  81.     }
  82.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  83. }
  84.  
  85. /* Output the line in linebuffer LINE to stream STREAM
  86.    provided the switches say it should be output.
  87.    CLASS is 1 for a line found only in file 1,
  88.    2 for a line only in file 2, 3 for a line in both. */
  89.  
  90. static void
  91. writeline (struct linebuffer *line, FILE *stream, int class)
  92. {
  93.   switch (class)
  94.     {
  95.     case 1:
  96.       if (!only_file_1)
  97.     return;
  98.       break;
  99.  
  100.     case 2:
  101.       if (!only_file_2)
  102.     return;
  103.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  104.       if (only_file_1)
  105.     putc ('\t', stream);
  106.       break;
  107.  
  108.     case 3:
  109.       if (!both)
  110.     return;
  111.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  112.       if (only_file_1)
  113.     putc ('\t', stream);
  114.       /* Skip the tab stop for case 2, if we are printing case 2.  */
  115.       if (only_file_2)
  116.     putc ('\t', stream);
  117.       break;
  118.     }
  119.  
  120.   fwrite (line->buffer, sizeof (char), line->length, stream);
  121.   putc ('\n', stream);
  122. }
  123.  
  124. /* Compare INFILES[0] and INFILES[1].
  125.    If either is "-", use the standard input for that file.
  126.    Assume that each input file is sorted;
  127.    merge them and output the result.
  128.    Return 0 if successful, 1 if any errors occur. */
  129.  
  130. static int
  131. compare_files (char **infiles)
  132. {
  133.   /* For each file, we have one linebuffer in lb1.  */
  134.   struct linebuffer lb1[2];
  135.  
  136.   /* thisline[i] points to the linebuffer holding the next available line
  137.      in file i, or is NULL if there are no lines left in that file.  */
  138.   struct linebuffer *thisline[2];
  139.  
  140.   /* streams[i] holds the input stream for file i.  */
  141.   FILE *streams[2];
  142.  
  143.   int i, ret = 0;
  144.  
  145.   /* Initialize the storage. */
  146.   for (i = 0; i < 2; i++)
  147.     {
  148.       initbuffer (&lb1[i]);
  149.       thisline[i] = &lb1[i];
  150.       streams[i] = strcmp (infiles[i], "-")
  151.     ? fopen (infiles[i], "r") : stdin;
  152.       if (!streams[i])
  153.     {
  154.       error (0, errno, "%s", infiles[i]);
  155.       return 1;
  156.     }
  157.  
  158.       thisline[i] = readline (thisline[i], streams[i]);
  159.     }
  160.  
  161.   while (thisline[0] || thisline[1])
  162.     {
  163.       int order;
  164.  
  165.       /* Compare the next available lines of the two files.  */
  166.  
  167.       if (!thisline[0])
  168.     order = 1;
  169.       else if (!thisline[1])
  170.     order = -1;
  171.       else
  172.     {
  173.       /* Cannot use bcmp -- it only returns a boolean value. */
  174.       order = memcmp (thisline[0]->buffer, thisline[1]->buffer,
  175.               min (thisline[0]->length, thisline[1]->length));
  176.       if (order == 0)
  177.         order = thisline[0]->length - thisline[1]->length;
  178.     }
  179.  
  180.       /* Output the line that is lesser. */
  181.       if (order == 0)
  182.     writeline (thisline[1], stdout, 3);
  183.       else if (order > 0)
  184.     writeline (thisline[1], stdout, 2);
  185.       else
  186.     writeline (thisline[0], stdout, 1);
  187.  
  188.       /* Step the file the line came from.
  189.      If the files match, step both files.  */
  190.       if (order >= 0)
  191.     thisline[1] = readline (thisline[1], streams[1]);
  192.       if (order <= 0)
  193.     thisline[0] = readline (thisline[0], streams[0]);
  194.     }
  195.  
  196.   /* Free all storage and close all input streams. */
  197.   for (i = 0; i < 2; i++)
  198.     {
  199.       free (lb1[i].buffer);
  200.       if (ferror (streams[i]) || fclose (streams[i]) == EOF)
  201.     {
  202.       error (0, errno, "%s", infiles[i]);
  203.       ret = 1;
  204.     }
  205.     }
  206.   if (ferror (stdout) || fclose (stdout) == EOF)
  207.     {
  208.       error (0, errno, _("write error"));
  209.       ret = 1;
  210.     }
  211.   return ret;
  212. }
  213.  
  214. int
  215. main (int argc, char **argv)
  216. {
  217.   int c;
  218.  
  219.   program_name = argv[0];
  220.   setlocale (LC_ALL, "");
  221.   bindtextdomain (PACKAGE, LOCALEDIR);
  222.   textdomain (PACKAGE);
  223.  
  224.   only_file_1 = 1;
  225.   only_file_2 = 1;
  226.   both = 1;
  227.  
  228.   while ((c = getopt_long (argc, argv, "123", long_options, (int *) 0)) != EOF)
  229.     switch (c)
  230.       {
  231.       case 0:
  232.     break;
  233.  
  234.       case '1':
  235.     only_file_1 = 0;
  236.     break;
  237.  
  238.       case '2':
  239.     only_file_2 = 0;
  240.     break;
  241.  
  242.       case '3':
  243.     both = 0;
  244.     break;
  245.  
  246.       default:
  247.     usage (1);
  248.       }
  249.  
  250.   if (show_version)
  251.     {
  252.       printf ("comm - %s\n", PACKAGE_VERSION);
  253.       exit (EXIT_SUCCESS);
  254.     }
  255.  
  256.   if (show_help)
  257.     usage (0);
  258.  
  259.   if (optind + 2 != argc)
  260.     usage (1);
  261.  
  262.   exit (compare_files (argv + optind) == 0
  263.     ? EXIT_SUCCESS : EXIT_FAILURE);
  264. }
  265.