/*
 * CHECKFILES.C
 *
 * This module contains routines to read in the disk directory, search
 * the file database for each filename found to see if it matches, etc.
 *
 */

#ifndef LATTICE_50
#include "system.h"
#endif
#include "bbsindex.h"

/*
 *		addunknown()
 *		------------
 *		Adds a new entry to the list of unknown files found. If necessary,
 *		more memory will be allocated to hold the new entry.
 */
int addunknown(name, size, date, dirnum)
char *name;
long size;
int date, dirnum;
{
	static DIRENTRY *dirbase, *dir;

	if (numdirentries >= MAXDIRENT) {
		scripterror("directory table full. Skipping rest of files\n");
		return (FALSE);
	}
	if ((numdirentries % DIRFRAG) == 0) 	/* Allocate new block */
		dirbase = mymalloc(DIRENTRYSIZE * DIRFRAG);

	dir = &dirbase[numdirentries % DIRFRAG];

	strcpy(dir->name, name);
	dir->size	= size;
	dir->date	= date;
	dir->dirnum	= dirnum;

	direntries[numdirentries++] = dir;
	return (TRUE);
}

/*
 *		find()
 *		------
 *		Does a binary search of the file records looking for the specified
 *		filename. Returns pointer to the file record if found, NULL if not
 *		found.
 */
UDHEAD *find(name)
char *name;
{
	long low = 0, high = numrecs -1;
	long mid;
	int cmp;

	while (high >= low) {
		mid = (low + high) / 2;
		cmp = stricmp(ptrblock[mid]->disk_name, name);
		if (!cmp && ptrblock[mid]->type == 0)		/* Found match */
			return (ptrblock[mid]);
		if (cmp > 0)						/* Too far ahead, go backwards */
			high = mid-1;
		else								/* Too far below, go forwards */
			low  = mid+1;
	}
	return (NULL);
}

/*
 *		scandir()
 *		---------
 *		Scans directory, adding the files found either to the 'unknown'
 *		list or updating the pointers in the main file database appropriately.
 */
int scandir(dirname, dirnum)
char *dirname;
int dirnum;
{
	UDHEAD *f;
	struct tm *filedate;
	int bbspcdate;

	dirlock = Lock(dirname, ACCESS_READ);
	if (dirlock == NULL) {
		scripterror("can't find directory ");
		print2(dirname, ", skipping\n");
		return (TRUE);
	}

	if (!Examine(dirlock, fib)) {
		scripterror("can't examine directory ");
		print2(dirname, ", skipping\n");
		UnLock(dirlock);
		dirlock = NULL;
		return (TRUE);
	}

	while (ExNext(dirlock, fib)) {
		chkabort();
		if (fib->fib_DirEntryType > 0)	/* Skip over directories */
			continue;
		if (f = find(fib->fib_FileName)) {
			f->online = 1;
			f->valid = (fib->fib_Size == f->length);
			f->dirnum = dirnum;
		} else {
			/*
			 *		Convert AmigaDOS date (# days since 1/1/78, # mins
			 *		since midnight) into ANSI date (# seconds since 1/1/70)
			 *		then convert that into BBS-PC! format.
			 */
			long ansidate;
			ansidate = (fib->fib_Date.ds_Days + (365 * 8) + 2) * 86400;
			filedate = gmtime(&ansidate);
			bbspcdate = (((filedate->tm_year * 13) +
						  (filedate->tm_mon+1)) * 32) +
						   filedate->tm_mday;
			if (!addunknown(fib->fib_FileName, fib->fib_Size, bbspcdate,
																	dirnum)) {
				UnLock(dirlock);
				dirlock = NULL;
				return (FALSE);
			}

		}
	}
	if (IoErr() != ERROR_NO_MORE_ENTRIES) {
		scripterror("error reading directory ");
		print2(dirname, ", skipping\n");
	}
	UnLock(dirlock);
	dirlock = NULL;
	return (TRUE);
}

/*
 *		diskcmp()
 *		---------
 *		Compares the disk filenames of two file headers, and returns true
 *		if they are equal. Used by the sort routine in check_files().
 */
int diskcmp(f1,f2)
UDHEAD **f1, **f2;
{
	return (stricmp((*f1)->disk_name, (*f2)->disk_name));
}

/*
 *		com_checkfiles()
 *		----------------
 *		This command scans the file directories specified on the command
 *		line (or if none, then using those specified in the CFGINFO.DAT
 *		file). Each file in the catalogue is searched for in the file
 *		catalogue, and those that are found are marked as "Online"; if
 *		the disk filesize matches the catalogue filesize, then their
 *		"valid" flag is also set. Files which are not found are stored
 *		in a seperate array, which can be printed with the FOREIGN command.
 *		After this, any other files listed in the IGNORE list are also marked
 *		as valid.
 *
 *		Note that before the scan, the files are sorted into alphabetical
 *		order, by diskname, to allow a binary search to be done. Afterwards,
 *		if this disrupted a previous sorting order, they are sorted back
 *		to their original state.
 */
void com_checkfiles()
{
	int dirnum;
	IGNORE *ig;
	UDHEAD *f;

	if (checkfiles)
		return;

	CHECKDATABASE();
	qsort(ptrblock, numrecs, sizeof(UDHEAD *), diskcmp);
	if (compos >= comlen) {
		readconfigfile();
		for (dirnum = 0; dirnum < NUM_SECT && dirnames[dirnum][0]; dirnum++) {
			chkabort();
			if (!scandir(dirnames[dirnum], dirnum))
				break;
		}
	} else {
		for (dirnum = 0; dirnum < NUM_SECT; dirnum++) {
			if (compos >= comlen)
				break;
			strcpy(dirnames[dirnum], getstring());
			chkabort();
			if (!scandir(dirnames[dirnum], dirnum))
				break;
		}
	}
	/*
	 *		Now mark any "ignored" files as valid.
	 */
	for (ig = firstignore; ig; ig = ig->next) {
		if (f = find(ig->name))
			f->valid = 1;
	}
	chkabort();
	if (sorted)
		qsort(ptrblock, numrecs, sizeof(UDHEAD *), sortcmp);
	checkfiles = TRUE;
}

/*
 *		com_foreign()
 *		-------------
 *		This command prints out the list of foreign files, i.e. files
 *		not in the catalogue, using the format string setup with
 *		FORMAT. Not all fields are valid.
 */
void com_foreign()
{
	/*
	 *		Standard file header template for foreign files.
	 *		Note file[] is an array, so we can say file->x instead of file.x
	 */
	static UDHEAD file[1] = {{
		0,						/* Record type (== valid)				*/
		1, 1, 1, 1, 			/* Local, Binary, Valid and Online :-)	*/
		0,						/* Directory number is set later		*/
		"",						/* Catalogue filename, filled in later	*/
		0,						/* Date is set later					*/
		0, 0, 0,				/* Section 0, Directory 0, no accesses	*/
		0,						/* Length is set later					*/
		"",						/* Disk name is set later				*/
		"AmigaDos",				/* The file owner						*/
		"Directory = ",			/* File comment, set later				*/
	}};

	int i;

	if (!checkfiles) {
		scripterror("can't do FOREIGN before CHECKFILES\n");
		Cleanup(10);
	}

	for (i = 0; i < numdirentries; i++) {
		chkabort();
		/* Setup actual file parameters for format() */
		strcpy(file->disk_name, direntries[i]->name);
		strncpy(file->cat_name, direntries[i]->name, CAT_LEN);
		strncpy(file->desc+12, dirnames[direntries[i]->dirnum], DESC_LEN-12);
		file->desc[DESC_LEN] = CHAR_NULL;
		file->length	= direntries[i]->size;
		file->date		= direntries[i]->date;
		file->dirnum	= direntries[i]->dirnum;
		format(out, MAXOUT, formatstring, file, checkfiles);
		putstring(out);
	}
}
