home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / d / diff20.zip / diff-2.0 / cmp.c < prev    next >
C/C++ Source or Header  |  1992-07-08  |  12KB  |  513 lines

  1. /* cmp -- compare two files.
  2.    Copyright (C) 1990, 1991, 1992 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Torbjorn Granlund and David MacKenzie. */
  19.  
  20. #include <stdio.h>
  21. #include "system.h"
  22. #include "getopt.h"
  23.  
  24. char *xmalloc ();
  25. int block_compare ();
  26. int block_compare_and_count ();
  27. int block_read ();
  28. int cmp ();
  29. void printc ();
  30. void error ();
  31.  
  32. /* Name under which this program was invoked.  */
  33. char *program_name;
  34.  
  35. /* Filenames of the compared files.  */
  36. char *file1;
  37. char *file2;
  38.  
  39. /* File descriptors of the files.  */
  40. int file1_desc;
  41. int file2_desc;
  42.  
  43. /* Read buffers for the files.  */
  44. char *buf1;
  45. char *buf2;
  46.  
  47. /* Optimal block size for the files.  */
  48. int buf_size;
  49.  
  50. /* Output format:
  51.    type_first_diff
  52.      to print the offset and line number of the first differing bytes
  53.    type_all_diffs
  54.      to print the (decimal) offsets and (octal) values of all differing bytes
  55.    type_status
  56.      to only return an exit status indicating whether the files differ */
  57. enum
  58.   {
  59.     type_first_diff, type_all_diffs, type_status
  60.   } comparison_type = type_first_diff;
  61.  
  62. /* If nonzero, print values of bytes quoted like cat -t does. */
  63. int opt_print_chars = 0;
  64.  
  65. struct option long_options[] =
  66. {
  67.   {"print-chars", 0, NULL, 'c'},
  68.   {"verbose", 0, NULL, 'l'},
  69.   {"silent", 0, NULL, 's'},
  70.   {"quiet", 0, NULL, 's'},
  71.   {NULL, 0, NULL, 0}
  72. };
  73.  
  74. void
  75. usage (reason)
  76.      char *reason;
  77. {
  78.   if (reason != NULL)
  79.     fprintf (stderr, "%s: %s\n", program_name, reason);
  80.  
  81.   fprintf (stderr, "\
  82. Usage: %s [-cls] [--print-chars] [--verbose] [--silent] [--quiet]\n\
  83.        from-file [to-file]\n", program_name);
  84.  
  85.   exit (2);
  86. }
  87.  
  88. int
  89. main (argc, argv)
  90.      int argc;
  91.      char *argv[];
  92. {
  93.   int c, opened = 0, exit_status;
  94.   struct stat stat_buf1, stat_buf2;
  95.  
  96.   program_name = argv[0];
  97.  
  98.   /* If an argument is omitted, default to the standard input.  */
  99.  
  100.   file1 = "-";
  101.   file2 = "-";
  102.   file1_desc = 0;
  103.   file2_desc = 0;
  104.  
  105.   /* Parse command line options.  */
  106.  
  107.   while ((c = getopt_long (argc, argv, "cls", long_options, (int *) 0)) != EOF)
  108.     switch (c)
  109.       {
  110.       case 'c':
  111.     opt_print_chars = 1;
  112.     break;
  113.  
  114.       case 'l':
  115.     comparison_type = type_all_diffs;
  116.     break;
  117.  
  118.       case 's':
  119.     comparison_type = type_status;
  120.     break;
  121.  
  122.       default:
  123.     usage ((char *) 0);
  124.       }
  125.  
  126.   if (optind < argc)
  127.     file1 = argv[optind++];
  128.  
  129.   if (optind < argc)
  130.     file2 = argv[optind++];
  131.  
  132.   if (optind < argc)
  133.     usage ("extra arguments");
  134.  
  135.   if (strcmp (file1, "-"))
  136.     {
  137.       opened = 1;
  138.       file1_desc = open (file1, O_RDONLY);
  139.       if (file1_desc < 0)
  140.     {
  141.       if (comparison_type == type_status)
  142.         exit (2);
  143.       else
  144.         error (2, errno, "%s", file1);
  145.     }
  146.     }
  147.  
  148.   if (strcmp (file2, "-"))
  149.     {
  150.       opened = 1;
  151.       file2_desc = open (file2, O_RDONLY);
  152.       if (file2_desc < 0)
  153.     {
  154.       if (comparison_type == type_status)
  155.         exit (2);
  156.       else
  157.         error (2, errno, "%s", file2);
  158.     }
  159.     }
  160.  
  161.   if (file1_desc == file2_desc)
  162.     {
  163.       if (opened)
  164.     error (2, 0, "standard input is closed");
  165.       else
  166.     usage ("at least one filename should be specified");
  167.     }
  168.  
  169.   if (fstat (file1_desc, &stat_buf1) < 0)
  170.     error (2, errno, "%s", file1);
  171.   if (fstat (file2_desc, &stat_buf2) < 0)
  172.     error (2, errno, "%s", file2);
  173.  
  174.   /* If both the input descriptors are associated with plain files,
  175.      we can make the job simpler in some cases.  */
  176.  
  177.   if (S_ISREG (stat_buf1.st_mode) && S_ISREG (stat_buf2.st_mode))
  178.     {
  179.       /* Find out if the files are links to the same inode, and therefore
  180.      identical.  */
  181.  
  182.       if (stat_buf1.st_dev == stat_buf2.st_dev
  183.       && stat_buf1.st_ino == stat_buf2.st_ino)
  184.     exit (0);
  185.  
  186.       /* If output is redirected to "/dev/null", we may assume `-s'.  */
  187.  
  188.       if (comparison_type != type_status)
  189.     {
  190.       struct stat sb;
  191.       dev_t nulldev;
  192.       ino_t nullino;
  193.  
  194.       if (stat ("/dev/null", &sb) == 0)
  195.         {
  196.           nulldev = sb.st_dev;
  197.           nullino = sb.st_ino;
  198.           if (fstat (1, &sb) == 0
  199.           && sb.st_dev == nulldev && sb.st_ino == nullino)
  200.         comparison_type = type_status;
  201.         }
  202.     }
  203.  
  204.       /* If only a return code is needed, conclude that
  205.      the files differ if they have different sizes.  */
  206.  
  207.       if (comparison_type == type_status
  208.       && stat_buf1.st_size != stat_buf2.st_size)
  209.     exit (1);
  210.     }
  211.  
  212.   /* Get the optimal block size of the files.  */
  213.  
  214.   buf_size = 8 * 1024;        /* Keep it simple.  */
  215.  
  216.   /* Allocate buffers, with space for sentinels at the end.  */
  217.  
  218.   buf1 = xmalloc (buf_size + sizeof (long));
  219.   buf2 = xmalloc (buf_size + sizeof (long));
  220.  
  221.   exit_status = cmp ();
  222.  
  223.   if (close (file1_desc) < 0)
  224.     error (2, errno, "%s", file1);
  225.   if (close (file2_desc) < 0)
  226.     error (2, errno, "%s", file2);
  227.   if (comparison_type != type_status
  228.       && (ferror (stdout) || fclose (stdout) == EOF))
  229.     error (2, errno, "write error");
  230.   exit (exit_status);
  231.   return exit_status;
  232. }
  233.  
  234. /* Compare the two files already open on `file1_desc' and `file2_desc',
  235.    using `buf1' and `buf2'.
  236.    Return 0 if identical, 1 if different, >1 if error. */
  237.  
  238. int
  239. cmp ()
  240. {
  241.   long line_number = 1;        /* Line number (1...) of first difference. */
  242.   long char_number = 1;        /* Offset (1...) in files of 1st difference. */
  243.   int read1, read2;        /* Number of bytes read from each file. */
  244.   int first_diff;        /* Offset (0...) in buffers of 1st diff. */
  245.   int smaller;            /* The lesser of `read1' and `read2'. */
  246.   int ret = 0;
  247.  
  248.   do
  249.     {
  250.       read1 = block_read (file1_desc, buf1, buf_size);
  251.       if (read1 < 0)
  252.     error (2, errno, "%s", file1);
  253.       read2 = block_read (file2_desc, buf2, buf_size);
  254.       if (read2 < 0)
  255.     error (2, errno, "%s", file2);
  256.  
  257.       /* Insert sentinels for the block compare.  */
  258.  
  259.       buf1[read1] = ~buf2[read1];
  260.       buf2[read2] = ~buf1[read2];
  261.  
  262.       if (comparison_type == type_first_diff)
  263.     {
  264.       int cnt;
  265.  
  266.       /* If the line number should be written for differing files,
  267.          compare the blocks and count the number of newlines
  268.          simultaneously.  */
  269.       first_diff = block_compare_and_count (&cnt, buf1, buf2, '\n');
  270.       line_number += cnt;
  271.     }
  272.       else
  273.     {
  274.       first_diff = block_compare (buf1, buf2);
  275.     }
  276.  
  277.       if (comparison_type != type_all_diffs)
  278.     char_number += first_diff;
  279.       else
  280.     smaller = min (read1, read2);
  281.  
  282.       if (first_diff < read1 && first_diff < read2)
  283.     {
  284.       switch (comparison_type)
  285.         {
  286.         case type_first_diff:
  287.           /* This format is a proposed POSIX standard.  */
  288.           printf ("%s %s differ: char %ld, line %ld",
  289.               file1, file2, char_number, line_number);
  290.           if (opt_print_chars)
  291.         {
  292.           printf (" is");
  293.           printf (" %3o ",
  294.               (unsigned) (unsigned char) buf1[first_diff]);
  295.           printc (stdout, 0,
  296.               (unsigned) (unsigned char) buf1[first_diff]);
  297.           printf (" %3o ",
  298.               (unsigned) (unsigned char) buf2[first_diff]);
  299.           printc (stdout, 0,
  300.               (unsigned) (unsigned char) buf2[first_diff]);
  301.         }
  302.           putchar ('\n');
  303.           /* Fall through. */
  304.         case type_status:
  305.           return 1;
  306.         case type_all_diffs:
  307.           while (first_diff < smaller)
  308.         {
  309.           if (buf1[first_diff] != buf2[first_diff])
  310.             {
  311.               if (opt_print_chars)
  312.             {
  313.               printf ("%6ld", first_diff + char_number);
  314.               printf (" %3o ",
  315.                   (unsigned) (unsigned char) buf1[first_diff]);
  316.               printc (stdout, 4,
  317.                   (unsigned) (unsigned char) buf1[first_diff]);
  318.               printf (" %3o ",
  319.                   (unsigned) (unsigned char) buf2[first_diff]);
  320.               printc (stdout, 0,
  321.                   (unsigned) (unsigned char) buf2[first_diff]);
  322.               putchar ('\n');
  323.             }
  324.               else
  325.             /* This format is a proposed POSIX standard. */
  326.             printf ("%6ld %3o %3o\n",
  327.                 first_diff + char_number,
  328.                 (unsigned) (unsigned char) buf1[first_diff],
  329.                 (unsigned) (unsigned char) buf2[first_diff]);
  330.             }
  331.           first_diff++;
  332.         }
  333.           ret = 1;
  334.           break;
  335.         }
  336.     }
  337.  
  338.       if (comparison_type == type_all_diffs)
  339.     char_number += smaller;
  340.  
  341.       if (read1 != read2)
  342.     {
  343.       switch (comparison_type)
  344.         {
  345.         case type_first_diff:
  346.         case type_all_diffs:
  347.           /* This format is a proposed POSIX standard. */
  348.           fprintf (stderr, "%s: EOF on %s\n",
  349.               program_name, read1 < read2 ? file1 : file2);
  350.           break;
  351.         case type_status:
  352.           break;
  353.         }
  354.       return 1;
  355.     }
  356.     }
  357.   while (read1);
  358.   return ret;
  359. }
  360.  
  361. /* Compare two blocks of memory P1 and P2 until they differ,
  362.    and count the number of occurences of the character C in the common
  363.    part of P1 and P2.
  364.    Assumes that P1 and P2 are aligned at long addresses!
  365.    If the blocks are not guaranteed to be different, put sentinels at the ends
  366.    of the blocks before calling this function.
  367.  
  368.    Return the offset of the first byte that differs.
  369.    Place the count at the address pointed to by COUNT.  */
  370.  
  371. int
  372. block_compare_and_count (count, p1, p2, c)
  373.      int *count;
  374.      char *p1, *p2;
  375.      unsigned char c;
  376. {
  377.   long w;            /* Word for counting C. */
  378.   long i1, i2;            /* One word from each buffer to compare. */
  379.   long *p1i, *p2i;        /* Pointers into each buffer. */
  380.   char *p1c, *p2c;        /* Pointers for finding exact address. */
  381.   unsigned int cnt = 0;        /* Number of occurrences of C. */
  382.   long cccc;            /* C, four times. */
  383.   long m0, m1, m2, m3;        /* Bitmasks for counting C. */
  384.  
  385.   cccc = ((long) c << 24) | ((long) c << 16) | ((long) c << 8) | ((long) c);
  386.  
  387.   m0 = 0xff;
  388.   m1 = 0xff00;
  389.   m2 = 0xff0000;
  390.   m3 = 0xff000000;
  391.  
  392.   p1i = (long *) p1;
  393.   p2i = (long *) p2;
  394.  
  395.   /* Find the rough position of the first difference by reading long ints,
  396.      not bytes.  */
  397.  
  398.   i1 = *p1i++;
  399.   i2 = *p2i++;
  400.   while (i1 == i2)
  401.     {
  402.       w = i1 ^ cccc;
  403.       cnt += (w & m0) == 0;
  404.       cnt += (w & m1) == 0;
  405.       cnt += (w & m2) == 0;
  406.       cnt += (w & m3) == 0;
  407.       i1 = *p1i++;
  408.       i2 = *p2i++;
  409.     }
  410.  
  411.   /* Find out the exact differing position (endianess independant).  */
  412.  
  413.   p1c = (char *) (p1i - 1);
  414.   p2c = (char *) (p2i - 1);
  415.   while (*p1c == *p2c)
  416.     {
  417.       cnt += c == *p1c;
  418.       p1c++;
  419.       p2c++;
  420.     }
  421.  
  422.   *count = cnt;
  423.   return p1c - p1;
  424. }
  425.  
  426. /* Compare two blocks of memory P1 and P2 until they differ.
  427.    Assumes that P1 and P2 are aligned at long addresses!
  428.    If the blocks are not guaranteed to be different, put sentinels at the ends
  429.    of the blocks before calling this function.
  430.  
  431.    Return the offset of the first byte that differs.  */
  432.  
  433. int
  434. block_compare (p1, p2)
  435.      char *p1, *p2;
  436. {
  437.   long *i1, *i2;
  438.   char *c1, *c2;
  439.  
  440.   /* Find the rough position of the first difference by reading long ints,
  441.      not bytes.  */
  442.  
  443.   for (i1 = (long *) p1, i2 = (long *) p2; *i1++ == *i2++;)
  444.     ;
  445.  
  446.   /* Find out the exact differing position (endianess independant).  */
  447.  
  448.   for (c1 = (char *) (i1 - 1), c2 = (char *) (i2 - 1); *c1 == *c2; c1++, c2++)
  449.     ;
  450.  
  451.   return c1 - p1;
  452. }
  453.  
  454. /* Read NCHARS bytes from descriptor FD into BUF.
  455.    Return the number of characters successfully read.  */
  456.  
  457. int
  458. block_read (fd, buf, nchars)
  459.      int fd;
  460.      char *buf;
  461.      int nchars;
  462. {
  463.   char *bp = buf;
  464.   int nread;
  465.  
  466.   for (;;)
  467.     {
  468.       nread = read (fd, bp, nchars);
  469.       if (nread < 0)
  470.     return -1;
  471.       bp += nread;
  472.       if (nread == nchars || nread == 0)
  473.     break;
  474.       nchars -= nread;
  475.     }
  476.   return bp - buf;
  477. }
  478.  
  479. /* Print character C on stream FS, making nonvisible characters
  480.    visible by quoting like cat -t does.
  481.    Pad with spaces on the right to WIDTH characters.  */
  482.  
  483. void
  484. printc (fs, width, c)
  485.      FILE *fs;
  486.      int width;
  487.      unsigned c;
  488. {
  489.   if (c >= 128)
  490.     {
  491.       putc ('M', fs);
  492.       putc ('-', fs);
  493.       c -= 128;
  494.       width -= 2;
  495.     }
  496.   if (c < 32)
  497.     {
  498.       putc ('^', fs);
  499.       c += 64;
  500.       --width;
  501.     }
  502.   else if (c == 127)
  503.     {
  504.       putc ('^', fs);
  505.       c = '?';
  506.       --width;
  507.     }
  508.  
  509.   putc (c, fs);
  510.   while (--width > 0)
  511.     putc (' ', fs);
  512. }
  513.