/*
 * active file access functions (big, fast, in-memory version)
 */

#ifdef DEBUG
#  define STAT(x)	{	fprintf(stderr,"%s: %d, ",__FUNC__,__LINE__);\
						fprintf x;\
						fflush(stderr); }
#else
#  define STAT(x)
#endif

#include <stdio.h>

#ifndef AMIGA
#  include <sys/types.h>
#  include <sys/stat.h>
#else
#  include <libraries/dos.h>
#endif /* AMIGA */

#include "libc.h"
#include "news.h"
#include "config.h"
#include "active.h"

/* private */
static char *active = NULL;	/* cache: points at entire active file */
static int actsize;			/* bytes in active: type int fixed by fread */
static char **actlnps;		/* point at lines in active file */
static unsigned actlines;	/* lines in actlnps actually used */

/* imports from active.c */
extern char actrelnm[];

/* forwards */
FORWARD statust actmkindx();

statust actfload(fp)
FILE *fp;
{
	statust status = ST_OKAY;

	if (fp != NULL && active == NULL) {
#ifndef AMIGA
		struct stat sb;

		errno = 0;
		if (fstat(fileno(fp), &sb) < 0)
			warning("can't fstat `%s'", ctlfile(actrelnm));
		else if (actsize = sb.st_size, /* squeeze into an int */
		    (unsigned)actsize != sb.st_size)
			warning("`%s' won't fit into memory", ctlfile(actrelnm));
		else
#else /* AMIGA */
		struct FileInfoBlock fib;
		struct FileLock *lock, *Lock();
		char *active_file;

		active_file = ctlfile(actrelnm);
		STAT((stderr, "creating cache for '%s'\n", active_file));
		if (lock = Lock(active_file, ACCESS_READ)) {
			STAT((stderr, "Examine(0x%06lx, 0x%06lx)\n", lock, &fib));
			Examine(lock, &fib);
			UnLock(lock);
			actsize = fib.fib_Size;		/* Squeeze into an integer */
			if ((unsigned) actsize != fib.fib_Size) {
				STAT((stderr, "about to issue size warning\n"));
				warning("`%s' won't fit into memory", ctlfile(actrelnm));
			}
		} else {
			STAT((stderr, "lock denied!\n"));
		}
#endif /* AMIGA */
		STAT((stderr, "about to allocate %d bytes for cache\n", actsize+1));
		if ((active = malloc((unsigned)actsize+1)) == NULL)
			warning("can't allocate memory for `%s'", ctlfile(actrelnm));
		else {
			rewind(fp);
			/*
			 * If we read with fgetms, we might be able to avoid
			 * calling linescan().
			 */
			STAT((stderr, "cache being read...\n"));
			if (fread(active, 1, actsize, fp) != actsize) {
				warning("error reading `%s'", ctlfile(actrelnm));
				status |= ST_DROPPED;
			} else {
				STAT((stderr, "calling actmkindx()\n"));
				status |= actmkindx();
			}
		}
		if (active == NULL)
			status |= ST_DROPPED;	/* give up! */
		if (status != ST_OKAY) {
			nnfree(&active);
			nnafree(&actlnps);
		}
	}
	return status;
}

static statust actmkindx()			/* build actlnps index for active */
{
	register statust status = ST_OKAY;
	unsigned lnpsz;
	int maxlines;

	active[actsize] = '\0';		/* make a proper string */
	/* +1 for a possible partial line +1 for a dummy to check overflow */

	STAT((stderr, "about to count lines...\n"));
	maxlines = charcount(active, '\n') + 2;
	lnpsz = sizeof(char *) * (long) maxlines;

	STAT((stderr, "%u lines; array size %lu\n",
		maxlines, (long) maxlines * sizeof(char *)));

	if (lnpsz != sizeof(char *) * (long)maxlines ||
			    (actlnps = (char **)malloc(lnpsz)) == NULL) {
		STAT((stderr, "warning:  index won't fit in memory\n"));
		warning("`%s' index won't fit in memory", ctlfile(actrelnm));
    	status |= ST_DROPPED;
	} else {
		actlnps[maxlines - 2] = "";	/* in case no partial line */
		actlnps[maxlines - 1] = "";	/* end sentinel */
		actlines = linescan(active, actlnps, maxlines);
		if (actlines >= maxlines) {
			(void) fprintf(stderr,
				"%s: too many newsgroups in `%s' (can't happen)\n",
				progname, ctlfile(actrelnm));
			status |= ST_DROPPED;
		}
	}
	return status;
}

/*
 * Store in lnarray the addresses of the starts of lines in s.
 * Return the number of lines found; if greater than nent,
 * store only nent and return nent.
 * Thus lnarray should be one bigger than needed to detect overflow.
 */

int linescan(s, lnarray, nent)
char *s;
char **lnarray;
register int nent;
{
	register char **lnarrp = lnarray;
	register int i = 0;
	register char *nlp = s;

	if (i < nent)
		*lnarrp++ = nlp;
	while (++i < nent && (nlp = index(nlp, '\n')) != NULL && *++nlp != '\0')
		*lnarrp++ = nlp;
	return i;		/* number of addrs stored */
}

statust actfsync(fp)			/* write to disk, fp is open */
FILE *fp;
{
	statust status = ST_OKAY;

	rewind(fp);
	if (active != NULL) {
		if (fwrite(active, actsize, 1, fp) != 1)
			status |= ST_DROPPED;	/* serious loss */
		nnfree(&active);
		nnafree(&actlnps);
	}
	return status;
}

/* ARGSUSED fp */
char *actfind(fp, ng, nglen)
FILE *fp;
register char *ng;
register int nglen;
{
	register char *pos;
	register unsigned line = 0;

	while (pos = actlnps[line], line++ < actlines && pos[0] != '\0')
		if (STREQN(pos, ng, nglen) && pos[nglen] == ' ')
			return pos;
	return NULL;
}

/* ARGSUSED */
statust actfwrnum(fp, pos)
FILE *fp;
char *pos;
{
	return ST_OKAY;
}
