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

#define same(s1,s2) (strcmp(s1,s2)==0)

char *getmem();
long sizmem();

struct _area msgarea;	/* current msg area */
struct _area filearea;	/* current file area */
struct _fido fido;
struct _node id,altid;

char kill_null = 0;	/* 1 == kill null messages */
char kill_files = 0;	/* 1 == kill MISSING FidoNet files */

main(argc,argv)
int argc;
char **argv;
{
int i;
char *cp,buff[SS];

	printf("KillARCM (13 Apr 90) FidoNet/EchoMail Cleanup Utility  /? for help\r\n");
	printf("Tom Jennings / World Power Systems\r\nBox 77731, San Francisco, CA 94107, USA\r\n");
	printf("(k) All wrights revered\r\n");

	allmem();
	fastfile(3);
	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) {
		printf("KillARCM doesn't match your Fido/FidoNet version!\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;

	while (--argc) {
		++argv;
/*		cpyarg(buff,*argv);		/* a copy of any filename */
*/		strip_switch(buff,*argv);	/* a copy of the switches, */
		for (cp= buff; *cp; ++cp) switch (*cp) {
			case 'N': kill_null= 1; break;
			case 'F': kill_files= 1; break;
			case '?':
				printf("\r\nKillARCM will always K)ill messages that are \"From: ARCMail\", ie. \r\n");
				printf("incoming ARCMail packets. It can do these things if you put magic symbols \r\n");
				printf("after the program name:\r\n");
				printf("\r\n");
				printf("    /N     Kills null FidoNet messages. (Messages with no text -- file-\r\n");
				printf("           requests, etc.)\r\n");
				printf("\r\n");
				printf("    /F     Removes all \"MISSING\" file entries from your FidoNet file\r\n");
				printf("           area. (ie. incoming ARCmail packets, automatically processed\r\n");
				printf("           NODEDIFFs, etc.)\r\n");
				exit(1);
		}
	}
	getmarea(fido.netmarea,&msgarea);		/* pick the area, */
	purge();					/* purge them */
	if (kill_files) kill_missing();			/* purge files */
}

/* Purge messages so many days old. */

purge() {
int arcm_count,null_count,n,i;
int msgfile;
char *cp,buff[SS],name[SS];
char work[256];
struct _xfbuf xfbuf;
struct _msg msg;		/* message header */
struct _node msg_orig;		/* actual message originator */
struct _node msg_dest;		/* actual message destination */

	arcm_count= null_count= 0;
	makemname(name,"*.msg");
	xfbuf.s_attrib= 0; i= 0;
	while (_find(name,i++,&xfbuf)) {		/* find a msg file */
		makemname(buff,xfbuf.name);		/* assemble its name */
		msgfile= open(buff,2);			/* read its header */
		if (msgfile == -1) continue;

		read(msgfile,&msg,sizeof(struct _msg));	/* load the header */
		work[read(msgfile,work,sizeof(work) - 1)]= '\0'; /* read some msg body */
		close(msgfile);				/* close the file */

		msg_orig.number= msg.orig_node;
		msg_orig.net= msg.orig_net;		/* assume addresses */
		msg_dest.number= msg.dest_node;		/* are from the header */
		msg_dest.net= msg.dest_net;
		msg_orig.zone= id.zone; 		/* in case no */
		msg_dest.zone= id.zone; 		/* IFNA Kludge */

/*		msg_dest.zone= msg.dest_zone;		/* so-called real msgs */
		msg_orig.zone= msg.orig_zone;
*/

/* Don't touch messages we created! */

		if (is_us(&msg_orig)) continue;

/* Check for the presence of an IFNA Kludge; if there is one, set the
addresses to that. */

		if (fcomp(work,"\001INTL",5) == 0) {
			cp= next_arg(work);
			set_nn(cp,&msg_dest);
			cp= next_arg(cp);
			set_nn(cp,&msg_orig);
		}

/* Now check to see if this message is From: ARCmail, not our node. */

		if ( (strcmp("ARCmail",msg.from) == 0) 	/* if From: ARCmail */
		  && (msg.attr & MSGFILE)		/* and a file attach, */
		  && (!is_us(&msg_orig)) ) {		/* and not from us */
			delete(buff);			/* kill it */
			++arcm_count;

/* Now check to see if this message is empty. Skip all IFNA kludge lines,
see if there is anything left. If 4 or less characters (CR/LF CR/LF) consider
it null. */

		} else {
			for (cp= work; *cp == '\01';) {	/* find 1st non-IFNA kludge */
				while (*cp != 13) {	/* look for the CR */
					if (!*cp) break;
					++cp;
				}
				if (*cp == 13) ++cp;	/* skip any CR */
				if (*cp == 10) ++cp;	/* skip any LF */
			}
			for (n= 0; *cp;) {		/* count printable chars */
				if (*cp++ > ' ') ++n;	/* but not spaces */
			}
			if (kill_null & (n == 0)) {	/* if a null message, */
				delete(buff);		/* kill it */
				++null_count;
			}
		}
	}
	printf("\r\nKilled ");
	if (kill_null) printf("%d null messages, ",null_count);
	printf("%d messages \"From: ARCmail\"\r\n",arcm_count);
}

kill_missing() {

struct _xfbuf xfbuf;
int dead,f,o,i,n;
char *cp,fname[SS],buff[SS],ln[200];

	getfarea(fido.netfarea,&filearea);		/* pick the area, */

/* Open FILES.BBS in the net file area, plus a new temp file. For each line
in FILES.BBS, copy those list files that do exist to the temporary file, ie.
delete lines with MISSING files. */

	makefname(buff,"FILES.BBS");		/* file list name */
	f= open(buff,0);			/* open source file, */
	if (f == -1) return;			/* no file to delete from */

	makefname(buff,"FILES.$$$");		/* temp. work file name */
	o= creat(buff,2);			/* create output file */
	if (o == -1) {
		close(f);
		printf("Cant create work file %s\r\n",buff);
		return;
	}
	dead= 0;
	xfbuf.s_attrib= 0;
	while (rline(f,ln,sizeof(ln))) {	/* process each line in FILES.BBS */
		if (*ln == SUB) break;		/* exit if Control-Z */
		cpyarg(buff,ln);		/* copy file name */
		makefname(fname,buff);		/* make full pathname */
		if (_find(fname,0,&xfbuf)) {	/* if the file exists, write it */
			if (write(o,ln,strlen(ln)) != strlen(ln)) {
				printf("Disk full! The file list is not updated\r\n");
				close(o);
				o= -1;
				break;
			}
			write(o,"\r\n",2);

		} else {
			cp= next_arg(ln);	/* point to comment */
			stoupper(buff);		/* make name upper case */
			printf("%13s %s: REMOVED\r\n",buff,cp);
			++dead;
		}
	}
	close(f);				/* close input */
	if (o != -1) {
		close(o);			/* save output */
		makefname(fname,"FILES.BBS");
		delete(fname);			/* delete and */
		makefname(buff,"FILES.$$$");
		rename(buff,fname);		/* rename, */
	}
	if (! dead) printf("No files marked \"MISSING\" in the FidoNet file area\r\n");
}

/* Assemble a full msg filename */

makefname(d,p)
char *d,*p;
{
	strcpy(d,filearea.path);	/* put in the path, */
	strcat(d,p);			/* add the filename */
}

/* Assemble a full msg filename */

makemname(d,p)
char *d,*p;
{
	strcpy(d,msgarea.path);		/* put in the path, */
	strcat(d,p);			/* add the filename */
}

/* Get message area N, return 0 if error. */

getmarea(n)
unsigned n;
{
	return(getarea(n,&msgarea,0));
}

/* Get file area N, return 0 if error. */

getfarea(n)
unsigned n;
{
	return(getarea(n,&filearea,1));
}

/* Get message or file area #n; return 0 if not set, illegal number
or other error. */

getarea(n,a,file)
unsigned n;		/* area number */
struct _area *a;	/* struct to load */
int file;		/* 1 == get a file area */
{
int f,i;
long off;

	if (n < 0) return(0);				/* no such area */
	off= 0L + sizeof(struct _fido);			/* offset to beg msg areas */
	if (file) {					/* if a file area, */
		if (n >= fido.farea_max) return(0);	/* bound it */
		off += (long)(sizeof(struct _area) * fido.marea_max); /* beg file areas */

	} else if (n >= fido.marea_max) return(0);	/* msg area max */

	f= open("fido.sys",0);
	if (f == -1) return(0);				/* no system file! */

	off += (long)(n * sizeof(struct _area));	/* offset plus area # */
	lseek(f,off,0);					/* seek there, */
	i= read(f,a,sizeof(struct _area));		/* read some, */
	close(f);					/* immediately close it */

	if (i != sizeof(struct _area)) return(0);	/* check read error */
	a-> number= n;					/* set the path number */
	return(*a-> path != NUL);			/* invalid if no path set */
}

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

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

/* Return true if this net/node combination is us. */

is_us(n)
struct _node *n;
{
	return(same_node(n,&id) || same_node(n,&altid));
}

/* 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
88/77			xx:88/88
77			xx:xx/77
99:88			99:88/00
88/			xx:88/00

*/

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

	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) */

	} else if (*cp == '/') {		/* if that was a net number, */
		n-> net= n-> number;		/* copy it up, */
		n-> number= atoi(++cp);		/* now set node number */
	}
}
/* Fixed length string compare. Returns 0 if a match */

fcomp(s1,s2,n)
char *s1,*s2;
int n;
{
	while (n) {
		if (*s1++ != *s2++) break;
		--n;
	}
	return(n);
}
