/*  $Revision: 1.13 $
**
**  Newsgroups and the active file.
*/
#include "actived.h"


/*
**  Newsgroup hashing stuff.  See comments in innd/ng.c.
*/

#define GRP_HASH(Name, p, j)	\
	for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++
#define GRP_SIZE	65536
#define GRP_BUCKET(j)	&GRPtable[j & (GRP_SIZE - 1)]

typedef struct _GRPHASH {
    int		Size;
    int		Used;
    GROUPENTRY	**Groups;
} GRPHASH;


STATIC GRPHASH		GRPtable[GRP_SIZE];
STATIC GROUPENTRY	*GRPentries;
STATIC int		GRPbuckets;
STATIC int		GRPsize;


/*
**  See if a given newsgroup exists.
*/
GROUPENTRY *
GRPfind(group)
    register char		*group;
{
    register char		*p;
    register unsigned int	j;
    register int		i;
    register GROUPENTRY		**gpp;
    GRPHASH			*htp;
    char			c;

    /* SUPPRESS 6 *//* Over/underflow from plus expression */
    GRP_HASH(group, p, j);
    htp = GRP_BUCKET(j);
    for (c = *group, gpp = htp->Groups, i = htp->Used; --i >= 0; gpp++)
	if (c == gpp[0]->Name[0] && EQ(group, gpp[0]->Name))
	    return gpp[0];
    return NULL;
}


STATIC void
GRPhash()
{
    register char		*p;
    register int		i;
    register GROUPENTRY		*gp;
    register unsigned int	j;
    register GRPHASH		*htp;

    /* Set up the default hash buckets. */
    GRPbuckets = GRPsize / GRP_SIZE;
    if (GRPbuckets == 0)
	GRPbuckets = 1;
    if (GRPtable[0].Groups)
	for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++)
	    htp->Used = 0;
    else
	for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++) {
	    htp->Size = GRPbuckets;
	    htp->Groups = NEW(GROUPENTRY*, htp->Size);
	    htp->Used = 0;
	}

    /* Now put all groups into the hash table. */
    for (i = GRPsize, gp = GRPentries; --i >= 0; gp++) {
	/* SUPPRESS 6 *//* Over/underflow from plus expression */
	GRP_HASH(gp->Name, p, j);
	htp = GRP_BUCKET(j);
	if (htp->Used >= htp->Size) {
	    htp->Size += GRPbuckets;
	    RENEW(htp->Groups, GROUPENTRY*, htp->Size);
	}
	htp->Groups[htp->Used++] = gp;
    }

    /* Note that we don't sort the buckets. */
}


/*
**  Read the active file into memory, sort it, and set the number of
**  newsgroups read in.  Return TRUE if okay, FALSE on error.
*/
BOOL
GetGroupList()
{
    static char			*active;
    register char		*p;
    register char		*q;
    register GROUPENTRY		*gp;
    register int		i;

    /* If re-scanning, free previous groups. */
    if (active != NULL) {
	DISPOSE(active);
	DISPOSE(GRPentries);
    }

    /* Get the new file. */
    active = ReadInFile(ACTIVE, (struct stat *)NULL);
    if (active == NULL) {
	syslog(L_ERROR, "cant read %s %m", ACTIVE);
	return FALSE;
    }

    /* Count lines. */
    for (p = active, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++)
	continue;

    /* Fill in the group array. */
    GRPentries = NEW(GROUPENTRY, i);
    for (i = 0, gp = GRPentries, p = active; *p; i++, gp++, p = q + 1) {
	gp->Name = p;
	if ((p = strchr(p, ' ')) == NULL) {
	    syslog(L_ERROR, "internal no_space1 \"%.20s...\"",
		gp->Name);
	    return FALSE;
	}
	*p++ = '\0';

	/* Get the high mark. */
	if ((q = strchr(p, ' ')) == NULL) {
	    syslog(L_ERROR, "internal no_space2 \"%.20s...\"",
		gp->Name);
	    return FALSE;
	}
	*q++ = '\0';
	gp->High = atol(p);

	/* Get the low mark. */
	if ((p = strchr(q, ' ')) == NULL) {
	    syslog(L_ERROR, "internal no_space3 \"%.20s...\"",
		gp->Name);
	    return FALSE;
	}
	*p++ = '\0';
	gp->Low = atol(q);

	/* Kill the newline. */
	if ((q = strchr(p, '\n')) == NULL) {
	    syslog(L_ERROR, "internal newline \"%.20s...\"",
		gp->Name);
	    return FALSE;
	}
	*q = '\0';
	gp->Flag = *p;
	gp->Alias = gp->Flag == NF_FLAG_ALIAS ? p + 1 : NULL;
    }

    GRPsize = i;
    GRPhash();
    return TRUE;
}
