#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <descrip.h>
#include <str$routines.h>
#include <strdef.h>
#include "bboard.h"

#define LOAD_LIST	0x0001
#define LOAD_SOURCE	0x0002
#define LOAD_RULE	0x0004
#define LOAD_DIGEST	0x0008
#define LOAD_MANAGER	0x0010
#define LOAD_LINKS	0x0020
#define LOAD_IGNORE	0x0040

#define LOAD_LOG	0x0100
#define LOAD_ERR	0x0200
#define LOAD_TIMER	0x0400
#define LOAD_COUNT	0x0800
#define LOAD_CONTACT	0x1000

#define LOAD_MANDATORY	0x0017
#define LOAD_SINGULAR	0x1f00

static int scan_ctl_file(FILE * in, char * node, char * user);
static void parse_ctl_file(FILE * in);
static void singular_cleanup(void);
static void parse_control_line(struct rule_ctl * cur_ctl, char * in_line,
	int item);
static void load_text(char * so, char * si, int len);
static void release_control_memory(void);
static int search_lists(FILE * note, struct rule_info * rule);
static int hdr_item_found(char * line, struct source_info * cur);
static void build_header_line(struct source_info * cur, char * line);

struct ctl_base {
    int count;
    int n_count;
    void * p;
};

struct rule_ctl {
    char * string;
    int section;
    int size;
    struct ctl_base * ctl;
};

struct digest_def {
    char digest_id;
    int split_len;
    int split_error;
};

struct manager_list {
    char user[9];
};

struct link_info {
    char link[9];
};

struct ignore_list {
    char node[9];
    char user[9];
};

static struct ctl_base lst, src, rul, dig, man;
static struct ctl_base lnk, ign;
static struct ctl_base s_log, s_err, s_tim, s_cou, s_ctct;

static struct rule_ctl rule_control[] = {
    { ":lists",       LOAD_LIST,    sizeof(struct list_info),    &lst },
    { ":sources",     LOAD_SOURCE,  sizeof(struct source_info),  &src },
    { ":rules",       LOAD_RULE,    sizeof(struct rule_info),    &rul },
    { ":digests",     LOAD_DIGEST,  sizeof(struct digest_def),	 &dig },
    { ":manager",     LOAD_MANAGER, sizeof(struct manager_list), &man },

    { ":links",       LOAD_LINKS,   sizeof(struct link_info),    &lnk },
    { ":ignore",      LOAD_IGNORE,  sizeof(struct ignore_list),  &ign },

    { ":log_file.",   LOAD_LOG,     256,                         &s_log },
    { ":err_conf.",   LOAD_ERR,     80,                          &s_err },
    { ":time_int.",   LOAD_TIMER,   14,                          &s_tim },
    { ":count_int.",  LOAD_COUNT,   4,                           &s_cou },
    { ":local_ctct.", LOAD_CONTACT, 12,                          &s_ctct },
    { NULL,	  0,		0,			     0 }
};

/* Read the control information, checking if it's all there */

int read_control_file(int reload, char * node, char * user)
{
    FILE * in;				/* Control file */
    struct rule_ctl * cur;		/* Used to obtain pointers */

    /* First, let's open the file */

    in = fopen(control_file.dsc$a_pointer, "r");
    if (in == NULL) {
	sprintf(temp, "Unable to open rules control file '%s'.",
		control_file.dsc$a_pointer);
	inter_error("F ", temp, node, user);
	return (FALSE);
    }

    /* Does the file have the necessary sections */

    if (!scan_ctl_file(in, node, user)) {
	if (!reload) {
	    sprintf(temp, "F Control file errors causing %s abort.\n",
		    hook_name.dsc$a_pointer);
	    critical_error(temp);
	}
	return(FALSE);
    }

    /* If we have old information, lose it */

    if (reload) {
	release_control_memory();
    }

    /* Allocate some new memory for all of the sections that need it */

#ifdef DEBUG
    printf("read_control_file: about to allocate structure memory\n");
#endif

    for (cur = rule_control; cur->string; cur++) {
	cur->ctl->count = cur->ctl->n_count;
	if (cur->ctl->count) {
	    cur->ctl->p = xmalloc(cur->size * (cur->ctl->count+SLOP_LEN));
	    memset(cur->ctl->p, 0, cur->size * (cur->ctl->count+SLOP_LEN));
	} else {
	    cur->ctl->p = NULL;
	}
    }

    /* Back to the beginning to read it again */

    rewind(in);

    /* And read in the info */

#ifdef DEBUG
    printf("read_control_file: about to build new control information\n");
#endif

    parse_ctl_file(in);

    /* What d'ya know... It worked! */

    return(TRUE);
}

/* Make sure that the right sections are present and figure out how many */

static int scan_ctl_file(FILE * in, char * node, char * user)
{
    char line[200];			/* For reading the control file */
    int cur_section;			/* What are we looking at? */
    int item_cnt;			/* How many have we seen? */
    int sections_seen;			/* What have we seen in total */
    struct rule_ctl * cur;		/* For figuring out what we're seeing */

    /* Zero out the new counts */

    for (cur = rule_control; cur->string; cur++) {
	cur->ctl->n_count = 0;
    }

    /* Nothing happened yet */

    cur_section = sections_seen = 0;

    /* Let's start reading the file */

    while (TRUE) {

	/* Read lines until we fall off the end or we get an error */

	if (!fgets_lcl(line, 200, in)) {    /* error or EOF */
	    break;			/* Stop in either event */
	}

	/* Stare at the first character of the line */

	switch (line[0]) {

	/* Comment character, so ignore the line */
	    case '!':
		continue;

	/* Section header, so handle it */
	    case ':':

#ifdef DEBUG
		printf("scan_ctl_file: new section header\n");
#endif

		/* We're currently in a section, save and start a new one */

		if (cur_section) {
		    cur->ctl->n_count = item_cnt;   /* Save the count */

		    /* Do we have to have anything here? */

		    if (!item_cnt && (cur_section & LOAD_MANDATORY)) {
			sprintf(temp,
				"'%s' section must have at least one line.",
				cur->string);
			inter_error("F ", temp, node, user);
			return(FALSE);
		    }
		}

		/* So which one are we going to be looking at */

		for (cur = rule_control; cur->string; cur++) {
		    if (strncmp(line, cur->string, strlen(cur->string))) {
			continue;
		    }
		    cur_section = cur->section;     /* Found the right one */
		    break;
		}

		/* Did we find the section, or is it unknown? */

		if (!cur->string) {
		    sprintf(temp, "Invalid control command line '%s'.", line);
		    inter_error("F ", temp, node, user);
		    return(FALSE);
		}

		/* Have we already seen this section? No duplicates, please */

		if (sections_seen & cur_section) {
		    sprintf(temp, "Duplicate section '%s' in control file.",
			    line);
		    inter_error("F ", temp, node, user);
		    return(FALSE);
		}

		/* Ok. So it's new and it's valid. Let's set up for it */

		sections_seen |= cur_section;	/* We've got it */
		item_cnt = 0;			/* And nothing in it, yet */

		/* But, is it a single-line section? If so, we're not */
		/*  actually in a section. */

		if (cur_section & LOAD_SINGULAR) {
		    cur_section = 0;
		    cur->ctl->n_count = 1;	/* Always just one */
		}

		break;				/* Nothing else to do */

	/* Data line. Is it ok? */
	    default:

		/* Are we in a section yet? */

		if (!cur_section) {
		    sprintf(temp, "Improper sequence for '%s'.", line);
		    inter_error("F ", temp, node, user);
		    return(FALSE);
		}

		/* One more line in the current section */

		item_cnt++;

		/* Nothing else to do right now */

		break;
	}
    }

    /* Did we fall off the end or did we have an error? */

    if (ferror(in)) {
	sprintf(temp, "Error: %s", strerror(errno, vaxc$errno));
	inter_error("F ", temp, node, user);
	return(FALSE);
    }

    /* Save the current count, please */

    if (cur_section) {
	cur->ctl->n_count = item_cnt;
    }

    /* Do we have lines for the mandatory sections? */

    if (!item_cnt && (cur_section & LOAD_MANDATORY) &&
	    !(cur_section && LOAD_SINGULAR)) {
	sprintf(temp, "'%s' section must have at least one line.", cur->string);
	inter_error("F ", temp, node, user);
	return(FALSE);
    }

    /* Do we have all of the mandatory sections? */

    if (sections_seen & LOAD_MANDATORY != LOAD_MANDATORY) {
	sprintf(temp, "Not all control file sections present. Status: %d",
		sections_seen);
	inter_error("F ", temp, node, user);
	return(FALSE);
    }

    /* It all worked. Yay! */

    return (TRUE);
}

/* Read and store the information from the control file */

static void parse_ctl_file(FILE * in)
{
    char line[200];			/* Buffer for the lines */
    struct rule_ctl * cur;		/* Current section */
    int item_cnt;			/* Where in the buffer? */

    /* Go through the entire file (again) */

    while (TRUE) {

	/* Get the next line until EOF */

	if (!fgets_lcl(line, 200, in)) {
	    break;
	}

	/* Do stuff based on the first character of the line */

	switch (line[0]) {

	/* Comment character, ignore the line */
	    case '!':
		continue;

	/* Section change, set up for it */
	    case ':':

#ifdef DEBUG
		printf("parse_ctl_file: new section header\n");
#endif

		for (cur = rule_control; cur->string; cur++) {
		    if (strncmp(line, cur->string, strlen(cur->string))) {
			continue;
		    }
		    break;
		}

		/* First off, is this singular or multiple? */

		if (cur->section & LOAD_SINGULAR) {
		    parse_control_line(cur, line, 0);
		    break;
		}

		/* Reset the item count once we've found a new section */

		item_cnt = 0;
		break;

	/* Data line, so just parse it out */
	    default:
		parse_control_line(cur, line, item_cnt++);
		break;
	}
    }

    singular_cleanup();

    return;
}

/* Make sure that all the singular pieces of information make sense */

static void singular_cleanup(void)
{
    struct dsc$descriptor temp_interval;
    unsigned int t_cnt;

    send_log_file("I Singular rules set as follows:");

    if (s_log.count) {
	str_desc(&log_file, s_log.p);
    } else {
	str_desc(&log_file, DEF_LOG_FILE);
    }
    sprintf(temp, "I NOTELOG file: %s", log_file.dsc$a_pointer);
    send_log_file(temp);

    if (s_err.count) {
	str_desc(&error_conf, s_err.p);
    } else {
	str_desc(&error_conf, DEF_ERR_CONF);
    }
    sprintf(temp, "I Error Conference: %s", error_conf.dsc$a_pointer);
    send_log_file(temp);

    if (s_tim.count) {
	str_desc(&temp_interval, s_tim.p);
    } else {
	str_desc(&temp_interval, DEF_TIME_INTERVAL);
    }
    sys$bintim(&temp_interval, bboard_time_interval);
    sprintf(temp, "I Time Interval: %s", temp_interval.dsc$a_pointer);
    send_log_file(temp);

    t_cnt = 0;
    if (s_cou.count) {
	t_cnt = atoi(s_cou.p);
    }
    if (t_cnt) {
	bboard_count_interval = t_cnt;
    } else {
	bboard_count_interval = DEF_COUNT_INTERVAL;
    }
    sprintf(temp, "I Count Interval: %d", bboard_count_interval);
    send_log_file(temp);

    if (s_ctct.count) {
	str_desc(&local_contact, s_ctct.p);
	sprintf(temp, "I Local Contact: %s", local_contact.dsc$a_pointer);
    } else {
	str_desc(&local_contact, "");
	sprintf(temp, "I Local Contact: <none>");
    }
    send_log_file(temp);
}

/* Split a control line into its control buffer */

static void parse_control_line(struct rule_ctl * cur_ctl, char * in_line,
	int item)
{
    struct list_info * c_lst;		/* Temporary pointers for all these */
    struct source_info * c_src;
    struct rule_info * c_rul;
    struct digest_def * c_dig;
    struct manager_list * c_man;
    struct link_info * c_lnk;
    struct ignore_list * c_ign;
    struct ctl_base * c_sing;
    struct dsc$descriptor interval_text;
    SYSTIM interval_check;

    /* Which type are we working on? */

#ifdef DEBUG
    printf("parse_control_line: Processing data type: ");
#endif

    switch (cur_ctl->section) {
	case LOAD_LIST:
#ifdef DEBUG
	    printf("LIST\n");
#endif
	    c_lst = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    load_text(c_lst[item].scan_data, in_line, 10);
	    load_text(c_lst[item].conf_hdr, in_line+11, 20);
	    load_text(c_lst[item].conf_user, in_line+32, 12);

	    c_lst[item].flag_digest = FALSE;
	    c_lst[item].flag_keyword = FALSE;

	    switch(in_line[45]) {
		case 'Y':
		case 'y':
		    c_lst[item].flag_digest = TRUE;
		    break;
		case 'K':
		case 'k':
		    c_lst[item].flag_keyword = TRUE;
		    break;
	    }

	    c_lst[item].new_topic_interval = in_line[47];
	    load_text(c_lst[item].conf_name, in_line+49, 39);
	    break;

	case LOAD_SOURCE:
#ifdef DEBUG
	    printf("SOURCE\n");
#endif
	    c_src = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    load_text(c_src[item].id, in_line, 2);
	    strupr(c_src[item].id);
	    load_text(c_src[item].scan_text, in_line+3, 20);
	    strlwr(c_src[item].scan_text);
	    c_src[item].reg_text_length = atoi(in_line+24);

	    /* Allocate some space for the header */

	    c_src[item].header_text = xmalloc(c_src[item].reg_text_length + 2);
	    break;

	case LOAD_RULE:
#ifdef DEBUG
	    printf("RULE\n");
#endif
	    c_rul = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    load_text(c_rul[item].ids[0], in_line, 2);
	    strupr(c_rul[item].ids[0]);
	    load_text(c_rul[item].comparison, in_line+3, 2);
	    strlwr(c_rul[item].comparison);	/* Keep this lower case */
	    load_text(c_rul[item].ids[1], in_line+6, 2);
	    strupr(c_rul[item].ids[1]);
	    load_text(c_rul[item].action, in_line+9, 2);
	    load_text(c_rul[item].id_from, in_line+12, 2);
	    strupr(c_rul[item].id_from);
	    load_text(c_rul[item].id_subj, in_line+15, 2);
	    strupr(c_rul[item].id_subj);
	    break;

	case LOAD_DIGEST:
#ifdef DEBUG
	    printf("DIGEST\n");
#endif
	    c_dig = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    c_dig[item].digest_id = in_line[0];
	    c_dig[item].split_len = atoi(in_line+2);
	    c_dig[item].split_error = atoi(in_line+6);
	    break;

	case LOAD_MANAGER:
#ifdef DEBUG
	    printf("MANAGER\n");
#endif
	    c_man = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    load_text(c_man[item].user, in_line, 8);
	    strupr(c_man[item].user);	/* Keep this upper case */
	    break;

	case LOAD_LINKS:
#ifdef DEBUG
	    printf("LINKS\n");
#endif
	    c_lnk = cur_ctl->ctl->p;	/* Grab a pointer */

	    /* Parse out the info */

	    load_text(c_lnk[item].link, in_line, 8);
	    strupr(c_lnk[item].link);	/* Keep this upper case */
	    break;

	case LOAD_IGNORE:
#ifdef DEBUG
	    printf("IGNORE\n");
#endif
	    c_ign = cur_ctl->ctl->p;	/* Grab a pointer */

	    load_text(c_ign[item].node, in_line, 8);
	    load_text(c_ign[item].user, in_line+9, 8);

	    /* Want both to be upper case */

	    strupr(c_ign[item].node);
	    strupr(c_ign[item].user);
	    break;

	case LOAD_LOG:
#ifdef DEBUG
	    printf("LOG\n");
#endif
    singular_common:
	    c_sing = cur_ctl->ctl;

	    load_text(c_sing->p, in_line+strlen(cur_ctl->string),
		    cur_ctl->size);
	    break;

	case LOAD_ERR:
#ifdef DEBUG
	    printf("ERR\n");
#endif
	    goto singular_common;
	    break;

	case LOAD_TIMER:
#ifdef DEBUG
	    printf("TIMER\n");
#endif
	    c_sing = cur_ctl->ctl;

	    load_text(c_sing->p, in_line+strlen(cur_ctl->string),
		    cur_ctl->size);

	    str_desc(&interval_text, c_sing->p);
	    status = sys$bintim(&interval_text, interval_check);

	    if (!(status & 1)) {	/* Bad time given */
		send_log_file("I Invalid time field selected");
		send_log_file("I Using default value instead");

		xfree(c_sing->p);	/* And we never saw this... */
		c_sing->count = 0;
		break;
	    }
	    break;

	case LOAD_COUNT:
#ifdef DEBUG
	    printf("COUNT\n");
#endif
	    goto singular_common;
	    break;

	case LOAD_CONTACT:
#ifdef DEBUG
	    printf("CONTACT\n");
#endif
	    goto singular_common;
	    break;
    }
    return;
}

/* Load a field, terminate it, then trim it down */

static void load_text(char * so, char * si, int len)
{
    strncpy(so, si, len);
    so[len] = '\0';
    trim_end(so);
}

/* Go through and throw away the memory we had */

static void release_control_memory(void)
{
    struct source_info * c_src;

    xfree(lst.p);
    for (c_src = src.p; c_src->id[0]; c_src++) {
	xfree(c_src->header_text);
    }
    xfree(src.p);
    xfree(rul.p);
    xfree(dig.p);
    xfree(man.p);
    if (lnk.count) {
	xfree(lnk.p);
    }
    if (ign.count) {
	xfree(ign.p);
    }
    if (s_log.count) {
	xfree(s_log.p);
    }
    if (s_err.count) {
	xfree(s_err.p);
    }
    if (s_tim.count) {
	xfree(s_tim.p);
    }
    if (s_cou.count) {
	xfree(s_cou.p);
    }
    if (s_ctct.count) {
	xfree(s_ctct.p);
    }

    return;
}

/* After we've loaded the headers, figure out what we should do with it */

void do_rules(FILE * note)
{
    struct rule_info * cur;		/* Look at all the rules */
    int id_present[2];			/* Are the IDs there? */
    int ids_equal;			/* Are they equal */
    int fulfilled;			/* And did we fulfill the comparison */

    char * id1; 			/* What's the info */
    char * id2;

    /* Go through all the rules until we get a match or fall off the end */

#ifdef DEBUG
    printf("do_rules: about to scan for rule match\n");
#endif

    for (cur = rul.p; cur->comparison[0]; cur++) {

	/* Start off without anything being set */

	id_present[1] = id_present[0] = FALSE;

	/* Get the first ID */

	id1 = find_source(cur->ids[0]);
	id_present[0] = (id1 != NULL);

	/* If we need to, get the second ID */

	if (cur->ids[1][0]) {
	    id2 = find_source(cur->ids[1]);
	    id_present[1] = (id2 != NULL);
	}

	/* If they're both there, are they equal? */

	if (id_present[0] && id_present[1]) {
	    ids_equal = (!strcmp(id1, id2));
	} else {
	    ids_equal = FALSE;
	}

	/* Now let's figure out what this all means... */

	fulfilled = FALSE;

	if (!strcmp(cur->comparison, "eq")) {
	    fulfilled = ids_equal;
	} else if (!strcmp(cur->comparison, "ne")) {
	    fulfilled = !ids_equal;
	} else if (!strcmp(cur->comparison, "pr")) {
	    fulfilled = id_present[0];
	} else if (!strcmp(cur->comparison, "np")) {
	    fulfilled = !id_present[0];
	}

	/* Did we get a match? */

#ifdef DEBUG
	printf("do_rules: %smatch\n", (fulfilled ? "" : "no "));
#endif

	if (!fulfilled) {
	    continue;
	}

	/* If so, try seeing if it has a match for the lists */

	if (search_lists(note, cur)) {
	    break;
	}
    }

    /* If we fell off the end, it's an unknown/error note */

    if (!cur->comparison[0]) {
	put_note(note, NULL, NULL, TRUE);
    }

    /* Go back home */

    return;
}

/* Use the search-ID to see if we can get a match for a list */

static int search_lists(FILE * note, struct rule_info * rule)
{
    struct list_info * cur;		/* Scan through the list tags */
    char * source;			/* ID text to scan into */
    char * t_text;			/* List scan text to use */
    char s_text[11];			/* Copy of the scan text */

    /* Get the search-ID */

    source = find_source(rule->action);
    if (!source) {
	return(FALSE);
    }

#ifdef DEBUG
    printf("search_lists: searching in %s\n", source);
#endif

    /* Now get an uppercase version of the text to search in */

    t_text = xmalloc(strlen(source)+1);
    strcpy(t_text, source);
    strupr(t_text);

    /* Test for presence of the list scan text */

    for (cur = lst.p; cur->scan_data[0]; cur++) {
	strcpy(s_text, cur->scan_data);
	strupr(s_text);
	if (strstr(t_text, s_text)) {	/* found it */
	    break;
	}
    }

    /* Lose the temporary copy of the header line */

    xfree(t_text);

    /* Did we find it? */

    if (!cur->scan_data[0]) {
	return(FALSE);
    }

    /* Yes, so stuff the note */

#ifdef DEBUG
    printf("search_lists: got match. Adding note\n");
#endif

    put_note(note, rule, cur, FALSE);

    return(TRUE);
}

/* Get a pointer to a source ID */

char * find_source(char * id)
{
    struct source_info * cur;		/* We need to scan through looking */

    for (cur = src.p; cur->id[0]; cur++) {
	if (!strcmp(id, cur->id)) {
	    break;
	}
    }

    if (!cur->id[0]) {			/* No ID found */
	return (NULL);
    } else if (!cur->header_text[0]) {	/* No text for ID */
	return (NULL);
    } else {
	return (cur->header_text);
    }
}

/* Fill out the source ID text sections */

void scan_header(FILE * note, struct source_info * p)
{
    struct source_info * cur;		/* Need to scan through */
    char line[256];			/* Place to stuff incoming info */
    int process_info;			/* Do we need to use this? */
    int end_of_header;			/* Found end of the header? */
    int temp_char_in;			/* Place to stuff fgetc()s */

    /* Do we use our own or get it from what was passed to us? */

    if (!p) {
	p = src.p;
    }

    /* Clear out the last info we scanned */

    for (cur = p; cur->id[0]; cur++) {
#ifdef DEBUG
	printf("scan_header: clearing text for >%s<\n", cur->id);
#endif
	memset(cur->header_text, 0, cur->reg_text_length+SLOP_LEN);
    }

    /* Wait until we get something that's non blank */

#ifdef DEBUG
    printf("scan_header: searching for beginning\n");
#endif

    do {
	if (!fgets_lcl(line, 255, note)) {
	    break;
	}
    } while (line[0] == ' ' || line[0] == '\n' || !line[0]);

#ifdef DEBUG
    printf("scan_header: processing line:\n");
    printf("   >%s<\n", line);
#endif

    /* Bail out if we've hit EOF */

    if (feof(note)) {
	return;
    }

#ifdef DEBUG
    printf("scan_header: Ready to start on the header\n");
#endif

    /* Start reading through the header */

    end_of_header = FALSE;

    while (!end_of_header) {

	/* Wade through the header pieces */

	strcpy(temp, line);
	strlwr(temp);

	for (cur = p; cur->id[0]; cur++) {
#ifdef DEBUG
	    printf("scan_header: comparing against >%s<\n", cur->scan_text);
#endif
	    if (hdr_item_found(temp, cur)) {
		break;
	    }
	}

	/* Did we get a match on anything? */

	process_info = (cur->id[0]);

	/* If we're working on something, start parsing it */

	if (process_info) {

#ifdef DEBUG
	    printf("scan_header: got header for %s\n", cur->scan_text);
#endif

	    /* First wipe out anything previous, for multiple tags */

	    memset(cur->header_text, 0, cur->reg_text_length+SLOP_LEN);

	    /* Skip past the Header tag (To:, etc.) */

	    strcpy(line, line+strlen(cur->scan_text));

	    /* And tack on the line */

	    scrunch(line);
	    build_header_line(cur, line);
	}

	/* Now let's finish out the tag */

	while (TRUE) {

	    /* Get the first character of the next line */

	    temp_char_in = fgetc(note);

	    if (temp_char_in == EOF) {
		return; 		/* Run away if we ran out of file */
	    }
	    line[0] = (char) temp_char_in;

	    /* This determines what we need to do next */

	    if (line[0] == ' ' || line[0] == '\t') {
		fgets_lcl(line, 255, note);
		if (feof(note)) {
		    return;
		}
		if (!line[0]) { 	/* Unless it's a blank line */
		    end_of_header = TRUE;   /* THAT means that we're done */
		    break;
		}
		if (process_info) {	/* We're using this, so use it */
		    scrunch(line);
		    strcat(cur->header_text, " ");  /* Space between lines */
		    build_header_line(cur, line);
		}
	    } else if (line[0] == '\n') {   /* Completely blank, means EOH */
		end_of_header = TRUE;
		break;
	    } else {			/* Otherwise, just stick it back out */
		ungetc(line[0], note);
		break;
	    }
	}

	/* If we're not done, read the next piece of the header */

	if (!end_of_header) {
	    fgets_lcl(line, 255, note);
	    if (feof(note)) {
		return;
	    }
	}
    }
    return;
}

/* Glue the line onto what we currently have */

static void build_header_line(struct source_info * cur, char * line)
{
    /* Do we already have sufficient text? */

    if (strlen(cur->header_text) == cur->reg_text_length) {
	return;
    }

    /* Just tack it onto the end */

    strncat(cur->header_text, line, cur->reg_text_length -
	    strlen(cur->header_text));
}

/* Do we have a match on this scan text? */

static int hdr_item_found(char * line, struct source_info * cur)
{
    return (!strncmp(line, cur->scan_text, strlen(cur->scan_text)));
}

/* Give me information about a digest format */

int digest_find(char dig_type, int * len, int * error)
{
    struct digest_def * cur;		/* What we're looking at */

    /* Is there anything there? */

    if (!dig.p) {
	return (FALSE);
    }

    for (cur = dig.p; cur->digest_id; cur++) {
	if (cur->digest_id != dig_type) {
	    continue;
	}
	*len = cur->split_len;
	*error = cur->split_error;
	return (TRUE);
    }
    return (FALSE);
}

/* Do we ignore messages from this user/node combination */

int ignore_msgs(char * n, char * u)
{
    struct ignore_list * cur;		/* Skip through */
    struct dsc$descriptor_s pat;	/* Pattern to use */
    struct dsc$descriptor_s cand_n;	/* Candidate for node */
    struct dsc$descriptor_s cand_u;	/* Candidate for user */

    /* Are there any users to ignore? */

    if (!ign.p) {
	return (FALSE);
    }

    /* Build some descriptors */

    str_desc(&cand_n, n);
    str_desc(&cand_u, u);

    /* Let's look through the list and see if we get a match */

    for (cur = ign.p; cur->node[0] || cur->user[0]; cur++) {

	str_desc(&pat, cur->node);
	if (str$match_wild(&cand_n, &pat) == STR$_NOMATCH) {
	    continue;
	}

	str_desc(&pat, cur->user);
	if (str$match_wild(&cand_u, &pat) == STR$_NOMATCH) {
	    continue;
	}

	return (TRUE);
    }
    return (FALSE);
}

/* Is this user allowed to tell us real commands? */

int valid_manager(char * n, char * u)
{
    struct manager_list * cur;

    /* If you're not local, we're not listening */

    if (strcmp(n, host)) {
	return (FALSE);
    }

    /* If you're not in the list, we're not interested */

    for (cur = man.p; cur->user[0]; cur++) {
	if (!strcmp(u, cur->user)) {
	    return (TRUE);
	}
    }

    return (FALSE);
}

/* Is the passed link one of the ones that must be down? */

int mandatory_link(char * l)
{
    struct link_info * cur;

    /* If there aren't any, there aren't any */

    if (!lnk.p) {
	return(FALSE);
    }

    /* Scan through to see if it's on the mandatory list */

    for (cur = lnk.p; cur->link[0]; cur++) {
	if (!strcmp(cur->link, l)) {
	    return (TRUE);
	}
    }
    return (FALSE);
}

#if defined(DEBUG) && (DEBUG & 1)
void dump_control_file(void)
{
    struct list_info * c_lst;
    struct source_info * c_src;
    struct rule_info * c_rul;
    struct digest_def * c_dig;
    struct manager_list * c_man;
    struct link_info * c_lnk;
    struct ignore_list * c_ign;

    FILE * out;

    sprintf(temp, "%sRULES.OUT", scratch_dir.dsc$a_pointer);

    out = fopen(temp, "w", "ctx=rec", "rat=cr", "rfm=var");

    fprintf(out, "Lists: %d\n", lst.count);
    for (c_lst = lst.p; c_lst->scan_data[0]; c_lst++) {
	fprintf(out, ">%s< >%s< >%s< %c %c %s\n", c_lst->scan_data,
		c_lst->conf_hdr, c_lst->conf_user, (c_lst->flag_digest ? 'Y' :
		'N'), c_lst->new_topic_interval, c_lst->conf_name);
    }

    fprintf(out, "\nSources: %d\n", src.count);
    for (c_src = src.p; c_src->id[0]; c_src++) {
	fprintf(out, "%s >%s< %d\n", c_src->id, c_src->scan_text,
		c_src->reg_text_length);
    }

    fprintf(out, "\nRules: %d\n", rul.count);
    for (c_rul = rul.p; c_rul->comparison[0]; c_rul++) {
	fprintf(out, "%s %s %s %s %s %s\n", c_rul->ids[0], c_rul->comparison,
		c_rul->ids[1], c_rul->action, c_rul->id_from, c_rul->id_subj);
    }

    fprintf(out, "\nDigests: %d\n", dig.count);
    if (!dig.p) {
	fputs("None\n", out);
    } else {
	for (c_dig = dig.p; c_dig->digest_id; c_dig++) {
	    fprintf(out, "%c %d/%d\n", c_dig->digest_id, c_dig->split_len,
		    c_dig->split_error);
	}
    }

    fprintf(out, "\nManagers: %d\n", man.count);
    for (c_man = man.p; c_man->user[0]; c_man++) {
	fprintf(out, "%s\n", c_man->user);
    }

    fprintf(out, "\nLinks: %d\n", lnk.count);
    if (!lnk.p) {
	fputs("None\n", out);
    } else {
	for (c_lnk = lnk.p; c_lnk->link[0]; c_lnk++) {
	    fprintf(out, "%s\n", c_lnk->link);
	}
    }

    fprintf(out, "\nIgnores: %d\n", ign.count);
    if (!ign.p) {
	fputs("None\n", out);
    } else {
	for (c_ign = ign.p; c_ign->node[0]; c_ign++) {
	    fprintf(out, "%s@%s\n", c_ign->user, c_ign->node);
	}
    }

    fclose(out);
}
#endif
