/*
 * Refer to file UPDNODES.H for copyright and version information
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "updnodes.h"
#ifdef	oldcc
#include <memory.h>
#endif	/* oldcc */

#define ADD 1
#define DEL 2
#define REP 3

char deltaline[VALUE_STR_LEN];
char baseline[VALUE_STR_LEN];

NODE_ENTRY cur_base;			/* Current entry from base */
NODE_ENTRY cur_delta;			/* Current entry from delta */

int16u old_checksum;			/* Old totcks, we calculate */
int16u old_checksum_verify;		/* Old totcks, from base */
int16u new_checksum;			/* New running totcks */
int16u new_checksum_verify;		/* New totcks, from update file */

					/* New from delta will be in cur_base */

#ifndef	oldcc
static int get_action_type(NODE_ENTRY * cur);
static void get_major_tag(char * tag, char * s);
static int process_prologue(void);
static int have_act(char * s);
#else
static int get_action_type();
static void get_major_tag();
static int process_prologue();
static int have_act();
#endif	/* oldcc */

#ifndef	oldcc
void merge_net(void)
#else
void merge_net()
#endif	/* oldcc */
{
    TAG_ENTRY * t;
    int cur_action;
    int cmp_result;
    int rd_delta, rd_base, wr_base;
    int base_eof_found;
    int base_links_found;

    memset(deltaline, 0, 256);
    memset(baseline, 0, 256);
    old_checksum = new_checksum = 0;
    memset((char *) &cur_base, 0, sizeof(NODE_ENTRY));
    memset((char *) &cur_delta, 0, sizeof(NODE_ENTRY));

    if (!process_prologue()) {
	exit(SYSERROR);
    }

    if (feof(delta)) {
	error("Update file does not contain any known actions.\n");
	exit(SYSERROR);
    }

    read_base_line();
    get_major_tag(major_tag, baseline);

    get_base_tag(&cur_base);		/* First one is definitely VERSnnnn */

    t = min_find(&cur_base, "totcks");
    if (!t) {
	error("No :totcks. tag in base %s entry.\n", cur_base.name);
	exit(SYSERROR);
    }
    old_checksum_verify = atoi(t->value);

    while (TRUE) {
	get_delta_tag(&cur_delta);
	if (!strncmp(cur_delta.name, "VERS", 4)) {
	    if(strcmp(cur_delta.name, cur_base.name) && get_action_type(&cur_delta) > ADD) {
		error("Base file version is %s, delta is for %s.\n", cur_base.name, cur_delta.name);
		exit(SYSERROR);
	    }
	    apply_vers_delta(&cur_base, &cur_delta);
	} else break;
    }

    if (cur_base.name[0]) {
	write_node(&cur_base);
    } else {
	error("No resulting VERSnnnn entry.\n");
	exit(SYSERROR);
    }
    t = min_find(&cur_base, "totcks");
    if (!t) {
	error("No :totcks. tag in output %s entry.\n", cur_base.name);
	exit(SYSERROR);
    } else
	new_checksum_verify = atoi(t->value);

    get_base_tag(&cur_base);		/* This may or may not be LINKSnnn */
    base_links_found = 0;		/* Assume the worst */

    while (TRUE) {
        if (!strncmp(cur_delta.name, "LINKS", 5)) {
	    base_links_found++;
	    apply_links_delta(&cur_base, &cur_delta);
	    get_delta_tag(&cur_delta);
	} else break;
    }

    if (base_links_found != 0) {
	if (cur_base.name[0]) {
	    write_node(&cur_base);
	}
	get_base_tag(&cur_base);	/* Load the first one to get ready */
    }

    base_eof_found = rd_delta = rd_base = wr_base = FALSE;
    while (TRUE) {

	if (wr_base) {
	    write_node(&cur_base);
	    rd_base = TRUE;
	}
	if (rd_base) {
	    if (!get_base_tag(&cur_base)) {
		base_eof_found = TRUE;
	    }
	}
	if (rd_delta) {
	    if (!get_delta_tag(&cur_delta)) {
		break;
	    }
	}

	rd_delta = rd_base = wr_base = FALSE;

	cur_action = get_action_type(&cur_delta);
	if (!cur_action) {
	    error("Invalid action %s for %s.\n", cur_delta.action,
		    cur_delta.name);
	    rd_delta = TRUE;
	    continue;			/* Try again */
	}

	if (base_eof_found) {
	    cmp_result = 1;		/* Pretend we're past everything */
	} else {
	    cmp_result = estrcmp(cur_delta.name, cur_base.name);
	}

	if (cur_action == ADD) {
	    if (base_eof_found) {	    /* Special case. It figures */
		cmp_result = -1;
	    }
	    if (!cmp_result) {		    /* existing node? */
		error("Attempt to ADD existing node %s - ignored.\n",
			cur_delta.name);
		rd_delta = TRUE;
	    } else if (cmp_result > 0) {    /* early, keep looking */
		wr_base = TRUE;
	    } else {			    /* right on time. */
		log_line(LOG_UPDATE, "%s added.\n", cur_delta.name);
		write_node(&cur_delta);     /* Write the addition */
		rd_delta = TRUE;
	    }
	} else if (cur_action == DEL) {
	    if (cmp_result < 0) {	    /* Missed it. */
		error("Attempt to DEL nonexistent node %s - ignored.\n",
			cur_delta.name);
		rd_delta = TRUE;
	    } else if (cmp_result > 0) {    /* early, keep looking */
		wr_base = TRUE;
	    } else {			    /* Found it */
		log_line(LOG_UPDATE, "%s deleted.\n", cur_delta.name);
		rd_delta = rd_base = TRUE;
	    }
	} else if (cur_action == REP) {
	    if (cmp_result < 0) {	    /* Missed it. */
		error("Attempt to REP nonexistent node %s - ignored.\n",
			cur_delta.name);
		rd_delta = TRUE;
	    } else if (cmp_result > 0) {    /* early, keep looking */
		wr_base = TRUE;
	    } else {			    /* Found it */
		min_incorporate(&cur_base, &cur_delta);
		log_line(LOG_UPDATE, "%s updated.\n", cur_base.name);
		wr_base = rd_delta = TRUE;
	    }
	}
    }

    while (!base_eof_found) {
	write_node(&cur_base);
	if (!get_base_tag(&cur_base)) {
	    base_eof_found = TRUE;
	}
    }

    log_line(LOG_UPDATE, "Checking input :totcks. calculation...\n");
    if (old_checksum != old_checksum_verify) {
	error("Input :totcks. tag doesn't match calculated :totcks.\n");
	error("Verification: %u/%x, Calculated: %u/%x\n", old_checksum_verify,
		old_checksum_verify, old_checksum, old_checksum);
    } else {
	log_line(LOG_UPDATE, "Input :totcks. matches.\n");
    }

    log_line(LOG_UPDATE, "Checking output :totcks. calculation...\n");
    if (new_checksum != (int16u) new_checksum_verify) {
	error("Updated :totcks. tag doesn't match calculated output :totcks.\n");
	error("Verification: %u/%x, Calculated: %u/%x\n", new_checksum_verify,
		new_checksum_verify, new_checksum, new_checksum);
    } else {
	log_line(LOG_UPDATE, "Output :totcks. matches.\n");
    }
}

#ifndef	oldcc
static int get_action_type(NODE_ENTRY * cur)
#else
static int get_action_type(cur)
NODE_ENTRY * cur;
#endif	/* oldcc */
{
    if (!*cur->action)			return (0);
    if (!strcmp(cur->action, "ADD"))    return (ADD);
    if (!strcmp(cur->action, "DEL"))    return (DEL);
    if (!strcmp(cur->action, "REP"))    return (REP);
    return (0);
}

#ifndef	oldcc
static void get_major_tag(char * tag, char * s)
#else
static void get_major_tag(tag, s)
char * tag;
char * s;
#endif	/* oldcc */
{
    char * t;

    memset((char *) tag, 0, STD_STR_LEN);
    if (strlen(s) < 3 || s[0] != ':' || s[1] == '.' || s[1] == ' ') {
	error("No major-level tag in Nodes files.\n");
	exit(SYSERROR);
    }

    /* Now let's get the tag... */

    t = strchr(s, '.');
    if (!t) {
	t = strchr(s, ' ');
	if (!t) {
	    t = s + strlen(s);
	}
    }

    if (t - s > STD_STR_LEN) {
	error("Major-level tag too long.\n");
	exit(SYSERROR);
    }

    strncpy(tag, s+1, t - s - 1);
}

#ifndef	oldcc
static int process_prologue(void)
#else
static int process_prologue()
#endif	/* oldcc */
{
    char * t;

    while (!feof(delta) && !have_act(deltaline)) {
	if (strlen(deltaline) > 11 && !strncmp(deltaline, "UPDNODES(", 9)) {
	    strcpy(deltaline, deltaline+9);	/* Move it over */
	    for (t = deltaline; *t && (isdigit(*t) || *t == '.'); t++);
	    *t = '\0';

	    if (*deltaline && strcmp(deltaline, VERSION) > 0) {
		error("This version (%s) is not smart enough.\n", VERSION);
		error("You require at least version %s.\n", deltaline);
		return (FALSE);
	    }
	}
	read_delta_line();
    }
    return(TRUE);
}

#ifndef	oldcc
static int have_act(char * s)
#else
static int have_act(s)
char * s;
#endif	/* oldcc */
{
    if (strlen(s) < 3)			return (FALSE);
    if (!strncmp(s, "DEL", 3))          return (TRUE);
    if (!strncmp(s, "REP", 3))          return (TRUE);
    if (!strncmp(s, "ADD", 3))          return (TRUE);
    return (FALSE);
}
