#include <fido.h>
#include <ascii.h>
#include <xfbuf.h>

/* This program compiles the nodelist. */

int nodefile;		/* .NMP */
int ntcfile;		/* .NTC */
int ndatfile;		/* .BBS (in) */
int idxfile;		/* .IDX */
int ndxfile;		/* .NDX */
int zdxfile;		/* .ZDX */
int bbsfile;		/* .BBS out */

int doscode;		/* program result code (0 == OK) */

struct _nmap nmap;			/* .NMP file structure */
struct _ndat ndat;			/* .BBS file intermediate structure */
struct _ntc ntc;			/* .NTC file not used here */
struct _idx index;			/* .IDX file structure */

struct _n {				/* in-memory table of nodes/passwords */
	struct _node node;		/* target node, */
	char pwd[9];			/* password, */
	FLAG success;			/* was-found flag */
};
struct _n *s;				/* table pointer */
unsigned max;				/* max size of table/number of entries */

char *membuf;				/* output buffer */
unsigned bufsize;			/* output buffer size */

struct _node id;			/* satisfy link */
struct _node altid;			/* satisfy link */
int nodetotal;				/* satisfy link */
struct _fido fido;			/* satisfy link */
int dial_tries,connect_tries;		/* satisfy link */

int sysfile;				/* during initialization only */

main(argc,argv)
int argc;
char **argv;
{
char *p,fn[SS],sw[SS];
int i;

	printf("MAKELIST (21 Jun 91) NodeList Compiler for ");
	copr();

	allmem();				/* get all available */
	if (! fastfile(8)) {			/* init the buffered file system */
		printf(" * Cant start the file system\r\n");
		exit(1);
	}
	doscode= 0;				/* no error (yet) */
	newdelim(" \t;,\r\n");			/* arg delimiter list, */
	i= open("fido.sys",0);			/* load system stuff */
	if (i == -1) {
		printf("The Fido system file \"FIDO.SYS\" is missing!\r\n");
		exit(1);
	}
	read(i,&fido,sizeof(fido));
	close(i);
	if ((fido.fido_version != FIDOVER) || (fido.file_version != FILEVER)) {
		cprintf(" * The Fido system file \"FIDO.SYS\" doesn't match this \"FIDO.EXE\"\r\n");
		cprintf(" * program; make sure you have the latest versions.\r\n\r\n");
		cprintf(" * FIDO.SYS is from Fido/FidoNet version %d%c\r\n",fido.fido_version,fido.file_version + 96);
		cprintf(" * This program is  Fido/FidoNet version %d%c\r\n",FIDOVER,96+FILEVER);
		cprintf(" * You must run \"SET-FIDO\" to change Fido/FidoNet program versions\r\n");
		exit(1);
	}
	if (fido.event != -1) {
		printf("\r\n\r\n * A FidoNet event was left incomplete; you must run Fido\r\n");
		printf("and terminate the event, or use \"FIDO/C\", before you can run \"MAKELIST\".\r\n");
		exit(1);
	}
	id.zone= fido.zone;			/* node ID */
	id.net= fido.net;
	id.number= fido.number;
	id.point= fido.point;

	altid.zone= fido.altzone;		/* alternate ID */
	altid.net= fido.altnet;
	altid.number= fido.altnumber;
	altid.point= fido.altpoint;

	bufsize= sizmem();			/* get a bunch of memory, */
	membuf= getmem(bufsize);		/* allocate the table */

	load_pwds();				/* load password table */
	if (bufsize <= 256) {
		printf(" * NOT ENOUGH MEMORY?!\r\n");
		exit(1);
	}
	init_node();				/* build nodelist */
	exit(doscode);
}

/* Build the in-memory table of nodes to process. */

load_pwds() {
int io;
char *cp,ln[SS];
int n;

	s= (struct _n *) membuf;		/* define start of buffer */
	max= 0;					/* how much is in it */

	makenname(ln,"nodelist.pwd");
	io= open(ln,0);				/* open it, */
	if (io == -1) return;			/* nothing to do */

	printf(" * Reading session password list NODELIST.PWD\r\n");
	max= (bufsize + sizeof(struct _n) - 1) / sizeof(struct _n); /* max == # elements */
	n= 0;					/* table empty */
	while (rline(io,ln,sizeof(ln))) {
		if (*cp == ';') continue;	/* ignore comments */
		cp= skip_delim(ln);		/* skip leading garbage, */
		if (! *cp) continue;		/* skip blank lines */

		if (n >= max) {
			printf(" * Too many entries to process at once!\r\n");
			break;
		}
		cpy_node(&s[n].node,&id);	/* set default zone/net */
		set_nn(cp,&s[n].node);		/* parse node, */
		s[n].success= 0;		/* not processed yet */

		cp= next_arg(cp);		/* point to password */
		cp[8]= NUL;			/* clip to 8 chars */
		if (! *cp) {
			printf(" * For %s: no password specified?\r\n",str_node(&s[n].node));
			continue;
		}
		strcpy(&s[n].pwd,cp);		/* add password to table */
		++n;				/* anoth entry */
	}
	close(io);				/* done with it */
	max= n;					/* max == max. nodes in list */

	bufsize -= max * sizeof(struct _n);	/* this much was used */
	membuf= (char *) &s[max];		/* it starts here */
	printf(" * %d nodes to receive session passwords\r\n",max);
}

/* Create the node list, in a digestible form. This just builds a
fixed structure version of the ASCII list, and leaves it laying
around on the disk.

CAUTION: This builds NODELIST.IDX and NODELIST.NMP at the same
time, and in parallel. IDX is the index to the nodelist, and contains
the net/node numbers for the nodelist. They must be kept in sync, record
by record! */

init_node() {
int zonecnt,regcnt,netcnt,nodecnt; /* tally for display only */
int host_num;			/* 0 if "host", else -1 for old "region" */
char c,arg[SS],nextarg[SS];
char *ln;			/* pointer to line "buffer" */
char *cp;
struct _rptrcd *r;
int i;
int o;				/* output buffer count */
int n;				/* offset to start of .BBS data */
unsigned recd;			/* current record number */
unsigned ndx_recd;		/* record number of NDX file */
long pos;			/* current .BBS file position */

FLAG watch_it;			/* to detect IFNA nodelist fuckups (missing REGION statements) */
long watch_pos;			/* generate missing REGION */
struct _rptrcd rptrcd;		/* dummy repeat record */

	printf(" * Creating the node list index and routing table\r\n");
/*	printf(" * NOTE: The file \"NODELIST.SYS\" is no longer used\r\n");
*/
	makenname(arg,"nodelist.bbs");
	stoupper(arg);
	ndatfile= open(arg,0);			/* open source file, */
	if (ndatfile == -1) {
		stoupper(arg);
		printf(" * Cannot find \"%s\"\r\n",arg);
		doscode= 2;
		return;
	}
	bbsfile= -1;				/* assume no output file */
	if (max > 0) {				/* if we have passwords to add */
		makenname(arg,"nodelist.$b$");
		stoupper(arg);
		bbsfile= creat(arg,2);		/* .BBS output file */
		if (bbsfile == -1) {
			printf(" * Cannot create \"%s\", is your \"node_path\" (in FIDO.INI) OK?\r\n",arg);
			doscode= 2;
			return;
		}
	}
	makenname(arg,"nodelist.idx");
	stoupper(arg);
	idxfile= creat(arg,2);			/* the index */
	if (idxfile == -1) {
		printf(" * Cannot create \"%s\", is your \"node_path\" (in FIDO.INI) OK?\r\n",arg);
		doscode= 2;
		return;				/* if not there skip it */
	}

	makenname(arg,"nodelist.zdx");		/* zone index */
	zdxfile= creat(arg,2);

	makenname(arg,"nodelist.ndx");		/* net/region index */
	ndxfile= creat(arg,2);

	makenname(arg,"nodelist.nmp");
	nodefile= creat(arg,2);			/* the node map */

	makenname(arg,"nodelist.ntc");		/* we dont use this, but */
	ntcfile= creat(arg,2);			/* put_, get_, etc check it */

	makenname(arg,"nodemap.?");
	killall(arg);				/* kill preserved nodemaps */

	if (node_files()) {			/* if any not open, */
		cprintf(" * Cannot create one of the NODELIST.* files\r\n");
		close_node();
		doscode= 2;
		return;
	}

/* First .NMP record; this contains the revision marker and the REPEAT 
record used to potentially re-use a previously created routing table. Because
of the funny way it is stored we have to write it in the last two elements. */

	for (cp= (char *) &rptrcd, i= sizeof(struct _rptrcd); i--;) 
		*cp++= 255;				/* set to all -1's */

	rptrcd.bits= 0;					/* except these */
	*rptrcd.mdmstr= NUL;
	*rptrcd.dial_pref= NUL;
	rptrcd.revision= NODEVERS;			/* set revision, */
	write(nodefile,&rptrcd,sizeof(struct _rptrcd));
	
/* The first .IDX record; place keeper and index. */

	ndx_recd= 0;				/* record number */
	recd= 1;				/* next will be real data */
	pos= 0L;				/* .BBS ($B$) file position */

	index.zone= index.net= index.number= -1;
	index.pos= pos;				/* first record position */
	write(idxfile,&index,sizeof(index));	/* write index */

/* The watch_it flag is used to detect missing REGION or NET definitions after
a ZONE definition. IFNA is leaving the old region 1 nodes right after the
ZONE statement, which is meant to leave them in REGION 1. We gotta fix
this and add the missing REGION. */

	watch_it= 0;

	cpy_node(&nmap.node,&id);		/* default to "us" */
	nmap.bits= 0;				/* we dont set these here */
	index.zone= id.zone;			/* index too */
	index.net= id.net;
	index.number= id.number;

	zonecnt= regcnt= nodecnt= netcnt= 0;	/* count of zones, nodes and nets */

/* Load one line/record from NODELIST.BBS into the buffer; if there is not
enough room (assume 256 chars needed) flush it first. */

	o= 0;						/* buffer is empty */
	while (doscode == 0) {
		if (bbsfile == -1) o= 0;		/* not outputting */

		else if (bufsize - o < 256) {		/* if it needs flushing */
			fw(bbsfile,membuf,o);
			o= 0;
		}
		cp= ln= &membuf[o];			/* start of current buffer */
		n= 0;					/* bytes read so far */
		while (read(ndatfile,cp,1)) {		/* one char at a time */
			++n;
			if (*cp == CR) continue;
			if (*cp == LF) break;
			if (n > 255) break;		/* max. line length */
			++cp;				/* room for the next */
		}
		*cp= NUL;				/* terminate the line */
		if (! n) break;				/* end of file */
		if (*ln == NUL) continue;		/* blank line */
		if (*ln == ';') continue;		/* skip comments */

/* Now we need to strip off any existing session password. Lines should 
look like:

HOST 102 26 9600 SoCalNet 1-213-874-9484 Los_Angeles_CA
135 26 9600 Manhattan_Transfer 1-213-372-4800 Manhattan_Beach
13 26 9600 Manhattan_Transfer 1-213-372-4800 Manhattan_Beach  PASSWORD

Each record is basically six fields, possibly preceded by a keyword, possibly
followed by a password. We need to strip off the password, plus any trailing 
delimiters. */

		if (bbsfile != -1) {
			cp= skip_delim(ln);		/* skip over the basic fields */
			n= isdigit(*cp) ? 6 : 7;	/* 6 or 7 fields */
			while (n--) cp= next_arg(cp);
			if (*cp) {			/* if a password, */
				while (*(cp - 1) == ' ') --cp;	/* truncate it */
				*cp= NUL;		/* and any delims */
			}
		}

/* Now we need to process the keyword. All that Fido/FidoNet wants to see 
at runtime is the info starting with cost field. The offset in the .IDX record
then is made to point to the cost field; we skip the leading junk here (so it
doesn't have to be done at runtime). */

		cp= skip_delim(ln);		/* if the first item is not */
		if (!isdigit(*cp)) cp= next_arg(cp); /* a number, skip it */
		cp= next_arg(cp);		/* skip the node (net, etc) number */
		n= cp - ln;			/* n == add'l offset */
		cp= ln;				/* cp == line to process */

		if (isdigit(*cp)) {

/* We have a regular node definition. The watch_it flag will be clear if we
had a REGION or HOST definition; if not this node doesnt belong to a REGION
(which is mathematically OK but the IFNA nodelist assumes that they are in 
region 1 in the current zone). */

			if (watch_it) {
				nmap.node.number= 0;		/* is region */
				nmap.node.net= watch_it;	/* IFNA region kludge */

				cpyi(&index,&nmap.node);	/* duplicate it */
				index.pos= watch_pos;		/* current file position */
				fw(idxfile,&index,sizeof(index)); /* write index */

				index.pos= 0L + recd;		/* net/region index entry */
				fw(ndxfile,&index,sizeof(index));

				nmap.route_recd= recd;		/* route-to self */
				nmap.attr= NMAP_REGION;		/* not a host */
				put_node(recd);			/* node route data */
				++recd;
				++regcnt; ++nodecnt;

				watch_it= 0;
			}
			nmap.node.number= make_node(ln);	/* a node */
			nmap.attr= 0;			/* not a region host */
			index.number= nmap.node.number;
			++nodecnt;

/* Well, maybe its a keyword. */

		} else {
			cpyatm(arg,cp);
			stolower(arg);		/* copy of the arg word, */
			cp= next_arg(cp);
			cpyatm(nextarg,cp);	/* and the next one */
			stolower(nextarg);
			nmap.attr= 0;		/* assume not a region host */
/*

ZONE zz 	zz:-1/00	Zone #zz
REGION nn	zz:nn/-1	current Zone, Net #nn, no host
HOST nn		zz:nn/00	current Zone, Net #nn, with host
mm		zz:nn/mm	current Zone, current Net, node #mm
HUB nn		zz:nn/mm	treat as node

*/
			if (same(arg,"hub")) {
				nmap.node.number= make_node(cp);/* set node number */
				cpyi(&index,&nmap.node);
				++nodecnt;

			} else if (same(arg,"host")) {
				nmap.node.net= make_node(cp);	/* set Net number */
				nmap.node.number= 0;		/* is a host */
				cpyi(&index,&nmap.node);
				++netcnt; ++nodecnt;
				watch_it= 0;

			} else if (same(arg,"region")) {
				nmap.node.net= make_node(cp);	/* set Net number */
				nmap.node.number= 0;		/* is a host */
				nmap.attr= NMAP_REGION;		/* flag as region host */
				cpyi(&index,&nmap.node);	/* duplicate, */
				index.number= 0;		/* except node 0 */
				++regcnt; ++nodecnt;
				watch_it= 0;

			} else if (same(arg,"zone")) {
				nmap.node.zone= make_node(cp);	/* set Zone number */
				nmap.node.net= 0;		/* always 0 */
				nmap.node.number= 0;		/* always 0 */
				cpyi(&index,&nmap.node);	/* full copy */
				++zonecnt; ++nodecnt;
				watch_it= nmap.node.zone;	/* check for missing REGION */
				watch_pos= pos + n;		/* save since we have to */

			} else continue;

			cpy_node(&ndat.node,&nmap.node);	/* for listing */
			listnode(&ndat);			/* list it */
		}

/* Now see if the node we just processed is in the list of nodes to get session
passwords; if so, append the password text to the data record. (Note that the
_idx structure doesnt really contain a _node structure, but the same_node() 
func works anyways.) */

		for (i= 0; i < max; i++) {		/* check the table, */
			if (same_node(&s[i].node,&index)) { /* if found, */
				strcat(ln," ");		/* add a space, */
				strcat(ln,s[i].pwd);	/* add the password */
				s[i].success= 1;	/* flag as successful */
				break;
			}
		}

/* Now write out the records we just created; the .IDX file gets the node
number plus the offset to this data record; the .NDX file gets only /0 records;
the .NMP file gets an un-filled nodemap entry; the .$B$ file (.BBS output) is
only written if bbsfile != -1, and in any case the data is collected in membuf
until it is nearly full. The ZDX file is an index into the NDX file; its record
numbers are relative to the NDX file. Hence the separate record number counter
for NDX records. */

		index.pos= pos + n;			/* .IDX file */
		fw(idxfile,&index,sizeof(index));	/* write index */

		nmap.route_recd= recd;			/* route-to self */
		put_node(recd);				/* .NMP record */

		if (is_zonehost(&index)) {		/* .ZDX zone index */
			index.pos= 0L + ndx_recd;
			fw(zdxfile,&index,sizeof(index));
		}
		if (is_host(&index)) {			/* .NDX net/region index */
			index.pos= 0L + recd;
			fw(ndxfile,&index,sizeof(index));
			++ndx_recd;			/* recd # in NDX file */
		}
		strcat(ln,"\r\n");			/* add the CR/LF terminator */
		i= strlen(ln);				/* length of this recd */
		pos += i;				/* current .$B$ position */
		o += i;					/* current buffer position */

		++recd;					/* logical record number */
	}
	printf(" * %d Zones, %d Regions, %d Nets, %d total Nodes           \r\n",
	    zonecnt,regcnt,netcnt,nodecnt);
	close_node();

/* If we were processing passwords, replace the original .BBS file with the
one we just created, that contains all the passwords. */

	if (bbsfile != -1) {
		if (o) fw(bbsfile,membuf,o);
		close(bbsfile);
		if (! doscode) {			/* if OK */
			makenname(membuf,"nodelist.$b$");/* our output file */
			makenname(arg,"nodelist.bbs");	/* our input file */
			delete(arg);			/* kill the old one */
			rename(membuf,arg);		/* rename */
		}
	}
	n= 0;
	for (i= 0; i < max; i++) {
		if (! s[i].success) {
			if (! n) printf("\r\n * Nodes not found in the nodelist (hence not passworded)\r\n");
			printf("   Node %s, password \"%s\"\r\n",str_node(&s[i].node),s[i].pwd);
			n= 1;
		}
	}
}

/* Do a disk write, return doscode set if write error. */

fw(f,b,n)
int f;
char *b;
unsigned n;
{
	if (write(f,b,n) != n) {
		printf("DISK FULL!!!!!!                       \r\n");
		doscode= 3;
	}
}

/* Fill the index structure's node address. */

cpyi(i,n)
struct _idx *i;
struct _node *n;
{
	i-> zone= n-> zone;
	i-> net= n-> net;
	i-> number= n-> number;
}

/* Parse a text string into a nodemap, and return the number found. 
Zone, Net and Number are left untouched. The string has the format:

num cost name phone city comments

*/

char *maken0();
char *maken1();

make_node(s)
char *s;
{
int n;

	s= maken0(&n,s);
	s= maken0(&ndat.cost,s);
	s= maken0(&ndat.rate,s);

	s= maken1(ndat.name,sizeof(ndat.name),s);
	s= maken1(ndat.phone,sizeof(ndat.phone),s);
	s= maken1(ndat.city,sizeof(ndat.city),s);

	return(n);
}
/* Make node function #0; pull a decimal number out, skip to the next
argument. */

char *maken0(i,sp)
int *i;
char *sp;
{
	*i= atoi(sp);
	return(next_arg(sp));
}

/* Make node function #1; copy one atom out, put it in the array, fixing
for maximum length and converting underscores to spaces, and returning
a pointer to the next atom. */

char *maken1(cp,n,sp)
char *cp;
int n;
char *sp;
{
char atom[SS];

	cpyatm(atom,sp);		/* one clean copy */
	atom[n - 1]= NUL;		/* truncate to maximum */
	maken3(cp,n,atom);		/* copy in, converting, */
	return(next_arg(sp));		/* next ... */
}
/* Copy a string, with a maximum length, converting underscodes to spaces. */

maken3(cp,n,sp)
char *sp;
int n;
char *cp;
{
	if (n) --n;			/* room for the null */
	while (*sp && --n) {
		if (*sp == '_') *cp= ' ';
		else *cp= *sp;
		++sp; ++cp;
	}
	*cp= NUL;
}

/* List node information. */

listnode(nd)
struct _ndat *nd;
{
struct _node d;
char buff[256];

	cpy_node(&d,&nd-> node);
	sprintf(buff," * %s %s, %s                                     ",
	    str_node(&d),nd-> name,nd-> city);
	buff[70]= NUL;
	printf("%s\r",buff);
}

/* Dummy */

clprintf() {}
lprintf() {}

/* Error printer */

error(s)
char *s;
{
	printf(" * %s\r\n",s);
}
/* Return true if the FBIT is set. */

fbit(bit)
int bit;
{
	return(fido.fbits[bit / sizeof(char)] & (1 << bit % sizeof(char)));
}

/* Delete all matching files in the upload path. */

static killall(s)
char *s;
{
int i;
char path[SS],file[SS];
struct _xfbuf xfbuf;

	i= 0;
	xfbuf.s_attrib= 0;
	strip_path(path,s);			/* save a copy of the path, */
	while (_find(s,i++,&xfbuf)) {		/* find each file */
		strcpy(file,path);		/* build the full filename */
		strcat(file,xfbuf.name);
		delete(file);			/* kill it */
	}
}
