home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / txtut122.zip / textutil / src / comm.c < prev    next >
C/C++ Source or Header  |  1998-04-11  |  7KB  |  273 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 Foundation,
  16.    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.       puts (_("\nReport bugs to textutils-bugs@gnu.ai.mit.edu"));
  82.     }
  83.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  84. }
  85.  
  86. /* Output the line in linebuffer LINE to stream STREAM
  87.    provided the switches say it should be output.
  88.    CLASS is 1 for a line found only in file 1,
  89.    2 for a line only in file 2, 3 for a line in both. */
  90.  
  91. static void
  92. writeline (struct linebuffer *line, FILE *stream, int class)
  93. {
  94.   switch (class)
  95.     {
  96.     case 1:
  97.       if (!only_file_1)
  98.     return;
  99.       break;
  100.  
  101.     case 2:
  102.       if (!only_file_2)
  103.     return;
  104.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  105.       if (only_file_1)
  106.     putc ('\t', stream);
  107.       break;
  108.  
  109.     case 3:
  110.       if (!both)
  111.     return;
  112.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  113.       if (only_file_1)
  114.     putc ('\t', stream);
  115.       /* Skip the tab stop for case 2, if we are printing case 2.  */
  116.       if (only_file_2)
  117.     putc ('\t', stream);
  118.       break;
  119.     }
  120.  
  121.   fwrite (line->buffer, sizeof (char), line->length, stream);
  122.   putc ('\n', stream);
  123. }
  124.  
  125. /* Compare INFILES[0] and INFILES[1].
  126.    If either is "-", use the standard input for that file.
  127.    Assume that each input file is sorted;
  128.    merge them and output the result.
  129.    Return 0 if successful, 1 if any errors occur. */
  130.  
  131. static int
  132. compare_files (char **infiles)
  133. {
  134.   /* For each file, we have one linebuffer in lb1.  */
  135.   struct linebuffer lb1[2];
  136.  
  137.   /* thisline[i] points to the linebuffer holding the next available line
  138.      in file i, or is NULL if there are no lines left in that file.  */
  139.   struct linebuffer *thisline[2];
  140.  
  141.   /* streams[i] holds the input stream for file i.  */
  142.   FILE *streams[2];
  143.  
  144.   int i, ret = 0;
  145.  
  146.   /* Initialize the storage. */
  147.   for (i = 0; i < 2; i++)
  148.     {
  149.       initbuffer (&lb1[i]);
  150.       thisline[i] = &lb1[i];
  151.       streams[i] = strcmp (infiles[i], "-")
  152.     ? fopen (infiles[i], "r") : stdin;
  153.       if (!streams[i])
  154.     {
  155.       error (0, errno, "%s", infiles[i]);
  156.       return 1;
  157.     }
  158.  
  159.       thisline[i] = readline (thisline[i], streams[i]);
  160.     }
  161.  
  162.   while (thisline[0] || thisline[1])
  163.     {
  164.       int order;
  165.  
  166.       /* Compare the next available lines of the two files.  */
  167.  
  168.       if (!thisline[0])
  169.     order = 1;
  170.       else if (!thisline[1])
  171.     order = -1;
  172.       else
  173.     {
  174.       /* Cannot use bcmp -- it only returns a boolean value. */
  175.       order = memcmp (thisline[0]->buffer, thisline[1]->buffer,
  176.               min (thisline[0]->length, thisline[1]->length));
  177.       if (order == 0)
  178.         order = thisline[0]->length - thisline[1]->length;
  179.     }
  180.  
  181.       /* Output the line that is lesser. */
  182.       if (order == 0)
  183.     writeline (thisline[1], stdout, 3);
  184.       else if (order > 0)
  185.     writeline (thisline[1], stdout, 2);
  186.       else
  187.     writeline (thisline[0], stdout, 1);
  188.  
  189.       /* Step the file the line came from.
  190.      If the files match, step both files.  */
  191.       if (order >= 0)
  192.     thisline[1] = readline (thisline[1], streams[1]);
  193.       if (order <= 0)
  194.     thisline[0] = readline (thisline[0], streams[0]);
  195.     }
  196.  
  197.   /* Free all storage and close all input streams. */
  198.   for (i = 0; i < 2; i++)
  199.     {
  200.       free (lb1[i].buffer);
  201.       if (ferror (streams[i]) || fclose (streams[i]) == EOF)
  202.     {
  203.       error (0, errno, "%s", infiles[i]);
  204.       ret = 1;
  205.     }
  206.     }
  207.   if (ferror (stdout) || fclose (stdout) == EOF)
  208.     {
  209.       error (0, errno, _("write error"));
  210.       ret = 1;
  211.     }
  212.   return ret;
  213. }
  214.  
  215. int
  216. main (int argc, char **argv)
  217. {
  218.   int c;
  219.  
  220. #ifdef __EMX__
  221. _wildcard(&argc, &argv);
  222. #endif
  223.  
  224.  
  225.  
  226.   program_name = argv[0];
  227. #ifndef __EMX__
  228.   setlocale (LC_ALL, "");
  229.   bindtextdomain (PACKAGE, LOCALEDIR);
  230.   textdomain (PACKAGE);
  231. #endif
  232.   only_file_1 = 1;
  233.   only_file_2 = 1;
  234.   both = 1;
  235.  
  236.   while ((c = getopt_long (argc, argv, "123", long_options, (int *) 0)) != EOF)
  237.     switch (c)
  238.       {
  239.       case 0:
  240.     break;
  241.  
  242.       case '1':
  243.     only_file_1 = 0;
  244.     break;
  245.  
  246.       case '2':
  247.     only_file_2 = 0;
  248.     break;
  249.  
  250.       case '3':
  251.     both = 0;
  252.     break;
  253.  
  254.       default:
  255.     usage (1);
  256.       }
  257.  
  258.   if (show_version)
  259.     {
  260.       printf ("comm (%s) %s\n", GNU_PACKAGE, VERSION);
  261.       exit (EXIT_SUCCESS);
  262.     }
  263.  
  264.   if (show_help)
  265.     usage (0);
  266.  
  267.   if (optind + 2 != argc)
  268.     usage (1);
  269.  
  270.   exit (compare_files (argv + optind) == 0
  271.     ? EXIT_SUCCESS : EXIT_FAILURE);
  272. }
  273.