/*****************************************************************************
 * bytediff.c                                                                *
 *                                                                           *
 * by Erik Waling <erikw@acc.umu.se>                                         *
 *                                                                           *
 * Compares two files of equal size and output the differing bytes and       *
 * their location.                                                           *
 *                                                                           *
 * Usage: bytediff <file1> <file2>                                           *
 *                                                                           *
 *****************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define BUFSIZE 8192

int open_files(int *fd, char **argv)
{
	struct stat st[2];
	int i;

	/* open files */
	for (i = 0; i < 2; i++) 
		if ((fd[i] = open(argv[i + 1], O_RDONLY)) == -1) 
			return (-1) * (i + 1);

	/* probably no need to check return value from stat() since open()
	 * worked fine :)
	 */
	for (i = 0; i < 2; i++)
		stat(argv[i + 1], &st[i]);

	/* check if files are of equal size */
	if (st[0].st_size != st[1].st_size)
		return -3;

	return 0;
}

inline ssize_t fill_bufs(int *fd, unsigned char **buf)
{
	int i;
	ssize_t bytes[2];

	for (i = 0; i < 2; i++)
		bytes[i] = read(fd[i], buf[i], BUFSIZE);

	/* EOF on both files */
	if (bytes[0] == 0 && bytes[1] == 0)
		return 0;

	/* error reading or amount data read differs */
	if (bytes[0] != bytes[1] || bytes[0] == -1 || bytes[1] == -1 || bytes[0] == 0 || bytes[1] == 0)
		return -1;

	/* return number of bytes read */

	return bytes[0];
}

inline void cmp(unsigned char **buf, ssize_t bytes)
{

	int i;
	static unsigned long long total = 0;

	for (i = 0; i < bytes; i++) 
		if (buf[0][i] != buf[1][i])
			printf("%08X:   %02X  %02X\n", total + i, buf[0][i], buf[1][i]);

	total += bytes;
}

int main(int argc, char *argv[])
{

	int  fd[2];
	unsigned char **buf;
	ssize_t bytes;
	int i;

	if (argc != 3) {
		fprintf(stderr, "%s <file> <file>\n", argv[0]); 
		return 1;
	}

	/* alloc mem for buffers */
	buf = (unsigned char **) malloc(2 * sizeof(unsigned char *));

	for (i = 0; i < 2; i++)
		buf[i] = (unsigned char *) malloc(BUFSIZE * sizeof(unsigned char *));

	switch (open_files(fd, argv)) {
		case -1:
			fprintf(stderr, "Unable to open %s\n", argv[1]);
			return 1;
		case -2:
			fprintf(stderr, "Unable to open %s\n", argv[2]);
			return 1;
		case -3:
			fprintf(stderr, "Files differ in size.\n");
			return 2;
	}

	while ((bytes = fill_bufs(fd, buf)) > 0) {
		if (bytes == -1) {
			fprintf(stderr, "Error reading.\n");
			return 3;
		}
		cmp(buf, bytes);
	}

	close(fd[0]);
	close(fd[1]);

	for (i = 0; i < 2; i++)
		free(buf[i]);

	free(buf);

	return 0;
}


