/*
 * This file is part of the FEddi package
 *
 * Personal use allowed under the terms of the
 *
 *              GNU GENERAL PUBLIC LICENSE Version 2
 *              (see LICENSE for the complete text)
 *
 *-------------------------------------------------------------------
 *
 *    ENTER AT YOUR OWN RISK !!
 *
 * This source is without any documentation and can drive you mad.
 * In case of sudden epileptic seizures please call your doctor.
 *
 */

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <math.h>
#include <glob.h>
#include "regex.h"
#include "structs.h"
#include "proc.h"
#include "nlfunct.h"

#include "crc16.h"

#define NIL -1

struct
{
	int P, p;
	int Z, z;
	int R, r;
	int H, h;
	int U, u;
	int V, v;
	int O, o;
	int D, d;
	int N, n;
} Statistic={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

struct Dial
{
	char *from, *to;
} *dial=NULL;

Addr4dType *valid=NULL, *invalid=NULL;

int maxdial=0, maxvalid=0, maxinvalid=0;

char *FirstCmd=NULL, *LastCmd=NULL;

typedef struct
{
	char name[40];
	int Day, Year;
} moreLists;

typedef struct
{
	char name[40], newname[40], orig[40];
	int action;           /* 0==new      1==diff */
	int type;             /* 0==Nodelist 1==Pointlist */
	Addr4dType addr;      /* Base Address for Pointlists */
	int Day, Year;
	int numfound, oldlist;
	moreLists *found;
} Lists;

Lists *lists=NULL;

typedef struct
{
	char name[80];
	int Day, Year;
} oldLists;

oldLists *oldlists=NULL;

int maxlists=0, maxoldlists=0;

static char _VALIDNLLINE[100]="([^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[0-9]+(,.*)?)|[bB][oO][sS][sS],[0-9:/]";
struct re_pattern_buffer VALIDLINERE;
int ISVALID=0;

char _UNPUBLISHED[12]="unpublished";
char _DAYNUMBER[14]="-- day number";

int NLlines=0, change=0, NLignore=0;

StrIndexType *StrIndex=NULL;
char **StrTable=NULL;
int *StrPosTable=NULL;
int sie=0, stm=0;

void readlistfile();
int testlist(char *);
void writelistfile(int);

int processrc();
int adddialentry(char *);
int addnodelist(char *);
int addpointlist(char *);
int addpointlists(char *, int *);
int addvalid(char *, int);

int processlists();
int testaktuality(int);
int difflist(char *, char *, char *);

int builduserlist();
int incorppoint(FILE *, FILE *, Addr4dType, char *);
int incorpnode(FILE *, FILE *, char *);

int makeindex();
void searchkey(KeyEntryType, int, int *, KeyEntryType *);
void searchstr(StrEntryType, int, int *, StrEntryType *);
int hashseed();

int getstrpart(char *, short, char *);
void changephone(char *);
void swapname(char *);

void searchlists(int);
void dayoflist(char *, int *, int *);
int sortlists(const void *, const void *);

int validline(char *line, int num);
int validadr(char *);

void nllookup(char *s);

int main(int argc, char **argv)
{
	int i, off=0, sys;
	strcpy(ProgName,"fnlc");
	while (argc-off>1)
	{
		if (strcmp(argv[off+1],"--config")==0 || strcmp(argv[off+1],"-c")==0)
		{
			strcpy(ConfigName,argv[off+2]);
			off+=2;
			continue;
		}
		if (strcmp(argv[off+1],"--quiet")==0 || strcmp(argv[off+1],"-q")==0)
		{
			QUIET=1;
			off++;
			continue;
		}
		if (strcmp(argv[off+1],"--force")==0 || strcmp(argv[off+1],"-f")==0)
		{
			change=1;
			off++;
			continue;
		}
		break;
	}
	if (argc-off<1 /* || argc-off>2 */)
	{
		printERR("Illegal number of params!\n");
		exit(1);
	}
	if (!loadrc(0))
		return EXIT_FAILURE;
	if (argc-off>1)
	{
		Log("*FEddi nodelist-lookup");
		QUIET=1;
		for (i=1; i<argc-off; i++)
			nllookup(argv[off+i]);
		Log("*Leaving lookup\n");
		exit(0);
	}
	Log("*FEddi Nodelistcompiler");
	readlistfile();
	memset(&VALIDLINERE,0,sizeof(struct re_pattern_buffer));
	re_set_syntax(RE_SYNTAX_POSIX_EGREP);
	if (re_compile_pattern(_VALIDNLLINE,strlen(_VALIDNLLINE),&VALIDLINERE))
	{
		ISVALID=1;
	}
	if (!(i=processrc()))
	{
		if (FirstCmd)
		{
			printERR("Exec dofirst cmd...");
			Log("+Exec dofirst");
			sys=system(FirstCmd);
			printERR(" system returns %u\n",sys);
			Log("%cExec returns %u",(sys?'!':'-'),sys);
		}
		if (!(i=processlists()))
			if (!(i=builduserlist()))
				i=makeindex();
		if (LastCmd)
		{
			printERR("Exec dolast cmd...");
			Log("+Exec dolast");
			sys=system(LastCmd);
			printERR(" system returns %u\n",sys);
			Log("%cExec returns %u",(sys?'!':'-'),sys);
		}
	}
	writelistfile(i==1);
	Log("*Leaving Nodelist\n");
	free(dial);
	free(lists);
	free(oldlists);
	if (i==1) return 1;
	printERR("\n\
Nodelist Statistics:\n\
\n\
              included  excluded\n\
    Zone       %6u    %6u\n\
    Region     %6u    %6u\n\
    Host       %6u    %6u\n\
    Hub        %6u    %6u\n\
    Pvt        %6u    %6u\n\
    Hold       %6u    %6u\n\
    Down       %6u    %6u\n\
    Node       %6u    %6u\n\
    Point      %6u    %6u\n\
\n\
    Total      %6u    %6u  =  %6u\n\
\n",
	Statistic.Z,Statistic.z,
	Statistic.R,Statistic.r,
	Statistic.H,Statistic.h,
	Statistic.U,Statistic.u,
	Statistic.V,Statistic.v,
	Statistic.O,Statistic.o,
	Statistic.D,Statistic.d,
	Statistic.N,Statistic.n,
	Statistic.P,Statistic.p,
	NLlines,NLignore,NLlines+NLignore);
	return (i?100:0);
}

void readlistfile()
{
	FILE *f;
	char name[PATH_MAX], *d1, *d2;
	sprintf(name,"%sfnlc.Lists",NodelistPath);
	if ((f=fopen(name,"rt"))!=NULL)
	{
		while (fgets(name,80,f)!=NULL)
		{
			d1=strchr(name,'\n');
			*d1=0;
			maxoldlists++;
			oldlists=(oldLists *)realloc(oldlists,sizeof(oldLists)*maxoldlists);
			d1=strchr(name,32);
			if (d1)
			{
				*d1++=0;
				d2=strchr(d1,32);
				*d2++=0;
				oldlists[maxoldlists-1].Day=atoi(d1);
				oldlists[maxoldlists-1].Year=atoi(d2);
			} else
				oldlists[maxoldlists-1].Day=
				oldlists[maxoldlists-1].Year=-1;
			strcpy(oldlists[maxoldlists-1].name,name);
		}
		fclose(f);
	}
}

int testlist(char *list)
{
	int i;
	for (i=0; i<maxoldlists; i++)
		if (strcasecmp(list,oldlists[i].name)==0)
			return 0;
	Log("-%s is new",list);
	printERR("%s is new\n",list);
	return 1;
}

void writelistfile(int error)
{
	FILE *f;
	char name[PATH_MAX];
	int i;
	if (error)
	{
		sprintf(name,"rm -f %sfnlc.Lists",NodelistPath);
		system(name);
	} else
	{
		sprintf(name,"%sfnlc.Lists",NodelistPath);
		if ((f=fopen(name,"wt"))!=NULL)
		{
			for (i=0; i<maxlists; i++)
				if (lists[i].Day!=-1 && lists[i].Year!=-1)
					fprintf(f,"%s %u %u\n",lists[i].orig,lists[i].Day,lists[i].Year);
				else
					fprintf(f,"%s\n",lists[i].orig);
			fclose(f);
			chmod(name,FIDO_MOD);
			chown(name,FIDO_UID,FIDO_GID);
		}
	}
}

int processrc()
{
	FILE *rcf;
	char name[PATH_MAX], *d1;
	int line;
	printERR("Processing fnlcrc:\n");
	Log("+Processing fnlcrc");
	sprintf(name,"%s%s",NodelistPath,"fnlcrc");
	if ((rcf=fopen(name,"rt"))!=NULL)
	{
		for (line=1; fgets(name,99,rcf)!=NULL; line++)
		{
		  for (d1=name; *d1; d1++) if (*d1==9) *d1=32;
		  d1=strchr(name, 0xa);
		  if (d1) *d1=0;
		  if (strncasecmp(name,"nodelist ",9)==0)
		  	if (addnodelist(name))
		  		goto lineerr;
		  	else
		  		change|=testlist(lists[maxlists-1].orig);
		  if (strncasecmp(name,"pointlist ",10)==0)
		  	if (addpointlist(name))
		  		goto lineerr;
		  	else
		  		change|=testlist(lists[maxlists-1].orig);
		  if (strncasecmp(name,"pointlists ",11)==0)
		  	if (addpointlists(name,&change))
		  		goto lineerr;
		  if (strncasecmp(name,"dial ",5)==0)
		  	if (adddialentry(name))
		  		goto lineerr;
		  if (strncasecmp(name,"valid ",6)==0)
		  	if (addvalid(name+5,1))
		  		goto lineerr;
		  if (strncasecmp(name,"invalid ",8)==0)
		  	if (addvalid(name+7,0))
		  		goto lineerr;
		  if (strncasecmp(name,"dofirst ",8)==0)
		  	if (!FirstCmd)
		  		FirstCmd=strdup(name+8);
		  	else
		  		goto lineerr;
		  if (strncasecmp(name,"dolast ",7)==0)
		  	if (!LastCmd)
		  		LastCmd=strdup(name+7);
		  	else
		  		goto lineerr;
		}		
	} else
	{
		Log("!Can't open %sfnlcrc",NodelistPath);
		printERR("Can't open %sfnlcrc!\n\n",NodelistPath);
		return 1;
	}
	fclose(rcf);
	return 0;
	lineerr:
	fclose(rcf);
	printERR("%s\n",name);
	Log("!Error in fnlcrc - Line %u",line);
	printERR("Error in fnlcrc - Line %u!\n\n",line);
	return 1;
}

int adddialentry(char *l)
{
	char *d1, *d2, line[100];
	strcpy(line,l);
	maxdial++;
	dial=(struct Dial *)realloc(dial,sizeof(struct Dial)*maxdial);
	for (d1=strchr(line,32); *d1==32; d1++);
	d2=strchr(d1,32);
	if (d2)
	{
		*d2++=0;
		while (*d2==32) d2++;
	} else
		return 1;
	if (*d1=='*')
		dial[maxdial-1].from=NULL;
	else
	{
		dial[maxdial-1].from=(char *)malloc(strlen(d1)+1);
		strcpy(dial[maxdial-1].from,d1);
	}
	if (*d2=='*')
		dial[maxdial-1].to=NULL;
	else
	{
		dial[maxdial-1].to=(char *)malloc(strlen(d2)+1);
		strcpy(dial[maxdial-1].to,d2);
	}
	return 0;
}

int addnodelist(char *l)
{
	char *d1, *d2, line[100];
	int i;
	strcpy(line,l);
	for (d1=strchr(line,32); *d1==32; d1++);
	d2=strchr(d1,32);
	if (d2)
	{
		*d2++=0;
		while (*d2==32) d2++;
	}	else
		for (d2=d1; *d2; d2++);
	lowerstr(d1);
	for (i=0; i<maxlists; i++)
		if (strcmp(lists[i].name,d1)==0)
			return 2;
	maxlists++;
	lists=(Lists *)realloc(lists,sizeof(Lists)*maxlists);
	memset(&(lists[maxlists-1]),0,sizeof(Lists));
	strcpy(lists[maxlists-1].name,d1);
	strcpy(lists[maxlists-1].orig,d1);
	d1=strchr(d2,32);
	if (d1)
	{
		*d1++=0;
		while (*d1==32) d1++;
	} else
		for (d1=d2; *d1; d1++);
	if (strcasecmp(d2,"diff")==0)
		lists[maxlists-1].action=1;
	else
	{
		if (strcasecmp(d2,"new")==0 || *d2==0)
			lists[maxlists-1].action=0;
		else
			return 1;
	}
	d2=strchr(d1,32);
	if (d2)
	{
		*d2++=0;
		while (*d2==32) d2++;
	}
	if (*d1==0)
		strcpy(lists[maxlists-1].newname,lists[maxlists-1].name);
	else
	{
		lowerstr(d1);
		strcpy(lists[maxlists-1].newname,d1);
	}
	lists[maxlists-1].type=0;
	return 0;
}

int addpointlist(char *l)
{
	char *d1, *d2, line[100], newline[200];
	int err;
	strcpy(line,l);
	if ((d1=strchr(line,'@')))
	{
		*d1++=0;
		strcpy(newline,line);
		if ((d2=strchr(d1,32)))
		{
			strcat(newline,d2);
			*d2=0;
		}
	} else
		strcpy(newline,line);
	if ((err=addnodelist(newline)))
		return err;
	else
		if (d1)
		{
			if (Str2Addr(d1,&(lists[maxlists-1].addr)))
			{
				printERR("Wrong address in line\n%s\n",l);
				Log("!Wrong address in fnlcrc");
				exit(EXIT_FAILURE);
			}
		}
	lists[maxlists-1].type=1;
	return 0;
}

int sortchar(const void *s1, const void *s2)
{
	return (strcasecmp(*(char **)s1,*(char **)s2));
}

int addpointlists(char *l, int *change)
{
	char *d1, *d2, *d3, pl[PATH_MAX], line[100];
	char **names;
	int i, num;
	glob_t pg;
	memset(&pg,0,sizeof(glob_t));
	strcpy(line,l);
	for (d1=strchr(line,32); *d1==32; d1++);
	if ((d2=strchr(d1,32)))
		*d2++=0;
	else
	{
		printERR("Can't parse pointlists line!\n");
		Log("!fnlcrc - can't parse pointlists line");
		return 1;
	}
	lowerstr(d1);
	sprintf(pl,"%s%s",InboundPath,d1);
	glob(pl,GLOB_APPEND,NULL,&pg);
	sprintf(pl,"%s%s",NodelistPath,d1);
	glob(pl,GLOB_APPEND,NULL,&pg);
	names=(char **)malloc(pg.gl_pathc*sizeof(char *));
	for (i=0; i<pg.gl_pathc; i++)
	{
		if ((d3=strrchr(pg.gl_pathv[i],'/')))
			d3++;
		else
			d3=pg.gl_pathv[i];
		names[i]=strdup(d3);
	}
	qsort(names,pg.gl_pathc,sizeof(char *),sortchar);
	while (*d2==32) d2++;
	if ((d1=strchr(d2,32))) *d1++=0;
	pl[0]=32;
	for (i=0; i<pg.gl_pathc; i++)
	{
		if (sscanf(names[i],d2,&num)==1)
		{
			sprintf(pl+1,d1,num,num);
			if ((num=addpointlist(pl)))
			{
				if (num==1)
				{
					printERR("Can't add %s\n",pl);
					Log("!fnlcrc - found list %s but can't add it",pl);
				} else
				{
					printERR("Skipped double %s\n",pl);
					Log("!fnlcrc - skipped double %s",pl);
				}
			} else
				*change|=testlist(lists[maxlists-1].orig);
		}
		free(names[i]);
	}
	free(names);
	globfree(&pg);
	return 0;
}

int addvalid(char *address, int val)
{
	char *d1, *d2;
	for (d1=address; d1; d1=d2)
	{
		while (*d1==32) d1++;
		if (val)
		{
			valid=(Addr4dType *)realloc(valid,Addr4dSize*(maxvalid+1));
			if ((d2=strchr(d1,32))) d2++;
			if (strtoadr(d1,&valid[maxvalid])) return 1;
			maxvalid++;
		} else
		{
			invalid=(Addr4dType *)realloc(invalid,Addr4dSize*(maxinvalid+1));
			if ((d2=strchr(d1,32))) d2++;
			if (strtoadr(d1,&invalid[maxinvalid])) return 1;
			maxinvalid++;
		}
	}
	return 0;
}

int processlists()
{
	int i,j;
	char list[PATH_MAX], newlist[PATH_MAX], name[PATH_MAX], *d1;
	printERR("Processing Nodelists:\n");
	Log("+Processing Nodelists");
	for (i=0; i<maxlists; i++)
	{
		searchlists(i);
		change|=testaktuality(i);
		if (lists[i].oldlist && KeepNl && lists[i].numfound)
		{
			sprintf(name,"gzip -c %s%s >%s%s.gz",
					NodelistPath,lists[i].name,CopyPath,lists[i].name);
			system(name);
		}
		for (j=0; j<lists[i].numfound; j++)
		{
			if (lists[i].action==0 || (lists[i].action==1 &&
					strncasecmp(lists[i].found[j].name,lists[i].orig,
					strlen(lists[i].orig))==0))
			{
				if (lists[i].action==1 &&
						(lists[i].Year>lists[i].found[j].Year ||
						(lists[i].Year==lists[i].found[j].Year &&
						lists[i].Day>=lists[i].found[j].Day)))
				{
					Log("!%s is older",lists[i].found[j].name);
					printERR("%s is older\n",lists[i].found[j].name);
					sprintf(name,"mv -f %s%s %s%s.reject",
							InboundPath,lists[i].found[j].name,
							CopyPath,lists[i].found[j].name);
					system(name);
					continue;
				}
				Log("-Copying new list %s",lists[i].found[j].name);
				printERR("Copying new list %s\n",lists[i].found[j].name);
				strcpy(list,lists[i].found[j].name);
				lowerstr(list);
				sprintf(name,"rm -f %s%s ; mv %s%s %s%s",NodelistPath,lists[i].name,
						InboundPath,lists[i].found[j].name,NodelistPath,list);
				if (!system(name))
				{
					sprintf(name,"%s%s",NodelistPath,list);
					chmod(name,FIDO_MOD);
					chown(name,FIDO_UID,FIDO_GID);
					strcpy(lists[i].name,list);
					change=1;
					writelistfile(1);
				} else
				{
					sprintf(name,"mv -f %s%s %s%s.reject",
							InboundPath,lists[i].found[j].name,
							CopyPath,lists[i].found[j].name);
					system(name);
				}
			} else
			{
				Log("-Diffing %s with %s",lists[i].name,
						lists[i].found[j].name);
				printERR("Diffing %s with %s\n",lists[i].name,
						lists[i].found[j].name);
				sprintf(list,"%s%s",NodelistPath,lists[i].name);
				sprintf(newlist,"%s%s",InboundPath,lists[i].found[j].name);
				strcpy(name,lists[i].name);
				d1=strrchr(name,'.');
				if (d1) *d1=0;
				d1=strrchr(lists[i].found[j].name,'.');
				strcat(name,d1);
				if (!difflist(list,newlist,name))
				{
					strcpy(lists[i].name,name);
					sprintf(name,"%s%s",NodelistPath,lists[i].name);
					chmod(name,FIDO_MOD);
					chown(name,FIDO_UID,FIDO_GID);
					change=1;
					writelistfile(1);
				} else
				{
					sprintf(name,"mv -f %s%s %s%s.reject",
							InboundPath,lists[i].found[j].name,
							CopyPath,lists[i].found[j].name);
					system(name);
				}
			}
		}
		dayoflist(lists[i].name,&(lists[i].Day),&(lists[i].Year));
	}
	return 0;
}

int testaktuality(int l)
{
	int i;
	for (i=0; i<maxoldlists; i++)
		if (strncasecmp(lists[l].name,oldlists[i].name,
				strlen(oldlists[i].name))==0)
			if (oldlists[i].Day==-1 && oldlists[i].Year==-1)
				return 0;
			else
				if (oldlists[i].Day!=lists[l].Day || oldlists[i].Year!=lists[i].Year)
				{
					Log("-%s is out of date",lists[l].name);
					printERR("%s is out of date\n",lists[l].name);
					return 1;
				} else
					return 0;
	return 1;
}

int difflist(char *list, char *diff, char *newl)
{
	FILE *nldf, *onlf, *nnlf;
	char oline[256], nline[256], name[PATH_MAX], *d1;
	word first=1, CRC=0, CRCo=0; 
	int c1, c2, lines=0;
	if ((nldf=fopen(diff,"rt"))==NULL)
	{
		Log("!Can't open %s",diff);
		printERR("Can't open %s!\n\n",diff);
		return 1;
	}
	if ((onlf=fopen(list,"rt"))==NULL)
	{
		Log("!Can't open %s",list);
		printERR("Can't open %s!\n\n",list);
		fclose(nldf);
		return 1;
	}
	fgets(nline,255,nldf);
	fgets(oline,255,onlf);
	fseek(onlf,0,SEEK_SET);
	if (strcmp(nline,oline)!=0)
	{
		Log("!Wrong Diff");
		printERR("Wrong Diff!\n\n");
		fclose(nldf);
		fclose(onlf);
		return 1;
	}
	sprintf(name,"%s%s",NodelistPath,newl);
	if ((nnlf=fopen(name,"wt"))==NULL)
	{
		Log("!Can't create %s",name);
		printERR("Can't create %s!\n\n",name);
		fclose(nldf);
		fclose(onlf);
		return 1;
	}
	while (fgets(nline,255,nldf)!=NULL)
	{
		if ((d1=strchr(nline,0xd))) *d1=0;
		c1=atoi(nline+1);
		switch(nline[0])
		{
			case ';': continue;
			case 'A':
		  	for (c2=0; c2<c1; c2++)
		    {
		      fgets(nline,255,nldf);
	  	    fputs(nline,nnlf);
	    	  if (!first)
						CRC=CRC16s(nline,CRC);
	      	else
					{
		  			first=0;
					  d1=strrchr(nline,':');
		  			if (d1)
				    {
				      d1++;
		    		  while (*d1==32) d1++;
				      CRCo=atoi(d1);
				    }
					}
	    	  first=0;
	    	 	printERR("\rDiff-add [%u]",lines++);
		    }
			  break;
			case 'C':
			  for (c2=0; c2<c1; c2++)
	  	  {
	    	  fgets(nline,255,onlf);
	      	fputs(nline,nnlf);
		      CRC=CRC16s(nline,CRC);
	    	 	printERR("\rDiff-copy[%u]",lines++);
		    }
	  		break;
			case 'D':
	  		for (c2=0; c2<c1; c2++)
	  		{
			    fgets(nline,255,onlf);
	    	 	printERR("\rDiff-del [%u]",lines);
	    	}
	  		break;
	  	default:
	  		Log("!Illegal diff line");
	  		printERR("Illegal diff line!\n");
	  		break;
		}
	}
	fclose(nnlf);
	fclose(onlf);
	fclose(nldf);
	printERR("\n");
	if (CRC!=CRCo)
	{
		Log("!CRC Error (old %u!=%u)",CRCo,CRC);
		printERR("CRC Error (old %u!=%u)!\n\n",CRCo,CRC);
	}
	remove(list);
	remove(diff);
	return 0;
}

int builduserlist()
{
	FILE *f, *uf;
	int x;
	char line[PATH_MAX], log[PATH_MAX];
	if (!change)
	{
		Log("+Userlist is up to date");
		printERR("Userlist is up to date!\n\n");
		return 2;
	}
	Log("+Building Userlist");
	sprintf(line,"rm -f %sfnlc.*",NodelistPath);
	system(line);
	printERR("Building Userlist:\n");
	sprintf(line,"%sfnlc.Userlist",NodelistPath);
	if ((uf=fopen(line,"wt"))==NULL)
	{
		Log("!Can't open %sfnlc.Userlist", NodelistPath);
		printERR("Can't open %sfnlc.Userlist!\n\n", NodelistPath);
		return 1;
	}
	chmod(line,FIDO_MOD);
	chown(line,FIDO_UID,FIDO_GID);
	for (x=0; x<maxlists; x++)
	{
		sprintf(line,"%s%s",NodelistPath,lists[x].name);
		if ((f=fopen(line,"rt"))==NULL)
		{
			Log("!Can't open %s",lists[x].name);
		  printERR("Can't open %s!\n",lists[x].name);
	  	continue;
		}
		Log("-Incoporating %s",lists[x].name);
		printERR("Incoporating %s",lists[x].name);
		sprintf(log,"Incoporating %s",lists[x].name);
		if (lists[x].type)
		{
			if (incorppoint(f,uf,lists[x].addr,log)) return 1;
		} else
			incorpnode(f,uf,log);
		fclose(f);
	}
	fclose(uf);
	return 0;
}

int incorppoint(FILE *f, FILE *uf, Addr4dType address, char *log)
{
	char addr[80], line[256], *d1, *d2, ad[40];
	Addr4dType baseaddr;
	int lines=0;
	memset(&baseaddr,0,Addr4dSize);
	sprintf(addr,"%u:%u/%u",address.Zone,address.Net,address.Node);
	while (fgets(line,255,f))
	{
		if ((d1=strchr(line,0xd))) *d1=0;
		if ((d1=strchr(line,0xa))) *d1=0;
		for (d1=line; *d1; d1++) if (*d1=='_') *d1=32;
		if (line[0]!=';' && line[0]!=26 && line[0]!=0)
		{
			if (validline(line,lines))
			{
				if (strncasecmp(line,"boss",4)==0)
				{
					if (address.Zone==0)
					{
						d1=strchr(line,',')+1;
						Str2Addr(d1,&baseaddr);
						sprintf(addr,"%u:%u/%u",
								baseaddr.Zone?baseaddr.Zone:address.Zone,
								baseaddr.Net?baseaddr.Net:address.Net,
								baseaddr.Node?baseaddr.Node:address.Node);
						Log("-Used BOSS %s",addr);
					} else
						Log("-Ignored BOSS");
					continue;
				}
				if (strncasecmp(line,"zone",4)==0)
				{
					d1=strchr(line,',')+1;
					baseaddr.Zone=atoi(d1);
					sprintf(addr,"%u:%u/%u",
							baseaddr.Zone?baseaddr.Zone:address.Zone,
							baseaddr.Net?baseaddr.Net:address.Net,
							baseaddr.Node?baseaddr.Node:address.Node);
					continue; 
				}
				if (strncasecmp(line,"region",6)==0)
				{
					d1=strchr(line,',')+1;
					baseaddr.Net=atoi(d1);
					sprintf(addr,"%u:%u/%u",
							baseaddr.Zone?baseaddr.Zone:address.Zone,
							baseaddr.Net?baseaddr.Net:address.Net,
							baseaddr.Node?baseaddr.Node:address.Node);
					continue;
				}
				if (strncasecmp(line,"host",4)==0)
				{
					d1=strchr(line,',')+1;
					d1=strchr(d1,',')+1;
					if ((d2=strchr(d1,'/'))!=NULL)
					{
						baseaddr.Net=atoi(d1);
						d1=d2+1;
					}
					baseaddr.Node=atoi(d1);
					sprintf(addr,"%u:%u/%u",
							baseaddr.Zone?baseaddr.Zone:address.Zone,
							baseaddr.Net?baseaddr.Net:address.Net,
							baseaddr.Node?baseaddr.Node:address.Node);
					continue;
				}
				if (addr[0]=='0')
				{
					Log("!Don't have valid address (%s)",addr);
					printERR("\n%s is not a valid address!\n",addr);
					return 1;
				}
				changephone(line);
				swapname(line);
			  d1=strchr(line,',')+1;
			  sprintf(ad,"%s.%u",addr,atoi(d1));
			  if (validadr(ad))
			  {
				  fprintf(uf,"P%s.%s\n",addr,d1);
			  	NLlines++;
			  	lines++;
			  	Statistic.P++;
			  } else
			  {
			  	Statistic.p++;
			  	NLignore++;
			  }
			}
		  printERR("\r%s [%u]",log,lines);
		}
	}
	printERR("\r%s [%u lines]\n",log,lines);
	return 0;
}

int incorpnode(FILE *f, FILE *uf, char *log)
{
	char line[256], *d1, adr[40];
	char zone[5], host[5], region[5], number[5], r;
	int breg=0, bhost=0, lines=0;
	while (fgets(line,255,f))
	{
		if ((d1=strchr(line,0xd))) *d1=0;
		if ((d1=strchr(line,0xa))) *d1=0;
		for (d1=line; *d1; d1++) if (*d1=='_') *d1=32;
		if (line[0]!=';' && line[0]!=26 && line[0]!=0)
		{
			if (validline(line,lines))
			{
				changephone(line);
				swapname(line);
				d1=strchr(line,',')+1;
				d1=strchr(d1,',');
			  if (strncasecmp(line,"Zone",4)==0)
		    {
	  	    breg=0;
		      bhost=0;
		      getstrpart(line,2,zone);
		      sprintf(adr,"%s:%s/0",zone,zone);
		     	r='Z';
	  	  } else
			    if (strncasecmp(line,"Region",6)==0)
		      {
						breg=1;
						bhost=0;
						getstrpart(line,2,region);
						sprintf(adr,"%s:%s/0",zone,region);
						r='R';
		      } else
		  	    if (strncasecmp(line,"Host",4)==0)
						{
						  bhost=1;
						  breg=0;
						  getstrpart(line,2,host);
						  sprintf(adr,"%s:%s/0",zone,host);
						  r='H';
						} else
						{
						  if (strncasecmp(line,"Hub",3)==0)
						  	r='U';
		  				else
						    if (strncasecmp(line,"Pvt",3)==0)
						    	r='V';
		  				  else
						      if (strncasecmp(line,"Hold",4)==0)
						      	r='O';
						      else 
										if (strncasecmp(line,"Down",4)==0)
											r='D';
										else
											r='N';
						  getstrpart(line,2,number);
						  sprintf(adr,"%s:%s/%s",
						  		zone,
						  		(breg && !bhost)?region:(bhost?host:zone),number);
						}
				if (validadr(adr))
				{
			    fprintf(uf,"%c%s%s\n",r,adr,d1);
			    NLlines++;
			    lines++;
					switch (r)
					{
						case 'Z': Statistic.Z++; break;
						case 'R': Statistic.R++; break;
						case 'H': Statistic.H++; break;
						case 'U': Statistic.U++; break;
						case 'V': Statistic.V++; break;
						case 'O': Statistic.O++; break;
						case 'D': Statistic.D++; break;
						case 'N': Statistic.N++; break;
					}
				} else
				{
					NLignore++;
					switch (r)
					{
						case 'Z': Statistic.z++; break;
						case 'R': Statistic.r++; break;
						case 'H': Statistic.h++; break;
						case 'U': Statistic.u++; break;
						case 'V': Statistic.v++; break;
						case 'O': Statistic.o++; break;
						case 'D': Statistic.d++; break;
						case 'N': Statistic.n++; break;
					}
				}
			}
		  printERR("\r%s [%u]",log,lines);
		}
	}
	printERR("\r%s [%u lines]\n",log,lines);
	return 0;
}

int makeindex()
{
	FILE *f, *fi;
	char line[PATH_MAX], file[PATH_MAX], Name[80], *d1;
	int h, q, stm, sroot=NIL, i, j, prim, pos, l;
	StrEntryType y, v;
	int *KeyIndex;
	Log("+Generating Indexfiles");
	printERR("Generating Indexfiles:\n");
	KeyIndex=NULL;
	StrIndex=NULL;
	StrPosTable=NULL;
	sie=stm=0;
	sprintf(file,"%s%s",NodelistPath,"fnlc.Userlist");
	if ((prim=hashseed())==-1)
	{
		Log("!Can't find fitting prim");
		printERR("Can't find fitting prim!\n\n");
		return 1;
	}
	KeyIndex=(int *)malloc(prim*sizeof(int));
	for (i=0; i<prim; i++) KeyIndex[i]=-1;
	if ((f=fopen(file,"rt"))!=NULL)
	{
		for (pos=ftell(f), l=1; fgets(line,255,f)!=NULL; pos=ftell(f), l++)
		{
			printERR("\rNodeindex [%u/%u]",l,NLlines);
	  	d1=strchr(line,',');
		  *d1++=0;
		  j=(getkey(&line[1])%prim)+1;
		  q=(getkey(&line[1])%79)+1;
		  if (KeyIndex[j-1]==-1)
		  	KeyIndex[j-1]=pos;
		  else
		  {
		  	do j=((j+q)%prim)+1; while (KeyIndex[j-1]!=-1);
		  	KeyIndex[j-1]=pos;
		  }
		}
		printERR("\n");
		sprintf(file,"%s%s",NodelistPath,"fnlc.Nodeindex");
		if ((fi=fopen(file,"wb"))!=NULL)
		{
		  fwrite(KeyIndex, sizeof(int), prim, fi);
		  fclose(fi);
		  chmod(file,FIDO_MOD);
		  chown(file,FIDO_UID,FIDO_GID);
		} else
		{
			Log("!Can't write fnlc.Nodeindex");
			printERR("Can't write fnlc.Nodeindex!\n\n");
			return 1;
		}
		Log("-Nodeindex complete");
		free(KeyIndex);
		StrTable=(char **)malloc(NLlines*sizeof(char *));
		memset(StrTable,0,NLlines*sizeof(char *));
		StrPosTable=(int *)malloc(NLlines*sizeof(int));
		memset(StrPosTable,0xff,NLlines*sizeof(int));
		fseek(f,0,SEEK_SET);
		for (pos=ftell(f), l=1; fgets(line,255,f)!=NULL; pos=ftell(f), l++)
		{
			printERR("\rNameindex [%u/%u]",l,NLlines);
		  getstrpart(line,4,Name);
	  	y.pos=stm;
		  stm++;
	  	StrTable[stm-1]=(char *)malloc(strlen(Name)+1);
		  strcpy(StrTable[stm-1],Name);
		  StrPosTable[stm-1]=pos;
			y.next=NIL;
		  searchstr(y,sroot,&h,&v);
			if (h)
	  	{
	      q=sroot;
	      sroot=sie;
	      sie++;
	      StrIndex=(StrIndexType *)realloc(StrIndex,StrIndexSize*sie);
	      StrIndex[sroot].num=1;
	      StrIndex[sroot].next0=q;
	      StrIndex[sroot].entry[0]=v;
	    }
		}
		printERR("\n");
		sprintf(file,"%s%s",NodelistPath,"fnlc.Nameindex");
		if ((fi=fopen(file,"wb"))!=NULL)
		{
	  	for (i=0; i<sie; i++)
		    for (j=0; j<StrIndex[i].num; j++)
		      StrIndex[i].entry[j].pos=StrPosTable[StrIndex[i].entry[j].pos];
	  	fwrite(&sroot,sizeof(int),1,fi);
		  fwrite(StrIndex,StrIndexSize,sie,fi);
		  fclose(fi);
		  chmod(file,FIDO_MOD);
		  chown(file,FIDO_UID,FIDO_GID);
		} else
		{
			Log("!Can't write fnlc.Nameindex");
			printERR("Can't write fnlc.Nameindex!\n\n");
			return 1;
		}
		Log("-Nameindex complete");
		for (i=0; i<NLlines; i++) free(StrTable[i]);
		free(StrTable);
		free(StrPosTable);
		free(StrIndex);
	} else
	{
		Log("!Can't open fnlc.Userlist");
		printERR("Can't open fnlc.Userlist!\n\n");
		return 1;
	}
	return 0;
}

int hashseed()
{
	byte *prim;
	int i,v;
	prim=(byte *)malloc(sizeof(byte)*rint(NLlines*1.5));
	memset(prim,1,rint(NLlines*1.5));
	prim[0]=prim[1]=0;
	for (i=0; i<rint(sqrt(NLlines*1.5)); i++)
		if (prim[i])
		{
			v=i*i;
			while (v<rint(NLlines*1.5))
			{
				prim[v]=0;
				v=v+i;
			}
		}
	for (i=rint(NLlines*1.2); i<rint(NLlines*1.5); i++)
		if (prim[i]) break;
	free(prim);
	if (i==rint(NLlines*1.5)) return -1;
	return i;
}

void searchstr(StrEntryType x, int a, int *h, StrEntryType *v)
{
	int i, j, q;
	StrEntryType u;
	if (a==NIL)
	{
		*h=1;
		*v=x;
	} else
	{
		for (i=0; i<StrIndex[a].num &&\
	  strcmp(StrTable[StrIndex[a].entry[i].pos],StrTable[x.pos])<1; i++);
		if (i==0)
			q=StrIndex[a].next0;
		else
			q=StrIndex[a].entry[i-1].next;
		searchstr(x,q,h,&u);
		if (*h) /* INSERT */
		{
		  if (StrIndex[a].num<4)
	    {
	      if (i<StrIndex[a].num)
					for (j=StrIndex[a].num; j>i; j--)
					  StrIndex[a].entry[j]=StrIndex[a].entry[j-1];
	      StrIndex[a].entry[i]=u;
	      StrIndex[a].num++;
	      *h=0;
		  } else
	    {
	      sie++;
	      StrIndex=(StrIndexType *)realloc(StrIndex,sie*StrIndexSize);
	      if (i<=2)
				{
				  if (i==2)
				    *v=u;
				  else
			    {
		  	    *v=StrIndex[a].entry[1];
		    	  if (i==0) StrIndex[a].entry[1]=StrIndex[a].entry[0];
		      	StrIndex[a].entry[i]=u;
			    }
				  for (j=0; j<2; j++)
		  		  StrIndex[sie-1].entry[j]=StrIndex[a].entry[j+2];
	      } else
				{
				  *v=StrIndex[a].entry[2];
				  if (i==4)
				    StrIndex[sie-1].entry[0]=StrIndex[a].entry[3];
				  else
				    StrIndex[sie-1].entry[1]=StrIndex[a].entry[3];
				  StrIndex[sie-1].entry[i-3]=u;
				}
	      StrIndex[a].num=StrIndex[sie-1].num=2;
	      StrIndex[sie-1].next0=v->next;
	      v->next=sie-1;
	    }
		}
	}
} 

void changephone(char *line)
{
	char pnumber[30], l[256], *d2, *d3, newnumber[30];
	int i=5;
	getstrpart(line,6,pnumber);
	if (strcasestr(pnumber,_UNPUBLISHED)==NULL)
	{
		strcpy(l,line);
		for (d2=l; i--;)
			d2=strchr(d2, ',')+1;
		d3 = strchr(d2, ',');
		*d2=0;
		for (i=0; i<maxdial; i++)
			if (dial[i].from==NULL ||
					strncmp(pnumber,dial[i].from,strlen(dial[i].from))==0)
		  {
	  	  strcpy(newnumber, dial[i].to?dial[i].to:"");
	    	strcat(newnumber,
				   dial[i].from?&pnumber[strlen(dial[i].from)]:pnumber);
		    break;
		  }
			if (i!=maxdial)
			{
			  strcpy(line,l);
	  		strcat(line,newnumber);
			  strcat(line,d3);
			}
	}	
}

int getstrpart(char *line, short count, char *result)
{
	char l[256], *d1, *d2;
	strcpy(l,line);
	d1=l;
	while (--count)
	{
		if (!(d1=strchr(d1,','))) break;
		d1++;
	}
	if (!d1) return 1;
	d2=strchr(d1,',');
	*d2=0;
	strcpy(result,d1);
	return 0;
}

void swapname(char *line)
{
	char l[100], *d1, *d2;
	int i;
	d1=line;
	for (i=4; i--;)
		d1=strchr(d1,',')+1;
	d2 = strchr(d1,',');
	*d2=0;
	strcpy(l,d1);
	*d1=0;
	if ((d1=strrchr(l,32)))
	{
		*d1++=0;
		strcat(line,d1);
		strcat(line," ");
	}
	strcat(line,l);
	*d2=',';
}

void searchlists(int l)
{
	char file[PATH_MAX];
	DIR *dir;
	struct dirent *ent;
	chdir(InboundPath);
	if ((dir=opendir(InboundPath))!=NULL)
	{
		while ((ent=readdir(dir))!=NULL)
			if (strncasecmp(ent->d_name,lists[l].newname,
					strlen(lists[l].newname))==0 ||
					strncasecmp(ent->d_name,lists[l].name,
					strlen(lists[l].name))==0)
				unpack(0,ent->d_name,1);
		closedir(dir);
		sync();
		dir=opendir(InboundPath);
		while ((ent=readdir(dir))!=NULL)
			if (strncasecmp(ent->d_name,lists[l].newname,
					strlen(lists[l].newname))==0 ||
					strncasecmp(ent->d_name,lists[l].name,
					strlen(lists[l].name))==0)
			{
				lists[l].numfound++;
				lists[l].found=(moreLists *)realloc(lists[l].found,
						lists[l].numfound*sizeof(moreLists));
				strcpy(lists[l].found[lists[l].numfound-1].name,ent->d_name);
				sprintf(file,"%s%s",InboundPath,ent->d_name);
				dayoflist(file,&(lists[l].found[lists[l].numfound-1].Day),
						&(lists[l].found[lists[l].numfound-1].Year));
			}
		closedir(dir);
	}
	if ((dir=opendir(NodelistPath))!=NULL)
	{
		while ((ent=readdir(dir))!=NULL)
			if (strncasecmp(ent->d_name,lists[l].name,
					strlen(lists[l].name))==0)
			{
				strcpy(lists[l].name,ent->d_name);
				sprintf(file,"%s%s",NodelistPath,ent->d_name);
				dayoflist(file,&(lists[l].Day),&(lists[l].Year));
				lists[l].oldlist=1;
				break;
			}
		closedir(dir);
	}
	qsort(lists[l].found,lists[l].numfound,sizeof(moreLists),sortlists);
}

void dayoflist(char *name, int *d, int *y)
{
	FILE *f;
	char line[PATH_MAX], *d1, *d2;
	*d=*y=-1;
	if ((f=fopen(name,"rt"))!=NULL)
	{
		if (fgets(line,PATH_MAX-1,f))
		{
			d1=strcasestr(line,_DAYNUMBER);
			if (d1)
			{
				for (d2=d1; *d2!=','; d2--);
				*y=atoi(d2+1);
				*d=atoi(d1+14);
			}
		}
		fclose(f);
	}
}

int sortlists(const void *e1, const void *e2)
{
	const moreLists *ee1, *ee2;
	ee1=(moreLists *)e1;
	ee2=(moreLists *)e2;
	if (ee1->Year<ee2->Year) return -1;
	if (ee1->Year==ee2->Year && ee1->Day<ee2->Day) return -1;
	if (ee1->Year==ee2->Year && ee1->Day==ee2->Day) return 0;
  return 1;
}

void spezExt(char *s)
{
}

int validline(char *line, int num)
{
	struct re_registers regs;
	regs.start=NULL;
	regs.end=NULL;
	regs.num_regs=0;
	if (ISVALID)
		return 1;
	if (re_match(&VALIDLINERE,line,strlen(line),0,&regs)>=1)
	{
		free(regs.start);
		free(regs.end);
		return 1;
	}
	printERR("\nIgnoring invalid line [%u]:\n%s\n",num,line);
	Log("!Ignoring invalid line [%u]",num);
	return 0;
}

int addrmatch(Addr4dType a, Addr4dType b)
{
	int cmp=0;
	cmp=a.Zone==b.Zone;
	if (a.Net!=-1)
		cmp&=(a.Net==b.Net);
	if (a.Node!=-1)
		cmp&=(a.Node==b.Node);
	if (a.Point!=-1)
		cmp&=(a.Node==b.Point);
	return cmp;
}

int validadr(char *ad)
{
	Addr4dType adr;
	int i, ok=0;
	if (strtoadr(ad,&adr)) return 0;
	if (maxvalid)
	{
		for (i=0; i<maxvalid; i++)
			if (addrmatch(valid[i],adr)) ok=1;
	} else
		ok=1;
	for (i=0; i<maxinvalid; i++)
		if (addrmatch(invalid[i],adr)) ok=0;
	return ok;
}

static char NodeType[9][7]={"Zone","Region","Host","Hub","Pvt","Hold","Down",
"Node","Point"};

void printentry(NLentryType *nlent)
{
	char *d1;
	fprintf(stdout,"##NODELIST_ENTRY##\n");
	fprintf(stdout,"#Type: %s\n",NodeType[nlent->Type]);
	fprintf(stdout,"#Address: %u:%u/%u.%u\n",
			nlent->ad.Zone,nlent->ad.Net,nlent->ad.Node,nlent->ad.Point);
	fprintf(stdout,"#Box: %s\n",nlent->Box);
	if ((d1=strchr(nlent->Sysop,32)))
	{
		*d1++=0;
		fprintf(stdout,"#Sysop: %s %s\n",d1,nlent->Sysop);
	} else
		fprintf(stdout,"#Sysop: %s\n",nlent->Sysop);
	fprintf(stdout,"#Phone: %s\n",nlent->Phone);
	fprintf(stdout,"#Speed: %u\n",nlent->Speed);
	fprintf(stdout,"#Flags: %s\n",nlent->Flags);
}

void nllookup(char *s)
{
	Addr4dType addr;
	NLentryType nlent;
	char addrs[80];
	int i;
	Aka=Profile.Aka;
	fprintf(stdout,"###NODELIST_LOOKUP###[%s]\n",s);
	if (Str2Addr(s,&addr))
	{
		nameentry(s,&nlent,0);
		for (i=0; i<maxEntries; i++)
			printentry(&(Entries[i]));
	} else
	{
		sprintf(addrs,"%u:%u/%u.%u",addr.Zone,addr.Net,addr.Node,addr.Point);
		if (!keyentry(addrs,&nlent))
			printentry(&nlent);
	}
	fprintf(stdout,"###NODELIST_END###[%u]\n",maxEntries);
}
