/*****************************************************************************
 *
 * File ..................: mbindex.c
 * Purpose ...............: Nodelist Compiler
 * Last modification date : 08-Aug-2001
 *
 *****************************************************************************
 * Copyright (C) 1997-2001
 *   
 * Michiel Broek		FIDO:		2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../lib/libs.h"
#include "../lib/structs.h"
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/dbcfg.h"
#include "../lib/dbftn.h"


#define TMPNAME "TMP."
#define LCKNAME "LOCKFILE"


typedef struct _nl_list {
	struct _nl_list	*next;
	struct _nlidx	idx;
} nl_list;


#include "mbindex.h"


FILE		*ifp, *ufp, *ffp;
long		total = 0, entries = 0;
int		filenr = 0;
unsigned short	regio;
nl_list		*nll = NULL;
static 		char lockfile[81];


extern		int do_quiet;		/* Quiet flag			    */
extern		int show_log;		/* Show logging on screen	    */
time_t		t_start;		/* Start time			    */
time_t		t_end;			/* End time			    */


/*
 *  Put a lock on this program.
 */
int lockindex(void)
{
	char	Tmpfile[81];
	FILE	*fp;
	pid_t	oldpid;

	sprintf(Tmpfile, "%s/", CFG.nodelists);
	strcpy(lockfile, Tmpfile);
	sprintf(Tmpfile + strlen(Tmpfile), "%s%u", TMPNAME, getpid());
	sprintf(lockfile + strlen(lockfile), "%s", LCKNAME);

	if ((fp = fopen(Tmpfile, "w")) == NULL) {
		WriteError("$Can't create lockfile \"%s\"", Tmpfile);
		return 1;
	}
	fprintf(fp, "%10u\n", getpid());
	fclose(fp);

	while (TRUE) {
		if (link(Tmpfile, lockfile) == 0) {
			unlink(Tmpfile);
			return 0;
		}
		if ((fp = fopen(lockfile, "r")) == NULL) {
			WriteError("$Can't open lockfile \"%s\"", Tmpfile);
			unlink(Tmpfile);
			return 1;
		}
		if (fscanf(fp, "%u", &oldpid) != 1) {
			WriteError("$Can't read old pid from \"%s\"", Tmpfile);
			fclose(fp);
			unlink(Tmpfile);
			return 1;
		}
		fclose(fp);
		if (kill(oldpid,0) == -1) {
			if (errno == ESRCH) {
				Syslog('+', "Stale lock found for pid %u", oldpid);
				unlink(lockfile);
				/* no return, try lock again */  
			} else {
				WriteError("$Kill for %u failed",oldpid);
				unlink(Tmpfile);
				return 1;
			}
		} else {
			Syslog('+', "mbindex already running, pid=%u", oldpid);
			if (!do_quiet)
				printf("Another mbindex is already running.\n");
			unlink(Tmpfile);
			return 1;
		}
	}
}



void ulockindex(void)
{
	if (lockfile)
		(void)unlink(lockfile);
}



/*
 * If we don't know what to type
 */
void Help(void)
{
	do_quiet = FALSE;
	ProgName();

	colour(11, 0);
	printf("\nUsage: mbindex <options>\n\n");
	colour(9, 0);
	printf("	Options are:\n\n");
	colour(3, 0);
	printf("	-quiet		Quiet mode\n");
	colour(7, 0);
	printf("\n");
	die(0);
}



/*
 * Header, only if not quiet.
 */
void ProgName(void)
{
	if (do_quiet)
		return;

	colour(15, 0);
	printf("\nMBINDEX: MBSE BBS %s Nodelist Index Compiler\n", VERSION);
	colour(14, 0);
	printf("         %s\n", Copyright);
	colour(3, 0);
}



void die(int onsig)
{
	if (onsig && (onsig < NSIG))
		signal(onsig, SIG_IGN);

	ulockindex();

	if (!do_quiet) {
		colour(3, 0);
		show_log = TRUE;
	}

	if (IsSema((char *)"mbindex"))
		RemoveSema((char *)"mbindex");

	if (onsig) {
		if (onsig <= NSIG)
			WriteError("$Terminated on signal %d (%s)", onsig, SigName[onsig]);
		else
			WriteError("Terminated with error %d", onsig);
	}

	time(&t_end);
	Syslog(' ', "MBINDEX finished in %s", t_elapsed(t_start, t_end));
	
	if (!do_quiet)
		colour(7, 0);

	ExitClient(onsig);
}



int main(int argc,char *argv[])
{
	int	i, rc;
	char	*cmd;
	struct	passwd *pw;

#ifdef MEMWATCH
        mwInit();
#endif
	InitConfig();
	InitFidonet();
	TermInit(1);
	t_start = time(NULL);
	umask(002);

	/*
	 *  Catch all the signals we can, and ignore the rest.
	 *  Don't listen to SIGTERM.
	 */
	for(i = 0; i < NSIG; i++) {

		if ((i == SIGHUP) || (i == SIGINT) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV))
			signal(i, (void (*))die);
		else
			signal(i, SIG_IGN);
	}

	cmd = xstrcpy((char *)"Command line: mbindex");

	if (argc > 2)
		Help();

	if (argc == 2) {
		cmd = xstrcat(cmd, (char *)" ");
		cmd = xstrcat(cmd, argv[1]);

		if (strncasecmp(argv[1], "-q", 2) == 0)
			do_quiet = TRUE;
		else
			Help();
	}

	ProgName();
	pw = getpwuid(getuid());
	InitClient(pw->pw_name, (char *)"mbindex", CFG.location, CFG.logfile, CFG.util_loglevel, CFG.error_log);

	Syslog(' ', " ");
	Syslog(' ', "MBINDEX v%s", VERSION);
	Syslog(' ', cmd);
	free(cmd);

	if (!diskfree(CFG.freespace))
		die(101);

	if (lockindex()) {
		if (!do_quiet)
			printf("Can't lock mbindex, abort.\n");
		die(104);
	}

	rc = nodebld();
	die(rc);
	return 0;
}



void tidy_nllist(nl_list **fap)
{
	nl_list	*tmp, *old;

	Syslog('S', "tidy_nllist");
	for (tmp = *fap; tmp; tmp = old) {
		old = tmp->next;
		free(tmp);
	}
	*fap = NULL;
}



int in_nllist(struct _nlidx idx, nl_list **fap, int replace)
{
	nl_list	*tmp;

	Syslog('S', "Seeking nlidx match for %u:%u/%u.%u", idx.zone, idx.net, idx.node, idx.point);

	for (tmp = *fap; tmp; tmp = tmp->next)
		if ((tmp->idx.zone == idx.zone) && (tmp->idx.net == idx.net) &&
		    (tmp->idx.node == idx.node) && (tmp->idx.point == idx.point)) {
			Syslog('S', "Match found");
			if (replace) {
				tmp->idx = idx;
				entries++;
			}
			regio = tmp->idx.region;
			return TRUE;
	}
	return FALSE;
}



void fill_nllist(struct _nlidx idx, nl_list **fap)
{
	nl_list	*tmp;

	Syslog('S', "fill_nllist %u:%u/%u.%u", idx.zone, idx.net, idx.node, idx.point);
	tmp = (nl_list *)malloc(sizeof(nl_list));
	tmp->next = *fap;
	tmp->idx  = idx;
	*fap = tmp;
	total++;
	entries++;
}



char *fullpath(char *fname)
{
	static char path[128];

	sprintf(path, "%s/%s", CFG.nodelists, fname);
	return path;
}



void sort_nllist(nl_list **fap)
{
	nl_list	*ta, **vector;
	size_t	n = 0, i;

	if (*fap == NULL)
		return;

	for (ta = *fap; ta; ta = ta->next)
		n++;

	vector = (nl_list **)malloc(n * sizeof(nl_list *));
	Syslog('s', "Sorting %d nodelist entries", n);

	i = 0;
	for (ta = *fap; ta; ta = ta->next) {
		vector[i++] = ta;
		Syslog('S', "Before %u:%u/%u.%u", ta->idx.zone, ta->idx.net, ta->idx.node, ta->idx.point);
	}

	qsort(vector, n, sizeof(nl_list *), 
		(int(*)(const void*, const void *))comp_node);

	(*fap) = vector[0];
	i = 1;

	for (ta = *fap; ta; ta = ta->next) {
		if (i < n)
			ta->next = vector[i++];
		else
			ta->next = NULL;
		Syslog('S', "After %u:%u/%u.%u", ta->idx.zone, ta->idx.net, ta->idx.node, ta->idx.point);
	}

	free(vector);
	return;
}



int comp_node(nl_list **fap1, nl_list **fap2)
{
	if ((*fap1)->idx.zone != (*fap2)->idx.zone)
		return ((*fap1)->idx.zone - (*fap2)->idx.zone);
	else if ((*fap1)->idx.net != (*fap2)->idx.net)
		return ((*fap1)->idx.net - (*fap2)->idx.net);
	else if ((*fap1)->idx.node != (*fap2)->idx.node)
		return ((*fap1)->idx.node - (*fap2)->idx.node);
	else
		return ((*fap1)->idx.point - (*fap2)->idx.point);
}



int compile(char *nlname, unsigned short zo, unsigned short ne, unsigned short no)
{
	int		num, i, rc = 0;
	int		lineno, boss = FALSE, bossvalid = FALSE;
	unsigned short	upnet, upnode;
	char		buf[256], *p, *q;
	faddr		*tmpa;
	FILE		*nl;
	struct _nlidx	ndx;
	struct _nlusr	udx;

	rc = 0;
	if ((nl = fopen(fullpath(nlname), "r")) == NULL) {
		WriteError("$Can't open %s", fullpath(nlname));
		return 102;
	}

	Syslog('+', "Compiling \"%s\" (%d)", nlname, filenr);
	IsDoing("Compile NL %d", filenr +1);

	memset(&ndx, 0, sizeof(ndx));
	ndx.type = NL_NODE;
	ndx.fileno = filenr;
	upnet = 0;
	upnode = 0;

	/*
	 *  If zone is set, it is a overlay segment
	 */
	if (zo) {
		ndx.zone  = zo;
		ndx.net   = ne;
		ndx.node  = no;
		ndx.point = 0;
	}
	entries = 0;
	lineno  = 0;

	while (!feof(nl)) {

		ndx.offset = ftell(nl);
		lineno++;
		if (fgets(buf, sizeof(buf)-1, nl) == NULL)
			continue;

		/*
		 * Next check at <lf> and <eof> characters
		 */
		if ((*(buf+strlen(buf) -1) != '\n') && (*(buf + strlen(buf) -1) != '\012')) {
			while (fgets(buf, sizeof(buf) -1, nl) &&
			       (*(buf + strlen(buf) -1) != '\n')) /*void*/;
			if (strlen(buf) > 1) /* Suppress EOF character */
				Syslog('s', "Nodelist: too long line junked (%d)", lineno);
			continue;
		}

		if (*(p=buf+strlen(buf) -1) == '\n') 
			*p-- = '\0';
		if (*p == '\r') 
			*p = '\0';
		if ((buf[0] == ';') || (buf[0] == '\032') || (buf[0] == '\0'))
			continue;

		if (CFG.slow_util && do_quiet) {
			if (zo) {
				usleep(1);
			} else {
				if ((lineno % 40) == 0)
					usleep(1);
			}
		}

		if ((p = strchr(buf, ','))) 
			*p++ = '\0';
		if ((q = strchr(p, ','))) 
			*q++ = '\0';

		ndx.type = NL_NONE;
		ndx.pflag = 0;

		if (buf[0] == '\0') {
			if (boss)
				ndx.type = NL_POINT;
			else
				ndx.type = NL_NODE;
		} else 
		if (strcasecmp(buf,"Boss") == 0) {
			ndx.type = NL_POINT;
			bossvalid = FALSE;
			if ((tmpa=parsefnode(p)) == NULL) {
				WriteError("%s(%u): unparsable Boss addr \"%s\"",
					nlname,lineno,p);
				continue;
			}
			boss = TRUE;
			if (tmpa->zone) 
				ndx.zone = tmpa->zone;
			ndx.net   = tmpa->net;
			ndx.node  = tmpa->node;
			ndx.point = 0;
			tidy_faddr(tmpa);
			Syslog('S', "Boss %u:%u/%u", ndx.zone, ndx.net, ndx.node);
			ndx.type = NL_NONE;

			if (in_nllist(ndx, &nll, FALSE)) {
				Syslog('S', "Boss exists");
				bossvalid = TRUE;
			}
			else
				Syslog('S', "Boss not found");
			continue; /* no further processing */
		} else {
			boss = FALSE;
			ndx.type = NL_NONE;
			if (!strcasecmp(buf, "Down")) {
				ndx.pflag |= NL_DOWN;
				ndx.type = NL_NODE;
			}
			if (!strcasecmp(buf, "Hold")) {
				ndx.pflag |= NL_HOLD;
				ndx.type = NL_NODE;
			}
			if (!strcasecmp(buf, "Pvt")) {
				ndx.pflag |= NL_PVT;
				ndx.type = NL_NODE;
			}

			if (!strcasecmp(buf, "Zone"))
				ndx.type = NL_ZONE;
			if (!strcasecmp(buf, "Region"))
				ndx.type = NL_REGION;
			if (!strcasecmp(buf, "Host"))
				ndx.type = NL_HOST;
			if (!strcasecmp(buf, "Hub"))
				ndx.type = NL_HUB;
			if (!strcasecmp(buf, "Point")) {
				ndx.type = NL_POINT;
				bossvalid = TRUE;
			}
		}

		if (ndx.type == NL_NONE) {
			for (q = buf; *q; q++) 
				if (*q < ' ') 
					*q='.';
			WriteError("%s(%u): unidentified entry \"%s\"",
				nlname, lineno, buf);
			continue;
		}

		Syslog('S',"Got \"%s\" as \"%s\" typ %d", buf, p, ndx.type);
		if ((num=atoi(p)) == 0) {
			WriteError("%s(%u): bad numeric \"%s\"",
				nlname,lineno,p);
			continue;
		}

		/*
		 * now update the current address
		 */
		switch (ndx.type) {
		case NL_REGION:	ndx.net   = num;
				ndx.node  = 0;
				ndx.point = 0;
				ndx.upnet = ndx.zone;
				ndx.upnode= 0;
				ndx.region= num;
				upnet     = num;
				upnode    = 0;
				break;
		case NL_ZONE:	ndx.zone  = num;
				ndx.net   = num;
				ndx.node  = 0;
				ndx.point = 0;
				ndx.upnet = 0;
				ndx.upnode= 0;
				ndx.region= 0;
				upnet     = num;
				upnode    = 0;
				break;
		case NL_HOST:	ndx.net   = num;
				ndx.node  = 0;
				ndx.point = 0;
				ndx.upnet = ndx.region;
				ndx.upnode= 0;
				upnet     = num;
				upnode    = 0;
				break;
		case NL_HUB:	ndx.node  = num;
				ndx.point = 0;
				ndx.upnet = ndx.net;
				ndx.upnode= 0;
				upnet     = ndx.net;
				upnode    = num;
				break;
		case NL_NODE:	ndx.node  = num;
				ndx.point = 0;
				ndx.upnet = upnet;
				ndx.upnode= upnode;
				break;
		case NL_POINT:	ndx.point = num;
				ndx.upnet = ndx.net;
				ndx.upnode= ndx.node;
				if ((!ndx.region) && bossvalid)
					ndx.region = regio;
				break;
		}
		if (!do_quiet) {
			printf("\rZone %-6uRegion %-6uNet %-6uNode %-6uPoint %-6u", 
				ndx.zone, ndx.region, ndx.net, ndx.node, ndx.point);
			fflush(stdout);
		}

		memset(&udx, 0, sizeof(udx));
		udx.record = total;

		/*
		 *  Read nodelist line and extract username.
		 */
		for (i = 0; i < 3; i++) {
			p = q;
			if (p == NULL)
				continue;
			if ((q = strchr(p, ',')))
				*q++ = '\0';
		}
		if (strlen(p) > 35)
			p[35] = '\0';
		sprintf(udx.user, "%s", p);

		/*
		 *  Now search for the baudrate field, 300 means it's
		 *  and ISDN or TCP/IP only node which is a special case.
		 */
		for (i = 0; i < 2; i++) {
			p = q;
			if (p == NULL)
				continue;
			if ((q = strchr(p, ',')))
				*q++ = '\0';
		}
		if ((strlen(p) == 3) && (!strcmp(p, "300"))) {
			if (q != NULL) {
				if ((strstr(q, (char *)"X75")) ||
				    (strstr(q, (char *)"V110L")) ||
				    (strstr(q, (char *)"V110H")) ||
				    (strstr(q, (char *)"V120L")) ||
				    (strstr(q, (char *)"V120H")) ||
				    (strstr(q, (char *)"ISDN")))
					ndx.pflag |= NL_ISDN;
				if ((strstr(q, (char *)"IFC")) ||
				    (strstr(q, (char *)"IBN")) ||
				    (strstr(q, (char *)"ITN")) ||
				    (strstr(q, (char *)"IVM")) ||
				    (strstr(q, (char *)"IFT")) ||
				    (strstr(q, (char *)"IP")))
					ndx.pflag |= NL_TCPIP;
			}
		}

		Syslog('S',"put: %u:%u/%u.%u reg %u upl %u/%u typ %u flg %02X as (%u,%lu)",
			ndx.zone,ndx.net,ndx.node,
			ndx.point,ndx.region,ndx.upnet,ndx.upnode,
			ndx.type,ndx.pflag,ndx.fileno,ndx.offset);


		/*
		 *  If zone, net and node given, then this list is an
		 *  overlay so we will call in_list() to replace the
		 *  existing records, or append them if they don't exist.
		 *  Also, only points with a valid boss will be added.
		 */
		if (zo) {
			if (!(in_nllist(ndx, &nll, TRUE))) {
				if (ndx.point && bossvalid) {
					fill_nllist(ndx, &nll);
					Syslog('S', "Add point %u:%u/%u.%u", ndx.zone, ndx.net, ndx.node, ndx.point);
				}
				if (!ndx.point)
					fill_nllist(ndx, &nll);
			}
		} else
			fill_nllist(ndx, &nll);
	}

	Syslog('+', "%d entries", entries);

	if (!do_quiet) {
		printf(" %ld entries\n", entries);
		fflush(stdout);
	}

	return rc;
}



/*
 * Tidy the filearray
 */
void tidy_fdlist(fd_list **fdp)
{
	fd_list	*tmp, *old;

	for (tmp = *fdp; tmp; tmp = old) {
		old = tmp->next;
		free(tmp);
	}
	*fdp = NULL;
}



/*
 * Add a file on the array.
 */
void fill_fdlist(fd_list **fdp, char *filename, time_t filedate)
{
	fd_list	*tmp;

	tmp = (fd_list *)malloc(sizeof(fd_list));
	tmp->next = *fdp;
	sprintf(tmp->fname, "%s", filename);
	tmp->fdate = filedate;
	*fdp = tmp;
}



int compfdate(fd_list **, fd_list **);


/*
 * Sort the array of files by filedate
 */
void sort_fdlist(fd_list **fdp)
{
	fd_list	*ta, **vector;
	size_t	n = 0, i;

	if (*fdp == NULL) {
		return;
	}

	for (ta = *fdp; ta; ta = ta->next)
		n++;

	vector = (fd_list **)malloc(n * sizeof(fd_list *));

	i = 0;
	for (ta = *fdp; ta; ta = ta->next) {
		vector[i++] = ta;
	}

	qsort(vector, n, sizeof(fd_list*), (int(*)(const void*, const void*))compfdate);

	(*fdp) = vector[0];
	i = 1;

	for (ta = *fdp; ta; ta = ta->next) {
		
		if (i < n)
			ta->next = vector[i++];
		else
			ta->next = NULL;
	}

	free(vector);
	return;
}



int compfdate(fd_list **fdp1, fd_list **fdp2)
{
	return ((*fdp1)->fdate - (*fdp2)->fdate);
}



/*
 * Return the name of the oldest file in the array
 */
char *pull_fdlist(fd_list **fdp)
{
	static char	buf[65];
	fd_list		*ta;

	if (*fdp == NULL)
		return NULL;

	ta = *fdp;
	memset(&buf, 0, sizeof(buf));
	sprintf(buf, "%s", ta->fname);

	if (ta->next != NULL)
		*fdp = ta->next;
	else
		*fdp = NULL;

	free(ta);
	return buf;
}



int makelist(char *base, unsigned short zo, unsigned short ne, unsigned short no)
{
	int		rc = 0, files = 0;
	struct dirent	*de;
	DIR		*dp;
	char		*p = NULL, *q;
	struct _nlfil	fdx;
	fd_list		*fdl = NULL;

	if (!strlen(base)) {
		WriteError("Error, no nodelist defined for %d:%d/%d", zo, ne, no);
		return 0;
	}

	if ((dp = opendir(CFG.nodelists)) == NULL) {
		WriteError("$Can't open dir %s", CFG.nodelists);
		rc = 103;
	} else {
		while ((de = readdir(dp)))
			if (strncmp(de->d_name, base, strlen(base)) == 0) {
				/*
				 *  Extension must be at least 2 digits
				 */
				q = (de->d_name) + strlen(base);
				if ((*q == '.') && (strlen(q) > 2) &&
				    (strspn(q+1,"0123456789") == (strlen(q)-1))) {
					/*
					 * Add matched filenames to the array
					 */
					fill_fdlist(&fdl, de->d_name, file_time(fullpath(de->d_name)));
					files++;
				}
			}
		closedir(dp);

		if (files == 0) {
			Syslog('+', "No nodelist found for %s", base);
			return 103;
		}

		/*
		 * Sort found nodelists by age and kill all but the newest 2.
		 */
		sort_fdlist(&fdl);
		while (files) {
			p = pull_fdlist(&fdl);
			if (files > 2) {
				Syslog('+', "Remove old \"%s\"", p);
				unlink(fullpath(p));
			}
			files--;
		}
		tidy_fdlist(&fdl); 

		memset(&fdx, 0, sizeof(fdx));
		sprintf(fdx.filename, "%s", p);
		sprintf(fdx.domain, "%s", fidonet.domain);
		fdx.number = filenr;
		fwrite(&fdx, sizeof(fdx), 1, ffp);

		rc = compile(p, zo, ne, no);
		filenr++;
	}

	return rc;
}



int nodebld(void)
{
	int		rc = 0, i;
	char		*im, *fm, *um, *old, *new;
	struct _nlfil	fdx;
	FILE		*fp;
	nl_list		*tmp;

	memset(&fdx, 0, sizeof(fdx));
	im = xstrcpy(fullpath((char *)"temp.index"));
	fm = xstrcpy(fullpath((char *)"temp.files"));
	um = xstrcpy(fullpath((char *)"temp.users"));

	if ((ifp = fopen(im, "w+")) == NULL) {
		WriteError("$Can't create %s",MBSE_SS(im));
		return 101;
	}
	if ((ufp = fopen(um, "w+")) == NULL) {
		WriteError("$Can't create %s", MBSE_SS(um));
		fclose(ifp);
		unlink(im);
		return 101;
	}
	if ((ffp = fopen(fm, "w+")) == NULL) {
		WriteError("$Can't create %s", MBSE_SS(fm));
		fclose(ifp);
		unlink(im);
		fclose(ufp);
		unlink(um);
		return 101;
	}

	if (!do_quiet) {
		colour(3, 0);
		printf("\n");
	}

	if ((fp = fopen(fidonet_fil, "r")) == 0)
		rc = 102;
	else {
		fread(&fidonethdr, sizeof(fidonethdr), 1, fp);

		while (fread(&fidonet, fidonethdr.recsize, 1, fp) == 1) {
			if (fidonet.available) {
				rc = makelist(fidonet.nodelist, 0, 0, 0);
				if (rc)
					break;

				for (i = 0; i < 6; i++) {
					if (fidonet.seclist[i].zone) {
						rc = makelist(fidonet.seclist[i].nodelist,
							fidonet.seclist[i].zone,
							fidonet.seclist[i].net, 
							fidonet.seclist[i].node);
						if (rc)
							break;
					}
				}
				if (rc)
					break;
			}
		}

		fclose(fp);
	}

	fclose(ufp);
	fclose(ffp);

	if (rc == 0) {
		IsDoing("Sorting nodes");
		sort_nllist(&nll);

		IsDoing("Writing files");
		for (tmp = nll; tmp; tmp = tmp->next)
			fwrite(&tmp->idx, sizeof(struct _nlidx), 1, ifp);
		fclose(ifp);

		Syslog('+', "Compiled %d entries", total);

		/*
		 *  Rename existing files to old.*, they stay on disk in
		 *  case they are open by some program. The temp.* files
		 *  are then renamed to node.*
		 */
		old = xstrcpy(fullpath((char *)"old.index"));
		new = xstrcpy(fullpath((char *)"node.index"));
		unlink(old);
		rename(new, old);
		rename(im, new);
		free(old);
		free(new);
		old = xstrcpy(fullpath((char *)"old.users"));
		new = xstrcpy(fullpath((char *)"node.users"));
		unlink(old);
		rename(new, old);
		rename(um, new);
		free(old);
		free(new);
		old = xstrcpy(fullpath((char *)"old.files"));
		new = xstrcpy(fullpath((char *)"node.files"));
		unlink(old);
		rename(new, old);
		rename(fm, new);
		free(old);
		free(new);
	} else {
		fclose(ifp);
		Syslog('+', "Compile failed, rc=%d", rc);
		unlink(im);
		unlink(fm);
		unlink(um);
	}

	free(im);
	free(fm);
	free(um);
	tidy_nllist(&nll);

	return rc;
}