/* compare output of crc from two systems
 * command line options
 *	-i	"incremental"   write updated checksum file, not commands;
 *
 *  Here is the script run nightly at AT&T's netlib...
 *	#!/bin/sh
 *	DIR=$HOME/lib/netlib
 *	netlib mkmaster
 *	cd $DIR/master
 *	sites=`echo *`
 *	cd /netlib
 *	for i in $sites;do
 *		lsr -t `cat $DIR/master/$i` | $DIR/exclude | sort > /tmp/crc$$
 *		netlib masterslave -i /tmp/crc$$  /netlib/crc/res/$i > /tmp/$i
 *		cp /tmp/$i /netlib/crc/res/$i
 *		rm /tmp/crc$$ /tmp/$i
 *	done
 *
 * Eric Grosse  3 Jan 93   first version
 *    29 Mar 93  added -i option
 *    13 Jun 95  cope with blanks in filenames;  better handling sum=0
 *    12 Jul 95  added -v option
 *     5 Jul 96  added -t9 option to tolerate small clock skew
 *
 * The author of this software is Eric Grosse.  Copyright (c) 1993 by AT&T.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

/* for internationalization... */
#define MESS_OPEN "can't open %s\n"
#define MESS_SUM "can't sum %s\n"
#define MESS_TIM "%s: same time, different contents?!!\n"
#define MESS_SORT "input should be sorted:\n\t%s\n\t%s\n"
#define MESS_BAD "%i bad input: %s\n"
#define _POSIX_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
extern FILE *zopen(char*);  /* like fopen, but for compressed files */
extern int crc(int,unsigned long*,unsigned long*);

int incremental = 0;
int verbose = 0;
char *progname;

typedef struct Fsum{
	unsigned long tim, len, sum;
	char name[2001];
	char ok;	/* other fields are valid if this is nonzero */
} Fsum;

FILE*
Fopen(char *path, char *mode)
{
	FILE *f;
	if( strcmp(mode,"r")==0 && 
		( strcmp(path+strlen(path)-2,".Z")==0 ||
		  strcmp(path+strlen(path)-3,".gz")==0 ) )
		f = zopen(path);
	else
		f = fopen(path,mode);
	if(!f){
		fprintf(stderr,MESS_OPEN,path);
		perror("");
		exit(1);
	}
	return f;
}

void
bad(int i,char*b)
{
	fprintf(stderr,MESS_BAD,i,b);
	exit(1);
}

void
next_Fsum(FILE *f, Fsum *s)
{
	/* Parse right-to-left to cope with blanks in filenames. */
	Fsum old;
	int n;
	char *b = s->name, *p;
	s->ok = 0;
	strcpy(old.name,b);
	if(fgets(b,sizeof(s->name),f)==NULL)
		return; /* EOF or read error */
	n = strlen(b);
	p = b+n-1;  /* p=='\n' */
	for(*p-- = '\0'; *p!=' '; p--)
		if(p<=b || !strchr("0123456789abcdef",*p))  bad(801,b);
	n = sscanf(p+1,"%lx",&s->sum);
	if(n!=1)  bad(802,b);
	for(*p-- = '\0'; *p!=' '; p--)
		if(p<=b || !strchr("0123456789",*p))  bad(803,b);
	n = sscanf(p+1,"%lu",&s->len);
	if(n!=1)  bad(804,b);
	for(*p-- = '\0'; *p!=' '; p--)
		if(p<=b || !strchr("0123456789",*p))  bad(805,b);
	n = sscanf(p+1,"%lu",&s->tim);
	if(n!=1)  bad(806,b);
	*p-- = '\0';
	if(strcmp(old.name,b)>=0 ){
		fprintf(stderr,MESS_SORT,old.name,s->name);
		exit(1);
	}
	s->ok = 1;
}

void
cmd(char *c, struct Fsum *s)
{
	int fd;
	if(incremental){
		switch(*c){
			case 'g':	/* "get" */
				if(verbose) fprintf(stderr,"crc %s\n",s->name);
				fd = open(s->name,O_RDONLY);
				if(crc(fd,&s->sum,&s->len))
					fprintf(stderr,MESS_SUM,s->name);
				close(fd);
			case 'o':	/* "ok" or fall-through from "get" */
				printf("%s %lu %lu %lx\n",
					s->name,s->tim,s->len,s->sum);
			/* "time" can't happen;  if "rm", do nothing */
		}
	}else{	/* normal case */
		if(*c!='o')	/* skip if "ok" */
			printf("%s %s %lu %lu %lx\n",
				c,s->name,s->tim,s->len,s->sum);
	}
}

int
main(int argc, char **argv)
{
	int cmp;
	FILE *master, *slave;
	Fsum m, s;
	long toler = 0;

	progname = argv[0];
	if(argc>=4 && strcmp(argv[1],"-v")==0){
		verbose = 1;		argv++; argc--;
	}
	if(argc>=4 && strncmp(argv[1],"-t",2)==0){
		toler = atol(argv[1]+2);argv++; argc--;
	}
	if(argc==4 && strcmp(argv[1],"-i")==0){
		incremental = 1;	argv++; argc--;
	}
	if(argc!=3) exit(3);
	master = Fopen(argv[1],"r");
	slave = Fopen(argv[2],"r");
	m.name[0] = '\0';  /* for benefit of sort check in next_Fsum */
	s.name[0] = '\0';
	next_Fsum(master,&m);
	next_Fsum(slave,&s);
	while(m.ok||s.ok){
		if( !m.ok ) cmp = 1;
		else if ( !s.ok ) cmp = -1;
		else cmp = strcmp(m.name,s.name);
		if( cmp < 0 ){
			cmd("get",&m);
			next_Fsum(master,&m);
		}else if( cmp > 0 ){
			cmd("rm",&s);
			next_Fsum(slave,&s);
		}else{ /* cmp==0 */
			if( labs(m.tim-s.tim)>toler ){
				if(m.len==s.len && m.sum==s.sum)
					cmd("time",&m);
				else
					cmd("get",&m);
			}else if( m.len!=s.len ){
				fprintf(stderr,MESS_TIM,m.name);
				cmd("get",&m);
			}else if( m.sum!=s.sum ){
				if(incremental && m.sum==0){
					m.sum = s.sum;
					cmd("ok",&m);
				}else if(incremental && s.sum==0){
					cmd("ok",&m);
				}else{
					fprintf(stderr,MESS_TIM,m.name);
					cmd("get",&m);
				}
			}else{
				cmd("ok",&m);
			}
			next_Fsum(master,&m);
			next_Fsum(slave,&s);
		}
	}
	exit(0);
}
