/*  :ts=4
 * transmit batch file management
 *
 *	$Log$
 */

#include <stdio.h>
#ifdef unix
# include <sys/types.h>
#endif /* unix */
#include "libc.h"
#include "news.h"
#include "msgs.h"
#include "trbatch.h"

/* tunable parameters */
#ifndef FLUSHEVERY
#define FLUSHEVERY 1	/* fflush batch files every this many lines */
#endif			/* FLUSHEVERY */
#ifndef NOPENBFS
#define NOPENBFS 10	/* # batchfiles kept open for batching (arbitrary) */
#endif			/* NOPENBFS */

static struct batchfile batchfile[NOPENBFS];	/* try to keep open always */
#define lastbf &batchfile[NOPENBFS-1]
/*
 * More than one pointer in ordtobfs may point at a given batchfile,
 * to permit sharing of open batch files among multiple sys entries.
 * ordtobfs[ordinal # of batch sys entry] -> (usually open) batch file,
 * if the index is in range.
 */
static struct batchfile *ordtobfs[NOPENBFS];
static struct batchfile fakebatf;	/* for non-cached batch files */

/* forwards */
FORWARD statust bfclose(), bfrclose();
FORWARD struct batchfile *bfincache(), *fakebf();

/*
 * open "name" for appending, for batch sys entry with ordinal # "ord".
 *
 * if ord is too big, see if any batchfile has been assigned to "name" yet;
 * if not, set up a fake batchfile for temporary use.  if ord is in range,
 * ensure that (name, ord) are mapped to a batchfile.
 *
 * if an attempt to open the batchfile's stream fails, close an arbitrary
 * batchfile stream and retry the open.
 */
struct batchfile *
bfopen(name, ord)
register char *name;
register int ord;
{
	register struct batchfile *bf;

	if (ord >= NOPENBFS) {			/* no mapping possible */
		bf = bfisopen(name);
		if (bf == NULL)
			bf = fakebf((FILE *)NULL, name);
	} else
		bf = bfincache(name, ord);

	if (bf->bf_str == NULL)
		bf->bf_str = fopenclex(name, "a");
	if (bf->bf_str == NULL) {
		if (bfrclose() != ST_OKAY)
			return NULL;
		bf->bf_str = fopenwclex(name, "a");	/* retry, may bitch */
	}
	return bf;
}

/*
 * returns a batchfile, never NULL, corresponding to name and ord.
 * if ord isn't mapped, search the batchfile cache for name;
 * if missing, initialise batchfile[ord] and map ord to it.
 * if ord wasn't mapped, but name was in the cache, map ord to the cache hit.
 */
STATIC struct batchfile *
bfincache(name, ord)
char *name;
int ord;
{
	register struct batchfile *bf = ordtobfs[ord];

	if (bf == NULL) {
		bf = bfisopen(name);
		if (bf == NULL) {
			/* establish new mapping for a new file */
			bf = &batchfile[ord];
			bf->bf_name = strsave(name);
			bf->bf_str = NULL;	/* paranoia */
#ifdef notdef
			bf->bf_ref = 0;
#endif
			bf->bf_lines = FLUSHEVERY;
		}
		ordtobfs[ord] = bf;
	}
	/* mapping is now set (ord -> bf) */
	return bf;
}

/* ARGSUSED ord */
statust
bffkclose(ord)			/* close current (ord's) batchfile, if fake */
int ord;
{
	register statust status = ST_OKAY;

	if (fakebatf.bf_str != NULL)
		status |= bfclose(&fakebatf);
	return status;
}

STATIC statust
bfclose(bf)
register struct batchfile *bf;
{
	register statust status = ST_OKAY;

	if (nfclose(bf->bf_str) == EOF)
		status = prfulldisk(bf->bf_name);
	bf->bf_str = NULL;	/* prevent accidents; mark as closed */
	return status;
}

STATIC struct batchfile *
fakebf(stream, name)
FILE *stream;
char *name;
{
	fakebatf.bf_name = name;
	fakebatf.bf_str = stream;
	return &fakebatf;
}

/*
 * search the batchfile cache for "name"; return the hit, if any.
 */
struct batchfile *
bfisopen(name)
register char *name;
{
	register struct batchfile *bf;

	for (bf = batchfile; bf <= lastbf; bf++)
		if (bf->bf_name != NULL && STREQ(name, bf->bf_name))
			return bf;
	return NULL;
}

/*
 * a performance hack: only fflush bf->bf_str every FLUSHEVERY calls.
 */
int
bfflush(bf)
register struct batchfile *bf;
{
	register int ret = 0;

	if (--bf->bf_lines <= 0) {
		bf->bf_lines = FLUSHEVERY;
		ret = fflush(bf->bf_str);
	}
	return ret;
}

STATIC statust
bfrclose()				/* close an arbitrary batchfile */
{
	register struct batchfile *bf;
	register statust status = ST_OKAY;

	for (bf = batchfile; bf <= lastbf; bf++)
		if (bf->bf_str != NULL) {
			status |= bfclose(bf);
			break;
		}
	return status;
}

statust
bfrealclose()				/* close all open batch files */
{
	register struct batchfile *bf;
	register statust status = ST_OKAY;

	for (bf = batchfile; bf <= lastbf; bf++) {
		if (bf->bf_str != NULL)		/* batch file stream open */
			status |= bfclose(bf);
		nnfree(&bf->bf_name);
#ifdef notdef
		bf->bf_ref = 0;
#endif
		ordtobfs[bf - batchfile] = NULL;	/* unmap batch file */
	}
	return status;
}
