#include "fidoterm.h"
#include "mem.h"
#include <ctype.h>

static nclose(int *);
static nopen(char *,unsigned);
/*

open_node()
	This opens the nodelist files, and leaves the global
handles set. If any one of the files is missing, all handles
are closed:

	NODELIST.BBS		system name, city, phone #
	NODELIST.IDX		index
	NODELIST.NDX		net index/index
	NODELIST.ZDX		zone index/index

close_node()
	Closes the files above, and mark them closed. (handle == -1)

makenname(dest,src)
	Create a full pathname using the node path.

index_node(&node)
	Return the record number of the specified node, or -1 if
not found. This does a logical compare, ie. negative net/node
numbers are normalized to 0 for comparison.

find_node(&node)
	Locate a node in the nodemap, return its record number or -1
if not found. This does a logical compare, as above. 

find_ndat(&node)
	Locate a node in the nodelist, return its record number or -1
if not found. This does a logical compare, as above. The node structure
in memory is filled in.

get_ndat(recd)
	Read the specified ndat record. Returns its index, or -1 if error.
This fills in the ndat structure from the ASCII .BBS file that contains 
the system information, and the node number from the .IDX index file.


NODES AND POINTS:

The functions below copy, compare, and otherwise process _node
structures. Comparisons uses only zone, net and number; point is
generally not used, since it's handling is limited to replying, entering
and packeting for points to pickup; there is no routing of points.


fix_node(&node)
	Fixes negative net and node numbers to 0.

cpy_node(&dest,&src)
	Copies one node into another, including point.

char *str_node(&node)
	Generates a static string expression of the node, and returns a 
pointer to it. Through a kludge it can be called up to 4 times without
overwriting a previous invokation. The point field will be expressed if
it is non-zero.

set_nn(string,&node)

	Parse the string into various zone:net/node numbers. This accepts
"zone:net/node" format. If zone or net isnt specified, it is left untouched.
This will parse points.

*/

/* Open all the NODELIST files; The indices are not buffered. */

open_node() {

	idxfile= nopen("nodelist.idx",0 + 0x100);/* we read this only */
	ndxfile= nopen("nodelist.ndx",0 + 0x100);/* we read this only */
	zdxfile= nopen("nodelist.zdx",0 + 0x100);/* we read this only */
	ndatfile= nopen("nodelist.bbs",0);	/* we read this only */

	if (node_files()) close_node();
}

/* Close the node file */

close_node() {

	nclose(&idxfile);
	nclose(&ndxfile);
	nclose(&zdxfile);
	nclose(&ndatfile);
}

/* Return true if any one of the nodelist files is not open. */

node_files() {

	return((idxfile == -1) || (ndxfile == -1) 
	    || (zdxfile == -1) || (ndatfile == -1));
}

/* Return the record number of this node, or -1 if not found. This works by 
searching through the three indices: the ZONE INDEX to locate the first node
in that zone; the net/region index to find the first record in that net; then
a sequential search to locate the specific node. This works with the nodelist 
in any order. 

Note that there is not a _node structure in the index file, but we treat it
as one; the elements (zone, net, number) are in the correct order, and point
is not used by the comparison functions; cpy_node will copy two bytes of what
follows the number, but we just fill that part in later. 

We use the same code for all three searches because the files are so similar.
The NDX and ZDX files contain record numbers in the position field; the IDX
file does not, and so we have to set the starting recd number from the NDX
file and increment as we search IDX.

Also there are some speedups: in the NDX file, we check for the correct
node itself; default host routing should increase the probability of hits.
(The ZDX record number is relative to the NDX file itself; the NDX record
number is relative to the IDX file and the rest of the database.) Also in 
the IDX search, since the list is heirarchical by definition, if we encounter 
another NET during the search, we can assume that the target node does not 
exist. */

index_node(n)
struct _node *n;
{
struct _node target;		/* logical node to search for */
int recd;			/* record number, from ZDX and NDX file */
struct _idx *ip,buff[10];	/* for file buffering */
unsigned ii,ic;
int f;				/* file we are currently searching */
char level;			/* file rotator */

	cpy_node(&target,n);				/* make a copy, */
	fix_node(&target);				/* normalize for compare */

	f= zdxfile;					/* start with ZONE index */
	lseek(zdxfile,0L,0);				/* seek to BOF */
	level= ii= ic= 0;				/* buffer empty, etc */

	while (1) {
		if (ii >= ic) {				/* load buffer */
			ii= 0;
			ic= (read(f,buff,sizeof(buff)) / sizeof(struct _idx));
			if (! ic) return(-1);		/* not found -- no such node */
			ip= (struct _idx *) buff;
		}
		switch (level) {
			case 0:				/* ZONE index */
				if (! same_zone((struct _node *)ip,&target)) break;
				recd= ip-> pos;		/* (cuz NODE search is linear) */
				f= ndxfile;		/* found ZONE host */
				goto start;		/* continue from next file */

			case 1:				/* NET index */
				if (! same_net((struct _node *)ip,&target)) break;
				recd= ip-> pos;		/* (cuz NODE search is linear) */
				if (same_node((struct _node *)ip,&target)) return(recd);
				f= idxfile;		/* found NET host */

start:				++level;		/* next search ... */
				ip-> pos *= sizeof(struct _idx);
				lseek(f,ip-> pos,0);	/* (long) compiler bug */
				ii= ic= 0;		/* buffer empty, etc */
				break;

			case 2:				/* NODE index */
				if (same_node((struct _node *)ip,&target)) return(recd);
				if (! same_net((struct _node *)ip,&target)) return(-1);
				++recd;			/* generate recd number */
				break;
		}
		++ip; ++ii;				/* next struct, next record */
	}
}
/* Return true if the two nodes are members of the same net. */

int same_zone(n1,n2)
struct _node *n1,*n2;
{
	return(n1-> zone == n2-> zone);
}

/* Return true if the two nodes are members of the same net. */

int same_net(n1,n2)
struct _node *n1,*n2;
{
	return(
		(n1-> net == n2-> net) &&
		(n1-> zone == n2-> zone)
	);
}

/* Return true if the two nodes are the same. */

int same_node(n1,n2)
struct _node *n1,*n2;
{
	return(
		(n1-> number == n2-> number) &&
		(n1-> net == n2-> net) &&
		(n1-> zone == n2-> zone)
	);
}

/* Parse a string into net and node number. zone and net are untouched if
not found in the input string. This parses as follows: (xx means unchanged)

string			output

99:88/77		99:88/77.0
88/77			xx:88/88.0
77			xx:xx/77.0
99:88			99:88/00.0
88/			xx:88/00.0
99:88/77.3		99:88/77.3

*/

void set_nn(cp,n)
char *cp;
struct _node *n;
{
int x;

	if (isdigit(*cp)) {
		n-> number= atoi(cp);		/* assume its node only */
		while (isdigit(*cp)) ++cp;	/* skip the number, */
	}
	if (*cp == ':') {			/* if it was a zone, */
		n-> zone= n-> number;		/* that was a zone number, */
		n-> net= atoi(++cp);		/* net must follow */
		while (isdigit(*cp)) ++cp;	/* skip the net number */
		if (*cp == '/') {		/* if that was a net number */
			n-> number= atoi(++cp);	/* set node number (or zero) */
			while (isdigit(*cp)) ++cp; /* skip node number */
		}

	} else if (*cp == '/') {		/* if that was a net number, */
		n-> net= n-> number;		/* copy it up, */
		n-> number= atoi(++cp);		/* now set node number */
		while (isdigit(*cp)) ++cp;	/* skip node number */
	}
}

/* Pull the descriptive info on a particular node from NODELIST.BBS. Return -1
if not found. */

find_ndat(n)
struct _node *n;
{
int recd;

	recd= index_node(n);		/* look for it, */
	if (recd == -1) return(-1);	/* error! */
	recd= get_ndat(recd);		/* get it if found, */
	return(recd);
}

/* Load a ndat record into memory; return the record number. This involves
loading the .IDX index record to get the node address and .BBS file offset
to find the actual data. 

Note that this treats the address within the index file as a _node struct;
since the elements are in the right order this works (except that it copies
2 bytes of garbage instead of the 'point' field). */

get_ndat(recd)
int recd;
{
long o;
struct _idx index;
char *cp,buff[512];
char *next_li();

	o= 0L + recd; o *= sizeof(struct _idx);		/* compiler (long) bug */

	lseek(idxfile,o,0);				/* load the .IDX */
	read(idxfile,&index,sizeof(struct _idx));	/* record, */

	lseek(ndatfile,index.pos,0);			/* seek in .BBS file */
	rline(ndatfile,buff,sizeof(buff));		/* load raw data */

	cpy_node(&ndat.node,(struct _node *)&index);	/* set node address, */

	ndat.node.point= 0;				/* clear copied garbage */
	cp= buff;
	ndat.cost= atoi(cp); cp= next_li(cp);		/* set the cost, */
	ndat.rate= atoi(cp); cp= next_li(cp);		/* baud rate, */
	cpynli(ndat.name,cp,24); cp= next_li(cp);	/* copy/process name, */
	cpynli(ndat.phone,cp,40); cp= next_li(cp);	/* phone, */
	cpynli(ndat.city,cp,40); cp= next_li(cp);	/* city */
	cpynli(ndat.pwd,cp,8); cp= next_li(cp);		/* password */
	return(recd);
}

/* Local routine to copy a nodelist.bbs field item, expanding _'s 
to spaces and stopping at the first delimiter. */

static cpynli(d,s,n)
char *d,*s;
{
	while (*s && (*s != ' ') && --n) {
		if (*s == '_') *d++= ' ';
		else *d++= *s;
		++s;
	}
	*d= NUL;
}

/* Skip to the next nodelist.bbs argument. */

static char *next_li(cp)
char *cp;
{
	while (*cp && (*cp != ' ')) ++cp;		/* skip non-blanks, */
	while (*cp == ' ') ++cp;			/* then spaces */
	return(cp);
}

/* Set negative node numbers to zero, their logical equivelant, 
for routing, etc. */

void fix_node(n)
struct _node *n;
{
	if (n-> net < 0) n-> net= 0;
	if (n-> number < 0) n-> number= 0;
}

/* Copy one node structure into another. IMPORTANT NOTE: Notice that the
structures are treated as char * pointers. */

void cpy_node(d,s)
struct _node *d,*s;
{
	d-> zone= s-> zone;
	d-> net= s-> net;
	d-> number= s-> number;
	d-> point= s-> point;
}

/* Open a file in the nodelist path. */

static nopen(fn,m)
char *fn;
unsigned m;
{
char buff[80];

	strcpy(buff,fido.nodepath);
	strcat(buff,fn);
	return(open(buff,m));
}

/* Close a file, mark it with -1. */

static nclose(h)
int *h;
{
	if (*h != -1) close(*h);
	*h= -1;
}




