home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / ohlutil / cmp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-08-24  |  12.8 KB  |  546 lines

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