/*
	dircmp2 command

	14.03.1996 (first Amiga release)
	(C) 1996 Thomas Radtke
	E-Mail: Thomas.Radtke@rz.Uni-Osnabrueck.DE
	This software is freeware, see copyright notice above.

	revision 28.03.96: (2nd Amiga release)

	- fix memory leak (AllocDosObject())
	- new options -x,-X,-D,-v
	- better memory management (free memory after usage ;)

	revision 6.04.96: (3rd Amiga release)

	- abort handling
	- resource tracking
	- fixing double unlocking

	revision 11.04.96: (never released)

	- more resource tracking (locks)

	revision 1.05.96: (4th Amiga release)

	- removed bug on empty files
	- added warnings about non-fatal locking errors 

	revision 18.07.96:

	- removed faulty operator ( _ex_&_ dir1)
	- existing dirs were printed on -d regardless of -i, fixed
*/

#include <stdio.h>
#include <dos/dos.h>

#define MAX_LEVEL 256

char *dummy="$VER: dircmp2 V4.1 (18.07.1996)";

int differ,match,case_match,tab,rec,inv,cmp,dirs,fls,pathpart,help;
int tabulator=0;
char *pattern;

struct FileInfoBlock *tmp_fib[MAX_LEVEL];
int fsp=0;

int tmp_lock[MAX_LEVEL];
int lsp=0;

#ifndef HAVE_XMALLOC
int xmalloc(int size)
{
	int result;

	if (!(result=malloc(size))) {
		fprintf(stderr,"Can't allocate %d bytes of memory\n",size);
		cleanup(20);
	}
	else return result;
}
#endif /* HAVE_XMALLOC */

cleanup(int retval)
{
	int i;

	for (i=0; i<fsp; i++)
	{
		if (tmp_fib[i]!=(struct FileInfoBlock *)0)
		{
			FreeDosObject(DOS_FIB,tmp_fib[i]);
		}
	}
	for (i=0; i<lsp; i++)
	{
		if (tmp_lock[i])
		{
			UnLock(tmp_lock[i]);
		}
	}
	exit(retval);
}

raise()
{
	int i;

	fprintf(stderr,"^C\n");
	cleanup(1);
}

char errbuff[1024];
int errn;

void examine(int lock, struct FileInfoBlock *fib, char *path,char *path_2)
{
	int ok,lock2=0,lock_2=0,lock3,path_len;
	char *path2,*path2_2;
	int n,ex,dir1,dir2,size,real_size,equ,len1,len2;
	struct FileHandle *fh1,*fh2;
	char *file1,*file2;
	struct FileInfoBlock *fib2_2=0,*fib2=0;

	while (ExNext(lock,fib))
	{
		__chkabort();
		path_len=strlen(path)+strlen(fib->fib_FileName)+2;
		path2=(char *)xmalloc(path_len);
		strcpy(path2,path);
		AddPart(path2,fib->fib_FileName,path_len);
		if (!(lock3=Lock(path2,ACCESS_READ)))
		{
			errn=IoErr();
			Fault(errn,0,errbuff,1024);
			fprintf(stderr,"%s: warning: error %d, %s\n",path2,errn,errbuff);
			if (cmp) continue;
		}
		else UnLock(lock3);
		ok=1;
		if (match)
		{
			if (case_match)
			{
				if (pathpart) ok=MatchPattern(pattern,path2);
				else ok=MatchPattern(pattern,fib->fib_FileName);
			}
			else
			{
				if (pathpart) ok=MatchPatternNoCase(pattern,path2);
				else ok=MatchPatternNoCase(pattern,fib->fib_FileName);
			}
		}
		path_len=strlen(path_2)+strlen(fib->fib_FileName)+2;
		path2_2=(char *)xmalloc(path_len);
		strcpy(path2_2,path_2);
		AddPart(path2_2,fib->fib_FileName,path_len);			

		if (!(fib2_2=(struct FileInfoBlock *)AllocDosObject(DOS_FIB,0)))
		{
			fprintf(stderr,"can't allocate DosObject of type DOS_FIB\n");
			cleanup(20);
		}
		tmp_fib[fsp++]=fib2_2;
		ex=0;
		if ((lock_2=Lock(path2_2,ACCESS_READ))) {
			ex=Examine(lock_2,fib2_2);
			UnLock(lock_2);
		}
		else if ((errn=IoErr())!=205)
		{
			Fault(errn,0,errbuff,1024);
			fprintf(stderr,"%s: warning: error %d, %s\n",path2_2,errn,errbuff);
			goto no_use;
		}
/* ! */		dir1=fib->fib_DirEntryType>0;
		dir2=(ex && fib2_2->fib_DirEntryType>0);

		if (rec && dir1 && dir2)
		{
/* ! */			if (!fls && ok && ((!ex && !inv) || (ex && inv)))
			{
				if (tab && tabulator)
					for (n=0; n<tabulator; n++) printf(" ");
				if (pathpart)
					printf("%s\n",path2);
				else
					printf("%s\n",fib->fib_FileName);
			}
			if (!(fib2=(struct FileInfoBlock *)AllocDosObject(DOS_FIB,0)))
			{
				fprintf(stderr,"can't allocate DosObject of type DOS_FIB\n");
				FreeDosObject(DOS_FIB,fib2_2);
				tmp_fib[--fsp]=(struct FileInfoBlock *)0;
				cleanup(20);
			}
			tmp_fib[fsp++]=fib2;
			lock2=Lock(path2,ACCESS_READ);
			tmp_lock[lsp++]=lock2;
			if (Examine(lock2,fib2))
			{
				tabulator++;
				examine(lock2,fib2,path2,path2_2);
				UnLock(lock2);
				tmp_lock[--lsp]=0;
				FreeDosObject(DOS_FIB,fib2);
				tmp_fib[--fsp]=(struct FileInfoBlock *)0;
				--tabulator;
			}
		}
		else /* files || !recursion */
		{
			equ=1;
			if (!dir1 && !dir2 && lock_2 && cmp)
			{
				if ((size=fib->fib_Size)!=fib2_2->fib_Size) equ=0;
				else if (size)
				{
					real_size=2*size;
					file1=0;
					file2=0;
					do {
						if (file1 && !file2) {
							free(file1);
							real_size/=2;
						}
						else if (!file1 && file2) {
							free(file2);
							real_size/=2;
						}
						if (!file1 && !file2) {
							real_size/=2;
						}
						file1=(char *)malloc(real_size);
						file2=(char *)malloc(real_size);
					} while (!file1 || !file2);

					if (!(fh1=(struct FileHandle *)Open(path2,MODE_OLDFILE))) {
						errn=IoErr();
						Fault(errn,0,errbuff,1024);
						fprintf(stderr,"%s: warning: error %d, %s\n",path2_2,errn,errbuff);
						equ=0;
						free(file1);
						goto no_use;
					}
					if (!(fh2=(struct FileHandle *)Open(path2_2,MODE_OLDFILE))) {
						errn=IoErr();
						Fault(errn,0,errbuff,1024);
						fprintf(stderr,"%s: warning: error %d, %s\n",path2_2,errn,errbuff);
						Close(fh1);
						equ=0;
						free(file1);
						free(file2);
						goto no_use;
					}

					do {
						if (!(len1=Read(fh1,file1,real_size))) break;
						len2=Read(fh2,file2,real_size);
						for (n=0; n<len1; n++) {
							if (file1[n]!=file2[n]) {
								equ=0;
								break;
							}
						}
						if (!equ) break;
					} while (len1==real_size);
					if (fh1) Close(fh1);
					if (fh2) Close(fh2);
					free(file1);
					free(file2);
				}
			}
			if (!lock_2 || dir1!=dir2) equ=0;
			if ((!equ && !inv) || (equ && inv))
			{
				if (ok && ((fls && !dir1) || (dirs && dir1)))
				{
					if (!differ || (differ && lock_2))
					{
						if (tab && tabulator)
							for (n=0; n<tabulator; n++) printf(" ");
						if (pathpart) printf("%s\n",path2);
						else printf("%s\n",fib->fib_FileName);
					}
				}
			}
		}
no_use:		free(path2);
		free(path2_2);
		FreeDosObject(DOS_FIB,fib2_2);
		tmp_fib[--fsp]=(struct FileInfoBlock *)0;
	}
}

void dircmp2(char *progname, char *dir1, char *dir2)
{
	struct FileInfoBlock *fib;
	int lock;

	fib=(struct FileInfoBlock *)AllocDosObject(DOS_FIB,0);
	tmp_fib[fsp++]=fib;

	lock=Lock(dir1,ACCESS_READ);
	tmp_lock[lsp++]=lock;

	if (Examine(lock,fib))
	{
		examine(lock,fib,dir1,dir2);
		UnLock(lock);
		tmp_lock[--lsp]=0;
		FreeDosObject(DOS_FIB,fib);
		tmp_fib[--fsp]=(struct FileInfoBlock *)0;
	}
}

int main(int argc, char **argv)
{
	int ptrcnt,cnt,len,explain=0;
	char *expl;
	int testlock;

	for (cnt=0; cnt<MAX_LEVEL; cnt++) tmp_fib[cnt]=(struct FileInfoBlock *)0;

	rec=0;
	inv=0;
	cmp=0;
	dirs=1;
	fls=1;
	pathpart=0;
	help=0;
	tab=0;
	match=0;
	differ=0;

	cnt=0;
	if (argc>1)
	{
		while (argv[++cnt][0]=='-' && cnt<argc)
		{
			switch(argv[cnt][1])
			{
				case 't':
					tab=1;
					break;
				case 'h':
					help=1;
					break;
				case 'p':
					pathpart=1;
					break;
				case 'f':
					fls=1;
					dirs=0;
					break;
				case 'i':
					if (differ)
					{
						fprintf(stderr,"warning: Option -D ignored\n");
						differ=0;
					}
					inv=1;
					break;
				case 'd':
					if (differ)
					{
						fprintf(stderr,"warning: Option -D ignored\n");
						differ=0;
					}
					dirs=1;
					fls=0;
					break;
				case 'D':
					if (dirs && !fls)
					{
						fprintf(stderr,"warning: Option -d ignored\n");
						dirs=0;
						fls=1;
					}
					if (inv)
					{
						fprintf(stderr,"warning: Option -i ignored\n");
						inv=0;
					}
					differ=1;
					break;
				case 'c':
					cmp=1;
					break;
				case 'r':
					rec=1;
					break;
				case 'x':
					len=3*strlen(argv[++cnt]);
					pattern=(char *)xmalloc(len);
					ParsePatternNoCase(argv[cnt],pattern,len);
					ptrcnt=cnt;
					match=1;
					case_match=0;
					break;
				case 'X':
					len=3*strlen(argv[++cnt]);
					pattern=(char *)xmalloc(len);
					ParsePattern(argv[cnt],pattern,len);
					ptrcnt=cnt;
					match=1;
					case_match=1;
					break;
				case 'v':
					explain=1;
					break;
				default:
					fprintf(stderr,"%s: unknown option -%c\n",argv[0],argv[cnt][1]);
					exit(20);
					break;
			}
		}
		argc-=(cnt-1);
	}
	if (differ && !cmp)
	{
		cmp=1;
		fprintf(stderr,"warning: option -D activates compare mode -c\n");
	}
	if (argc<2 || help)
	{
		fprintf(stderr,"dircmp2 V4.1 (18.07.1996)\n");
		fprintf(stderr,"(C) Thomas Radtke 1996\n");
		fprintf(stderr,"syntax: %s [-{cdDfhiprtxXv}] <directory1> [directory2]\n",*argv);
		if (help)
		{
			fprintf(stderr,"\nThe %s command lists all files and directories of directory1, that\n",*argv);
			fprintf(stderr,"cannot be found in directory2. If directory2 is omitted, the current\n");
			fprintf(stderr,"working dir is used\n\nFlags:\n\n");
			fprintf(stderr,"-c            compare files\n");
			fprintf(stderr,"-d            show only directorys\n");
			fprintf(stderr,"-D            show differing files only\n");
			fprintf(stderr,"-f            show only files\n");
			fprintf(stderr,"-h            this page\n");
			fprintf(stderr,"-i            inverse comparison\n");
			fprintf(stderr,"-p            show full pathnames\n");
			fprintf(stderr,"-r            subdir scan\n");
			fprintf(stderr,"-t            indent subdir results\n");
			fprintf(stderr,"-x <pattern>  case insensitive pattern specification\n");
			fprintf(stderr,"-X <pattern>  case sensitive pattern specification\n");
			fprintf(stderr,"-v            explain the results, but do not operate\n");
			exit(0);
		}
		else {
			fprintf(stderr,"use option -h to show the help page\n");
			exit(20);
		}
	}
	if (explain)
	{
		expl=(char *)xmalloc(4096);
		strcpy(expl,"I will show those ");
		if (fls)
		{
			strcat(expl,"files ");
			if (dirs) strcat(expl,"and ");
		}
		if (dirs) strcat(expl,"directories ");
		strcat(expl,"from direcory ");
		strcat(expl,argv[cnt]);
		if (rec) strcat(expl," recursively, ");
		else strcat(expl,", ");
		strcat(expl,"which can");
		if (!inv && !differ) strcat(expl,"not");
		strcat(expl," be found in ");
		if (argc==3)
		{
			strcat(expl,"directory ");
			 strcat(expl,argv[cnt+1]);
		}
		else strcat(expl,"the current directory");
		if (differ) strcat(expl,", but are differing");
		else if (!inv)
		{
			strcat(expl," because they are non-existant");
			if (cmp) strcat(expl," or differ");
		}
		if (match)
		{
			strcat(expl," and are matching the pattern ");
			strcat(expl,argv[ptrcnt]);
			if (case_match) strcat(expl," complying with the given cases");
		}
		strcat(expl,".");
		if (tab || pathpart)
		{
			strcat(expl," I will ");
			if (tab)
			{
				strcat(expl,"indent all results");
				if (pathpart) strcat(expl," and ");
			}
			if (pathpart) strcat(expl,"show full pathes");
			strcat(expl,".");
		}
		printf("%s\n",expl);
		fprintf(stderr,"warning: operation not done\n");
		exit(5);
	}
	if (!(testlock=Lock(argv[cnt],ACCESS_READ)))
	{
		fprintf(stderr,"can't lock %s\n",argv[cnt]);
		exit(20);
	}
	UnLock(testlock);
	if (argc==3)
	{
		if (!(testlock=Lock(argv[cnt+1],ACCESS_READ)))
		{
			fprintf(stderr,"can't lock %s\n",argv[cnt]);
			exit(20);
		}
		UnLock(testlock);
		dircmp2(*argv,argv[cnt],argv[cnt+1]);
	}
	else dircmp2(*argv,argv[cnt],(char *)"\0");
	cleanup(0);
}
