/*
* transmit batch file management (exploder version)
*/
#include <stdio.h>
#include <errno.h>
#include "fixerrno.h"
#include <sys/types.h>
#include "hdbm.h"
#include "hash.h"
#include "libc.h"
#include "news.h"
/* tunable parameter */
#ifndef NOPENBFS
#define NOPENBFS 250 /* # batchfiles kept open for batching (250 for SunOS 4.1.2 [UUNET]) */
#endif /* NOPENBFS */
#include "trbatch.h"
/* tunable parameters */
#ifndef HASHFILSZ
#define HASHFILSZ (NOPENBFS/2)
/* # of hash buckets for batch file names */
#endif /* HASHFILSZ */
static HASHTABLE *nmbftbl; /* name -> batchfile mapping */
static struct batchfile *bfisopen(char *name), *bfincache(char *name);
static statust bfrealclose(), bfrclose();
/*
* open "name" for appending.
*
* see if any batchfile has been assigned to "name" yet.
* if an attempt to open the batchfile's stream fails, close an arbitrary
* batchfile stream and retry the open.
*/
struct batchfile *
bfopen(name)
register char *name;
{
register struct batchfile *bf;
/* malloc_debug(0); */ /* DEBUG */
bf = bfincache(name);
if (bf->bf_str == NULL) {
bf->bf_str = fopenclex(name, "a"); /* silent try */
if (bf->bf_str == NULL && errno == EMFILE) {
/* try to free up a descriptor */
if (bfrclose() != ST_OKAY)
return NULL;
errno = 0;
warning(
"had to close a descriptor to reuse it; this should not happen!", "");
/* bad input? canthappen? */
}
if (bf->bf_str == NULL) {
/* retry, noisily this time */
bf->bf_str = fopenwclex(name, "a");
}
if (bf->bf_str != NULL)
bfsetup(bf);
}
return bf;
}
STATIC
hfinstall(name)
char *name;
{
register struct batchfile *bf;
if (nmbftbl == NULL)
nmbftbl = hashcreate(HASHFILSZ, (unsigned (*)())NULL);
bf = (struct batchfile *)hashfetch(nmbftbl, name);
if (bf != NULL)
return; /* error: name present */
/* allocate, append & initialise a new entry */
bf = (struct batchfile *)nemalloc(sizeof *bf);
(void) memset((char *)bf, 0, sizeof *bf);
bf->bf_name = strsave(name); /* NEEDED? */
bf->bf_str = NULL; /* paranoia */
#ifdef notdef
bf->bf_ref = 0;
#endif
bf->bf_lines = FLUSHEVERY;
if (!hashstore(nmbftbl, bf->bf_name, (HASHDATUM)bf))
error("can't store under hash key `%s'", name); /* canthappen */
}
/*
* returns a batchfile, never NULL, corresponding to name.
*/
static struct batchfile *
bfincache(name)
char *name;
{
register struct batchfile *bf;
bf = bfisopen(name);
if (bf == NULL) {
hfinstall(name);
bf = bfisopen(name);
if (bf == NULL)
abort(); /* DEBUG */
}
return bf;
}
/*
* write filename, message-id or size on batch file "bf".
* under the 'f' flag, include the size in bytes of the article
* after "name" to assist the C news batcher. under the 'n' flag,
* write the article's message-id. afterward, flush "bf" in case
* the machine crashes before the stream is closed.
* don't check putc return value for portability; use ferror.
*/
int /* boolean */
bfappend(bf, flag, batname, artname, msgid, size)
register struct batchfile *bf;
int flag;
char *batname, *artname, *msgid;
long size;
{
register FILE *bfstr = bf->bf_str;
if (flag == 'I')
artname = msgid; /* cheat */
if (fputs(artname, bfstr) == EOF)
return NO;
if (flag == 'f' && fprintf(bfstr, " %ld", size) == EOF)
return NO;
if (flag == 'n') {
(void) putc(' ', bfstr);
if (ferror(bfstr) || fputs(msgid, bfstr) == EOF)
return NO;
}
(void) putc('\n', bfstr);
if (ferror(bfstr) || bfflush(bf) == EOF)
return NO;
return YES;
}
/* --- hashing --- */
struct closehook {
short closedone;
statust status;
};
STATIC int
closefirst(key, data, hook)
HASHDATUM key, data;
char *hook;
{
register struct closehook *chp = (struct closehook *)hook;
register struct batchfile *bf;
if (chp->closedone)
return;
bf = (struct batchfile *)data;
if (bf->bf_str == NULL)
return;
chp->status = bfclose(bf);
chp->closedone = YES;
}
STATIC statust
bfrclose() /* close an arbitrary batchfile */
{
struct closehook closehook;
register struct closehook *chp = &closehook;
chp->closedone = NO;
chp->status = ST_OKAY;
if (nmbftbl == NULL)
return chp->status;
hashwalk(nmbftbl, closefirst, (char *)chp);
if (!chp->closedone) {
errno = 0;
warning("bfrclose couldn't find an open descriptor to close",
""); /* canthappen */
}
return chp->status;
}
STATIC int
closeone(key, data, hook) /* close a given open batch file */
HASHDATUM key, data;
char *hook;
{
register struct closehook *chp = (struct closehook *)hook;
register struct batchfile *bf = (struct batchfile *)data;
if (bf->bf_str != NULL) /* batch file stream open */
chp->status |= bfclose(bf);
#ifdef notdef
bf->bf_ref = 0;
#endif
if (!hashdelete(nmbftbl, key))
error("can't delete hash key `%s'", key); /* canthappen */
}
STATIC statust
bfrealclose() /* close all open batch files */
{
struct closehook closehook;
register struct closehook *chp = &closehook;
chp->status = ST_OKAY;
if (nmbftbl != NULL)
hashwalk(nmbftbl, closeone, (char *)chp);
return chp->status;
}
/*
* search the batchfile cache for "name"; return the corresponding
* open master batch file, if any.
*/
STATIC struct batchfile *
bfisopen(name)
char *name;
{
return nmbftbl == NULL? NULL:
(struct batchfile *)hashfetch(nmbftbl, name);
}
syntax highlighted by Code2HTML, v. 0.9.1