/* catalog -- cli-based disk catalog -- Peter da Silva */

/* Usage:
 *
 * Catalog [TO|IN catalog] [ADD] [MULTI] volumes [FIND patterns] [LIST]
 */
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <stdio.h>
#include <ctype.h>
#define BPTR long

char *catalog = 0;
#define DEFCATALOG "disk.catalog"

int loaded;
int modified;
int mode;
int multi;
#define ADD 0
#define FIND 1

UBYTE *AllocMem();
char *malloc();
char *strchr();

main(ac, av)
int ac;
char **av;
{
	int i;

	if(ac<=1) {
		printf("Usage:\n");
		printf("Catalog [TO|IN catalog] [ADD] [MULTI] volumes [FIND patterns] [LIST]\n\n");
		printf("Default catalog=\"%s\"\n", DEFCATALOG);
		exit(0);
	}
	mode = ADD;
	loaded = 0;
	modified = 0;
	multi = 0;

	for(i = 1; i < ac; i++) {
		if(strmatch(av[i], "TO") || strmatch(av[i], "IN")) {
			catalog = av[++i];
		} else if(strmatch(av[i], "FIND")) {
			mode = FIND;
		} else if(strmatch(av[i], "ADD")) {
			mode = ADD;
		} else if(strmatch(av[i], "LIST")) {
			list();
		} else if(strmatch(av[i], "MULTI")) {
			multi = 1;
		} else {
			switch(mode) {
				case ADD: add(av[i]); break;
				case FIND: find(av[i]); break;
			}
		}
	}
	if(modified)
		dump_files();
}

struct _f {
	char *file;
	char *path;
	long size;
	char *note;
	struct _f *next, *prev;
} *filelist = 0;

freenode(n)
struct _f *n;
{
	if(n->file)
		free(n->file);
	if(n->path)
		free(n->path);
	if(n->note)
		free(n->note);
	free(n);
}

printnode(n)
struct _f *n;
{
	printf("%-24s %8ld %s\n", n->file, n->size, n->path);
	if(n->note)
		printf("%24s %s\n", "", n->note);
}

add_file(filename, pathname, filesize, filenote)
char *filename;
char *pathname;
long filesize;
char *filenote;
{
	struct _f *tmp, *ptr;
	int cmp;

	tmp = malloc(sizeof(struct _f));
	if(!tmp) return 0;
	tmp->next = tmp;
	tmp->prev = tmp;
	tmp->size = filesize;
	tmp->file = 0;
	tmp->path = 0;
	tmp->note = 0;
	tmp->file = malloc(strlen(filename)+1);
	if(!tmp->file) {
		freenode(tmp);
		return 0;
	}
	strcpy(tmp->file, filename);

	tmp->path = malloc(strlen(pathname)+1);
	if(!tmp->path) {
		freenode(tmp);
		return 0;
	}
	strcpy(tmp->path, pathname);

	if(filenote && filenote[0]) {
		tmp->note = malloc(strlen(filenote)+1);
		if(!tmp->note) {
			freenode(tmp);
			return 0;
		}
		strcpy(tmp->note, filenote);
	}

	if(!filelist) {
		filelist = tmp;
	} else {
		ptr = filelist;

		/* first check if you can insert it at the end of the list */
		/* if so, don't bother scanning. This should improve the */
		/* performance on inserting sorted lists */
		if((cmp = order(ptr->prev, tmp)) >= 0) {
			do {
				if((cmp=order(ptr, tmp)) >= 0)
					break;
				ptr = ptr->next;
			} while(ptr != filelist);
		}

		if(cmp==0) {
			freenode(tmp);
		} else {
			tmp->prev = ptr->prev;
			tmp->next = ptr;
			ptr->prev->next = tmp;
			ptr->prev = tmp;
			if(ptr==filelist && cmp>=0)
				filelist = tmp;
		}
	}
	return 1;
}

order(n1, n2)
struct _f *n1, *n2;
{
	int cmp;

	cmp = compare(n1->file, n2->file);
	if(cmp==0)
		cmp = compare(n1->path, n2->path);
	return cmp;
}

del_vol(vol)
char *vol;
{
	char *ptr, *nxt;
	int just_moved;

	if(!filelist)
		return 1;

	ptr = filelist;
	do {
		nxt = ptr->next;
		just_moved = 0;
		if(strncmp(vol, ptr->path, strlen(vol))) {
			ptr->prev->next = ptr->next;
			ptr->next->prev = ptr->prev;
			freenode(ptr);
			if(ptr==filelist) {
				if(filelist==nxt) {
					filelist = 0;
					break;
				}
				filelist = nxt;
				just_moved = 1;
			}
		}
		ptr = nxt;
	} while(!just_moved && ptr != filelist);
	return 1;
}

dump_files()
{
	FILE *fp;
	struct _f *ptr;

	if(catalog==0)
		catalog = DEFCATALOG;
	if(!(fp = fopen(catalog, "w"))) {
		perror(catalog);
		return 0;
	}

	if(filelist) {
		ptr = filelist;
		do {
			if(ptr->note)
				fprintf(fp, "%s:%ld:%s:%s\n",
					ptr->file, ptr->size, ptr->path, ptr->note);
			else
				fprintf(fp, "%s:%ld:%s\n",
					ptr->file, ptr->size, ptr->path);
			ptr = ptr->next;
		} while(ptr != filelist);
	}

	fclose(fp);
	return 1;
}

int loaded_files, files, directories;

load_files()
{
	FILE *fp;
	char *p;
	char buffer[BUFSIZ];
	long filesize;
	char *filename, *pathname, *filenote;

	if(catalog==0)
		catalog = DEFCATALOG;

	if(!(fp = fopen(catalog, "r"))) {
		perror(catalog);
		return 0;
	}

	loaded_files = 0;
	while(fgets(buffer, BUFSIZ, fp)) {
		buffer[strlen(buffer)-1] = 0;
		filename = buffer;
		p = strchr(buffer, ':');
		if(p) {
			*p++ = 0;
			filesize = atoi(p);
			p = strchr(p, ':');
			if(p) {
				*p++ = 0;
				pathname = p;
				p = strchr(p, ':'); /* skip ':' in path name */
				if(p) p = strchr(p+1, ':');
				if(p) {
					*p++ = 0;
					if(*p)
						filenote = p;
					else
						filenote = 0;
				} else
					filenote = 0;
				add_file(filename, pathname, filesize, filenote);
				loaded_files++;
			}
		}
	}

	printf("%s: %d files\n", catalog, loaded_files);
	fclose(fp);
}

char pathname[BUFSIZ];

scan_disk()
{
	struct FileInfoBlock *FIB;
	BPTR lock;
	int len;

	if(!(lock=Lock(pathname, ACCESS_READ))) {
		fprintf(stderr, "Can't access %s: error %d\n",
			pathname, IoErr());
		return 0;
	}
	FIB = (struct FileInfoBlock *)AllocMem(
		sizeof(struct FileInfoBlock),
		MEMF_CLEAR);
	if(!FIB) {
		UnLock(lock);
		return 0;
	}
	if(!Examine(lock, FIB)) {
		fprintf(stderr, "Can't examine %s: error %d\n",
			pathname, IoErr());
		UnLock(lock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	if(FIB->fib_DirEntryType < 0) {
		fprintf(stderr, "%s: is not a directory.\n");
		UnLock(lock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	while(ExNext(lock, FIB)) {
		if(FIB->fib_DirEntryType < 0) {
			add_file(
				FIB->fib_FileName,
				pathname,
				FIB->fib_Size,
				FIB->fib_Comment);
			files++;
		} else {
			len = strlen(pathname);
			if(pathname[len-1] == ':')
				strcpy(&pathname[len], FIB->fib_FileName);
			else
				sprintf(&pathname[len], "/%s", FIB->fib_FileName);
			if(!scan_disk()) {
				UnLock(lock);
				FreeMem(FIB, sizeof(struct FileInfoBlock));
				return 0;
			}
			pathname[len] = 0;
			directories++;
		}
	}
	UnLock(lock);
	FreeMem(FIB, sizeof(struct FileInfoBlock));
	return 1;
}

add(vol)
char *vol;
{
	if(multi == 0)
		_add(vol);
	else {
		while(1) {
			char tmp[BUFSIZ];
			printf("Scan %s [Y]? ", vol);
			gets(tmp);
			if(tmp[0]=='n' || tmp[0]=='N')
				break;
			_add(vol);
		}
	}
}

_add(vol)
char *vol;
{
	struct FileInfoBlock *FIB;
	BPTR lock;
	int len;

	if(!(lock=Lock(vol, ACCESS_READ))) {
		fprintf(stderr, "Can't access %s: error %d\n",
			vol, IoErr());
		return 0;
	}
	FIB = (struct FileInfoBlock *)AllocMem(
		sizeof(struct FileInfoBlock),
		MEMF_CLEAR);
	if(!FIB) {
		UnLock(lock);
		return 0;
	}
	if(!Examine(lock, FIB)) {
		fprintf(stderr, "Can't examine %s: error %d\n",
			vol, IoErr());
		UnLock(lock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	sprintf(pathname, "%s:", FIB->fib_FileName);
	UnLock(lock);
	FreeMem(FIB, sizeof(struct FileInfoBlock));

	if(!loaded) {
		load_files();
		loaded = 1;
	}
	del_vol(vol);
	files = 0;
	directories = 1;
	if(scan_disk()) {
		modified = 1;
		printf("%s %d files %d directories.\n",
			pathname, files, directories);
	}
}

find(s)
char *s;
{
	struct _f *ptr;
	static WORD PatCode[128];

	if(!loaded) {
		load_files();
		loaded = 1;
	}

	CmplPat(s, PatCode);
	if(ptr = filelist) {
		do {
			if(Match(s, PatCode, ptr->file))
				printnode(ptr);
			ptr = ptr->next;
		} while(ptr != filelist);
	}
}

list()
{
	struct _f *ptr;

	if(!loaded) {
		load_files();
		loaded = 1;
	}

	if(ptr = filelist) {
		do {
			printnode(ptr);
			ptr = ptr->next;
		} while(ptr != filelist);
	}
}

strmatch(s1, s2)
char *s1, *s2;
{
	while(*s1) {
		if(*s1!=*s2 &&
		   ((islower(*s1) && toupper(*s1)!=*s2) ||
		    (islower(*s2) && *s1!=toupper(*s2)) )
		  ) {
			return 0;
		}
		s1++;
		s2++;
	}
	return !*s2;
}

compare(s1, s2)
UBYTE *s1, *s2;
{
	UBYTE c1, c2;

	while(*s1 && *s2) {
		c1 = *s1;
		c2 = *s2;
		if(isalpha(c1)) {
			if(isalpha(c2)) {
				if(isupper(c1))
					c1 = tolower(c1);
				if(isupper(c2))
					c2 = tolower(c2);
			} else
				return 1;
		} else if(isalpha(c2))
			return -1;
		if(c1 > c2)
			return 1;
		if(c1 < c2)
			return -1;
		s1++;
		s2++;
	}
	if(*s1)
		return 1;
	if(*s2)
		return -1;
	return 0;
}
