/* gs-domains-hash.c
 *
 * part of the getstats program
 */


#include <stdio.h>
#include <string.h>
#include "gs-domains.h"				/* for DOMAINLEN */
#include "gs-domains-hash.h"			/* for HASHSIZE */

static struct IPtable *iptable[HASHSIZE] = {0};
int nhashed = 0;

/* add this IP address to the hash table, with corresponding domain name
 */
void addip(ipaddress, domain)
char *ipaddress;
char *domain;
{
    char thisip[DOMAINLEN];
    struct IPtable *ip;
    unsigned hashval;

    truncate_ip(thisip, ipaddress);	/* copy to thisip, truncate */
    ip = (struct IPtable *) emalloc(sizeof(*ip));
    ip->ipaddr = e_strdup(thisip);
    hashval = hash(thisip);
    ip->next = iptable[hashval];
    iptable[hashval] = ip;
    ip->domain = e_strdup(domain);
    nhashed++;
    return;
} /* addhash */



char *find_domain(ipaddress)
char *ipaddress;
{
    char thisip[DOMAINLEN];
    struct IPtable *ip;

    truncate_ip(thisip, ipaddress);	/* copy to thisip, truncate */
    for (ip = iptable[hash(thisip)]; ip != NULL; ip = ip->next)
	if (strcmp(thisip, ip->ipaddr) == 0)
	    return ip->domain;
    return NULL;
}



char *dump_hash_table(hasfilnam)
char *hasfilnam;
{
    FILE *has;
    int bucket;
    struct IPtable *ip;

    if (!nhashed)
	return "Hash table is empty";
    if ((has = fopen(hasfilnam, "w")) == NULL)
	return "Unable to create hash file";

    for (bucket = 0; bucket < HASHSIZE; bucket++)
	for (ip = iptable[bucket]; ip != NULL; ip = ip->next)
	    fprintf(has, "%s\t%s\n", ip->ipaddr, ip->domain);
    fclose(has);
    fprintf(stderr, "%d hash table entries written to %s\n",
			nhashed, hasfilnam);
    return NULL;
} /* dump_hash_table */



char *load_hash_table(hasfilnam)
char *hasfilnam;
{
    FILE *has;
    int xhashed = nhashed;			/* entries before loading */
    char ipaddr[DOMAINLEN];
    char *domain, *p;

    if ((has = fopen(hasfilnam, "r")) == NULL) {	/* print error msg */
	perror("load_hash_table");
	return "Unable to open hash file";
    }
    if (xhashed)
	fprintf(stderr, "hash table contains %d entries before loading\n",
		xhashed);
    while (fgets(ipaddr, DOMAINLEN, has)) {
	makelower(ipaddr);			/* cvt to lower case */

	/* find the TAB that separates the fields */
	for (domain = ipaddr; (*domain != '\t') && (*domain); domain++)
	    ;
	if (*domain == '\0') {
	    fprintf(stderr, "invalid hash record: %s\n", ipaddr);
	    continue;
	}
	/* change TAB to a NULL, domain points after the NULL */
	*domain++ = '\0';

	/* remove trailing newline */
	for (p = domain; (*p != '\n') && (*p); p++)
	    ;
	*p = '\0';
	if (isip(ipaddr))			/* ignore bogus entries */
	    addip(ipaddr, domain);		/* add hash table entry */
    }
    fclose(has);
    fprintf(stderr, "%d hash table entries loaded from %s\n",
		nhashed-xhashed, hasfilnam);
    if (xhashed)
	fprintf(stderr, "hash table now contains %d entries\n", nhashed);
    return NULL;
} /* load_hash_table */



/* copy the IP address, truncating at the third dot: 128.59.39
 */
void truncate_ip(s1, s2)
char *s1;
char *s2;
{
    int dot;
    for (dot=0; *s2; s1++, s2++) {
	if (*s2 == '.')
	    if (++dot == 3) {
		*s1 = '\0';		/* truncate here */
		return;
	    }
	*s1 = *s2;
    }
    *s1 = '\0';
}



unsigned int hash(s)
char *s;
{
    unsigned int hashval = 0,
		c, range = '~' - '!' + 1;

    for ( ; *s != '\0'; s++) {
	  /* constrain chars to the range '!' to '~' */
	if (*s < '!')
	    c = 0;
	else if (*s <= '~')
	    c = *s - '!';
	else
	    c = '~' - '!';

	hashval = c + hashval * range;
    }
    return hashval % HASHSIZE;
} /* hash */
