/*
**	List compare
**
**	V1.0 by Pasi Ristioja
**
**	PD (Freely distributable)
**
*/

long __oslibversion = 37;

#include <dos/dosextens.h>
#include <dos/dosasl.h>
#include <dos/rdargs.h>

#include <proto/dos.h>
#include <proto/exec.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void	__asm BuildLines(register __a0 APTR, register __d0 LONG, register __a1 APTR, register __d1 LONG, register __a2 APTR,register __d2 BOOL);
void	__asm BuildHash	(register __a0 APTR, register __a1 APTR, register __a2 APTR, register __d0 ULONG);
BOOL	__asm CheckLine	(register __a0 APTR, register __a1 APTR, register __d0 ULONG);

#define CASE			1
#define	EQU				2
#define	STRIP			4

struct MyArgs
{
	char	*file_cmp;
	char	*file_old;
	char	*file_include;
	char	*file_exclude;
	ULONG	*buf;
	ULONG	strip;
	ULONG	usecase;
	ULONG	equ;
};

struct CmpData
{
	struct CmpData *next;
	UBYTE	*data;
	LONG	size;
};

struct BuildRet
{
	LONG	lines;
	LONG	sleft;
	LONG	tleft;
};

static char	ver[] 		= "$VER: ListCmp 1.0 ("__DATE__")";
static char	template[]= "FILE/A,FILE/A,I=INCLUDE/K,E=EXCLUDE/K,B=BUFSIZE/N/K,STRIP=STRIPSUFFIX/S,CASE/S,EQU/S";

LONG main(void)
{
struct RDArgs		*RDArgs;
struct MyArgs		args;
struct CmpData	*cmp=0,*tmp;
struct BuildRet	br;
LONG	err=0,s_size,t_size,s_left,t_left,s_left2,t_left2,bufsize=16384;
FILE	*fp[3]={0,0,0};
UBYTE	*source,*target;
APTR	hash,hashstore;
UBYTE	line[256];
LONG	i=0,j=0,k=100,size,flag=0;

	args.file_cmp			= 0;
	args.file_old			= 0;
	args.file_include	= 0;
	args.file_exclude	= 0;
	args.buf					= 0;
	args.strip				= 0;
	args.equ					= 0;
	args.usecase			= 0;

	if ( (RDArgs=ReadArgs(template,(LONG *)&args,NULL)) == NULL) {
		err = IoErr(); goto fail;
	}

	if (args.buf != 0) bufsize = (*args.buf > 2048) ? *args.buf : 2048;

	if (args.usecase)	flag |= CASE;
	if (args.equ		)	flag |= EQU;
	if (args.strip	)	flag |= STRIP;

	/* Build 'hash' */
	if ( (fp[0] = fopen(args.file_cmp,"r")) == 0) { printf("Cannot open cmp file!\n"); goto fail; }
	if ( (hash		= halloc(262144))	== 0) { printf("Not enough memory!\n");	goto fail; }
	if ( (source	= halloc(bufsize))== 0) { printf("Not enough memory!\n");	goto fail; }
	if ( (cmp			= halloc(12))			== 0) { printf("Not enough memory!\n");	goto fail; }
	if ( (target	= halloc(bufsize))== 0) { printf("Not enough memory!\n");	goto fail; }
	cmp->next = 0;
	cmp->data = target;
	t_left = t_size = bufsize;

	memset(hash,0,262144);
	br.lines = 0;
	printf("\rLoading, %d lines analyzed...",br.lines); fflush(stdout);

	while ( (s_left = s_size = fread(source,1,bufsize,fp[0])) > 0 )
	{
		printf("\rCutting"); fflush(stdout);
		do
		{
			s_left2 = s_left;
			t_left2 = t_left;
			BuildLines(source+s_size-s_left,s_left,target+t_size-t_left,t_left,&br,args.strip);
			s_left = br.sleft;
			t_left = br.tleft;
			if ( ((t_left == t_left2) || t_left == 0) && (s_left != 0 && t_left != bufsize) ) {
				if ( (tmp = halloc(12)) == 0) { printf("Not enough memory!\n");	goto fail; }
				if ( (target = halloc(bufsize)) == 0) { printf("Not enough memory!\n");	goto fail; }
				cmp->size = t_size - t_left;
				tmp->next = cmp;
				tmp->data = target;
				cmp = tmp;
				t_left = t_size = bufsize;
			}
		} while ( (s_left != s_left2) && s_left != 0 );
		fseek(fp[0],-s_left,SEEK_CUR);
		printf("\rLoading, %d lines analyzed...",br.lines); fflush(stdout);
	}

	cmp->size = t_size - t_left;
	if (cmp->next != 0 && cmp->size == 0) {
		tmp = cmp;
		cmp = tmp->next;
		free(tmp->data);
		free(tmp);
	}
	if (cmp->size == 0) { printf("No lines in cmp file!\n"); goto fail; }
	if ( (hashstore	= halloc(br.lines*8)) == 0) { printf("Not enough memory!\n");	goto fail; }
	printf("\rHashing"); fflush(stdout);
	BuildHash(cmp,hash,hashstore,flag);
	printf("\n");
	fclose(fp[0]);

	/* Braindead and SLOW way to do this, but easy:-) */
	if ( (fp[0] = fopen(args.file_old,"r")) == 0) { printf("Cannot open file to compare!\n"); goto fail; }
	setvbuf(fp[0],source,0,bufsize/2);
	if (args.file_include != 0) {
		if ( (fp[1] = fopen(args.file_include,"w")) == 0) { printf("Cannot open file to write included (matched) lines!\n"); goto fail; }
		setvbuf(fp[1],source+bufsize/2,0,bufsize/4);
	}
	if (args.file_exclude != 0) {
		if ( (fp[2] = fopen(args.file_exclude,"w")) == 0) { printf("Cannot open file to write excluded (unmatched) lines!\n"); goto fail; }
		setvbuf(fp[2],source+bufsize/2+bufsize/4,0,bufsize/4);
	}

	while ( fgets(line,254,fp[0]) != 0 )
	{
		size = strlen(line);
		if (line[size-1] != 0x0a) {
			line[size++] = 0x0a; line[size] = 0x00;
		}

		if (CheckLine(line,hash,flag)) {
			if (fp[1] != 0) fputs(line,fp[1]); i++;
		} else {
			if (fp[2] != 0) fputs(line,fp[2]); j++;
		}

		if (k++ > 200) {
			printf("\rMatched: %d, unmatched: %d, total: %d",i,j,i+j); fflush(stdout);
			k=0;
		}
	}
	printf("\rMatched: %d, unmatched: %d, total: %d\n",i,j,i+j);

fail:
	if (RDArgs != 0) FreeArgs(RDArgs);
	if (err != 0) {
		PrintFault (err,"ListCmp");
		return(10);
	}
	return(0);
}
