
/***************************************************************************
 *   Copyright (C) 1999 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program 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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "wimp.h"
#include "werr.h"
#include "dbox.h"
#include "menu.h"
#include "os.h"
#include "flex.h"
#include "bbc.h"
#include "event.h"

#include "narc.h"
#include "hdrs.h"
#include "regexmod.h"


extern char *path_choices;
extern OPTIONS options;
extern int transport_opts;
extern FOLDREC src_fr;
extern FOLDREC cty_fr;
extern FOLDREC list_fr[N_LISTS];
extern CARDFILE_REC cardfile[N_CARDFILES];
extern TEXTR text_rec_temp;
extern int dbox_menu_field;
extern wimp_menustr *wmenu_boxes_names;
extern int lists_line_height;
extern int lists_font_offset;
extern int standard_font;
extern char fname_temp2[];

extern int regex_err;
extern NEWSG **ng_index;
extern int  n_ngroups;

extern menu blist_current_menu;
extern int blist_current_menu_line;
extern int blist_current_menu_blist;


/******************************************************************/
/*               KILLFILE                                         */
/******************************************************************/

static int  kill_num;
BLIST killlist = {BLIST_KILL,100,500,NULL,80,1040,0,HELP_KILLLIST};
int *killfile_regex = NULL;
int  n_killfile;
KILLFILE **killfile;

static menu menu_filters;

static TEXTR *t_killfile;
static dbox dbox_kill;
static dbox dbox_options;

static dbox_kill_type;
static dbox_kill_type2;
static dbox_kill_source;
static dbox_kill_category;
static dbox_kill_box;
static dbox_kill_copybox;
static dbox_kill_score_op;
static dbox_kill_set_status;
static dbox_kill_source1;
static dbox_kill_source2;
static dbox_score_box;

static int  killfile_max;
static int  killfile_add_flag;
static int  killfile_resort;

static char *menustr_filters = "Add,Edit,Activate,Deactivate,Select all ^A,Delete ^X,Clear ^Z,List,Preferences";

static char *menustr_kill_types =
	"(none),Source,Author,Domain,In address book,Subject,Subject contains,Subj all caps,Text body,-----,Header,Content-type,Envelope-to,Newsgroups,Number of groups,Path,News thread,News subthread";

#ifdef deleted
static char *menustr_kill_types = "(none),Author,Domain,Subject,Subject contains,News thread,News subthread,Header,Text body,Newsgroup,Source,Path,Envelope to,Number of groups,Subject all caps,In address book,Content-type";
#endif

wimp_menustr *wmenu_kill_types=NULL;

static char *kill_types[] = {
	"","Author","Domain","Subject","Subj contains","News thread","Path","Newsgroup",
	"Header","Text body","Subj all caps","Num.of groups","News subthread","Envelope-to","Source","In Addr book","Content-type"
};


static char *menustr_score_ops = "(none),=,+,-,*,/";
static char *score_ops[] = {"(none)","=","+","-","*","/"};
wimp_menustr *wmenu_score_ops=NULL;

static char *menustr_set_status = "(none),Unread,Read,Locked,Marked";
static char *set_status[] = {"(none)","Unread","Read","Locked","Marked"};
wimp_menustr *wmenu_set_status=NULL;

static char *flags_letters = "NMLIO";
static char *bool_strings[4] = {"AND","OR","NOT",""};

/* translate menu selection to kill_type */
static char killfile_type[] = {0,14,1,2,15,3,4,10,9,0,8,16,13,7,11,6,5,12,0};

/* fields to fade if the action is Discard */
char kill_fade[9] = {3,9,17,19,30,38,39,40,0};



void regex_load()
/***************/
{
	os_cli("RMEnsure RegEx 1.01 RMLoad System:Modules.RegEx");
}   /* end of regex_load */





int compile_regex_expression(char *string, int case_sensitive)
/************************************************************/
{
	int  default_syntax;
	os_error *error;
	os_regset regs;

	error = os_swix(REGX_GetDefaultSyntax,&regs);
	if(error && (error->errnum == 486))
	{
		regex_load();
		error = os_swix(REGX_GetDefaultSyntax,&regs);
		if(error && (error->errnum == 486))
		{
			werr(0,"REGEX module is not loaded.  A Filter has the REGEX option set.");
			return(0);
		}
	}

	if(error == NULL)
	{
		default_syntax = regs.r[0];

		regs.r[0] = 0;
		regs.r[1] = (int)string;
		regs.r[3] = 0;

		if(case_sensitive == 0)
			regs.r[2] = default_syntax | RE_CASE_INSENSITIVE;
		else
			regs.r[2] = default_syntax & ~RE_CASE_INSENSITIVE;

		error = os_swix(REGX_CompileExtendedPattern,&regs);
	}

	if(error)
	{
		werr(0,"REGEX error in Filters List:  %s",error->errmess);
		return(0);
	}

	return(regs.r[0]);
}   /* end of compile_regex_expression */




int kill_compile_regex()
/***********************/
{
	int  ix;
	KILLFILE *k;

	if(killfile_regex != NULL)
	{
		killfile_free_regex();
		free(killfile_regex);
	}

	killfile_regex = calloc(n_killfile+1,sizeof(int)*2);
	if(killfile_regex == NULL)
	{
		malloc_err(94);
		return(-1);
	}

	for(ix=0; ix<n_killfile; ix++)
	{
		k = killfile[ix];
		if(k->regex & 1)
		{
			if((killfile_regex[ix*2] = compile_regex_expression(k->string,0)) == 0)
				break;
		}
		if(k->regex & 2)
		{
			if((killfile_regex[ix*2+1] = compile_regex_expression(&k->string[k->string2],0)) == 0)
				return(-1);
		}
	}
	return(0);
}   /* end of kill_compile_regex */



int kill_length(KILLFILE *k)
/**************************/
{
	return(26 + strlen(k->string) + strlen(&k->string[k->string2]));
}   /* end of kill_length */






void kill_save()
/**************/
{
	FILE *f;
	int  ix;
	int  version = 0x81;
	int  length;
	KILLFILE *kf;
	char buf[256];

	sprintf(buf,"%s.killfile",path_choices);
	f = fopen_werr(buf,"w","");
	if(f == NULL)
		return;

	length = (version << 24) + n_killfile;
	fwrite(&length,sizeof(int),1,f);
	for(ix=0; ix<n_killfile; ix++)
	{
		kf = killfile[ix];
		length = kill_length(kf);
		if(length > 255)
		{
			continue;   /* too long */
		}
		fputc(length,f);
		fwrite(killfile[ix],1,length,f);
	}
	fclose(f);
}   /* end of kill_save */



void kill_load()
/**************/
{
	FILE *f;
	int  ix;
	int  length;
	int  version;
	KILLFILE *kf;
	char buf[256];
	KILLFILE kbuf;
	KILLFILE_1 kbuf1;

	n_killfile = 0;
	killfile_max = 0;

	sprintf(buf,"%s.killfile",path_choices);
	f = fopen_werr(buf,"r","");

	if(f != NULL)
	{
		fread(&n_killfile,sizeof(int),1,f);
		if(feof(f))
		{
			n_killfile = 0;
		}
		else
		{
			version = (n_killfile >> 24) & 0xff;
			n_killfile = n_killfile & 0xffff;
		}
	}

	killfile = (KILLFILE **)calloc(n_killfile+50,sizeof(void *));
	if(killfile == NULL)
	{
		n_killfile = 0;
		malloc_err(91);
		fclose(f);
		return;
	}
	killfile_max = n_killfile+50;

	if(f == NULL)
		return;

	for(ix=0; ix<n_killfile; ix++)
	{
		length = fgetc(f);
		if(length > sizeof(kbuf))
		{
			werr(0,"Data error in %s",buf);
			break;
		}
		fread(&kbuf,1,length,f);

		if(version < 0x81)
		{
			/* convert from old format */
			memcpy(&kbuf1,&kbuf,sizeof(kbuf1));
			memset(&kbuf,0,sizeof(kbuf));
			memcpy(&kbuf,&kbuf1,8);
			kbuf.expiry = kbuf1.expiry;
			strcpy(kbuf.string,kbuf1.string);

			length = 26 + strlen(kbuf.string);
		}

		kf = (KILLFILE *)calloc(1,length+1);
		if(kf == NULL)
		{
			malloc_err(92);
			fclose(f);
			n_killfile = ix;
			return;
		}

		memcpy(kf,&kbuf,length);
		killfile[ix] = kf;
		kf->selected = 0;
	}
	fclose(f);

	if((n_killfile > 0) && (version < 0x81))
	{
		/*      kill_save(); */
	}
	kill_compile_regex();
}   /* end of kill_load */




int killfile_count_cats(int type, int cat, int new_cat)
/************************************************/
{
	int  ix;
	KILLFILE *k;
	int  count=0;

	for(ix=0; ix<n_killfile; ix++)
	{
		k = killfile[ix];

		if(type == 1)
		{
			if(k->category == cat)
			{
				count++;

				if(new_cat > 0)
					k->category = new_cat;
			}
		}
		else
		{
			if(k->source == cat)
			{
				count++;

				if(new_cat > 0)
					k->source = new_cat;
			}
		}
	}
	if((new_cat > 0) && (count > 0))
	{
		kill_save();
	}
	return(count);
}   /* end of killfile_count_cats */




int kill_sorter(const void *p1, const void *p2)
/*********************************************/
{
	int  i;
	KILLFILE *k1, *k2;

	k1 = *(KILLFILE **)p1;
	k2 = *(KILLFILE **)p2;

	i = k1->priority - k2->priority;
	if(i != 0)
		return(i);

	i = k1->type - k2->type;

	if(i != 0)
		return(i);

	return(strcmp_lc(k1->string,k2->string));
}   /* end of kill_sorter */



void kill_newsg_selected(int *hits)
/**********************************/
{
	dbox_setfield(dbox_kill,dbox_menu_field,newsg_menu_name(hits[0],NULL));
}   /* end of kill_newsg_selected */



void kill_source_selected(int *hits)
/**********************************/
{
	char *name_ptr;
	int cat;

	if(hits[0] == 0)
	{
		cat = 0;
		name_ptr = "";
	}
	else
	{
		hits[0]--;
		cat = category_menu_hit(hits, &name_ptr);
	}


	switch(dbox_menu_field)
	{
	case 5:
		dbox_kill_source1 = cat;
		break;
	case 11:
		dbox_kill_source2 = cat;
		break;
	case 17:
		dbox_kill_source = cat;
		break;
	case 19:
		dbox_kill_category = cat;
		break;
	}

	if(name_ptr != NULL)
		dbox_setfield(dbox_kill,dbox_menu_field,name_ptr);
}   /* end of kill_source_selected */




void kill_box_selected(int *hits)
/********************************/
{
	int  box;

	box = box_lookup_number(hits[0]);

	switch(dbox_menu_field)
	{
	case 3:
		if(box == BOX_BIN)
		{
			box = 255;
		}
		dbox_kill_box = box;
		break;

	case 9:
		if(box == BOX_BIN)
		{
			box = -1;
		}
		dbox_kill_copybox = box+1;
		break;
	}

	dbox_setfield(dbox_kill,dbox_menu_field,get_box_name(box));
	dbox_showstatic(dbox_kill);
}   /* end of kill_box_selected */






void text_killfile_add2(TEXTR *t, int k_num, int type, int field)
/***************************************************************/
/* A full-size KILLFILE buffer is allocated when this function iscalled */
{
	KILLFILE *k;
	int  c;
	int  i;
	char *p;
	int  argument2;
	int  arg_end;
	int  length;
	int  id_start;
	int  start=0;
	int  ktype= 0;
	int  add_comment=0;
	int  expiry;
	CARD_EXPANDED *cardex;
	char buf[256];
	char buf2[256];

	k = killfile[k_num];

	length = sizeof(buf)-1;
	cardex = t->cardex;
	buf[0]=0;

	ktype = type;
	switch(type)
	{
	case KF_TITLE:
	case KF_TITLE_CONTAINS:
		strncpy(buf,cardex->title,length);
		break;

	case KF_AUTHOR:
		p = reply_extract_email_addr(cardex->author,3);
		if(p != NULL)
		{
			strncpy(buf,p,length);
		}
		break;

	case KF_DOMAIN:
		p = reply_extract_email_addr(cardex->author,3);
		if(p != NULL)
		{
			p = strchr(p,'@');
			if(p != NULL)
			{
				strncpy(buf,p,length);
			}
		}
		break;

	case KF_NEWSGROUP:   /* newsgroup name */
		if(cardex->status_other & STATUS_BIT_NEWS)
		{
			strcpy(buf,expand_source(cardex->source,0));
		}
		break;

	case KF_SOURCE:
		strcpy(buf,expand_source(cardex->source,0));
		break;


	case KF_MESSAGE_ID:   /* 1st Reference or message ID */
	case KF_MESSAGE_ID2:   /* message ID */
		if(t->internet_header == 0)
		{
			beep();
			break;
		}

		id_start = -1;
		while((c=interpret_article_key(&t->text_base,&start,t->internet_header,&argument2,&arg_end)) >= 0)
		{
			switch(c)
			{
			case 8:   /* references, only use for type 5 */
				if(type==5)
					id_start = argument2;
				break;

			case 9:   /* Message-ID */
				if(id_start < 0)
				{
					id_start = argument2;
				}
			}
		}

		if(id_start >= 0)
		{
			p = &t->text_base[id_start];
			c = 0;
			for(i=0; i<length; i++)
			{
				if(c == '>')
					break;

				c = p[i];
				if(c <= ' ') break;
				buf[i] = c;
			}
			buf[i] = 0;
			ktype = KF_MESSAGE_ID;

			/* add message title as a comment */
			add_comment = 1;
			break;
		}
		break;
	}
	buf[sizeof(buf)-1] = 0;

	if(field==0)
	{
		buf2[0] = 0;

		if(k->type == 0)
			k->type = ktype;

		if(k->string2 > 0)
		{
			strcpy(buf2,&k->string[k->string2]);
		}
		else
		{
			if(add_comment)
			{
				strcpy(buf2,cardex->title);
			}
		}

		strcpy(k->string,buf);
		length = strlen(k->string)+1;

		if(buf2[0] != 0)
		{
			strncpy(&k->string[length],buf2,sizeof(k->string)-(length));
			k->string2 = length;
		}
	}
	else
	{
		k->type2 = ktype;
		length = strlen(k->string) + 1;
		strncpy(&k->string[length],buf,sizeof(k->string)-(length));
		k->string2 = length;
	}

	k->string[sizeof(k->string)-1] = 0;
	k->box = BOX_BIN;

	if(options.filter_expire != 0)
	{
		expiry = date_addition(get_today_date(),options.filter_expire);

		if((type==KF_AUTHOR) || (type==KF_TITLE) || (type==KF_MESSAGE_ID) || (type==KF_MESSAGE_ID2))
			k->expiry = expiry;
	}

	if(k->expiry != 0)
		dbox_setfield(dbox_kill,26,decode_date(k->expiry,0));
	else
		dbox_setfield(dbox_kill,26,"");

}   /* end of text_killfile_add2 */





void kill_type_selected(int *hits)
/********************************/
{
	int  type;
	KILLFILE *k;

	type = killfile_type[hits[0]];

	k = killfile[kill_num];

	if(dbox_menu_field == 2)
	{
		dbox_kill_type = type;
	}
	else
	{
		dbox_kill_type2 = type;
	}
	dbox_setfield(dbox_kill,dbox_menu_field,kill_types[type]);

	if((t_killfile != NULL) && (t_killfile->cardex != NULL))
	{
		k = realloc(killfile[kill_num],sizeof(KILLFILE));
		killfile[kill_num] = k;

		if(dbox_menu_field == 2)
		{
			text_killfile_add2(t_killfile, kill_num, type, 0);
			dbox_setfield(dbox_kill,5,k->string);
		}
		else
		{
			text_killfile_add2(t_killfile, kill_num, type, 1);
			dbox_setfield(dbox_kill,11,&k->string[k->string2]);
		}
	}

	dbox_showstatic(dbox_kill);
}   /* end of kill_type_selected */




void score_op_selected(int *hits)
/*******************************/
{
	char buf[16];

	dbox_kill_score_op = hits[0];
	if(dbox_kill_score_op == 0)
		buf[0] = 0;
	else
		strcpy(buf,score_ops[dbox_kill_score_op]);
	dbox_setfield(dbox_kill,29,buf);
}
/* end of score_op_selected */



void status_selected(int *hits)
/*****************************/
{
	char buf[16];

	dbox_kill_set_status = hits[0];
	if(dbox_kill_set_status == 0)
		buf[0] = 0;
	else
		strcpy(buf,set_status[dbox_kill_set_status]);
	dbox_setfield(dbox_kill,31,buf);
}
/* end of status_selected */





BOOL dbox_kill_raw_handler(dbox d, void *event, void *handle)
/***********************************************************/
{
	int  i;
	int  box_control=0;
	wimp_eventstr *e;
	wimp_mousestr *mouse;

	e = (wimp_eventstr *)event;

	switch (e->e)
	{
	case wimp_EBUT:
		/* click on icon.  get icon number. */
		mouse = &e->data.but.m;
		switch(dbox_menu_field = mouse->i)
		{
		case 9:
		case 3:
			box_control = 0x200;
			if(wmenu_boxes_names == NULL)
				break;

			make_boxes_menu(box_control,NULL);
			dbox_menu(wmenu_boxes_names,kill_box_selected,mouse);
			return(TRUE);

		case 2:
		case 15:
			dbox_menu(wmenu_kill_types,kill_type_selected,mouse);
			return(TRUE);

		case 17:
		case 19:
			dbox_menu(category_make_menu((dbox_menu_field==17)?&src_fr:&cty_fr,1),kill_source_selected,mouse);
			return(TRUE);

		case 5:
		case 11:
			if(dbox_menu_field==5)
				i = dbox_kill_type;
			else
				i = dbox_kill_type2;

			if((i==KF_NEWSGROUP) && (mouse->bbits == 0x2))
			{
				dbox_menu(newsg_make_menu(0),kill_newsg_selected,mouse);
				return(TRUE);
			}
			else if(i==KF_SOURCE)
			{
				dbox_menu(category_make_menu(&src_fr,1),kill_source_selected,mouse);
				return(TRUE);
			}
			break;

		case 20:   /* Discard option */
			dbox_fade_list(d,kill_fade,dbox_getnumeric(d,20));
			break;

		case 28:   /* Score operations */
			dbox_menu(wmenu_score_ops,score_op_selected,mouse);
			return(TRUE);

		case 30:   /* Set status */
			dbox_menu(wmenu_set_status,status_selected,mouse);
			return(TRUE);
		}
		break;
	}
	return(help_handler(event,HELP_FILTER));
}   /* end of dbox_kill_raw_handler */




void dbox_kill_handler(dbox d, void *handle)
/*********************************************/
{
	KILLFILE *k;
	int  num;
	int  c;
	int  field;
	int  length;
	char buf[32];
	char buf2[32];
	char string1[128];
	char string2[128];

	k = killfile[kill_num];

	switch(field = dbox_get(d))
	{
	case 24:
		if((c = dbox_getnumeric(d,13)) < 99)
			dbox_setnumeric(d,13,c+1);
		killfile_resort = 1;
		return;

	case 25:
		if((c = dbox_getnumeric(d,13)) > 0)
			dbox_setnumeric(d,13,c-1);
		killfile_resort = 1;
		return;


	case 0:
		if(dbox_kill_type == 0)
		{
			werr(0,"No 'Match on' filter type");
			return;
		}
		else
		{
			k->type = dbox_kill_type;
			k->type2 = dbox_kill_type2;
			k->source = dbox_kill_source;
			k->category = dbox_kill_category;
			k->box = dbox_kill_box;
			k->copy_box = dbox_kill_copybox;
			k->score_op = dbox_kill_score_op;
			k->status = dbox_kill_set_status | (k->status & ~0x7);

			dbox_getfield(dbox_kill,5,string1,sizeof(string1));
			if(k->type == KF_SOURCE)
			{
				string1[0] = dbox_kill_source1;
				string1[1] = 0;
			}

			dbox_getfield(dbox_kill,11,string2,sizeof(string2));
			if(k->type2 == KF_SOURCE)
			{
				string2[0] = dbox_kill_source2;
				string2[1] = 0;
			}

			k->score = dbox_getnumeric(dbox_kill,27);

			if(k->priority != dbox_getnumeric(d,13))
			{
				k->priority = dbox_getnumeric(d,13);
				killfile_resort = 1;
			}


			if((strlen(string1) + strlen(string2) + 26) >= 255)
			{
				werr(0,"String is too long");
				return;
			}

			k->flags = (dbox_getnumeric(dbox_kill,8) << 2) |
					   (dbox_getnumeric(dbox_kill,6) << 1) |
					   dbox_getnumeric(dbox_kill,7) |
					   (dbox_getnumeric(dbox_kill,22) << 3) |
					   (dbox_getnumeric(dbox_kill,23) << 4);

			k->regex =  dbox_getnumeric(dbox_kill,12) |
						(dbox_getnumeric(dbox_kill,14) << 1) |
						(dbox_getnumeric(dbox_kill,16) << 7) |
						(dbox_getnumeric(dbox_kill,18) << 6);


			if((k->type != KF_CAPS) && (k->type != KF_ADDR_BOOK) && (string1[0] == 0))
			{
				werr(0,"Text string field is empty");
				return;
			}

			if((k->type != KF_MESSAGE_ID) && (k->type != KF_SOURCE) && ((k->regex & 1)==0))
			{
				/* convert to lower case */
				strcpy_no_accents(string1,string1);
			}

			if((k->type2 != 0) && (k->type2 != KF_CAPS) && (k->type2 != KF_ADDR_BOOK) && (string2[0] == 0))
			{
				werr(0,"Text string field is empty");
				return;
			}

			if((k->type2 != KF_MESSAGE_ID) && (k->type2 != KF_SOURCE) && ((k->regex & 2)==0))
			{
				/* convert to lower case */
				strcpy_no_accents(string2,string2);
			}



			dbox_getfield(dbox_kill,26,buf,sizeof(buf));
			k->expiry = encode_date(buf,2);

			if((k->expiry == 0) && (sscanf(buf,"%d %s",&num,buf2)==1))
			{
				if(num > 0)
					k->expiry = date_addition(get_today_date(),num);
			}

			if(dbox_getnumeric(dbox_kill,20))
			{
				k->box = BOX_BIN;
			}
			else
			{
				if(k->box == BOX_BIN)
					k->box = 255;   /* don't set box */
			}

			length = strlen(string1) + strlen(string2) + 26;
			k = realloc(killfile[kill_num],length);
			killfile[kill_num] = k;

			strcpy(k->string,string1);
			k->length = strlen(string1);

			k->string2 = strlen(k->string)+1;
			strcpy(&k->string[k->string2],string2);

			if(killfile_add_flag)
			{
				n_killfile++;
				killlist.n_entries = n_killfile;
			}

			if(killfile_resort)
			{
				killfile_resort=0;
				qsortG(killfile,n_killfile,sizeof(void *),kill_sorter);
				redraw_blist(&killlist);
			}

			kill_save();
			blist_set_extent(&killlist,6);
			if(kill_compile_regex() < 0) return;

			if(dbox_persist()) return;
		}
		break;

	default:
		if(killfile_add_flag)
			free(k);
		break;
	}

	dbox_hide(dbox_kill);
}   /* end of dbox_kill_handler */





void killfile_edit(int line, TEXTR *t)
/************************************/
{
	KILLFILE *k;
	int  box;
	char buf[16];

	t_killfile = t;

	if(line == -1)
	{
		/* add new entry */
		killfile_add_flag = 1;
		kill_num = n_killfile;
		killfile_resort = 1;
	}
	else
	{
		killfile_add_flag = 0;
		kill_num = line;
		killfile_resort = 0;
	}

	k = killfile[kill_num];
	if(k == NULL)
		return;

	dbox_kill_type = k->type;
	dbox_kill_type2 = k->type2;
	dbox_kill_box = k->box;
	dbox_kill_copybox = k->copy_box;
	dbox_kill_source = k->source;
	dbox_kill_category = k->category;
	dbox_kill_score_op = k->score_op;
	dbox_kill_set_status = k->status & 0x7;
	dbox_kill_source1 = k->string[0];
	dbox_kill_source2 = k->string[k->string2];

	dbox_setfield(dbox_kill,2,kill_types[k->type]);
	dbox_setfield(dbox_kill,15,kill_types[k->type2]);
	dbox_setnumeric(dbox_kill,13,k->priority);
	dbox_setnumeric(dbox_kill,12,k->regex & 1);
	dbox_setnumeric(dbox_kill,14,k->regex & 2);
	dbox_setnumeric(dbox_kill,16,k->regex & KF_AND_NOT);
	dbox_setnumeric(dbox_kill,18,k->regex & KF_OR);
	dbox_setnumeric(dbox_kill,21,(k->regex & 0xc0)==0);
	dbox_setnumeric(dbox_kill,27,k->score);

	if(k->score_op == 0)
		buf[0] = 0;
	else
		strcpy(buf,score_ops[k->score_op]);
	dbox_setfield(dbox_kill,29,buf);

	if((k->status & 0x7) == 0)
		buf[0] = 0;
	else
		strcpy(buf,set_status[k->status & 0x7]);
	dbox_setfield(dbox_kill,31,buf);

	if(k->box == 255)
		box = -1;   /* no box set */
	else
		box = k->box;

	if(k->box == BOX_BIN)
	{
		box = -1;
		dbox_setnumeric(dbox_kill,20,1);
		dbox_fade_list(dbox_kill,kill_fade,1);
	}
	else
	{
		dbox_setnumeric(dbox_kill,20,0);
		dbox_fade_list(dbox_kill,kill_fade,0);
	}
	dbox_setfield(dbox_kill,3,get_box_name2(box,1));
	dbox_setfield(dbox_kill,9,get_box_name2(k->copy_box-1,1));   /* box -1 will give blank */
	dbox_setfield(dbox_kill,17,category_name(&src_fr,k->source));
	dbox_setfield(dbox_kill,19,category_name(&cty_fr,k->category));

	if(k->expiry != 0)
		dbox_setfield(dbox_kill,26,decode_date(k->expiry,0));
	else
		dbox_setfield(dbox_kill,26,"");


	if(k->type==KF_SOURCE)
		dbox_setfield(dbox_kill,5,expand_source(k->string[0],0));
	else
		dbox_setfield(dbox_kill,5,k->string);

	if(k->string2 > 0)
	{
		if(k->type2==KF_SOURCE)
			dbox_setfield(dbox_kill,11,expand_source(k->string[k->string2],0));
		else
			dbox_setfield(dbox_kill,11,&k->string[k->string2]);
	}
	else
		dbox_setfield(dbox_kill,11,"");

	dbox_setnumeric(dbox_kill,6,k->flags & 2);
	dbox_setnumeric(dbox_kill,7,k->flags & 1);
	dbox_setnumeric(dbox_kill,8,k->flags & 4);
	dbox_setnumeric(dbox_kill,22,k->flags & 8);
	dbox_setnumeric(dbox_kill,23,k->flags & 16);

	dbox_hide(dbox_kill);
	dbox_showstatic(dbox_kill);
}   /* end of killfile_edit */





void kill_dump2(FILE *f, KILLFILE *k)
/***********************************/
{
	int  i;
	char *regex1="";
	char *regex2="";
	static char *regex="REGEX";
	char *boolop;
	char *string;
	char *active;
	static char *kill_apply[] = {"news","mail","maillists","iconbar","outgoing"};

	if(k->regex & 1)
		regex1 = regex;
	if(k->regex & 2)
		regex2 = regex;

	boolop = bool_strings[(k->regex >> 6) & 3] ;
	if(k->active)
		active = "active";
	else
		active = "inactive";

	fprintf(f,"FILTER priority %d  %s\n",k->priority,active);

	if(k->type==KF_SOURCE)
		string = expand_source(k->string[0],0);
	else
		string = k->string;

	fprintf(f," %s \"%s\" %s\n",kill_types[k->type],string,regex1);


	if(k->type2 >0)
	{
		if(k->type2==KF_SOURCE)
			string = expand_source(k->string[k->string2],0);
		else
			string = &k->string[k->string2];

		fprintf(f," %s %s \"%s\" %s\n",boolop,kill_types[k->type2],string,regex2);
	}

	fprintf(f," Apply_to: ");
	for(i=0; i<5; i++)
	{
		if(k->flags & (1 << i))
			fprintf(f,"%s ",kill_apply[i]);
	}
	fprintf(f,"\n");

	if(k->expiry != 0)
		fprintf(f," Expire: %s\n",decode_date(k->expiry,0));

	fprintf(f," Action: ");
	if(k->box==BOX_BIN)
		fprintf(f,"discard ");
	else if(k->box != 255)
		fprintf(f,"box=\"%s\" ",get_box_name2(k->box,1));

	if(k->copy_box != 0)
		fprintf(f,"copy=\"%s\" ",get_box_name2(k->copy_box-1,1));

	if((k->status & 0x7) != 0)
		fprintf(f,"status=%s ",set_status[k->status & 0x7]);

	if(k->score_op > 0)
		fprintf(f,"score%s%d ",score_ops[k->score_op],k->score);

	if(k->source != 0)
		fprintf(f,"source=\"%s\" ",category_name(&src_fr,k->source));

	if(k->category != 0)
		fprintf(f,"category=\"%s\" ",category_name(&cty_fr,k->category));

	fprintf(f,"\n\n");

}  /* end of kill_dump2 */



void kill_dump()
/**************/
/* Save Filters as a text file */
{
	int  ix;
	FILE *f;
	char fname[128];

	sprintf(fname,"%stmp.filters",pluto_path);
	f = fopen_werr(fname,"w",NULL);
	if(f == NULL)
	{
		return;
	}

	for(ix=0; ix<n_killfile; ix++)
	{
		kill_dump2(f,killfile[ix]);
	}
	fclose(f);

	dataopen(fname,0xfff);
}   /* end of kill_dump */





static void kill_select(BLIST *b, int select)
/*******************************************/
{
	int  i;
	for(i=0; i<n_killfile; i++)
	{
		if(killfile[i]->selected != select)
		{
			killfile[i]->selected = select;
			redraw_blist_line(b,i);
		}
	}
}   /* end of kill_select */




KILLFILE *killfile_add(int flags)
/*******************************/
{
	KILLFILE *k;

	if(n_killfile >= (killfile_max-1))
	{
		beep();
		return(NULL);
	}

	k = (KILLFILE *)calloc(sizeof(KILLFILE),1);
	if(k == NULL)
	{
		malloc_err(93);
		return(NULL);
	}

	k->flags = flags;
	k->active = 1;
	k->box = options.filter_box;
	k->priority = 5;
	k->string[0] = 0;
	killfile[n_killfile] = k;
	return(k);
}   /* end of killfile_add */




void kill_activate(int active)
/****************************/
{
	int  i;

	for(i=0; i<n_killfile; i++)
	{
		if(killfile[i]->selected)
			killfile[i]->active = active;
	}
	kill_save();
	redraw_blist(&killlist);
}   /* end of kill_activate */



void kill_delete(BLIST *b)
/************************/
/* Delete selected entries */
{
	int  i;
	int  j;

	for(i=0; i<n_killfile; i++)
	{
		if(killfile[i]->selected)
		{
			free(killfile[i]);
			for(j=i+1; j<n_killfile; j++)
			{
				killfile[j-1] = killfile[j];
			}
			b->n_entries--;
			n_killfile--;
			i--;
		}
	}
	kill_save();
	blist_set_extent(b,7);
	kill_compile_regex();
}   /* end of kill_delete */




void kill_click_buttons(BLIST *b, wimp_eventdata *d)
/**************************************************/
/* Click on the killfile list button bar */
{
	int  icon;
	int  click;
	KILLFILE *k;

	icon = d->but.m.i;
	click = d->but.m.bbits;

	redraw_blist(b);

	switch(icon)
	{
	case 4:    /* add new killfile entry */
		k = killfile_add(3);
		if(k == NULL)
			break;
		killfile_edit(-1,NULL);
		break;

	case 1:    /* delete selected items */
		kill_delete(b);
		break;

	case 2:    /* activate */
		kill_activate(1);
		break;

	case 3:    /* deactivate */
		kill_activate(0);
		break;
	}

}   /* end of kill_click_buttons */





void kill_click_window(BLIST *b,int linenum,int bbits)
/****************************************************/
{
	switch(bbits)
	{
	case wimp_BCLICKLEFT:
		kill_select(b,0);
		killfile[linenum]->selected = 1;
		redraw_blist_line(b,linenum);
		break;

	case wimp_BCLICKRIGHT:
		killfile[linenum]->selected ^= 1;
		redraw_blist_line(b,linenum);
		break;

	case wimp_BLEFT:
		killfile[linenum]->selected = 0;
		redraw_blist_line(b,linenum);
	case wimp_BRIGHT:        /* adjust, 2nd click */
		killfile_edit(linenum,NULL);
		break;
	}
}   /* end of kill_click_window */




void kill_key_window(BLIST *b,wimp_eventdata *d)
/**************************************************/
{
	int  c;

	switch(c = d->key.chcode)
	{
	case 1:    /* CTRL-A */
		kill_select(b,1);
		break;

	case 26:   /* CTRL-Z */
		kill_select(b,0);
		break;

	default:
		wimp_processkey(c);
		break;
	}
}   /* end of kill_key_window */





void kill_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/************************************************************************************/
{
	os_regset regs;
	KILLFILE *k;
	char *p_string;
	char *p_string2;
	char *type_string;
	int  i;
	int  j;
	int  tick;
	int  y_top;
	char date[16];
	char boxname[32];
	char buf[256];
	char priority[8];
	char flags_string[8];
	int sprite_iconblk[12];


	y_top = r->box.y1 - r->scy + 36 - lists_font_offset;

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		k = killfile[line];
		if((k->type < 0) || (k->type > 16))
			type_string = "???";
		else
			type_string = kill_types[k->type];

		tick = 0;

		if(k->selected)
			list_set_colour(0, 0,x,y);
		else
			list_set_colour(0, 1,x,y);


		if(k->expiry)
		{
			strcpy(date,decode_date(k->expiry,0));
			date[6]=0;  /* day and month only */
		}

		sprintf(priority,"%d",k->priority);
		strcpy(boxname,get_box_name(k->box));

		j=0;
		for(i=0; i<5; i++)
		{
			if(k->flags & (1<<i))
			{
				flags_string[j++] = flags_letters[i];
			}
			else
			{
				if(standard_font >= 0)
					flags_string[j++] = ' ';
			}
		}
		flags_string[j] = 0;


		p_string = k->string;

		if((k->type2 == 0) && ((k->type==KF_MESSAGE_ID) || (k->type==KF_MESSAGE_ID2)))
		{
			/* show the comment string instead of the message-id. it's more readable */
			if(k->string[k->string2] != 0)
				p_string = &k->string[k->string2];
		}
		else if(k->type==KF_SOURCE)
		{
			strcpy(buf,expand_source(k->string[0],0));
			p_string = buf;
		}

		if(k->type2 != 0)
		{
			p_string2 = &k->string[k->string2];
			if(k->type2==KF_SOURCE)
			{
				p_string2 = expand_source(p_string2[0],0);
			}

			sprintf(buf,"%s (%s %s)",p_string,bool_strings[(k->regex >> 6) & 3] ,p_string2);
			p_string = buf;
		}

		if(standard_font >= 0)
		{
			regs.r[0] = standard_font;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[4] = y;

			if(k->active)
			{
				if(k->expiry)
				{
					regs.r[1] = (int)date;
					regs.r[3] = x+8;
					regs.r[7] = strlen(date);
					os_swi(0x40086+os_X,&regs);
				}
				else
				{
					tick = 1;
					paint_string(flags_string,x+40,y,82);
				}
			}

			paint_string(priority,x+124,y,36);
			paint_string(type_string,x+160,y,138);
			paint_string(p_string,x+316,y,506);
			paint_string(boxname,x+840,y,186);

		}
		else
		{
			if(k->active)
			{
				bbc_move(x+8,y-4);
				if(k->expiry)
				{
					printf(date);
				}
				else
				{
					tick = 1;
					bbc_move(x+40,y-4);
					printf(flags_string);
				}
			}

			bbc_move(x+132, y-4);
			printf(priority);

			bbc_move(x+376, y-4);
			strncpy0(buf,p_string,sizeof(buf));
			buf[27] = 0;
			printf("%s",buf);

			bbc_move(x+156, y-4);
			strcpy(buf,type_string);
			buf[14] = 0;
			printf("%s",buf);

			bbc_move(x+826, y-4);
			printf("%s",boxname);

		}

		if(tick)
		{
			strcpy((char *)&sprite_iconblk[8],"\200");   /* tick sprite */
			sprite_iconblk[0] = 0;
			sprite_iconblk[1] = y-y_top;
			sprite_iconblk[2] = 0+40;
			sprite_iconblk[3] = y-y_top+36;
			if(k->selected)
				sprite_iconblk[4] = 0x20011a;
			else
				sprite_iconblk[4] = 0x11a;  /* bits 1, 3, 4, 8 */
			sprite_iconblk[5] = (int)&sprite_iconblk[8];
			sprite_iconblk[6] = 1;   /* wimp sprites */
			sprite_iconblk[7] = 1;  /* length of sprite name */

			wimp_ploticon((wimp_icon *)&sprite_iconblk);
		}

		y -= lists_line_height;
	}
}   /* kill_display_window */




void expire_killfile()
/********************/
{
	int  ix;
	int  i;
	int  t;
	int  count=0;
	int  current_date;

	current_date = get_today_date();

	for(ix=0; ix<n_killfile; ix++)
	{
		if(((t = killfile[ix]->expiry) != 0) && (t <= current_date))
		{
			/* this killfile entry has expired */
			free(killfile[ix]);
			count++;

			/* move up following entries */
			n_killfile--;
			for(i=ix; i<n_killfile; i++)
			{
				killfile[i] = killfile[i+1];
			}
		}
	}
	killlist.n_entries = n_killfile;
	if(count > 0)
	{
		kill_save();
		kill_compile_regex();
	}
}   /* end of expire_killfile */





void text_killfile_add(TEXTR *t, int type)
/***************************************/
{
	KILLFILE *k;
	int  flags;
	CARD_EXPANDED *cardex;
	char source_name[128];
	static char mail_type_flags[] = {8,1,2,2,4,0,0,0};

	cardex = t->cardex;

	strcpy(source_name,expand_source(cardex->source,0));
	flags = mail_type_flags[card_mail_type(cardex,source_name)];
	k = killfile_add(flags);
	if(k == NULL)
		return;


	k->priority = 7;   /* lower than the standard priority */
	k->type = 0;

	text_killfile_add2(t,n_killfile,type,0);
	killfile_edit(-1,t);
}   /* end of text_killfile_add */



void list_killfile_add(FOLDREC *fr)
/*********************************/
{
	CARD *cptr;
	TEXTR *t;
	CARD_EXPANDED cardex;

	list_enumerate_start(fr);
	cptr = list_enumerate_next(fr,fr->ixlist);
	if(cptr == NULL)
	{
		beep();
		return;
	}

	unpack_card(cptr, &cardex);
	t = &text_rec_temp;
	t->cardex = &cardex;
	if(article_read(t,cardex.addr,cardex.docbox,cardex.text_offset,cardex.text_length,0,0,0) < 0)
	{
		beep();
		return;
	}
	text_interpret_article(t,&cardex,0);
	if(cardex.status_other & STATUS_BIT_NEWS)
		text_killfile_add(t,KF_MESSAGE_ID);  /* news thread */
	else
		text_killfile_add(t,KF_TITLE);  /* subject */
	visdelay2_end();
}   /* end of list_killfile_add */





void filter_options_box_selected(int *hits)
/***************************************/
{

	dbox_score_box = box_lookup_number(hits[0]);
	dbox_setfield(dbox_options,dbox_menu_field,get_box_name2(dbox_score_box,3));
	dbox_showstatic(dbox_options);

}   /* end of filter_options_box_selected */




BOOL dbox_filter_options_raw_handler(dbox d, void *event, void *handle)
/*********************************************************************/
{
	wimp_eventstr *e;
	wimp_mousestr *mouse;

	e = (wimp_eventstr *)event;

	switch (e->e)
	{
	case wimp_EBUT:
		/* click on icon.  get icon number. */
		mouse = &e->data.but.m;
		switch(dbox_menu_field = mouse->i)
		{
		case 6:
			if(wmenu_boxes_names == NULL)
				break;

			make_boxes_menu(0x600,NULL);
			dbox_menu(wmenu_boxes_names,filter_options_box_selected,mouse);
			return(TRUE);
		}
		break;
	}
	return(help_handler(event,HELP_OPFILTER));
}   /* end of dbox_filter_options_raw_handler */



void filter_options_handler(dbox d, void *handle)
/***********************************************/
{
	int  i;

	if(dbox_get(dbox_options)==0)
	{
		options.filter_expire = dbox_getnumeric(dbox_options,2);

		i = dbox_getnumeric(dbox_options,3);
		options.filter_set_read = dbox_range_check(i,-9999,0);

		i = dbox_getnumeric(dbox_options,4);
		options.filter_discard = dbox_range_check(i,-9999,0);

		i = dbox_getnumeric(dbox_options,5);
		options.filter_show_tick = dbox_range_check(i,0,9999);

		options.filter_score_box = dbox_score_box;

		options_save();
		if(dbox_persist())
			return;
	}
	dbox_dispose(&dbox_options);
}   /* end of filter_options_handler */




void options_filters()
/********************/
{
	dbox_options = dbox_new("FilterOpts");
	dbox_eventhandler(dbox_options,filter_options_handler,NULL);
	dbox_raw_eventhandler(dbox_options,dbox_filter_options_raw_handler,NULL);

	dbox_setnumeric(dbox_options,2,options.filter_expire);
	dbox_setnumeric(dbox_options,3,options.filter_set_read);
	dbox_setnumeric(dbox_options,4,options.filter_discard);
	dbox_setnumeric(dbox_options,5,options.filter_show_tick);

	dbox_score_box = options.filter_score_box;
	dbox_setfield(dbox_options,6,get_box_name2(dbox_score_box,3));
	dbox_showstatic(dbox_options);
}   /* end of options_filters */



void filter_select(int line, int selected)
/************************************/
{
	if(selected == 2)
		killfile[line]->selected ^= 1;    /* invert */
	else
		killfile[line]->selected = selected;
	redraw_blist_line(&killlist,line);
}   /* filter_select */





void filter_menu_proc(void *handle,char *hit)
/*******************************************/
{
	KILLFILE *k;
	BLIST *b;
	int  ix;

	b = &killlist;

	switch(hit[0])
	{
	case 1:
		k = killfile_add(3);
		if(k == NULL)
			break;
		killfile_edit(-1,NULL);
		break;

	case 2:
		for(ix=0; ix<n_killfile; ix++)
		{
			if(killfile[ix]->selected != 0)
				killfile_edit(ix,NULL);
		}
		break;

	case 3:
		kill_activate(1);
		break;

	case 4:
		kill_activate(0);
		break;

	case 5:
		kill_select(b,1);
		return;     /* don't deselect line selected by menu-click */

	case 6:
		kill_delete(b);
		break;

	case 7:
		kill_select(b,0);
		break;

	case 8:
		kill_dump();
		break;

	case 9:
		options_filters();
		break;
	}
	if(blist_current_menu == menu_filters)
	{
		filter_select(blist_current_menu_line,0);
	}
}   /* end of filter_menu_proc */




menu filter_menu_maker(void *handle)
/**********************************/
{
	int  ix;
	int  count=0;
	int  fade=0;

	/* are there any selected items in this list */
	for(ix=0; ix<killlist.n_entries; ix++)
	{
		if(killfile[ix]->selected)
		{
			count++;
		}
	}

	if(count == 0)
	{
		/* no items selected, menu button selects one */
		/* get line number of last muse click */
		blist_current_menu = NULL;
		if((blist_current_menu_line = list_get_linenum(killlist.w_list)) < killlist.n_entries)
		{
			killfile[blist_current_menu_line]->selected = 1;
			redraw_blist_line(&killlist,blist_current_menu_line);

			blist_current_menu = menu_filters;
			blist_current_menu_blist = BLIST_KILL;
			count = 1;
		}
	}


	if(count != 1)
		fade = 1;
	menu_setflags(menu_filters,2,0,fade);
	return(menu_filters);
}   /* end of addrlist_menu_maker */





void kill_open()
/***************/
{
	blist_set_extent(&killlist,3);
}   /* end of kill_open */




void init_killfile()
/******************/
{
	menu m;

	/* killfile list */
	blist_register(&killlist,BLIST_KILL,"KillBar");
	menu_filters = menu_new("Filters",menustr_filters);
	event_attachmenumaker(killlist.w_list,filter_menu_maker,filter_menu_proc,NULL);

	n_killfile = 0;

	dbox_kill = dbox_new("KillFile");
	dbox_raw_eventhandler(dbox_kill,dbox_kill_raw_handler,NULL);
	dbox_eventhandler(dbox_kill, dbox_kill_handler, NULL);

	m = menu_new("Filter Types",menustr_kill_types);
	wmenu_kill_types = (wimp_menustr *)menu_syshandle(m);

	m = menu_new("Score Ops",menustr_score_ops);
	wmenu_score_ops = (wimp_menustr *)menu_syshandle(m);

	m = menu_new("Status",menustr_set_status);
	wmenu_set_status = (wimp_menustr *)menu_syshandle(m);

	kill_load();
	killlist.n_entries = n_killfile;
}   /* end of init_killfile */


