Blob Blame History Raw
---
 alpine/adrbkcmd.c         |    3 
 alpine/alpine.c           |    4 
 alpine/confscroll.c       |   33 
 alpine/dispfilt.c         |   60 +
 alpine/dispfilt.h         |    2 
 alpine/folder.c           |    3 
 alpine/mailcmd.c          |   18 
 alpine/mailindx.c         |    6 
 alpine/mailpart.c         |   12 
 alpine/mailview.c         |    2 
 alpine/osdep/termin.gen.c |   41 +
 alpine/reply.c            |  102 ++
 alpine/roleconf.c         |    5 
 alpine/send.c             |   40 +
 pith/Makefile.am          |    2 
 pith/Makefile.in          |    5 
 pith/adrbklib.c           |   10 
 pith/conf.c               |   90 ++
 pith/conf.h               |   40 +
 pith/conftype.h           |   15 
 pith/detoken.c            |   33 
 pith/indxtype.h           |   34 
 pith/mailcmd.c            |  435 +++++++++---
 pith/mailcmd.h            |    8 
 pith/mailindx.c           |  336 +++++++--
 pith/mailindx.h           |    3 
 pith/makefile.wnt         |    5 
 pith/pine.hlp             | 1150 +++++++++++++++++++++++++++++++++
 pith/reply.c              |  279 +++++++-
 pith/rules.c              | 1566 ++++++++++++++++++++++++++++++++++++++++++++++
 pith/rules.h              |  154 ++++
 pith/rulestype.h          |   94 ++
 pith/save.c               |    2 
 pith/send.c               |   58 +
 pith/sort.c               |   55 +
 pith/sort.h               |    2 
 pith/state.c              |    4 
 pith/state.h              |   11 
 pith/string.c             |   58 +
 pith/string.h             |    2 
 40 files changed, 4486 insertions(+), 296 deletions(-)

Index: alpine-2.26/alpine/adrbkcmd.c
===================================================================
--- alpine-2.26.orig/alpine/adrbkcmd.c
+++ alpine-2.26/alpine/adrbkcmd.c
@@ -4125,6 +4125,8 @@ ab_compose_internal(BuildTo bldto, int a
 	 * won't do anything, but will cause compose_mail to think there's
 	 * already a role so that it won't try to confirm the default.
 	 */
+	if (ps_global->role)
+	   fs_give((void **)&ps_global->role);
 	if(role)
 	  role = copy_action(role);
 	else{
@@ -4132,6 +4134,7 @@ ab_compose_internal(BuildTo bldto, int a
 	    memset((void *)role, 0, sizeof(*role));
 	    role->nick = cpystr("Default Role");
 	}
+	ps_global->role = cpystr(role->nick);
     }
 
     compose_mail(addr, fcc, role, NULL, NULL);
Index: alpine-2.26/alpine/alpine.c
===================================================================
--- alpine-2.26.orig/alpine/alpine.c
+++ alpine-2.26/alpine/alpine.c
@@ -502,6 +502,7 @@ main(int argc, char **argv)
     /* Set up optional for user-defined display filtering */
     pine_state->tools.display_filter	     = dfilter;
     pine_state->tools.display_filter_trigger = dfilter_trigger;
+    pine_state->tools.exec_rule		     = exec_function_rule;
 
 #ifdef _WINDOWS
     if(ps_global->install_flag){
@@ -3290,6 +3291,9 @@ goodnight_gracey(struct pine *pine_state
     extern KBESC_T *kbesc;
 
     dprint((2, "goodnight_gracey:\n"));    
+    strncpy(pine_state->cur_folder, pine_state->inbox_name, 
+					sizeof(pine_state->cur_folder));
+    pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0';
 
     /* We want to do this here before we close up the streams */
     trim_remote_adrbks();
Index: alpine-2.26/alpine/confscroll.c
===================================================================
--- alpine-2.26.orig/alpine/confscroll.c
+++ alpine-2.26/alpine/confscroll.c
@@ -48,6 +48,7 @@
 #include "../pith/tempfile.h"
 #include "../pith/pattern.h"
 #include "../pith/charconv/utf8.h"
+#include "../pith/rules.h"
 
 
 #define	CONFIG_SCREEN_HELP_TITLE	_("HELP FOR SETUP CONFIGURATION")
@@ -2461,6 +2462,9 @@ delete:
 	 * Now go and set the current_val based on user_val changes
 	 * above.  Turn off command line settings...
 	 */
+	set_current_val((*cl)->var,
+	    (strcmp((*cl)->var->name,"key-definition-rules") ? TRUE : FALSE),
+	    FALSE);
 	set_current_val((*cl)->var, TRUE, FALSE);
 	fix_side_effects(ps, (*cl)->var, 0);
 
@@ -5258,6 +5262,35 @@ fix_side_effects(struct pine *ps, struct
 	    var == &ps->vars[V_ABOOK_FORMATS]){
 	addrbook_reset();
     }
+    else if(var == &ps->vars[V_INDEX_RULES]){
+	   if(ps_global->rule_list)
+	      free_parsed_rule_list(&ps_global->rule_list);
+	   create_rule_list(ps->vars);
+	   reset_index_format();
+	   clear_index_cache(ps->mail_stream, 0);
+    }
+    else if(var == &ps->vars[V_COMPOSE_RULES] ||
+	    var == &ps->vars[V_FORWARD_RULES] ||
+	    var == &ps->vars[V_KEY_RULES] ||
+	    var == &ps->vars[V_REPLACE_RULES] ||
+	    var == &ps->vars[V_REPLY_INDENT_RULES] ||
+	    var == &ps->vars[V_REPLY_LEADIN_RULES] ||
+	    var == &ps->vars[V_RESUB_RULES] ||
+	    var == &ps->vars[V_SAVE_RULES] ||
+	    var == &ps->vars[V_SMTP_RULES] ||
+	    var == &ps->vars[V_SORT_RULES] ||
+	    var == &ps->vars[V_STARTUP_RULES] ||
+	    var == &ps->vars[V_THREAD_DISP_STYLE_RULES] ||
+	    var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){
+	if(ps_global->rule_list)
+	   free_parsed_rule_list(&ps_global->rule_list);
+	create_rule_list(ps->vars);
+	if(var == &ps->vars[V_REPLACE_RULES] ||
+	   var == &ps->vars[V_RESUB_RULES]){
+	  reset_index_format();
+	  clear_index_cache(ps->mail_stream, 0);
+	}
+    }
     else if(var == &ps->vars[V_INDEX_FORMAT]){
 	reset_index_format();
 	clear_index_cache(ps->mail_stream, 0);
Index: alpine-2.26/alpine/dispfilt.c
===================================================================
--- alpine-2.26.orig/alpine/dispfilt.c
+++ alpine-2.26/alpine/dispfilt.c
@@ -457,3 +457,63 @@ df_valid_test(struct mail_bodystruct *bo
 
     return(passed);
 }
+
+char *
+exec_function_rule(char *rawcmd, gf_io_t input_gc, gf_io_t output_pc)
+{
+    char *status = NULL, *cmd,  *tmpfile = NULL;
+
+    if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,NULL,NULL,NULL,NULL,NULL)) != NULL){
+	suspend_busy_cue();
+	ps_global->mangled_screen = 1;
+	if(tmpfile){
+	    PIPE_S	  *filter_pipe;
+	    FILE          *fp;
+	    gf_io_t	   gc, pc;
+	    STORE_S       *tmpf_so;
+
+	    /* write the tmp file */
+	    if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+	        /* copy input to tmp file */
+		gf_set_so_writec(&pc, tmpf_so);
+		gf_filter_init();
+		status = gf_pipe(input_gc, pc);
+		gf_clear_so_writec(tmpf_so);
+		if(so_give(&tmpf_so) != 0 && status == NULL)
+		  status = error_description(errno);
+
+		/* prepare the terminal in case the filter uses it */
+		if(status == NULL){
+		    if((filter_pipe = open_system_pipe(cmd, NULL, NULL,
+						      PIPE_USER|PIPE_PROT|PIPE_NOSHELL|PIPE_SILENT,
+						      0, pipe_callback, NULL)) != NULL){
+			if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){
+			    /* pull result out of tmp file */
+			    if((fp = our_fopen(tmpfile, "rb")) != NULL){
+				gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE);
+				gf_filter_init();
+				status = gf_pipe(gc, output_pc);
+				fclose(fp);
+			    }
+			    else
+			      status = "Can't read result of EXEC command";
+			}
+			else
+			  status = "EXEC command command returned error.";
+		    }
+		    else
+		      status = "Can't open pipe for EXEC command";
+		}
+
+		our_unlink(tmpfile);
+	    }
+	    else
+	      status = "Can't open EXEC command tmp file";
+	}
+
+	resume_busy_cue(0);
+	fs_give((void **)&cmd);
+    }
+
+    return(status);
+}
Index: alpine-2.26/alpine/dispfilt.h
===================================================================
--- alpine-2.26.orig/alpine/dispfilt.h
+++ alpine-2.26/alpine/dispfilt.h
@@ -25,7 +25,7 @@ char	*dfilter_trigger(BODY *, char *, si
 char	*expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *);
 char	*filter_session_key(void);
 char	*filter_data_file(int);
-
+char	*exec_function_rule(char *, gf_io_t, gf_io_t);
 
 
 #endif /* PINE_DISPFILT_INCLUDED */
Index: alpine-2.26/alpine/folder.c
===================================================================
--- alpine-2.26.orig/alpine/folder.c
+++ alpine-2.26/alpine/folder.c
@@ -244,7 +244,7 @@ folder_screen(struct pine *ps)
     dprint((1, "=== folder_screen called ====\n"));
     mailcap_free(); /* free resources we won't be using for a while */
     ps->next_screen = SCREEN_FUN_NULL;
-
+    strcpy(ps->screen_name, "folder");
     /* Initialize folder state and dispatches */
     memset(&fs, 0, sizeof(FSTATE_S));
     fs.context		= cntxt;
@@ -341,6 +341,7 @@ folder_screen(struct pine *ps)
       pine_mail_close(*fs.cache_streamp);
 
     ps->prev_screen = folder_screen;
+    strcpy(ps->screen_name, "unknown");
 }
 
 
Index: alpine-2.26/alpine/mailcmd.c
===================================================================
--- alpine-2.26.orig/alpine/mailcmd.c
+++ alpine-2.26/alpine/mailcmd.c
@@ -69,6 +69,7 @@
 #include "../pith/tempfile.h"
 #include "../pith/search.h"
 #include "../pith/margin.h"
+#include "../pith/rules.h"
 #ifdef _WINDOWS
 #include "../pico/osdep/mswin.h"
 #endif
@@ -2721,6 +2722,9 @@ role_compose(struct pine *state)
 		role->nick = cpystr("Default Role");
 	    }
 
+	    if(state->role)
+	      fs_give((void **)&state->role);
+	    state->role = cpystr(role->nick); /* remember the role */
 	    state->redrawer = NULL;
 	    switch(action){
 	      case 'c':
@@ -2771,12 +2775,12 @@ save_prompt(struct pine *state, CONTEXT_
 	    char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section,
 	    SaveDel *dela, SavePreserveOrder *prea)
 {
-    int		      rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0;
+    int		      rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0;
     int		      delindex = 0, preindex = 0, r;
     char	      prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN];
     char              *buf = tmp_20k_buf;
     char              shortbuf[200];
-    char              *folder;
+    char              *folder, folder2[MAXPATH];
     HelpType	      help;
     SaveDel           del = DontAsk;
     SavePreserveOrder pre = DontAskPreserve;
@@ -2784,6 +2788,7 @@ save_prompt(struct pine *state, CONTEXT_
     static HISTORY_S *history = NULL;
     CONTEXT_S	     *tc;
     ESCKEY_S	      ekey[10];
+    RULE_RESULT	     *rule;
 
     if(!cntxt)
       alpine_panic("no context ptr in save_prompt");
@@ -2793,6 +2798,15 @@ save_prompt(struct pine *state, CONTEXT_
     if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt)))
       return(0);		/* message expunged! */
 
+    if ((rule = get_result_rule(V_SAVE_RULES, FOR_SAVE, env)) != NULL){
+       strncpy(folder2,rule->result,sizeof(folder2)-1);
+       folder2[sizeof(folder2)-1] = '\0';
+       folder = folder2;
+       if (rule->result)
+	   fs_give((void **)&rule->result);
+       fs_give((void **)&rule);
+    }
+
     /* how many context's can be saved to... */
     for(tc = state->context_list; tc; tc = tc->next)
       if(!NEWS_TEST(tc))
Index: alpine-2.26/alpine/mailindx.c
===================================================================
--- alpine-2.26.orig/alpine/mailindx.c
+++ alpine-2.26/alpine/mailindx.c
@@ -225,6 +225,8 @@ mail_index_screen(struct pine *state)
     state->prev_screen = mail_index_screen;
     state->next_screen = SCREEN_FUN_NULL;
 
+    setup_threading_display_style();
+
     if(THRD_AUTO_VIEW()
        && sp_viewing_a_thread(state->mail_stream)
        && state->view_skipped_index
@@ -236,10 +238,14 @@ mail_index_screen(struct pine *state)
 
     adjust_cur_to_visible(state->mail_stream, state->msgmap);
 
+    strcpy(state->screen_name,"index");
+
     if(THRD_INDX())
       thread_index_screen(state);
     else
       index_index_screen(state);
+
+    strcpy(state->screen_name,"unknown");
 }
 
 
Index: alpine-2.26/alpine/mailpart.c
===================================================================
--- alpine-2.26.orig/alpine/mailpart.c
+++ alpine-2.26/alpine/mailpart.c
@@ -178,7 +178,7 @@ attachment_screen(struct pine *ps)
 		  maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
 		  last_type = TYPEOTHER;
     long	  msgno;
-    char	 *q, *last_subtype = NULL, backtag[64], *utf8str;
+    char	 *q, *last_subtype = NULL, backtag[64], *utf8str, *screen_name;
     OtherMenu     what;
     ATTACH_S	 *a;
     ATDISP_S	 *current = NULL, *ctmp = NULL;
@@ -187,6 +187,10 @@ attachment_screen(struct pine *ps)
     ps->prev_screen = attachment_screen;
     ps->next_screen = SCREEN_FUN_NULL;
 
+    screen_name = ps->screen_name[0] ? cpystr(ps->screen_name) : NULL;
+    strncpy(ps->screen_name, "attachment", sizeof(ps->screen_name));
+    ps->screen_name[sizeof(ps->screen_name)-1] = '\0';
+
     ps->some_quoting_was_suppressed = 0;
 
     if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
@@ -907,6 +911,12 @@ attachment_screen(struct pine *ps)
 
     if(screen.titlecolor)
       free_color_pair(&screen.titlecolor);
+
+    if(screen_name){
+      strncpy(ps->screen_name, screen_name, sizeof(ps->screen_name));
+      ps->screen_name[sizeof(ps->screen_name)-1] = '\0';
+      fs_give((void **) &screen_name);
+    }
 }
 
 
Index: alpine-2.26/alpine/mailview.c
===================================================================
--- alpine-2.26.orig/alpine/mailview.c
+++ alpine-2.26/alpine/mailview.c
@@ -250,6 +250,8 @@ mail_view_screen(struct pine *ps)
     ps->prev_screen = mail_view_screen;
     ps->force_prefer_plain = ps->force_no_prefer_plain = 0;
 
+    strcpy(ps->screen_name, "text");
+
     if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
 	q_status_message(SM_ORDER | SM_DING, 0, 3,
 			 _("Screen too small to view message"));
Index: alpine-2.26/alpine/osdep/termin.gen.c
===================================================================
--- alpine-2.26.orig/alpine/osdep/termin.gen.c
+++ alpine-2.26/alpine/osdep/termin.gen.c
@@ -29,6 +29,8 @@
 #include "../../pith/newmail.h"
 #include "../../pith/conf.h"
 #include "../../pith/busy.h"
+#include "../../pith/list.h"
+#include "../../pith/rules.h"
 
 #include "../../pico/estruct.h"
 #include "../../pico/pico.h"
@@ -68,7 +70,8 @@ int	pcpine_oe_cursor(int, long);
  *     Generic tty input routines
  */
 
-
+void    process_init_cmds(struct pine *, char **);
+void    queue_init_errors(struct pine *);
 /*----------------------------------------------------------------------
         Read a character from keyboard with timeout
  Input:  none
@@ -110,6 +113,41 @@ read_command(char **utf8str)
       *utf8str = NULL;
 
     ucs = read_char(tm);
+    if(!ps_global->initial_cmds){
+      RULE_RESULT *rule;
+      char **list = NULL, *error = NULL;
+      int    commas = 0, k;   /* From args.c */
+
+      ps_global->pressed_key = cpystr(pretty_command(ucs));
+      rule = (RULE_RESULT *)get_result_rule(V_KEY_RULES, FOR_KEY, NULL);
+      if(ps_global->pressed_key)
+        fs_give((void **)&ps_global->pressed_key);
+      if (rule){
+         for(k = 0; rule->result[k]; k++)
+            if(rule->result[k] == ',') commas++;
+         list = parse_list(rule->result, commas+1, 0, &error);
+         if(error)
+            sprintf(tmp_20k_buf, "Error in parsing command list: %s, %s",
+                      rule->result, error);
+         if (rule->result)
+           fs_give((void **)&rule->result);
+         fs_give((void **)&rule);
+         if(error){
+            q_status_message(SM_ORDER | SM_DING, 0, 2, tmp_20k_buf);
+            return (NO_OP_COMMAND);
+         }
+	 process_init_cmds(ps_global, list);
+	 if(ps_global->init_errs){
+	    queue_init_errors(ps_global);
+            return (NO_OP_COMMAND);
+	 }
+	 ucs = read_char(tm);
+	 ps_global->in_init_seq = 1;  /* no output please */
+	 for(k = 0; k < commas; k++)
+            if(list[k]) fs_give((void **)&list[k]);
+         if (list) fs_give((void **)list);
+      }
+    }
     if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE)
       zero_new_mail_count();
 
@@ -1155,6 +1193,7 @@ process_config_input(UCS *ch)
 	if(ps_global->initial_cmds && !*ps_global->initial_cmds && ps_global->free_initial_cmds){
 	    fs_give((void **) &ps_global->free_initial_cmds);
 	    ps_global->initial_cmds = NULL;
+	    firsttime = (char) 1;
 	}
 
 	return(ret);
Index: alpine-2.26/alpine/reply.c
===================================================================
--- alpine-2.26.orig/alpine/reply.c
+++ alpine-2.26/alpine/reply.c
@@ -58,7 +58,8 @@ The evolution continues...
 #include "../pith/tempfile.h"
 #include "../pith/busy.h"
 #include "../pith/ablookup.h"
-
+#include "../pith/copyaddr.h"
+#include "../pith/rules.h"
 
 /*
  * Internal Prototypes
@@ -105,11 +106,12 @@ reply(struct pine *pine_state, ACTION_S
     long        msgno, j, totalm, rflags, *seq = NULL;
     int         i, include_text = 0, times = -1, warned = 0, rv = 0,
 		flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
-    int         rolemsg = 0, copytomsg = 0;
+    int         rolemsg = 0, copytomsg = 0, do_role_early = 0;
     gf_io_t     pc;
     PAT_STATE   dummy;
     REDRAFT_POS_S *redraft_pos = NULL;
     ACTION_S   *role = NULL, *nrole;
+    RULE_RESULT *rule;
 #if	defined(DOS) && !defined(_WINDOWS)
     char *reserve;
 #endif
@@ -135,6 +137,69 @@ reply(struct pine *pine_state, ACTION_S
        && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
       reply_raw_body = 1;
 
+    /* Setup possible role */
+    if(role_arg)
+      role = copy_action(role_arg);
+
+    if(!role && F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)){
+	for(msgno = mn_first_cur(pine_state->msgmap);
+	    msgno > 0L;  msgno = mn_next_cur(pine_state->msgmap)){
+
+	    env = pine_mail_fetchstructure(pine_state->mail_stream,
+					   mn_m2raw(pine_state->msgmap, msgno),
+					   NULL);
+	    if(!env) {
+		q_status_message1(SM_ORDER,3,4,
+			    _("Error fetching message %s. Can't reply to it."),
+				long2string(msgno));
+		goto done_early;
+	    }
+
+	    if((rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env)) != NULL){
+	        RULELIST *list = get_rulelist_from_code(V_REPLY_INDENT_RULES,
+                                                ps_global->rule_list);
+	        RULE_S *prule = get_rule(list, rule->number);
+		if(condition_contains_token(prule->condition, ROLE_TOKEN))
+		  do_role_early++;
+		if(rule->result)
+		  fs_give((void **)&rule->result);
+		fs_give((void **)&rule);
+	    }
+	}
+    }
+
+    if(do_role_early){
+	rflags = ROLE_REPLY;
+	if(nonempty_patterns(rflags, &dummy)){
+	    /* setup default role */
+	    nrole = NULL;
+	    j = mn_first_cur(pine_state->msgmap);
+	    do {
+		role = nrole;
+		nrole = set_role_from_msg(pine_state, rflags,
+					  mn_m2raw(pine_state->msgmap, j),
+					  NULL);
+	    } while(nrole && (!role || nrole == role)
+		    && (j=mn_next_cur(pine_state->msgmap)) > 0L);
+
+	    if(!role || nrole == role)
+	      role = nrole;
+	    else
+	      role = NULL;
+
+	    if(confirm_role(rflags, &role))
+	      role = combine_inherited_role(role);
+	    else{				/* cancel reply */
+		role = NULL;
+		cmd_cancelled("Reply");
+		goto done_early;
+	    }
+	}
+    }
+
+    if (role)
+	ps_global->role = cpystr(role->nick); /* remember the role */
+
     /*
      * We may have to loop through first to figure out what default
      * reply-indent-string to offer...
@@ -283,8 +348,18 @@ reply(struct pine *pine_state, ACTION_S
 		outgoing->subject = cpystr("Re: several messages");
 	    }
 	}
-	else
-	  outgoing->subject = reply_subject(env->subject, NULL, 0);
+	else{
+	   RULE_RESULT *rule;
+	   rule = get_result_rule(V_RESUB_RULES,FOR_RESUB|FOR_TRIM , env);
+	   if (rule){
+	     outgoing->subject = reply_subject(rule->result, NULL, 0);
+	     if (rule->result)
+	        fs_give((void **)&rule->result);
+	     fs_give((void **)&rule);
+	   }
+	   else
+	       outgoing->subject = reply_subject(env->subject, NULL, 0);
+	}
     }
 
     /* fill reply header */
@@ -303,13 +378,7 @@ reply(struct pine *pine_state, ACTION_S
     if(sp_expunge_count(pine_state->mail_stream))	/* cur msg expunged */
       goto done_early;
 
-    /* Setup possible role */
-     if (ps_global->reply.role_chosen)
-	role = ps_global->reply.role_chosen;
-     else if(role_arg)
-	role = copy_action(role_arg);
-
-    if(!role){
+    if(!do_role_early){
 	rflags = ROLE_REPLY;
 	if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){
 	    /* setup default role */
@@ -720,6 +789,9 @@ reply(struct pine *pine_state, ACTION_S
     if(prefix)
       fs_give((void **)&prefix);
 
+    if (ps_global->role)
+	fs_give((void **)&ps_global->role);
+
     if(fcc)
       fs_give((void **) &fcc);
 
@@ -1590,9 +1662,14 @@ forward(struct pine *ps, ACTION_S *role_
 	}
     }
 
-    if(role)
+    if (ps_global->role)
+      fs_give((void **)&ps_global->role);
+
+    if(role){
       q_status_message1(SM_ORDER, 3, 4,
 			_("Forwarding using role \"%s\""), role->nick);
+      ps_global->role = cpystr(role->nick);
+    }
 
     outgoing->message_id  = generate_message_id(role);
 
@@ -1826,6 +1903,7 @@ forward(struct pine *ps, ACTION_S *role_
 #if	defined(DOS) && !defined(_WINDOWS)
     free((void *)reserve);
 #endif
+    outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL;
     pine_send(outgoing, &body, "FORWARD MESSAGE",
 	      role, NULL, &reply, redraft_pos,
 	      NULL, NULL, 0);
Index: alpine-2.26/alpine/roleconf.c
===================================================================
--- alpine-2.26.orig/alpine/roleconf.c
+++ alpine-2.26/alpine/roleconf.c
@@ -7702,6 +7702,11 @@ role_text_tool_inick(struct pine *ps, in
 	    if(apval)
 	      *apval = (role && role->nick) ? cpystr(role->nick) : NULL;
 
+	    if (ps_global->role)
+		fs_give((void **)&ps_global->role);
+	    if (role && role->nick)
+		ps_global->role = cpystr(role->nick);
+
 	    if((*cl)->value)
 	      fs_give((void **)&((*cl)->value));
 
Index: alpine-2.26/alpine/send.c
===================================================================
--- alpine-2.26.orig/alpine/send.c
+++ alpine-2.26/alpine/send.c
@@ -59,7 +59,7 @@
 #include "../pith/mimetype.h"
 #include "../pith/send.h"
 #include "../pith/smime.h"
-
+#include "../pith/rules.h"
 
 typedef struct body_particulars {
     unsigned short     type, encoding, had_csp;
@@ -232,6 +232,11 @@ alt_compose_screen(struct pine *pine_sta
 	role->nick = cpystr("Default Role");
     }
 
+    if (ps_global->role)
+       fs_give((void **)&ps_global->role);  
+
+    ps_global->role = cpystr(role->nick);
+
     pine_state->redrawer = NULL;
     compose_mail(NULL, NULL, role, NULL, NULL);
     free_action(&role);
@@ -441,8 +446,12 @@ compose_mail(char *given_to, char *fcc_a
 
 	      ps_global->next_screen = prev_screen;
 	      ps_global->redrawer = redraw;
-	      if(role)
+	      if (ps_global->role)
+		  fs_give((void **)&ps_global->role);  
+	      if(role){
 		role = combine_inherited_role(role);
+		ps_global->role = cpystr(role->nick);
+	      }
 	    }
 	    break;
 	  
@@ -638,9 +647,14 @@ compose_mail(char *given_to, char *fcc_a
 	    }
 	}
 
-	if(role)
+	if (ps_global->role)
+	    fs_give((void **)&ps_global->role);
+
+	if(role){
 	  q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
 			    role->nick);
+	  ps_global->role = cpystr(role->nick);
+	}
 
         outgoing->message_id = generate_message_id(role);
 	/*
@@ -2485,6 +2499,26 @@ pine_send(ENVELOPE *outgoing, struct mai
 		    removing_trailing_white_space(pf->textbuf);
 		    (void)removing_double_quotes(pf->textbuf);
 		    build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+		    if (!strncmp(pf->name,"Lcc",3) && addr && *addr){
+			RULE_RESULT *rule;
+
+  			outgoing->date = (unsigned char *) cpystr(addr);
+			ps_global->procid = cpystr("fwd-lcc");
+			rule = get_result_rule(V_FORWARD_RULES,
+			           FOR_COMPOSE|FOR_TRIM, outgoing);
+			if (rule){
+			    addr = cpystr(rule->result);
+			    removing_trailing_white_space(addr);
+			    (void)removing_extra_stuff(addr);
+			    if (rule->result)
+				fs_give((void **)&rule->result);
+			    fs_give((void **)&rule);
+			}
+			fs_give((void **)&ps_global->procid);
+			if (outgoing->date)
+			    fs_give((void **)&outgoing->date);
+		    }   
+
 		    rfc822_parse_adrlist(pf->addr, addr,
 					 ps_global->maildomain);
 		    fs_give((void **)&addr);
Index: alpine-2.26/pith/Makefile.am
===================================================================
--- alpine-2.26.orig/pith/Makefile.am
+++ alpine-2.26/pith/Makefile.am
@@ -26,7 +26,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c a
 	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
 	keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
 	margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
-	readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+	readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \
 	state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
 	thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
 
Index: alpine-2.26/pith/Makefile.in
===================================================================
--- alpine-2.26.orig/pith/Makefile.in
+++ alpine-2.26/pith/Makefile.in
@@ -143,7 +143,7 @@ am_libpith_a_OBJECTS = ablookup.$(OBJEXT
 	mimedesc.$(OBJEXT) mimetype.$(OBJEXT) msgno.$(OBJEXT) \
 	newmail.$(OBJEXT) news.$(OBJEXT) pattern.$(OBJEXT) \
 	pipe.$(OBJEXT) readfile.$(OBJEXT) remote.$(OBJEXT) \
-	reply.$(OBJEXT) rfc2231.$(OBJEXT) save.$(OBJEXT) \
+	reply.$(OBJEXT) rfc2231.$(OBJEXT) rules.$(OBJEXT) save.$(OBJEXT) \
 	search.$(OBJEXT) sequence.$(OBJEXT) send.$(OBJEXT) \
 	sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
 	store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \
@@ -450,7 +450,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c a
 	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
 	keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
 	margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
-	readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+	readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \
 	state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
 	thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
 
@@ -584,6 +584,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
Index: alpine-2.26/pith/adrbklib.c
===================================================================
--- alpine-2.26.orig/pith/adrbklib.c
+++ alpine-2.26/pith/adrbklib.c
@@ -5134,8 +5134,14 @@ init_addrbooks(OpenStatus want_status, i
 	    if(as.cur >= as.how_many_personals)
 	      pab->type |= GLOBAL;
 
-	    pab->access = adrbk_access(pab);
-
+	    if(ps_global->mail_stream && 
+		ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){
+	      as.initialized = 0;
+	      pab->access = NoAccess;
+	    }
+	    else{
+	      pab->access = adrbk_access(pab);
+	    }
 	    /* global address books are forced readonly */
 	    if(pab->type & GLOBAL && pab->access != NoAccess)
 	      pab->access = ReadOnly;
Index: alpine-2.26/pith/conf.c
===================================================================
--- alpine-2.26.orig/pith/conf.c
+++ alpine-2.26/pith/conf.c
@@ -25,6 +25,7 @@
 #include "../pith/remote.h"
 #include "../pith/keyword.h"
 #include "../pith/mailview.h"
+#include "../pith/rules.h"
 #include "../pith/list.h"
 #include "../pith/status.h"
 #include "../pith/ldap.h"
@@ -221,6 +222,36 @@ CONF_TXT_T cf_text_unk_character_set[] =
 
 CONF_TXT_T cf_text_editor[] =		"Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature.";
 
+CONF_TXT_T cf_text_compose_rules[] =	"Allows a user to set rules when composing messages.";
+ 
+CONF_TXT_T cf_text_forward_rules[] =	"Allows a user to set rules when forwarding messages.";
+ 
+CONF_TXT_T cf_text_reply_rules[] =	"Allows a user to set rules when replying messages.";
+ 
+CONF_TXT_T cf_text_index_rules[] =	"Allows a user to supersede global index format variable in designated folders.";
+ 
+CONF_TXT_T cf_text_key_def_rules[] =	"Allows a user to override keystrokes in certain screens.";
+
+CONF_TXT_T cf_text_replace_rules[] =	"Allows a user to change the form a specify field in the index-format is \n# displayed.";
+ 
+CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules.";
+ 
+CONF_TXT_T cf_text_reply_leadin_rules[] =	"Allows a user to replace the reply-leadin message based on different parameters.";
+ 
+CONF_TXT_T cf_text_reply_subject_rules[] =	"Allows a user to replace the subject of a message in a customs based way";
+ 
+CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders";
+ 
+CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders";
+ 
+CONF_TXT_T cf_text_save_rules[] =	"Allows a user to specify a save folder message for specific senders or folders.";
+ 
+CONF_TXT_T cf_text_smtp_rules[] =	"Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here.";
+ 
+CONF_TXT_T cf_text_sort_rules[] =	"Allows a user to specify the sort default order of a specific folder.";
+ 
+CONF_TXT_T cf_text_startup_rules[] =	"Allows a user to specify the position of a highlighted message when opening a \n# folder.";
+ 
 CONF_TXT_T cf_text_speller[] =		"Specifies the program invoked by ^T in the Composer.";
 
 #ifdef _WINDOWS
@@ -572,6 +603,34 @@ static struct variable variables[] = {
 	NULL,			cf_text_thread_exp_char},
 {"threading-lastreply-character",	0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
 	"Threading Last Reply Character",	cf_text_thread_lastreply_char},
+{"threading-display-style-rule",	0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Threading Display Style Rule",	cf_text_thread_displaystyle_rule},
+{"threading-index-style-rule",		0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Threading Index Style Rule",	cf_text_thread_indexstyle_rule},
+{"compose-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Compose Rules",	cf_text_compose_rules},
+{"forward-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Forward Rules", 	cf_text_forward_rules},
+{"index-rules",				0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+	"Index Rules",		cf_text_index_rules},
+{"key-definition-rules",		0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+	"Key Definition Rules",	cf_text_key_def_rules},
+{"replace-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+	"Replace Rules",	cf_text_replace_rules},
+{"reply-indent-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Reply Indent Rules",	cf_text_reply_indent_rules},
+{"reply-leadin-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+	"Reply Leadin Rules",	cf_text_reply_leadin_rules},
+{"reply-subject-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+	"Reply Subject Rules",	cf_text_reply_subject_rules},
+{"save-rules",				0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Save Rules",		cf_text_save_rules},
+{"smtp-rules",				0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Smtp Rules",		cf_text_smtp_rules},
+{"sort-rules",				0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Sort Rules",		cf_text_sort_rules},
+{"startup-rules",			0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+	"Startup Rules",	cf_text_startup_rules},
 #ifndef	_WINDOWS
 {"display-character-set",		0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
 	NULL,			cf_text_disp_char_set},
@@ -2779,6 +2838,7 @@ init_vars(struct pine *ps, void (*cmds_f
       if(cmds_f)
         (*cmds_f)(ps, VAR_INIT_CMD_LIST);
 
+    (void)create_rule_list(ps_global->vars);
 #ifdef	_WINDOWS
     mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
 #endif	/* _WINDOWS */
@@ -3235,6 +3295,8 @@ feature_list(int index)
 	 F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0},
 	{"auto-move-read-msgs", "Auto Move Read Messages",
 	 F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0},
+	{"auto-move-read-msgs-using-rules", "Auto Move Read Messages Using Rules",
+	 F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC, 0},
 	{"auto-unselect-after-apply", NULL,
 	 F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0},
 	{"auto-unzoom-after-apply", NULL,
@@ -7935,6 +7997,34 @@ config_help(int var, int feature)
 	return(h_config_ab_sort_rule);
       case V_FLD_SORT_RULE :
 	return(h_config_fld_sort_rule);
+      case V_THREAD_DISP_STYLE_RULES:
+	return(h_config_thread_display_style_rule);
+      case V_THREAD_INDEX_STYLE_RULES:
+	return(h_config_thread_index_style_rule);
+      case V_COMPOSE_RULES:
+	return(h_config_compose_rules);
+      case V_FORWARD_RULES:
+	return(h_config_forward_rules);
+      case V_INDEX_RULES:
+	return(h_config_index_rules);
+      case V_KEY_RULES:
+	return(h_config_key_macro_rules);
+      case V_REPLACE_RULES:
+	return(h_config_replace_rules);
+      case V_REPLY_INDENT_RULES:
+	return(h_config_reply_indent_rules);
+      case V_REPLY_LEADIN_RULES:
+	return(h_config_reply_leadin_rules);
+      case V_RESUB_RULES:
+	return(h_config_resub_rules);
+      case V_SAVE_RULES:
+	return(h_config_save_rules);
+      case V_SMTP_RULES:
+	return(h_config_smtp_rules);
+      case V_SORT_RULES:
+	return(h_config_sort_rules);
+      case V_STARTUP_RULES:
+	return(h_config_startup_rules);
       case V_POST_CHAR_SET :
 	return(h_config_post_char_set);
       case V_UNK_CHAR_SET :
Index: alpine-2.26/pith/conf.h
===================================================================
--- alpine-2.26.orig/pith/conf.h
+++ alpine-2.26/pith/conf.h
@@ -162,6 +162,46 @@
 #define GLO_AB_SORT_RULE	     vars[V_AB_SORT_RULE].global_val.p
 #define VAR_FLD_SORT_RULE	     vars[V_FLD_SORT_RULE].current_val.p
 #define GLO_FLD_SORT_RULE	     vars[V_FLD_SORT_RULE].global_val.p
+#define VAR_COMPOSE_RULES	     vars[V_COMPOSE_RULES].current_val.l
+#define GLO_COMPOSE_RULES	     vars[V_COMPOSE_RULES].global_val.l
+#define USR_COMPOSE_RULES	     vars[V_COMPOSE_RULES].user_val.l
+#define VAR_FORWARD_RULES	     vars[V_FORWARD_RULES].current_val.l
+#define GLO_FORWARD_RULES	     vars[V_FORWARD_RULES].global_val.l
+#define USR_FORWARD_RULES	     vars[V_FORWARD_RULES].user_val.l
+#define VAR_INDEX_RULES		     vars[V_INDEX_RULES].current_val.l
+#define GLO_INDEX_RULES		     vars[V_INDEX_RULES].global_val.l
+#define USR_INDEX_RULES		     vars[V_INDEX_RULES].user_val.l
+#define VAR_KEY_RULES		     vars[V_KEY_RULES].current_val.l
+#define GLO_KEY_RULES		     vars[V_KEY_RULES].global_val.l
+#define USR_KEY_RULES		     vars[V_KEY_RULES].user_val.l
+#define VAR_REPLACE_RULES	     vars[V_REPLACE_RULES].current_val.l
+#define GLO_REPLACE_RULES	     vars[V_REPLACE_RULES].global_val.l
+#define USR_REPLACE_RULES	     vars[V_REPLACE_RULES].user_val.l
+#define VAR_REPLY_INDENT_RULES	     vars[V_REPLY_INDENT_RULES].current_val.l
+#define GLO_REPLY_INDENT_RULES	     vars[V_REPLY_INDENT_RULES].global_val.l
+#define USR_REPLY_INDENT_RULES	     vars[V_REPLY_INDENT_RULES].user_val.l
+#define VAR_REPLY_LEADIN_RULES	     vars[V_REPLY_LEADIN_RULES].current_val.l
+#define GLO_REPLY_LEADIN_RULES	     vars[V_REPLY_LEADIN_RULES].global_val.l
+#define USR_REPLY_LEADIN_RULES	     vars[V_REPLY_LEADIN_RULES].user_val.l
+#define VAR_RESUB_RULES		     vars[V_RESUB_RULES].current_val.l
+#define GLO_RESUB_RULES		     vars[V_RESUB_RULES].global_val.l
+#define USR_RESUB_RULES		     vars[V_RESUB_RULES].user_val.l
+#define VAR_THREAD_DISP_STYLE_RULES  vars[V_THREAD_DISP_STYLE_RULES].current_val.l
+#define GLO_THREAD_DISP_STYLE_RULES  vars[V_THREAD_DISP_STYLE_RULES].global_val.l
+#define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l
+#define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l
+#define VAR_SAVE_RULES		     vars[V_SAVE_RULES].current_val.l
+#define GLO_SAVE_RULES		     vars[V_SAVE_RULES].global_val.l
+#define USR_SAVE_RULES		     vars[V_SAVE_RULES].user_val.l
+#define VAR_SMTP_RULES		     vars[V_SMTP_RULES].current_val.l
+#define GLO_SMTP_RULES		     vars[V_SMTP_RULES].global_val.l
+#define USR_SMTP_RULES		     vars[V_SMTP_RULES].user_val.l
+#define VAR_SORT_RULES		     vars[V_SORT_RULES].current_val.l
+#define GLO_SORT_RULES		     vars[V_SORT_RULES].global_val.l
+#define USR_SORT_RULES		     vars[V_SORT_RULES].user_val.l
+#define VAR_STARTUP_RULES	     vars[V_STARTUP_RULES].current_val.l
+#define GLO_STARTUP_RULES	     vars[V_STARTUP_RULES].global_val.l
+#define USR_STARTUP_RULES	     vars[V_STARTUP_RULES].user_val.l
 #ifndef	_WINDOWS
 #define VAR_CHAR_SET		     vars[V_CHAR_SET].current_val.p
 #define GLO_CHAR_SET		     vars[V_CHAR_SET].global_val.p
Index: alpine-2.26/pith/conftype.h
===================================================================
--- alpine-2.26.orig/pith/conftype.h
+++ alpine-2.26/pith/conftype.h
@@ -71,6 +71,20 @@ typedef	enum {    V_PERSONAL_NAME = 0
 		, V_THREAD_MORE_CHAR
 		, V_THREAD_EXP_CHAR
 		, V_THREAD_LASTREPLY_CHAR
+		, V_THREAD_DISP_STYLE_RULES
+		, V_THREAD_INDEX_STYLE_RULES
+		, V_COMPOSE_RULES
+		, V_FORWARD_RULES
+		, V_INDEX_RULES
+		, V_KEY_RULES
+		, V_REPLACE_RULES
+		, V_REPLY_INDENT_RULES
+		, V_REPLY_LEADIN_RULES
+		, V_RESUB_RULES
+		, V_SAVE_RULES
+		, V_SMTP_RULES
+		, V_SORT_RULES
+		, V_STARTUP_RULES
 #ifndef	_WINDOWS
 		, V_CHAR_SET
 		, V_OLD_CHAR_SET
@@ -351,6 +365,7 @@ typedef enum {
 	F_FULL_AUTO_EXPUNGE,
 	F_EXPUNGE_MANUALLY,
 	F_AUTO_READ_MSGS,
+	F_AUTO_READ_MSGS_RULES,
 	F_AUTO_FCC_ONLY,
 	F_READ_IN_NEWSRC_ORDER,
 	F_SELECT_WO_CONFIRM,
Index: alpine-2.26/pith/detoken.c
===================================================================
--- alpine-2.26.orig/pith/detoken.c
+++ alpine-2.26/pith/detoken.c
@@ -21,7 +21,7 @@
 #include "../pith/reply.h"
 #include "../pith/mailindx.h"
 #include "../pith/options.h"
-
+#include "../pith/rules.h"
 
 /*
  * Hook to read signature from local file
@@ -87,6 +87,8 @@ detoken(ACTION_S *role, ENVELOPE *env, i
 
     if(is_sig){
 	/*
+	 * First we check if there is a rule about signatures, if there is
+	 * use it, otherwise keep going and do the following:
 	 * If role->litsig is set, we use it;
 	 * Else, if VAR_LITERAL_SIG is set, we use that;
 	 * Else, if role->sig is set, we use that;
@@ -100,14 +102,25 @@ detoken(ACTION_S *role, ENVELOPE *env, i
 	 * there is no reason to mix them, so we don't provide support to
 	 * do so.
 	 */
-	if(role && role->litsig)
-	  literal_sig = role->litsig;
-	else if(ps_global->VAR_LITERAL_SIG)
-	  literal_sig = ps_global->VAR_LITERAL_SIG;
-	else if(role && role->sig)
-	  sigfile = role->sig;
-	else
-	  sigfile = ps_global->VAR_SIGNATURE_FILE;
+        { RULE_RESULT *rule;
+           rule = get_result_rule(V_COMPOSE_RULES, FOR_COMPOSE, env);
+           if (rule){
+               sigfile = cpystr(rule->result);
+	       if (rule->result)
+	          fs_give((void **)&rule->result);
+	       fs_give((void **)&rule);
+	   }
+        }
+	if (!sigfile){
+	  if(role && role->litsig)
+	    literal_sig = role->litsig;
+	  else if(ps_global->VAR_LITERAL_SIG)
+	    literal_sig = ps_global->VAR_LITERAL_SIG;
+	  else if(role && role->sig)
+	    sigfile = role->sig;
+	  else
+	    sigfile = ps_global->VAR_SIGNATURE_FILE;
+	}
     }
     else if(role && role->template)
       sigfile = role->template;
@@ -298,7 +311,7 @@ top:
 			}
 		    }
 		}
-		else if(pt->what_for & FOR_REPLY_INTRO)
+		else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE))
 		  repl = get_reply_data(env, role, pt->ctype,
 					subbuf, sizeof(subbuf)-1);
 
Index: alpine-2.26/pith/indxtype.h
===================================================================
--- alpine-2.26.orig/pith/indxtype.h
+++ alpine-2.26/pith/indxtype.h
@@ -84,6 +84,11 @@ typedef enum {iNothing, iStatus, iFStatu
 	      iCurNews, iArrow,
 	      iMailbox, iAddress, iInit, iCursorPos,
 	      iDay2Digit, iMon2Digit, iYear2Digit,
+	      iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey,
+	      iNick, iFccFrom, iFccSender, iAltAddress,
+	      iAddressTo, iAddressCc, iAddressRecip, iAddressSender,
+	      iBcc, iLcc,
+	      iFfrom, iFadd,
 	      iSTime, iSTime24, iKSize,
 	      iRoleNick, iNewLine,
 	      iHeader, iText,
@@ -105,15 +110,26 @@ typedef struct index_parse_tokens {
 
 
 /* these are flags for the what_for field in INDEX_PARSE_T */
-#define FOR_NOTHING	0x00
-#define FOR_INDEX	0x01
-#define FOR_REPLY_INTRO	0x02
-#define FOR_TEMPLATE	0x04		/* or for signature */
-#define FOR_FILT	0x08
-#define DELIM_USCORE	0x10
-#define DELIM_PAREN	0x20
-#define DELIM_COLON	0x40
-
+#define FOR_NOTHING	0x00000
+#define FOR_INDEX	0x00001
+#define FOR_REPLY_INTRO	0x00002
+#define FOR_TEMPLATE	0x00004  /* or for signature */
+#define FOR_FILT	0x00008
+#define DELIM_USCORE	0x00010
+#define DELIM_PAREN	0x00020
+#define DELIM_COLON	0x00040
+#define FOR_FOLDER	0x00080  /* for rules */
+#define FOR_RULE	0x00100  /* for rules */
+#define FOR_TRIM	0x00200  /* for rules */
+#define FOR_RESUB	0x00400  /* for rules */
+#define FOR_REPLACE	0x00800  /* for rules */
+#define FOR_SORT	0x01000  /* for rules */
+#define FOR_FLAG	0x02000  /* for rules */
+#define FOR_COMPOSE	0x04000  /* for rules */
+#define FOR_THREAD	0x08000  /* for rules */
+#define FOR_STARTUP	0x10000  /* for rules */
+#define FOR_KEY		0x20000	 /* for rules */
+#define FOR_SAVE	0x40000  /* for rules */
 
 #define DEFAULT_REPLY_INTRO "default"
 
Index: alpine-2.26/pith/mailcmd.c
===================================================================
--- alpine-2.26.orig/pith/mailcmd.c
+++ alpine-2.26/pith/mailcmd.c
@@ -35,6 +35,7 @@
 #include "../pith/ablookup.h"
 #include "../pith/search.h"
 #include "../pith/charconv/utf8.h"
+#include "../pith/rules.h"
 
 #ifdef _WINDOWS
 #include "../pico/osdep/mswin.h"
@@ -661,6 +662,7 @@ do_broach_folder(char *newfolder, CONTEX
 	strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1);
 	ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
 	ps_global->context_current = ps_global->context_list;
+	setup_threading_index_style();
 	reset_index_format();
 	clear_index_cache(ps_global->mail_stream, 0);
         /* MUST sort before restoring msgno! */
@@ -1001,6 +1003,7 @@ do_broach_folder(char *newfolder, CONTEX
 
     clear_index_cache(ps_global->mail_stream, 0);
     reset_index_format();
+    setup_threading_index_style();
 
     /*
      * Start news reading with messages the user's marked deleted
@@ -1124,7 +1127,10 @@ do_broach_folder(char *newfolder, CONTEX
 
     if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){
 
-	perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream);
+	perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream,
+                                       V_STARTUP_RULES, newfolder);
+
+	reset_startup_rule(ps_global->mail_stream);
 
 	if(ps_global->start_entry > 0){
 	    mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap)
@@ -1146,124 +1152,7 @@ do_broach_folder(char *newfolder, CONTEX
 	    else
 	      use_this_startup_rule = ps_global->inc_startup_rule;
 
-	    switch(use_this_startup_rule){
-	      /*
-	       * For news in incoming collection we're doing the same thing
-	       * for first-unseen and first-recent. In both those cases you
-	       * get first-unseen if FAKE_NEW is off and first-recent if
-	       * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
-	       * same as first recent because all recent msgs are unseen
-	       * and all unrecent msgs are seen (see pine_mail_open).
-	       */
-	      case IS_FIRST_UNSEEN:
-first_unseen:
-		mn_set_cur(ps_global->msgmap,
-			(sp_first_unseen(m)
-			 && mn_get_sort(ps_global->msgmap) == SortArrival
-			 && !mn_get_revsort(ps_global->msgmap)
-			 && !get_lflag(ps_global->mail_stream, NULL,
-				       sp_first_unseen(m), MN_EXLD)
-			 && (n = mn_raw2m(ps_global->msgmap, 
-					  sp_first_unseen(m))))
-			   ? n
-			   : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc,
-					      THREADING() ? 0 : FSF_SKIP_CHID));
-		break;
-
-	      case IS_FIRST_RECENT:
-first_recent:
-		/*
-		 * We could really use recent for news but this is the way
-		 * it has always worked, so we'll leave it. That is, if
-		 * the FAKE_NEW feature is on, recent and unseen are
-		 * equivalent, so it doesn't matter. If the feature isn't
-		 * on, all the undeleted messages are unseen and we start
-		 * at the first one. User controls with the FAKE_NEW feature.
-		 */
-		if(IS_NEWS(ps_global->mail_stream)){
-		    mn_set_cur(ps_global->msgmap,
-			       first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
-					       THREADING() ? 0 : FSF_SKIP_CHID));
-		}
-		else{
-		    mn_set_cur(ps_global->msgmap,
-			       first_sorted_flagged(F_RECENT | F_UNSEEN
-						    | F_UNDEL,
-						    m, pc,
-					      THREADING() ? 0 : FSF_SKIP_CHID));
-		}
-		break;
-
-	      case IS_FIRST_IMPORTANT:
-		mn_set_cur(ps_global->msgmap,
-			   first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
-					      THREADING() ? 0 : FSF_SKIP_CHID));
-		break;
-
-	      case IS_FIRST_IMPORTANT_OR_UNSEEN:
-
-		if(IS_NEWS(ps_global->mail_stream))
-		  goto first_unseen;
-
-		{
-		    MsgNo flagged, first_unseen;
-
-		    flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
-					       THREADING() ? 0 : FSF_SKIP_CHID);
-		    first_unseen = (sp_first_unseen(m)
-			     && mn_get_sort(ps_global->msgmap) == SortArrival
-			     && !mn_get_revsort(ps_global->msgmap)
-			     && !get_lflag(ps_global->mail_stream, NULL,
-					   sp_first_unseen(m), MN_EXLD)
-			     && (n = mn_raw2m(ps_global->msgmap, 
-					      sp_first_unseen(m))))
-				? n
-				: first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
-					       THREADING() ? 0 : FSF_SKIP_CHID);
-		    mn_set_cur(ps_global->msgmap,
-			  (MsgNo) MIN((int) flagged, (int) first_unseen));
-
-		}
-
-		break;
-
-	      case IS_FIRST_IMPORTANT_OR_RECENT:
-
-		if(IS_NEWS(ps_global->mail_stream))
-		  goto first_recent;
-
-		{
-		    MsgNo flagged, first_recent;
-
-		    flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
-					       THREADING() ? 0 : FSF_SKIP_CHID);
-		    first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN
-							| F_UNDEL,
-							m, pc,
-					       THREADING() ? 0 : FSF_SKIP_CHID);
-		    mn_set_cur(ps_global->msgmap,
-			      (MsgNo) MIN((int) flagged, (int) first_recent));
-		}
-
-		break;
-
-	      case IS_FIRST:
-		mn_set_cur(ps_global->msgmap,
-			   first_sorted_flagged(F_UNDEL, m, pc,
-					      THREADING() ? 0 : FSF_SKIP_CHID));
-		break;
-
-	      case IS_LAST:
-		mn_set_cur(ps_global->msgmap,
-			   first_sorted_flagged(F_UNDEL, m, pc,
-			         FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID)));
-		break;
-
-	      default:
-		alpine_panic("Unexpected incoming startup case");
-		break;
-
-	    }
+	    find_startup_position(use_this_startup_rule, m, pc);
 	}
 	else if(IS_NEWS(ps_global->mail_stream)){
 	    /*
@@ -1441,9 +1330,11 @@ expunge_and_close(MAILSTREAM *stream, ch
 	    /* Save read messages? */
 	    if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0]
 	       && sp_flagged(stream, SP_INBOX)
-	       && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){
+	       && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) ||
+		  (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){
 
 		if(F_ON(F_AUTO_READ_MSGS,ps_global)
+		   || F_ON(F_AUTO_READ_MSGS_RULES, ps_global)
 		   || (pith_opt_read_msg_prompt
 		       && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER)))
 		  /* move inbox's read messages */
@@ -1726,6 +1617,9 @@ move_read_msgs(MAILSTREAM *stream, char
     char	  *bufp = NULL;
     MESSAGECACHE *mc;
 
+    if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global))
+     return move_read_msgs_using_rules(stream, dstfldr, buf);
+
     if(!is_absolute_path(dstfldr)
        && !(save_context = default_save_context(ps_global->context_list)))
       save_context = ps_global->context_list;
@@ -1765,8 +1659,9 @@ move_read_msgs(MAILSTREAM *stream, char
 	snprintf(buf, buflen, "Moving %s read message%s to \"%s\"",
 		comatose(searched), plural(searched), dstfldr);
 	we_cancel = busy_cue(buf, NULL, 0);
-	if(save(ps_global, stream, save_context, dstfldr, msgmap,
-		SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)
+	ps_global->exiting = 1;
+	if((save(ps_global, stream, save_context, dstfldr, msgmap,
+		SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched))
 	  strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */
 
 	buf[buflen-1] = '\0';
@@ -1804,7 +1699,9 @@ move_read_incoming(MAILSTREAM *stream, C
        && ((context_isambig(folder)
 	    && folder_is_nick(folder, FOLDERS(context), 0))
 	   || folder_index(folder, context, FI_FOLDER) > 0)
-       && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){
+       && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))
+	   || (F_ON(F_AUTO_READ_MSGS,ps_global) &&
+	       F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){
 
 	for(; f && *archive; archive++){
 	    char *p;
@@ -2770,3 +2667,295 @@ get_uname(char *mailbox, char *target, i
 
     return(*target ? target : NULL);
 }
+
+char *
+move_read_msgs_using_rules(MAILSTREAM *stream, char *dstfldr, char *buf)
+{
+    CONTEXT_S  *save_context = NULL;
+    char **folder_to_save = NULL;
+    int  num, we_cancel;
+    long i, j, success;
+    MSGNO_S *msgmap = NULL;
+    unsigned long nmsgs = 0L, stream_nmsgs;
+
+    if(!is_absolute_path(dstfldr)
+       && !(save_context = default_save_context(ps_global->context_list)))
+       save_context = ps_global->context_list;
+
+    folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *));
+    folder_to_save[0] = NULL;
+    mn_init(&msgmap, stream->nmsgs);
+    stream_nmsgs = stream->nmsgs;
+    for (i = 1L; i <= stream_nmsgs ; i++){
+        set_lflag(stream, msgmap, i, MN_SLCT, 0);
+        folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD)
+                            ? NULL : get_folder_to_save(stream, i, dstfldr);
+    }
+    for (i = 1L; i <= stream_nmsgs; i++){
+        num = 0;
+        if (folder_to_save[i]){
+           mn_init(&msgmap, stream_nmsgs);
+           for (j = i; j <= stream_nmsgs ; j++){
+                if (folder_to_save[j]){
+                   if (!strcmp(folder_to_save[i], folder_to_save[j])){
+                        set_lflag(stream, msgmap, j, MN_SLCT, 1);
+                        num++;
+                        if (j != i)
+                           fs_give((void **)&folder_to_save[j]);
+                   }
+                 }
+           }
+           pseudo_selected(stream, msgmap);
+           sprintf(buf, "Moving %s read message%s to \"%.45s\"",
+                      comatose(num), plural(num), folder_to_save[i]);
+           we_cancel = busy_cue(buf, NULL, 1);
+           ps_global->exiting = 1;
+           if((success = save(ps_global, stream,save_context, folder_to_save[i],
+                                msgmap, SV_DELETE | SV_FIX_DELS)))
+	      nmsgs += success;
+           if(we_cancel)
+             cancel_busy_cue(success ? 0 : -1);
+           for (j = i; j <= stream_nmsgs ; j++)
+               set_lflag(stream, msgmap, j, MN_SLCT, 0);
+           fs_give((void **)&folder_to_save[i]);
+           mn_give(&msgmap);
+        }
+    }
+    ps_global->exiting = 0; /* useful if we call from aggregate operations */
+    sprintf(buf, "Moved automatically %s message%s",
+                comatose(nmsgs), plural(nmsgs));
+    if (folder_to_save)
+        fs_give((void **)folder_to_save);
+    rule_curpos = 0L;
+    return buf;
+}
+
+char *
+get_folder_to_save(MAILSTREAM *stream, long i, char *dstfldr)
+{
+    MESSAGECACHE *mc = NULL;
+    RULE_RESULT *rule;
+    MSGNO_S *msgmap = NULL;
+    char *folder_to_save = NULL, *save_folder = NULL;
+    int n;
+    long msgno;
+
+    /* The plan is as follows: Select each message of the folder. We
+     * need to set the cursor correctly so that iFlag gets the value
+     * correctly too, otherwise iFlag will get the value of the position
+     * of the cursor. After that we need to look for a rule that applies
+     * to the message and get the saving folder. If we get a saving folder,
+     * and we used the _FLAG_ token, use that folder, if no
+     * _FLAG_ token was used, move only if seen and not deleted, to the
+     * folder specified in the saving rule. If we did not get a saving
+     * folder from the rule, just save in the default folder.
+     */
+    mn_init(&msgmap, stream->nmsgs);
+    rule_curpos = i;
+    msgno = mn_m2raw(msgmap, i);
+    if (msgno > 0L){
+        mc = mail_elt(stream, msgno);
+        rule = (RULE_RESULT *)
+            get_result_rule(V_SAVE_RULES, FOR_SAVE, mc->private.msg.env);
+        if (rule){
+            folder_to_save = cpystr(rule->result);
+            n = rule->number;
+            fs_give((void **)&rule->result);
+            fs_give((void **)&rule);
+        }
+    }
+
+    if (folder_to_save && *folder_to_save){
+        RULELIST *list = get_rulelist_from_code(V_SAVE_RULES,
+                                                ps_global->rule_list);
+        RULE_S *prule = get_rule(list, n);
+        if (condition_contains_token(prule->condition, "_FLAG_")
+             || (mc->valid && mc->seen && !mc->deleted)
+             || (!mc->valid && mc->searched))
+             save_folder = cpystr(folder_to_save);
+          else
+             save_folder = NULL;
+    }
+    else
+       if (!mc || (mc->seen && !mc->deleted))
+          save_folder = cpystr(dstfldr);
+    mn_give(&msgmap);
+    rule_curpos = 0L;
+    return save_folder;
+}
+
+unsigned long
+rules_cursor_pos(MAILSTREAM *stream)
+{
+  MSGNO_S *msgmap = sp_msgmap(stream);
+  return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap));
+}
+
+void
+setup_threading_index_style(void)
+{
+  RULE_RESULT *rule;
+  NAMEVAL_S *v;
+  int i;
+
+  rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD, NULL);
+  if (rule || ps_global->VAR_THREAD_INDEX_STYLE){
+     for(i = 0; (v = thread_index_styles(i)) != NULL; i++)
+        if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_INDEX_STYLE,
+                    rule ? (v ? v->name : "" ) : S_OR_L(v))){
+              ps_global->thread_index_style = v->value;
+              break;
+        }
+     if (rule){
+        if (rule->result)
+           fs_give((void **)&rule->result);
+        fs_give((void **)&rule);
+     }
+  }
+}
+
+unsigned
+get_perfolder_startup_rule(MAILSTREAM *stream, int rule_type, char *folder)
+{
+    unsigned    startup_rule;
+    char        *rule_result;
+
+    startup_rule =  reset_startup_rule(stream);
+    rule_result = get_rule_result(FOR_STARTUP, folder, rule_type);
+    if (rule_result && *rule_result){
+       int        i;
+       NAMEVAL_S *v;
+
+       for(i = 0; (v = incoming_startup_rules(i)) != NULL; i++)
+          if(!strucmp(rule_result, v->name)){
+             startup_rule = v->value;
+             break;
+          }
+       fs_give((void **)&rule_result);
+     }
+   return startup_rule;
+}
+
+void
+find_startup_position(int rule, MAILSTREAM *m, long pc)
+{
+  long n;
+  switch(rule){
+              /*
+               * For news in incoming collection we're doing the same thing
+               * for first-unseen and first-recent. In both those cases you
+               * get first-unseen if FAKE_NEW is off and first-recent if
+               * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
+               * same as first recent because all recent msgs are unseen
+               * and all unrecent msgs are seen (see pine_mail_open).
+               */
+              case IS_FIRST_UNSEEN:
+first_unseen:
+                mn_set_cur(ps_global->msgmap,
+                        (sp_first_unseen(m)
+                         && mn_get_sort(ps_global->msgmap) == SortArrival
+                         && !mn_get_revsort(ps_global->msgmap)
+                         && !get_lflag(ps_global->mail_stream, NULL,
+                                       sp_first_unseen(m), MN_EXLD)
+                         && (n = mn_raw2m(ps_global->msgmap,
+                                          sp_first_unseen(m))))
+                           ? n
+                           : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc,
+                                              THREADING() ? 0 : FSF_SKIP_CHID));
+                break;
+
+              case IS_FIRST_RECENT:
+first_recent:
+                /*
+                 * We could really use recent for news but this is the way
+                 * it has always worked, so we'll leave it. That is, if
+                 * the FAKE_NEW feature is on, recent and unseen are
+                 * equivalent, so it doesn't matter. If the feature isn't
+                 * on, all the undeleted messages are unseen and we start
+                 * at the first one. User controls with the FAKE_NEW feature.
+                 */
+                if(IS_NEWS(ps_global->mail_stream)){
+                    mn_set_cur(ps_global->msgmap,
+                               first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+                                               THREADING() ? 0 : FSF_SKIP_CHID));
+                }
+                else{
+                    mn_set_cur(ps_global->msgmap,
+                               first_sorted_flagged(F_RECENT | F_UNSEEN
+                                                    | F_UNDEL,
+                                                    m, pc,
+                                              THREADING() ? 0 : FSF_SKIP_CHID));
+                }
+                break;
+
+              case IS_FIRST_IMPORTANT:
+                mn_set_cur(ps_global->msgmap,
+                           first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+                                              THREADING() ? 0 : FSF_SKIP_CHID));
+                break;
+
+              case IS_FIRST_IMPORTANT_OR_UNSEEN:
+
+                if(IS_NEWS(ps_global->mail_stream))
+                  goto first_unseen;
+
+                {
+                    MsgNo flagged, first_unseen;
+
+                    flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+                                               THREADING() ? 0 : FSF_SKIP_CHID);
+                    first_unseen = (sp_first_unseen(m)
+                             && mn_get_sort(ps_global->msgmap) == SortArrival
+                             && !mn_get_revsort(ps_global->msgmap)
+                             && !get_lflag(ps_global->mail_stream, NULL,
+                                           sp_first_unseen(m), MN_EXLD)
+                             && (n = mn_raw2m(ps_global->msgmap,
+                                              sp_first_unseen(m))))
+                                ? n
+                                : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+                                               THREADING() ? 0 : FSF_SKIP_CHID);
+                    mn_set_cur(ps_global->msgmap,
+                          (MsgNo) MIN((int) flagged, (int) first_unseen));
+
+                }
+
+                break;
+
+              case IS_FIRST_IMPORTANT_OR_RECENT:
+
+                if(IS_NEWS(ps_global->mail_stream))
+                  goto first_recent;
+
+                {
+                    MsgNo flagged, first_recent;
+
+                    flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+                                               THREADING() ? 0 : FSF_SKIP_CHID);
+                    first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN
+                                                        | F_UNDEL,
+                                                        m, pc,
+                                               THREADING() ? 0 : FSF_SKIP_CHID);
+                    mn_set_cur(ps_global->msgmap,
+                              (MsgNo) MIN((int) flagged, (int) first_recent));
+                }
+
+                break;
+
+              case IS_FIRST:
+                mn_set_cur(ps_global->msgmap,
+                           first_sorted_flagged(F_UNDEL, m, pc,
+                                              THREADING() ? 0 : FSF_SKIP_CHID));
+                break;
+
+              case IS_LAST:
+                mn_set_cur(ps_global->msgmap,
+                           first_sorted_flagged(F_UNDEL, m, pc,
+                                 FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID)));
+                break;
+
+              default:
+                alpine_panic("Unexpected incoming startup case");
+                break;
+
+  }
+}
Index: alpine-2.26/pith/mailcmd.h
===================================================================
--- alpine-2.26.orig/pith/mailcmd.h
+++ alpine-2.26/pith/mailcmd.h
@@ -42,6 +42,8 @@
 #define DB_FROMTAB	0x02	/* opening because of TAB command      */
 #define DB_INBOXWOCNTXT	0x04	/* interpret inbox as one true inbox */
 
+static  MAILSTREAM *saved_stream;
+static  unsigned long rule_curpos = 0L;
 
 /*
  * generic "is aggregate message command?" test
@@ -63,7 +65,13 @@ int	   do_broach_folder(char *, CONTEXT_
 void	   expunge_and_close(MAILSTREAM *, char **, unsigned long);
 void	   agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int);
 char	  *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long);
+char	  *move_read_msgs_using_rules (MAILSTREAM *, char *, char *);
+unsigned   get_perfolder_startup_rule (MAILSTREAM *, int, char *);
+void	   setup_threading_index_style (void);
+void	   find_startup_position (int, MAILSTREAM *, long);
+char	  *get_folder_to_save (MAILSTREAM *, long, char *);
 char	  *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t);
+unsigned   long rules_cursor_pos (MAILSTREAM *);
 void	   cross_delete_crossposts(MAILSTREAM *);
 long	   zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int);
 int	   unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *);
Index: alpine-2.26/pith/mailindx.c
===================================================================
--- alpine-2.26.orig/pith/mailindx.c
+++ alpine-2.26/pith/mailindx.c
@@ -37,6 +37,7 @@
 #include "../pith/send.h"
 #include "../pith/options.h"
 #include "../pith/ablookup.h"
+#include "../pith/rules.h"
 #ifdef _WINDOWS
 #include "../pico/osdep/mswin.h"
 #endif
@@ -375,6 +376,13 @@ reset_index_format(void)
     PAT_STATE     pstate;
     PAT_S        *pat;
     int           we_set_it = 0;
+    char *rule;
+
+    if((rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)) != NULL){
+      init_index_format(rule, &ps_global->index_disp_format);
+      fs_give((void **)&rule);
+      return;
+    }
 
     if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
 	for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
@@ -468,56 +476,60 @@ static INDEX_PARSE_T itokens[] = {
     {"SUBJECTTEXT",	iSubjectText,	FOR_INDEX},
     {"SUBJKEYTEXT",	iSubjKeyText,	FOR_INDEX},
     {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX},
-    {"OPENINGTEXT",	iOpeningText,	FOR_INDEX},
-    {"OPENINGTEXTNQ",	iOpeningTextNQ,	FOR_INDEX},
-    {"KEY",		iKey,		FOR_INDEX},
-    {"KEYINIT",		iKeyInit,	FOR_INDEX},
+    {"OPENINGTEXT",	iOpeningText,	FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM},
+    {"OPENINGTEXTNQ",	iOpeningTextNQ,	FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM},
+    {"KEY",		iKey,		FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE},
+    {"KEYINIT",		iKeyInit,	FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE},
     {"DESCRIPSIZE",	iDescripSize,	FOR_INDEX},
     {"ATT",		iAtt,		FOR_INDEX},
     {"SCORE",		iScore,		FOR_INDEX},
     {"PRIORITY",	iPrio,		FOR_INDEX},
     {"PRIORITYALPHA",	iPrioAlpha,	FOR_INDEX},
-    {"PRIORITY!",	iPrioBang,	FOR_INDEX},
-    {"LONGDATE",	iLDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SHORTDATE1",	iS1Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SHORTDATE2",	iS2Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SHORTDATE3",	iS3Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SHORTDATE4",	iS4Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DATEISO",		iDateIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SHORTDATEISO",	iDateIsoS,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATE",	iSDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTTIME",	iSTime,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+    {"PRIORITY!",	iPrioBang,	FOR_INDEX},  
+    {"LONGDATE",	iLDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SHORTDATE1",	iS1Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SHORTDATE2",	iS2Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SHORTDATE3",	iS3Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SHORTDATE4",	iS4Date,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DATEISO",		iDateIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SHORTDATEISO",	iDateIsoS,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATE",	iSDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTTIME",	iSTime,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
     {"SMARTTIME24",	iSTime24,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATEISO",	iSDateIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATESHORTISO",iSDateIsoS,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATES1",	iSDateS1,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATES2",	iSDateS2,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATES3",	iSDateS3,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATES4",	iSDateS4,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIME",	iSDateTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMEISO",iSDateTimeIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES1",	iSDateTimeS1,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES2",	iSDateTimeS2,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES3",	iSDateTimeS3,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES4",	iSDateTimeS4,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIME24",		iSDateTime24,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMEISO24",	iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES124",	iSDateTimeS124,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES224",	iSDateTimeS224,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES324",	iSDateTimeS324,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SMARTDATETIMES424",	iSDateTimeS424,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"TIME24",		iTime24,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"TIME12",		iTime12,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"TIMEZONE",	iTimezone,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"MONTHABBREV",	iMonAbb,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAYOFWEEKABBREV",	iDayOfWeekAbb,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAYOFWEEK",	iDayOfWeek,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"FROM",		iFrom,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"TO",		iTo,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"SENDER",		iSender,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"CC",		iCc,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+    {"SMARTDATEISO",	iSDateIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATESHORTISO",iSDateIsoS,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATES1",	iSDateS1,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATES2",	iSDateS2,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATES3",	iSDateS3,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATES4",	iSDateS4,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIME",	iSDateTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMEISO",iSDateTimeIso,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES1",	iSDateTimeS1,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES2",	iSDateTimeS2,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES3",	iSDateTimeS3,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES4",	iSDateTimeS4,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIME24",		iSDateTime24,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMEISO24",	iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES124",	iSDateTimeS124,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES224",	iSDateTimeS224,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES324",	iSDateTimeS324,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"SMARTDATETIMES424",	iSDateTimeS424,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"TIME24",		iTime24,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"TIME12",		iTime12,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"TIMEZONE",	iTimezone,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"MONTHABBREV",	iMonAbb,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DAYOFWEEKABBREV",	iDayOfWeekAbb,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DAYOFWEEK",	iDayOfWeek,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"FROM",		iFrom,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE},
+    {"TO",		iTo,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE},
+    {"SENDER",		iSender,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"CC",		iCc,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_SAVE},
+    {"ADDRESSTO",	iAddressTo,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"ADDRESSCC",	iAddressCc,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"ADDRESSRECIPS",	iAddressRecip,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"ADDRESSSENDER",	iAddressSender,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
     {"RECIPS",		iRecips,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"NEWS",		iNews,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"TOANDNEWS",	iToAndNews,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
@@ -526,56 +538,71 @@ static INDEX_PARSE_T itokens[] = {
     {"NEWSANDRECIPS",	iNewsAndRecips,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"MSGID",		iMsgID,		FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"CURNEWS",		iCurNews,	FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAYDATE",		iRDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"PREFDATE",	iPrefDate,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"PREFTIME",	iPrefTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"PREFDATETIME",	iPrefDateTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAY",		iDay,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAYORDINAL",	iDayOrdinal,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"DAY2DIGIT",	iDay2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"MONTHLONG",	iMonLong,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"MONTH",		iMon,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"MONTH2DIGIT",	iMon2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"YEAR",		iYear,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"YEAR2DIGIT",	iYear2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"ADDRESS",		iAddress,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+    {"DAYDATE",		iRDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"PREFDATE",	iPrefDate,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"PREFTIME",	iPrefTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"PREFDATETIME",	iPrefDateTime,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DAY",		iDay,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DAYORDINAL",	iDayOrdinal,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"DAY2DIGIT",	iDay2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"MONTHLONG",	iMonLong,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"MONTH",		iMon,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"MONTH2DIGIT",	iMon2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"YEAR",		iYear,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"YEAR2DIGIT",	iYear2Digit,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+    {"ADDRESS",		iAddress,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE},
     {"MAILBOX",		iMailbox,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"ROLENICK",       	iRoleNick,	FOR_REPLY_INTRO|FOR_TEMPLATE},
     {"INIT",		iInit,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
-    {"CURDATE",		iCurDate,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURDATEISO",	iCurDateIso,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURDATEISOS",	iCurDateIsoS,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURTIME24",	iCurTime24,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURTIME12",	iCurTime12,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURDAY",		iCurDay,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURDAY2DIGIT",	iCurDay2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURDAYOFWEEK",	iCurDayOfWeek,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+    {"CURDATE",		iCurDate,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURDATEISO",	iCurDateIso,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURDATEISOS",	iCurDateIsoS,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURTIME24",	iCurTime24,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURTIME12",	iCurTime12,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURDAY",		iCurDay,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURDAY2DIGIT",	iCurDay2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURDAYOFWEEK",	iCurDayOfWeek,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
     {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
-					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURMONTH",	iCurMon,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURMONTH2DIGIT",	iCurMon2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURMONTHLONG",	iCurMonLong,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURMONTHABBREV",	iCurMonAbb,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURYEAR",		iCurYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURYEAR2DIGIT",	iCurYear2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURPREFDATE",	iCurPrefDate,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"CURPREFTIME",	iCurPrefTime,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURMONTH",	iCurMon,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURMONTH2DIGIT",	iCurMon2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURMONTHLONG",	iCurMonLong,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURMONTHABBREV",	iCurMonAbb,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURYEAR",		iCurYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURYEAR2DIGIT",	iCurYear2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURPREFDATE",	iCurPrefDate,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"CURPREFTIME",	iCurPrefTime,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
     {"CURPREFDATETIME",	iCurPrefDateTime,
-					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTMONTH",	iLstMon,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTMONTH2DIGIT",	iLstMon2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTMONTHLONG",	iLstMonLong,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTMONTHABBREV",	iLstMonAbb,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTMONTHYEAR",	iLstMonYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTMONTH",	iLstMon,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTMONTH2DIGIT",	iLstMon2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTMONTHLONG",	iLstMonLong,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTMONTHABBREV",	iLstMonAbb,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTMONTHYEAR",	iLstMonYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
     {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
-					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTYEAR",	iLstYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
-    {"LASTYEAR2DIGIT",	iLstYear2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+					FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTYEAR",	iLstYear,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+    {"LASTYEAR2DIGIT",	iLstYear2Digit,	FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
     {"HEADER",		iHeader,	FOR_INDEX},
     {"TEXT",		iText,		FOR_INDEX},
     {"ARROW",		iArrow,		FOR_INDEX},
     {"NEWLINE",		iNewLine,	FOR_REPLY_INTRO},
     {"CURSORPOS",	iCursorPos,	FOR_TEMPLATE},
+    {"NICK",		iNick,		FOR_RULE|FOR_SAVE},
+    {"FCCFROM",		iFccFrom,	FOR_RULE|FOR_SAVE},
+    {"FCCSENDER",	iFccSender,	FOR_RULE|FOR_SAVE},
+    {"ALTADDRESS",	iAltAddress,	FOR_RULE|FOR_SAVE},
+    {"FOLDER",		iFolder,	FOR_RULE|FOR_SAVE|FOR_FOLDER},
+    {"ROLE",		iRole,		FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE},
+    {"PROCID",		iProcid,	FOR_RULE|FOR_RESUB|FOR_FLAG|FOR_COMPOSE|FOR_TRIM|FOR_TEMPLATE},
+    {"PKEY",		iPkey,		FOR_RULE|FOR_KEY},
+    {"SCREEN",		iScreen,	FOR_RULE|FOR_KEY},
+    {"FLAG",		iFlag,		FOR_RULE|FOR_SAVE|FOR_FLAG},
+    {"COLLECTION",	iCollection,	FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER},
+    {"BCC",		iBcc,		FOR_COMPOSE|FOR_RULE},
+    {"LCC",		iLcc,		FOR_COMPOSE|FOR_RULE},
+    {"FORWARDFROM",	iFfrom,		FOR_COMPOSE|FOR_RULE},
+    {"FORWARDADDRESS",	iFadd,		FOR_COMPOSE|FOR_RULE},
     {NULL,		iNothing,	FOR_NOTHING}
 };
 
@@ -2482,6 +2509,24 @@ format_index_index_line(INDEXDATA_S *ida
 		from_str(cdesc->ctype, idata, str, sizeof(str), ice);
 	        break;
 
+	      case iAddressTo:
+	      case iAddressCc:
+	      case iAddressRecip:
+		{ENVELOPE *env;
+		 int we_clear;
+		env = rules_fetchenvelope(idata, &we_clear); 
+		sprintf(str, "%-*.*s", ifield->width, ifield->width,
+		        detoken_src((cdesc->ctype == iAddressTo 
+				? "_ADDRESSTO_"
+				: (cdesc->ctype == iAddressCc
+				   ? "_ADRESSCC_"
+				   : "_ADRESSRECIPS_")), FOR_INDEX, 
+				env, NULL, NULL, NULL));
+		if(we_clear)
+		   mail_free_envelope(&env);
+		}
+		break;
+
 	      case iTo:
 		if(((field = ((addr = fetch_to(idata))
 			      ? "To"
@@ -3876,7 +3921,17 @@ try_again:
 
 			if(p > buf){
 			    size_t l;
+			    ENVELOPE *env;
+			    char *rule_result;
 
+			    if((rule_result = find_value((delete_quotes
+				      ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"), 
+				      buf, PROCESS_SP, idata, 4)) != NULL){
+			      collspaces(rule_result);
+			      strncpy(buf, rule_result, sizeof(buf));
+			      buf[sizeof(buf) - 1] = '\0';
+			      fs_give((void **) &rule_result);
+			    }
 			    l = strlen(buf);
 			    l += 100;
 			    firsttext = fs_get((l+1) * sizeof(char));
@@ -5455,10 +5510,10 @@ subj_str(INDEXDATA_S *idata, char *str,
 {
     char          *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
     char          *p, *border, *q = NULL, *free_subj = NULL;
-    char	  *sp;
+    char	  *sp, *rule_result;
     size_t         len;
     int            width = -1;
-    int            depth = 0, mult = 2;
+    int            depth = 0, mult = 2, collapsed, i, we_clear = 0;
     int            save;
     int            do_subj = 0, truncated_tree = 0;
     PINETHRD_S    *thd, *thdorig;
@@ -5514,6 +5569,14 @@ subj_str(INDEXDATA_S *idata, char *str,
      * to free it at the end of this routine.
      */
 
+    if ((rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)) != NULL){
+	if(origsubj)
+	  fs_give((void **)&origsubj);
+	we_clear++;
+	origsubj = cpystr(rule_result);
+	fs_give((void **)&rule_result);
+    }
+
     if(shorten)
        shorten_subject(origsubj);
 
@@ -5952,6 +6015,9 @@ subj_str(INDEXDATA_S *idata, char *str,
 
     if(free_subj)
       fs_give((void **) &free_subj);
+
+    if (we_clear && origsubj)
+      fs_give((void **)&origsubj);
 }
 
 
@@ -6317,16 +6383,33 @@ from_str(IndexColType ctype, INDEXDATA_S
 				 ? "To"
 				 : (addr = fetch_cc(idata))
 				 ? "Cc"
-				 : NULL))
-		       && set_index_addr(idata, field, addr, "To: ",
-					 strsize-1, fptr))
-		      break;
+				 : NULL))){
+			  char *rule_result;
+			  rule_result = find_value("_FROM_", NULL, 0, idata, 1);
+			  if (!rule_result)
+			  set_index_addr(idata, field, addr, "To: ",
+				strsize-1, fptr);
+			  else{
+			    sprintf(str, "%-*.*s", (int)(strsize-1), (int) (strsize-1),
+								rule_result);
+			    fs_give((void **)&rule_result);
+			  }
 
+		      break;
+		    }
 		    if(ctype == iFromTo &&
 		       (newsgroups = fetch_newsgroups(idata)) &&
 		       *newsgroups){
-			snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4),
-				newsgroups);
+			   char *rule_result;
+			   rule_result = find_value("_FROM_", NULL, 0, idata, 1);
+			   if (!rule_result)
+				sprintf(str, "To: %-*.*s", (int) (strsize-1-4), 
+					(int) (strsize-1-4), newsgroups);
+				else{
+				  sprintf(str, "%-*.*s", (int) (strsize-1), (int) (strsize-1),
+							 rule_result);
+				  fs_give((void **)&rule_result);
+				}
 			break;
 		    }
 
@@ -6339,7 +6422,15 @@ from_str(IndexColType ctype, INDEXDATA_S
 	      break;
 
 	  case iFrom:
-	    set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+	   { char *rule_result;
+	     rule_result = find_value("_FROM_", NULL, 0, idata, 4);
+	     if (!rule_result) 
+	      set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+	     else{
+		sprintf(str, "%-*.*s", (int) (strsize-1), (int) (strsize-1), rule_result);
+		fs_give((void **)&rule_result);
+	     }
+	   }
 	    break;
 
 	  case iAddress:
@@ -6637,3 +6728,64 @@ set_print_format(IELEM_S *ielem, int wid
 	}
     }
 }
+
+void
+setup_threading_display_style(void)
+{
+  RULE_RESULT *rule;
+  NAMEVAL_S *v;
+  int i;
+
+  rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL);
+  if (rule || ps_global->VAR_THREAD_DISP_STYLE){
+     for(i = 0; (v = thread_disp_styles(i)) != NULL; i++)
+        if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE, 
+		    rule ? (v ? v->name : "" ) : S_OR_L(v))){
+              ps_global->thread_disp_style = v->value;
+              break;
+        }
+     if (rule){
+	if (rule->result)
+	   fs_give((void **)&rule->result);
+	fs_give((void **)&rule);
+     }
+  }
+}
+
+char *
+find_value(char *token, char *use_this, int flag, INDEXDATA_S *idata, int nfcn)
+{
+ int n = 0, i, rule_context, we_clear;
+ char  *rule_result = NULL, **list;
+ ENVELOPE *env;
+ RULELIST *rule;
+ RULE_S *prule;
+
+ env = rules_fetchenvelope(idata, &we_clear);
+ if(env && env->sparep)
+    fs_give((void **)&env->sparep);
+ if(we_clear)
+   mail_free_envelope(&env);
+ if((rule = get_rulelist_from_code(V_REPLACE_RULES, ps_global->rule_list)) != NULL){
+    list = functions_for_token(token);
+    while(rule_result == NULL && (prule = get_rule(rule,n++))){
+        rule_context = 0;
+        if (prule->action->token && !strcmp(prule->action->token, token)){
+           for (i = 0; i < nfcn; i++)
+              if(list[i+1] && !strcmp(prule->action->function, list[i+1]))
+                 rule_context |= context_for_function(list[i+1]);
+           if (rule_context){
+	      env = rules_fetchenvelope(idata, &we_clear);
+	      if(use_this)
+		env->sparep = get_sparep_for_rule(use_this, flag);
+	     rule_result = process_rule(prule, rule_context, env);
+	     if(env->sparep)
+		free_sparep_for_rule(&env->sparep);
+	     if(we_clear)
+	       mail_free_envelope(&env);
+	   }
+        }
+    }
+ }
+ return rule_result;
+}
Index: alpine-2.26/pith/mailindx.h
===================================================================
--- alpine-2.26.orig/pith/mailindx.h
+++ alpine-2.26/pith/mailindx.h
@@ -30,6 +30,9 @@ extern void		(*setup_header_widths)(MAIL
 
 
 /* exported prototypes */
+SortOrder      translate (char *, int);
+char	      *find_value (char *, char *, int, INDEXDATA_S *, int);
+void	       setup_threading_display_style (void);
 int	       msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int);
 void	       adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *);
 unsigned long  line_hash(char *);
Index: alpine-2.26/pith/makefile.wnt
===================================================================
--- alpine-2.26.orig/pith/makefile.wnt
+++ alpine-2.26/pith/makefile.wnt
@@ -46,7 +46,8 @@ HFILES=	../include/system.h ../include/g
 	init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \
 	mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \
 	options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \
-	rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h smime.h smkeys.h sort.h sorttype.h \
+	rfc2231.h rules.h rulestype.h save.h savetype.h search.h send.h sequence.h signal.h \
+	smime.h smkeys.h sort.h sorttype.h \
 	state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \
 	thread.h url.h user.h util.h
 
@@ -56,7 +57,7 @@ OFILES=	ablookup.obj abdlc.obj addrbook.
 	ical.obj imap.obj init.obj \
 	keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \
 	margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \
-	readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj \
+	readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj \
 	smime.obj smkeys.obj sort.obj state.obj status.obj store.obj stream.obj string.obj strlst.obj \
 	takeaddr.obj tempfile.obj text.obj thread.obj adjtime.obj url.obj util.obj
 
Index: alpine-2.26/pith/pine.hlp
===================================================================
--- alpine-2.26.orig/pith/pine.hlp
+++ alpine-2.26/pith/pine.hlp
@@ -4916,6 +4916,7 @@ There are also additional details on
 <li><a href="h_config_alt_reply_menu">FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--></a>
 <li><a href="h_config_force_low_speed">FEATURE: <!--#echo var="FEAT_assume-slow-link"--></a>
 <li><a href="h_config_auto_read_msgs">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></a>
+<li><a href="h_config_auto_read_msgs_rules">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs-using-rules"--></a>
 <li><a href="h_config_auto_open_unread">FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></a>
 <li><a href="h_config_auto_unselect">FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></a>
 <li><a href="h_config_auto_unzoom">FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></a>
@@ -20446,6 +20447,7 @@ This set of special tokens may be used i
 <A HREF="h_config_index_format">&quot;<!--#echo var="VAR_index-format"-->&quot;</A> option,
 in the <A HREF="h_config_reply_intro">&quot;<!--#echo var="VAR_reply-leadin"-->&quot;</A> option,
 in signature files,
+in the <A HREF="h_config_reply_leadin_rules">&quot;new-rules&quot; option</A>,
 in template files used in
 <A HREF="h_rules_roles">&quot;roles&quot;</A>, and in the folder name
 that is the target of a Filter Rule.
@@ -20458,7 +20460,7 @@ and in the target of Filter Rules.
 <P>
 <P>
 
-<H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1>
+<H1><EM>Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)</EM></H1>
 
 <DL>
 <DT>SUBJECT</DT>
@@ -20492,6 +20494,22 @@ email address, never the personal name.
 For example, &quot;mailbox@domain&quot;.
 </DD>
 
+<DT>ADDRESSTO</DT>
+<DD>
+This is similar to the &quot;TO&quot; token, only it is always the
+email address of all people listed in the TO: field of the messages. Addresses
+are separated by a blank space. Example, &quot;mailbox@domain&quot; when
+the e-mail message contains only one person in the To: field, or
+&quot;peter@flintstones.com president@world.com&quot;.
+</DD>
+
+<DT>ADDRESSSENDER</DT>
+<DD>
+This is similar to the &quot;sender&quot; token, only it is always the
+email address of all person listed in the Sender: field of the message. 
+Example: &quot;mailbox@domain&quot;.
+</DD>
+
 <DT>MAILBOX</DT>
 <DD>
 This is the same as the &quot;ADDRESS&quot; except that the 
@@ -20539,6 +20557,15 @@ are unavailable) of the persons specifie
 message's &quot;Cc:&quot; header field.
 </DD>
 
+<DT>ADDRESSCC</DT>
+<DD>
+This is similar to the &quot;CC&quot; token, only it is always the
+email address of all people listed in the Cc: field of the messages. Addresses
+are separated by a blank space. Example: &quot;mailbox@domain&quot; when
+the e-mail message contains only one person in the Cc: field, or
+&quot;peter@flintstones.com president@world.com&quot;.
+</DD>
+
 <DT>RECIPS</DT>
 <DD>
 This token represents the personal names (or email addresses if the names
@@ -20547,6 +20574,14 @@ message's &quot;To:&quot; header field a
 the message's &quot;Cc:&quot; header field.
 </DD>
 
+<DT>ADDRESSRECIPS</DT>
+<DD>
+This token represent the e-mail addresses of the people in the To: and
+Cc: fields, exactly in that order separated by a space. It is almost obtained
+by concatenating the ADDRESSTO and ADDRESSCC tokens.
+</DD>
+
+
 <DT>NEWSANDRECIPS</DT>
 <DD>
 This token represents the newsgroups from the
@@ -21679,6 +21714,110 @@ This is an end of line marker.
 </DL>
 
 <P>
+<H1><EM>Tokens Available Only for New-Rules</EM></H1>
+
+<DL>
+<DT>FCCFROM</DT>
+<DD>
+The Fcc: folder assigned to the email address in the From: field in the
+addressbook.
+</DD>
+</DL>
+
+<DL>
+<DT>FCCSENDER</DT>
+<DD>
+The Fcc: folder assigned to the email address in the Sender: field in the
+addressbook.
+</DD>
+</DL>
+
+<DL>
+<DT>ALTADDRESS</DT>
+<DD>
+The value of your 
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+variable. At this time, no expansion of regular expressions is supported.
+</DD>
+</DL>
+
+<DL>
+<DT>NICK</DT>
+<DD>
+Nickname of the person in the From field in your addressbook.
+</DD>
+</DL>
+
+<DL>
+<DT>FOLDER</DT>
+<DD>
+Name of the folder where the rule will be applied.
+</DD>
+</DL>
+
+<DL>
+<DT>COLLECTION</DT>
+<DD>
+Name of the collection list where the rule will be applied.
+</DD>
+</DL>
+
+<DL>
+<DT>ROLE</DT>
+<DD>
+Name of the Role used to reply a message.
+</DD>
+</DL>
+
+<DL>
+<DT>BCC</DT>
+<DD>
+Not implemented yet, but it will be implemented in future versions. It will
+be used for <A HREF="h_config_compose_rules">compose</A>
+<A HREF="h_config_reply_rules">reply</A>
+<A HREF="h_config_forward_rules">forward</A>
+rules.
+</DD>
+</DL>
+
+<DL>
+<DT>LCC</DT>
+<DD>
+This is the value of the Lcc: field at the moment that you start the composition.
+</DD>
+</DL>
+
+<DL>
+<DT>FORWARDFROM</DT>
+<DD>
+This corresponds to the personal name (or address if there's no personal
+name) of the person who sent the message that you are forwarding.
+</DD>
+</DL>
+
+<DL>
+<DT>FORWARDADDRESS</DT>
+<DD>
+This is the address of the person that sent the message that you
+are forwarding.
+</DD>
+</DL>
+
+
+
+
+<DL>
+<DT>FLAG</DT>
+<DD> 
+A string containing the value of all the flags associated to a specific
+message. The possible values of allowed flags are "*" for Important, "N"
+for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for
+answered and "D" for deleted. See an example of its use in the 
+<A HREF="h_config_new_rules">new rules</A> explanation and example help.
+</DD>
+</DL>
+
+<P>
 <H1><EM>Token Available Only for Templates and Signatures</EM></H1>
 
 <DL>
@@ -25169,6 +25308,922 @@ character sets Alpine knows about by usi
 &lt;End of help on this topic&gt;
 </BODY>
 </HTML>
+====== h_config_procid =====
+<HTML>
+<HEAD>
+<TITLE>Token: PROCID</TITLE>
+</HEAD>
+<BODY>
+<H1>TOKEN: PROCID explained</H1>
+
+<P>
+The PROCID token is a way in which the user and the program can differentiate
+between different parts of a program. It allows the user to tell the
+program when to use a specific rule, and only use it at that specific
+moment.
+
+<P> The normal way in which this is done is by adding a new configuration 
+variable. The idea behind the PROCID token is that instead of adding a new 
+configuration variable (which means the user has to go through more 
+configuration variables just to tune the program to his liking), we reuse 
+an old variable and let the user look inside that variable for the desired
+behavior, which is actually set by setting the PROCID token.
+
+<P>
+Consider the following examples for forward-rules:
+
+<P>
+_ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+<P>
+and
+
+<P>
+_ROLE_ == {work} =&gt; _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+<P>
+both are triggered by the same condition. Since both are configured in the 
+same variable, only one of them will be executed all the time (whichever 
+is first). Therefore in order to differentiate, we add a _PROCID_ token. 
+So, for example, the first example above will be executed only when we are 
+determining the subject. In this case, the following rule will accomplish 
+this task
+
+<P>
+_PROCID_ == {fwd-subject} && _ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+<P>
+In this case, this rule will be tested fully only when we are determining 
+the subject line of a forwarded message, not otherwise.
+
+<P>
+It is wise to add the _PROCID_ token as the first condition in a rule, so 
+that other conditions will not be tested in a long list of rules.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_compose-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_compose-rules"--></H1>
+
+<P> At this time, this option is used to generate values for signature
+files that is not possible to do with the use of 
+<A HREF="h_rules_roles">roles</A>.
+
+<P> For example, you can have a rule like:<BR>
+_TO_ >> {Peter Flintstones} => _SIGNATURE_{~/.petersignature}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_forward_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_forward-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_forward-rules"--></H1>
+
+<P> This option has several uses. This feature uses the PROCID function
+to identify different features of forwarding. You can read more about PROCID
+by following <A HREF="h_config_procid">this link</A>.
+
+<P> If you want to edit the subject of a forwarded message, use the
+PROCID <I>fwd-subject</I>. For example you could have a rule like
+
+<P>
+_ROLE_ == {admin} && _SUBJECT_ !&gt; {[tag] } =&gt; _COPY_{[tag] _SUBJECT_}
+
+<P> Another way in which this option can be used, is to trim the values of 
+some fields. For this application the PROCID is <I>fwd-lcc</I>. For 
+example it can be used in the following way:
+
+<P>
+_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+<P> Other functions that can be used in this option are _EXEC_, _REXTRIM_ and
+_REXSUB_.
+
+<P> You can also use the _EXEC_ function. The documentation for this function
+is in the 
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+help text.
+
+<P> Another function that can be used is the _REXSUB_ function which does
+a substitution. This function takes three parameters: a pattern to search
+for, the text to be substituted for when the pattern is matched, and the
+number of times that the pattern will be replaced. Each of the parameters
+is enclosed between &quot;{&quot; and &quot;}&quot;. For example, to 
+delete only one ocurrence of the string &quot;Re: &quot; in a subject we 
+would write a rule such as
+
+<P>
+_FOLDER_ >> {} => _SUBJECT_ := _REXSUB_{Re: }{}{1}
+
+<P> The last parameter of the rexsub function is optional. In the sense 
+that its omission is understood as if the third parameter was 
+&quot;{1}&quot;. In order to make unlimited substitutions, use 
+&quot;{g}&quot; as the last parameter.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_index-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_index-rules"--></H1>
+
+<P> This option is used to supersede the value of the option <A
+HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> for specific folders. In
+this form you can have different index-formats for different folders. For
+example an entry here may be:
+
+<P>
+_FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pretty_command =====
+<HTML>
+<HEAD>
+<TITLE>Pretty-Command Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Pretty Command Explained</H1>
+
+<P> This text explains how to encode keys so that they will be recognized 
+by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the 
+same way. For example, the key ~ is recognized by the same character. The 
+issue is how control, or functions keys are recognized. The internal code 
+is most times easy to find out. If the key you want to use is not already 
+recognized by Alpine simply press it. Alpine will print its code. For example, 
+the return key is not recognized in this screen, so if you press it, you 
+will see the following message.
+
+<P> [Command &quot;RETURN&quot; not defined for this screen. Use ? for help]
+
+<P> from here you can guess that the code for the return command is 
+RETURN. You can try other commands, like Control-C, the TAB key, F4, etc. 
+to see their codes.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_key_macro_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_key-definition-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_key-definition-rules"--></H1>
+
+<P> This option can be used to define macros, that is, to define a key that
+when pressed executes a group of predetermined keystrokes. Since Alpine is
+a menu driven program, sometimes the same key may have different meanings
+in different screens, so a global redefinition of a key although possible
+is not advisable. 
+
+<P> <B>Always use the _SCREEN_ token as defined below.</B>. You have been
+warned!
+
+<P> In each screen, every time you press a recognized key, a command is 
+activated. In order to understand this feature, think of commands instead 
+of keystrokes. For example, you can think of the sort by thread command. 
+This command is associated to the keystrokes $ and h. You may want to 
+associate this command to a specific keystroke, like ~, so every time you
+press the ~ key, Alpine understand the $ and h keystrokes, which activates
+the sort by thread command.
+
+<P> Therefore, in order to use this option you must think of three 
+components. The screen where you will use the macro, the keystroke you 
+want to use and the set of keystrokes used by Alpine to accomplish the task 
+you want to accomplish. We will talk about these three components in what 
+follows.
+
+<P> First you must decide in which screen the macro will be used. This 
+feature is currently only available for the screen where your messages are 
+listed in index form (<A HREF="h_mail_index">MESSAGE INDEX</A>), the 
+screen where your message is displayed (<A HREF="h_mail_view">MESSAGE 
+TEXT</A>) the screen where the list of folders is displayed (<A 
+HREF="h_folder_maint">FOLDER LIST</A>) and the attachment index screen (<A 
+HREF="h_attachment_screen">ATTACHMENT INDEX</A>). The internal names of 
+these screens for this patch are &quot;index&quot;, &quot;text&quot;, 
+&quot;folder&quot;, and &quot;attachment&quot;, respectively. Please note 
+that the internal names are all in lowercase and are case sensitive.
+
+<P> In order to define the screen, you use the _SCREEN_ token, so for 
+example, you can write _SCREEN_ == {index}.
+
+<P> Second you must think of which key you will use to activate the macro. 
+Here you can use any key of your choice. The token you use to designate a 
+key is the _PKEY_ token (PKEY stands for &quot;pressed key&quot;). For 
+example you could use _PKEY_ == {~}, to designate the &quot;~&quot; 
+key to activate the command. Some keystrokes (like control, or 
+function keys) are encoded in special ways. You should read the 
+<A HREF="h_config_pretty_command">full explanation</A> on how to find
+out the encoding for each keystroke.
+
+<P> Last, you must think of the list of keys you will use to accomplish 
+the task you want Alpine to perform. Say for example you want to have the 
+folder sorted by thread. That means you want Aline to execute the keys 
+&quot;$&quot; and &quot;h&quot;. You use the _COMMAND_ function to specify 
+this. The syntax in this case is _COMMAND_{$,h}.
+
+<P> Observe that in the above example the different inputs are separated 
+by commas. This is the standard way in which the
+<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A> command works from 
+the command line. Due to restrictions in the way Alpine works, a comma is a 
+special character, which when added to a configuration option like this 
+will cause the configuration to split into several lines in the 
+configuration screen. This has the effect of producing several 
+configuration options, all of which are incorrect. This is undesirable 
+because what you want is to have it all in one line. In order to force the 
+configuration into one line you must quote the comma. The best way to 
+accomplish this is by quoting the full definition of the rule. For 
+example.
+
+<P>
+&quot;_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}&quot;
+
+<P> Another way to accomplish the same effect is by quoting the command and 
+not using quotes for the full command, nor commas to separate the 
+keystrokes in the command, for example
+
+<P>
+_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{&quot;$h&quot;}
+
+<P> For more information on how to define the argument of the _COMMAND_
+token see the help of 
+<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A>.
+
+<P> Because the $ command can also be used as the first character in the
+definition of an environemnt variable, no expansion of environment variables
+is done when parsing this variable. The $ character does not need quoting
+and quoting it will make Alpine fail to produce the correct result.
+
+<P> This configuration option is just one of many that allow you to 
+override the value of some global configurations within Alpine. There is a 
+help text explaining how to define all of them, which you can read by 
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_replace_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_replace-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_replace-rules"--></H1>
+
+<P> This option is used to have Alpine print different values for specific 
+tokens in the <A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>. For example you
+can replace strings like "To: newsgroup" by your name.
+
+<P> Here are examples of possible rules:
+
+<P>_FOLDER_ != {sent-mail} && _NICK_ != {} =&gt; _FROM_ := _REPLACE_{_FROM_ (_NICK_)}
+
+<P> or if you receive messages with tags that contain arbitrary numbers, and
+you want them removed from the index (but not from the subject), use a rule
+like the following
+
+<P>_FOLDER_ == {INBOX} =&gt; _SUBJECT_ := _REXTRIM_{&#92;&#91;some-tag-here &#35;&#91;0-9&#93;.*&#92;&#93;}
+
+<P> You can also use this configuration option to remove specific strings of
+the index display screen, so that you can trim unnecessary information in
+your index, like the reply leadin string in the OPENINGTEXTNQ token of the index.<BR>
+
+<P>_FOLDER_ == {some-folder} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: }
+
+<P> You can also use the _EXEC_ function. The documentation for this function
+is in the 
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+help text.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reply_leadin_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></H1>
+
+<P> This option is used to have Alpine generate a different 
+<A HREF="h_config_reply_intro"><!--#echo var="VAR_reply-leadin"--></A> string dependent either on
+the person you are replying to, or the folder where the message is being
+replied is in, or both.
+
+<P> Here there are examples of how this can be used. One can use the definition
+below to post to newsgroups and the pine-info mailing list, say:
+<P>
+_FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_"  "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):}
+
+<P> Here there is an example that one can use to change the reply indent string
+to reply people that speak spanish.
+<P>
+_FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribi&oacute; _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_resub_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-subject-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-subject-rules"--></H1>
+
+<P> This option is used to have Alpine generate a different subject when
+replying rather than the one Alpine would generate automatically. 
+
+<P> Here there are a couple of examples about how to use this
+configuration option:
+
+<P> In order to have messages with empty subject to be replied with the message
+"your message" use the rule<BR>
+<center>_SUBJECT_ == {} => _RESUB_{Re: your message}</center>
+
+<P> If you want to trim some parts of the subject when you reply use the
+rule<BR> 
+<center>_SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}</center> 
+
+<P>this rule removes the brackets "[" and "]" whenever the string "[one]"
+appears in it, it also removes the word "two" from it.
+
+<P>Another example where you may want to use this rule is when you 
+correspond with people that change the reply string from &quot;Re:&quot; 
+to &quot;AW:&quot; or &quot;Sv:&quot;. In this case a rule like<BR>
+<center>_SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }</center> 
+<P>
+would eliminate undesired strings in replies.
+
+<P> Another interesting use of this option is the use of the _EXEC_ function.
+This function takes as an argument a program or a script. This program
+must take as the input a file, and write its output to that file. For example,
+below is a sample of a script that removes the letter &quot;a&quot; of a file.
+
+<PRE>
+#!/bin/sh
+sed 's/a//g' $1 > /tmp/mytest
+mv /tmp/mytest $1
+</PRE>
+
+<P>
+As you can see this script took &quot;$1&quot; as input file, the sed program
+wrote its output to /tmp/mytest, and then the move program moved the file
+/tmp/mytest to the input file &quot;$1&quot;. This is the kind of behavior
+that your program is expected to have.
+
+<P>
+The content of the input file (&quot;$1&quot; above) is the value of a token
+like _SUBJECT_. In order to indicate this, we use the notation
+
+<P>
+_SUBJECT_ := _EXEC_{/path/to/script}
+
+<P> for the action. So for example
+
+<P>
+_FOLDER_ := {sent-mail} =&gt; _SUBJECT_ := _EXEC_{/path/to/script}
+
+<P> is a valid rule.
+
+<P> You can also use this configuration option to customize reply subjects
+according to the sender of the message.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sort_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sort-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sort-rules"--></H1>
+
+<P> This option is used to have Alpine sort different folders in different orders
+and thus override the value already set in the
+<A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A> configuration option.
+
+<P> Here's an example of the way it can be used. In this case all incoming
+folders are mailing lists, except for INBOX, so we sort INBOX by arrival
+(which is the default type of sort), but we want all the rest of mailing
+lists and newsgroups to be sorted by thread.
+
+<P>
+_COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread}
+
+<P> Another example could be<BR>
+_FOLDER_ == {Mailing List} => _SORT_{Reverse tHread}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_save_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_save-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_save-rules"--></H1>
+
+<P> This option is used to specify which folder should be used to save a
+message depending either on the folder the message is in, who the message
+is from, or text that the message contains in specific headers (Cc:,
+Subject:, etc).
+
+<P> If this option is set and the 
+<A HREF="h_config_auto_read_msgs"><!--#echo var="FEAT_auto-move-read-msgs"--></A> configuration
+option is also enabled then these definitions will be used to move messages
+from your INBOX when exiting Alpine.
+
+<P>Here there are some examples<BR>
+_FLAG_ >> {D} -> Trash<BR>
+_FROM_ == {U2} -> Bono<BR>
+_FOLDER_ == {comp.mail.pine} -> pine-stuff<BR>
+_NICK_ != {} -> _NICK_/_NICK_<BR>
+_DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_reply_indent_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-indent-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-indent-rules"--></H1>
+
+<P> This option is used to specify which reply-indent-string is to be used
+when replying to an e-mail. If none of the rules are successful, the result in
+the variable <a href="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></a>
+is used.
+
+<P> The associated function to this configuration option is called "RESTR" (for
+REply STRing). Some examples of its use are:<BR>
+_FROM_ == {Your Boss} => _RESTR_{"> "}<BR>
+_FROM_ == {My Wife} => _RESTR_{":* "}<BR>
+_FROM_ == {Perter Flintstone;Wilma Flintstone} => _RESTR_{"_INIT_ > "}<BR>
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_smtp_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smtp-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smtp-rules"--></H1>
+
+<P> This option is used to specify which SMTP server should be used when
+sending a message, if this rule is not defined, or the execution of the rule
+results in no server selected, then Alpine will look for 
+the value from the role that is being used to compose the message. If no smtp
+server is defined in that role or you are not using a role, then Alpine will get
+the name of the server from the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A> configuration
+option according to the rules used in that variable.
+
+<P> The function associated to this configuration option is _SMTP_, an example
+of the use of this function is<BR>
+_ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_startup_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_startup-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_startup-rules"--></H1>
+
+<P> This option is used when a folder is being opened. You can use it to specify its <A 
+HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A> and override 
+Alpine's global value set for all folders.
+
+<P> An example of the usage of this option is:<BR>
+_FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_new_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: New Rules Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: New Rules Explained</H1>
+
+This is a quite powerful option. Here you can define rules that override
+the values of any other option you have set in Alpine.
+
+<P>
+For example, you can set your folders to be sorted in a certain way when 
+you open them (say by Arrival). You may want, however, your newsgroups to 
+be sorted by thread. The set of &quot;rules&quot; options allows you to 
+configure this and many other options, including the index-format for 
+specific folders, the way the subject is displayed in the index screen or 
+the reply-leadin-string, to name a few.
+
+<P>
+Every rule has three parts: a condition, a separator and an action. The
+action is what will happen if the condition of the rule is satisfied.
+
+<P>
+  Here is an example:
+
+<P>
+   _FROM_ == {Fred Flintstone} =&gt; _SAVE_{Fred}
+
+<P>
+  Here the separator is "=&gt;". Whatever is to the left of the separator 
+is the condition (that is _FROM_ == {Fred Flintstone}) and to the right is 
+the action (_SAVE_{Fred}). The condition means that the rule will be 
+applied only if the message that you are reading is from &quot;Fred 
+Flintstone&quot;, and the action will be that you will be offered to save 
+it in the folder &quot;Fred&quot;, whenever you press the letter 
+&quot;S&quot; to save a message.
+
+<P>
+  The separator is always &quot;=&gt;&quot;, with one exception to be seen 
+later. But for the most part this will be the only one you will ever need.
+
+<P>
+  Now let us see how to do it. There are 14 functions already defined for 
+you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_, 
+_SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _REXSUB_, _THREADSTYLE and 
+_THREADINDEX_. The parameter of a function has to be enclosed between 
+&quot;{&quot; and &quot;}&quot;, so for example you can specify 
+_SAVE_{saved-messages} as a valid sentence.
+
+<P>
+  Later in the document you will find examples. Here is a short 
+description of what each function does:
+
+<P>
+<UL>
+<LI> _EXEC_ : This function takes as an argument a program. This program 
+gets as the input a file and must rewrite its output to that file, which 
+is then taken as the value to replace from the contents of that file. You 
+can use this function with 
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>, 
+<A HREF="h_config_replace_rules"><!--#echo var="VAR_replace-rules"--></A> and 
+<A HREF="h_config_forward_rules"><!--#echo var="VAR_forward-rules"--></A>. 
+See the help of those options for examples of how to use this function
+and configure these rules.
+<BR>&nbsp;<BR>
+<LI> _INDEX_ : This function takes as an argument an index-format, and
+makes that the index-format for the specified folder.
+<BR>&nbsp;<BR>
+<LI> _REPLACE_ : This function replaces the subject/from of the given e-mail by
+another subject/from only when displaying the index.
+<BR>&nbsp;<BR>
+<LI> _REPLY_ : This function takes as an argument a definition of a
+reply-leadin-string and makes this the reply-leading-string of the
+specified folder or person.
+<BR>&nbsp;<BR>
+<LI> _RESTR_ : This function takes as an argument the value of the 
+reply-indent-string to be used to answer the message being replied to.
+<BR>&nbsp;<BR>
+<LI> _RESUB_ : This function replaces the subject of the given e-mail by
+another subject only when replying to a message.
+<BR>&nbsp;<BR>
+<LI> _SAVE_ : The save function takes as an argument the name of a
+possibly non existing folder, whenever you want to save a message, that
+folder will be offered for you to save.
+<BR>&nbsp;<BR>
+<LI> _SIGNATURE_ : This function takes as an argument a signature file and
+uses that file as the signature for the message you are about to
+compose/reply/forward.
+<BR>&nbsp;<BR>
+<LI> _SMTP_ : This function takes as an argument the definition of a
+SMTP server.
+<BR>&nbsp;<BR>
+<LI> _SORT_ : This function takes as an argument a Sort Style, and sorts a
+specified folder in that sort order.
+<BR>&nbsp;<BR>
+<LI> _TRIM_ : This function takes as an argument a list of strings that
+you want removed from another string. At this time this only works for
+_FROM_ and _SUBJECT_.
+<BR>&nbsp;<BR>
+<LI> _REXTRIM_ : Same as _TRIM_ but its argument is one and
+only one extended regular expression.
+<BR>&nbsp;<BR>
+<LI> _REXSUB_ : This function takes three arguments. The structure
+is _REXSUB_{pattern}{text}{number of times}. The &quot;pattern&quot;
+to match is an extended regular expression. If the pattern is matched,
+it will be replaced by &quot;text&quot; as many times as the &quot;number
+of times&quot; argument specifies. If the last argument is
+omitted (that is, there are only two arguments) then the last argument
+is considered as &quot;{1}&quot;.
+<BR>&nbsp;<BR>
+<LI> _STARTUP_ : This function takes as an argument an
+incoming-startup-rule, and open an specified folder using that rule.
+<BR>&nbsp;<BR>
+<LI> _THREADSTYLE_ : This function takes as an argument a
+threading-display-style and uses it to display threads in a folder.
+<BR>&nbsp;<BR>
+<LI> _THREADINDEX_ : This function takes as an argument a
+threading-index-style and uses it to display threads in a folder.
+</UL>
+
+<P>
+You must me wondering how to define the person/folder over who to apply
+the action. This is done in the condition. When you specify a rule, the
+rule is only executed if the condition is satisfied. In another words for
+the rule:
+
+<P>
+   _FROM_ == {Fred Flintstone} =&gt; _SAVE_{Fred}
+
+<P> it will only be applied if the from is &quot;Fred Flintstone&quot;. If 
+the From is &quot;Wilma Flintstone&quot; the rule will be skipped.
+
+<P> In order to test a condition you can use the following tokens (in 
+alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE, 
+_SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what 
+it is between &quot;{&quot; and &quot;}&quot; in the condition, this part 
+of the condition is called the &quot;condition set&quot;. The definition 
+of each token can be found <A HREF="h_index_tokens">here</A>. 
+
+<P> A special testing token called _PROCID_ can be used to differentiate 
+inside a rule, between two rules that are triggered by the same condition. 
+A full explanation of the _PROCID_ token can be found in 
+<A HREF="h_config_procid">this link</A>.
+
+<P> There are two more tokens related to the option
+<A HREF="h_config_key_macro_rules">key-definition-rules</A>. Those tokens
+are only specific to that option, and hence are not explained here.
+
+<P> You can also test in different ways, you can use the following 
+&quot;test operands&quot;: &lt;&lt;, !&lt;, &gt;&gt;, !&gt;, == and !=. 
+All of them are two characters long. Here is the meaning of them:
+
+<P>
+<UL> 
+<LI> &lt;&lt; : It tests if the value of the token is contained in
+the condition set. Here for example if the condition set were equal to
+&quot;Freddy&quot;, then the condition: _NICK_ &lt;&lt; {Freddy}, would be true if
+the value of _NICK_ were &quot;Fred&quot;, &quot;red&quot; or &quot;Freddy&quot;. You are just looking
+for substrings here.
+<LI> &gt;&gt; : It tests if the value of the token contains the value of
+the condition set. Here for example if the condittion set were equal to
+&quot;Fred&quot;, then the condition: _FROM_ &gt;&gt; {Fred}, would be true if
+the value of _FROM_ were &quot;Fred Flintstone&quot; or &quot;Fred P. Flintstone&quot; or &quot;Freddy&quot;.
+<LI> == : It tests if the value of the token is exactly equal to the value
+of the set condition. For example _NICK_ == {Fred} will be false if the value 
+of _NICK_ is &quot;Freddy&quot; or &quot;red&quot;. 
+<LI> !&lt; : This is true only when &lt;&lt; is false and vice versa.
+<LI> !&gt; : This is true only when &gt;&gt; is false and vice versa.
+<LI> != : This is true only when == is false and vice versa.
+</UL>
+
+<P>
+  Now let us say that you want the same action to be applied to more than
+one person or folder, say you want &quot;folder1&quot; and &quot;folder2&quot; to be sorted by
+Ordered Subject upon entering. Then you can list them all of them in the
+condition part separting them by a &quot;;&quot;. Here is the way to do it.
+
+<P>
+ _FOLDER_ &lt;&lt; {folder1; folder2} =&gt; _SORT_{OrderedSubj}
+
+<P>
+  Here is the first subtlety about these definitions. Notice that the
+following rule:
+
+<P>
+ _FOLDER_ == {folder1; folder2} =&gt; _SORT_{Reverse OrderedSubj}
+
+<P> works only for &quot;folder1&quot; but not for &quot;folder2&quot;. This is because the
+comparison of the name of the folder is done with whatever is in between
+&quot;{&quot;, &quot;;&quot; or &quot;}&quot;, so in the above rule you would be testing <BR>
+&quot;folder2&quot; == &quot; folder2&quot;. The extra space makes the difference. 
+The reason why the first rule does not fail is because 
+&quot;folder2&quot; &lt;&lt; &quot; folder2&quot; is actually
+true. If something ever fails this may be something to look into.
+
+<P>
+ Here are a few examples of what we have talked about before.
+
+<P>
+_NICK_ == {lisa;kika} =&gt; _SAVE_{_NICK_/_NICK_} <BR>
+This means that if the nick is lisa, it will 
+save the message in the folder &quot;lisa/lisa&quot;, and if the nick 
+is &quot;kika&quot;, it will save the message in the folder &quot;kika/kika&quot;
+
+<P>
+_FOLDER_ == {Lynx} -&gt; lynx <BR>
+This, is an abbreviation of the following rule:<BR>
+_FOLDER_ == {Lynx} =&gt; _SAVE_{lynx} <BR>
+(note the change in separator from &quot;=&gt;&quot; to &quot;-&gt;&quot;). In the future 
+I will use that abbreviation.
+
+<P> _FOLDER_ &lt;&lt; {comp.mail.pine; pine-info; pine-alpha} -&gt; pine <BR>
+Any message in the folders &quot;comp.mail.pine&quot;, &quot;pine-info&quot; or &quot;pine-alpha&quot;
+will be saved to the folder &quot;pine&quot;.
+
+<P> _FROM_ &lt;&lt; {Pine Master} -&gt; pine <BR>
+Any message whose From field contains
+&quot;Pine Master&quot; will be saved in the folder pine.
+
+<P> _FOLDER_ &lt;&lt; {Lynx; pine-info; comp.mail.pine} =&gt;
+_INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)} <BR> Use a
+different index-format for the folders &quot;Lynx&quot;, &quot;pine-info&quot; and
+&quot;comp.mail.pine&quot;, where the size is not present.
+
+<P> _FOLDER_ == {Lynx;pine-info} =&gt; _REPLY_{*** _FROM_ (_ADDRESS_)
+wrote in the _FOLDER_ list _SMARTDATE_(&quot;Today&quot; &quot;today&quot; &quot;on
+_LONGDATE_&quot;):}<BR> If a message is in one of the incoming folders &quot;Lynx&quot;
+or &quot;pine-info&quot;, create a reply-leadin-string that acknowledges that. Note
+the absence of &quot;,&quot; in the function _SMARTDATE_. For example answering to a
+message in the pine-info list would look like:
+
+<P>
+*** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today:
+
+<P>
+However replying for a message in the Lynx list would look:
+
+<P>
+*** mattack@area.com (mattack@area.com) wrote in the Lynx list today:
+
+<P>
+If you write in more than one language you can use this feature to create
+Reply-leadin-strings in different languages.
+
+<P> Note that at least for people you can create particular
+reply-leadin-string using the role features, but it does not work as this
+one does. This seems to be the right way to do it.
+
+<P> _FOLDER_ &lt;&lt; {Lynx; comp.mail.pine; pine_info; pine-alpha} =&gt;
+_SORT_{OrderedSubj}<BR> This means upon opening, sort the folders &quot;Lynx&quot;,
+&quot;comp.mail.pine&quot;, etc in ordered subject. All the others use the default
+sort order. You can not sort in reverse in this form. The possible
+arguments of this function are listed in the definition of the
+default-sort-rule (Arrival, scorE, siZe, etc).
+
+<P> The last examples use the function _TRIM_ which has a special form.
+This function can only be used in the index list.
+
+<P> _FOLDER_ &lt;&lt; {Lynx} =&gt; _SUBJECT_ := _TRIM_{lynx-dev }<BR> In
+the folder &quot;Lynx&quot; eliminate from the subject the string &quot;lynx-dev &quot; (with
+the space at the end). For example a message whose subject is &quot;Re:
+lynx-dev unvisited Visited Links&quot;, would be shown in the index with
+subject: &quot;Re: unvisited Visited Links&quot;, making the subject shorter and
+giving the same information.
+
+<P> _FROM_ &gt;&gt; {Name (Comment)} =&gt; _FROM_ :=
+_TRIM_{ (Comment)}<BR> Remove the part &quot; (Comment)&quot;
+from the _FROM_, so when displaying in the index the real From &quot;Name&quot;
+will appear.
+
+<P> _SUBJECT_ == {} =&gt; _RESUB_{Re: your mail without subject}
+If there is no subject in the message, use the subject &quot;Re: your mail
+wiyhout subject&quot; as a subject for the reply message.
+
+<P> You can add more complexity to your rules by checking more than one
+conditions before a rule is executed. More than one condition can be
+checked by separating different conditions by the && (and) separator,
+or using the || (or) separator. For example we could have a rule that
+saves all
+messages in inbox from Rubye, to the Personal folder, as
+
+<P> _FOLDER_ == {INBOX} && _FROM_ >> {Rubye} => _SAVE_{Personal}
+
+<P> We could also have a rule that is triggered by an &quot;or&quot; 
+condition by, sat for messages from Andres or messages in the index
+to trigger a specific reply leadin string.
+
+<P> _FOLDER_ == {INBOX} || _FROM_ >> {Andres} =&gt; _REPLY_{You wrote:}
+
+<P>Observe that the construction 
+
+<P> _TOKEN_ == {value1} || _TOKEN_ == {value2}
+
+<P>can be shortened to
+
+<P> _TOKEN_ == {value1;value2}
+
+<P> Round parentheses can be used to group some conditions, for example
+
+<P> (_FROM_ &gt;&gt; {Andres} && _FOLDER_ == {INBOX}) || _FROM_ &gt;&gt; {Rubye}
+
+
+<P> You can also list your index by nick, in the following way:<BR>
+_NICK_ != {} => _FROM_ := _REPLACE_{_NICK_}
+
+<P>
+  If you want to open the folder &quot;pine-info&quot; in the first non-read message
+use the rule:<BR>
+_FOLDER_ == {pine-info} => _STARTUP_{first-unseen}
+
+<P>
+ If you want to move your deleted messages to a folder, called &quot;Trash&quot;, use
+the following rule:<BR>
+_FLAG_ >> {D} -> Trash
+
+<P>
+The reason why the above test is not &quot;_FLAG_ == {D}&quot; is because that would mean
+that this is the only flag set in the message. It's better to test by containment in this case.
+
+<P> If you want to use a specific signature when you are in a specific collection
+use the following rule:<BR>
+_COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature}
+
+<P> Finally about the question of which rule will be executed. Only the
+first rule that matches will be executed. It is important to notice though
+that &quot;saving&quot; rules do not compete with &quot;sorting&quot; rules. So the first
+&quot;saving&quot; rule that matches will be executed in the case of saving and so
+on.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
 ====== h_config_char_set =====
 <HTML>
 <HEAD>
@@ -28921,6 +29976,76 @@ the From field is used to show the relat
 &lt;End of help on this topic&gt;
 </BODY>
 </HTML>
+====== h_config_thread_display_style_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Threading-Display-Style-Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Threading-Display-Style-Rule</H1>
+
+This option is very similar to <A HREF="h_config_thread_disp_style">
+<!--#echo var="VAR_threading-display-style"--></A>, but it is a rule which specifies the
+display styles for a thread that you want displayed in a specific
+folder or collection.
+<P>
+The token to be used in this function is _THREADSTYLE_. Here there is
+an example of its use
+<P>
+_FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like}
+<P>
+The values that can be given for the _THREADSTYLE_ function are the
+values of the threading-display-style function, which can be found
+listed in the <A HREF="h_config_thread_disp_style">threading-display-style</A>
+configuration option.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>
+<UL>   
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_index_style_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Threading-Index-Style-Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Threading-Index-Style-Rule</H1>
+
+This option is very similar to <A HREF="h_config_thread_index_style">
+<!--#echo var="VAR_threading-index-style"--></A>, but it is a rule which specifies the
+index styles for a thread that you want displayed in a specific
+folder or collection.
+<P>
+The token to be used in this function is _THREADINDEX_. Here there is
+an example of its use
+<P>
+_FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads}
+<P>
+The values that can be given for the _THREADINDEX_ function are the
+values of the threading-index-display function, which can be found
+listed in the <A HREF="h_config_thread_index_style"><!--#echo var="VAR_threading-index-style"--></A>
+configuration option.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>
+<UL>   
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
 ====== h_config_pruning_rule =====
 <HTML>
 <HEAD>
@@ -32587,6 +33712,29 @@ automatically transfer all read messages
 them as deleted in the INBOX.  Messages in the INBOX marked with an 
 &quot;N&quot; (meaning New, or unseen) are not affected.
 <P>
+<UL>   
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_read_msgs_rules =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: auto-move-read-msgs-using-rules</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: auto-move-read-msgs-using-rules</H1>
+This feature controls an aspect of Alpine's behavior upon quitting.  If set,
+and the 
+<A HREF="h_config_read_message_folder">&quot;<!--#echo var="VAR_read-message-folder"-->&quot;</A>
+option is also set, then Alpine will automatically transfer all read
+messages to the designated folder using the rules that you have defined in
+your
+<A HREF="h_config_save_rules">&quot;<!--#echo var="VAR_save-rules"-->&quot;</A> and mark
+them as deleted in the INBOX.  Messages in the INBOX marked with an 
+&quot;N&quot; (meaning New, or unseen) are not affected.
+<P>
 <UL>   
 <LI><A HREF="h_finding_help">Finding more information and requesting help</A>
 </UL><P>
Index: alpine-2.26/pith/reply.c
===================================================================
--- alpine-2.26.orig/pith/reply.c
+++ alpine-2.26/pith/reply.c
@@ -43,6 +43,8 @@
 #include "../pith/mailcmd.h"
 #include "../pith/margin.h"
 #include "../pith/smime.h"
+#include "../pith/copyaddr.h"
+#include "../pith/rules.h"
 
 
 /*
@@ -860,8 +862,27 @@ char *
 reply_quote_str(ENVELOPE *env)
 {
     char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
+    char reply_string[MAX_PREFIX+1];
 
-    strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
+    { RULE_RESULT *rule;
+      rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env);
+       if (rule){
+           strncpy(reply_string,rule->result,sizeof(reply_string));
+	   reply_string[sizeof(reply_string)-1] = '\0';
+           if (rule->result)
+	      fs_give((void **)&rule->result);
+	   fs_give((void **)&rule);
+       }
+       else
+	  if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){
+	    strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1);
+	    reply_string[sizeof(reply_string)-1] = '\0';
+	  }
+	  else
+           strncpy(reply_string,"> ", 3);
+    }
+
+    strncpy(buf, reply_string, sizeof(buf)-1);
     buf[sizeof(buf)-1] = '\0';
 
     /* set up the prefix to quote included text */
@@ -913,10 +934,29 @@ reply_quote_str(ENVELOPE *env)
 int
 reply_quote_str_contains_tokens(void)
 {
-    return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
-	   (strstr(ps_global->VAR_REPLY_STRING, from_token) ||
-	    strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
-	    strstr(ps_global->VAR_REPLY_STRING, init_token)));
+   char *reply_string;
+      
+   reply_string = (char *) malloc( 80*sizeof(char));
+   { RULE_RESULT *rule;
+     rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE, NULL);
+      if (rule){
+          reply_string = cpystr(rule->result);
+	  if (rule->result)
+	     fs_give((void **)&rule->result);
+	  fs_give((void **)&rule);
+      }
+      else
+	  if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){
+	    strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1);
+	    reply_string[sizeof(reply_string)-1] = '\0';
+	  }
+	  else
+          reply_string = cpystr("> ");
+   }
+    return(reply_string && reply_string[0] &&
+	   (strstr(reply_string, from_token) ||
+	    strstr(reply_string, nick_token) ||
+	    strstr(reply_string, init_token)));
 }
 
 
@@ -1482,6 +1522,10 @@ get_addr_data(ENVELOPE *env, IndexColTyp
     buf[0] = '\0';
 
     switch(type){
+      case iFfrom:
+	addr = env && env->sparep ? env->sparep : NULL;
+	break;
+
       case iFrom:
 	addr = env ? env->from : NULL;
 	break;
@@ -1894,22 +1938,194 @@ get_reply_data(ENVELOPE *env, ACTION_S *
 
 	break;
 
+      case iProcid:
+        if(ps_global->procid){
+	   strncpy(buf, ps_global->procid, maxlen);
+	   buf[maxlen] = '\0';
+	}
+      break;
+
+      case iRole:
+        if (ps_global->role){
+	   strncpy(buf, ps_global->role, maxlen);
+	   buf[maxlen] = '\0';
+	}
+      break;
+
+      case iRoleNick:
+	if(role && role->nick){
+	  strncpy(buf, role->nick, maxlen);
+	  buf[maxlen] = '\0';
+	}
+	break;
+
+      case iPkey:
+	if(ps_global->pressed_key){
+	  strcpy(buf, ps_global->pressed_key);
+	  buf[maxlen] = '\0';
+	}
+	break;
+
+      case iScreen:
+	if(ps_global->screen_name){
+	  strncpy(buf, ps_global->screen_name, maxlen);
+	  buf[maxlen] = '\0';
+	}
+	break;
+
+      case iFfrom:
       case iFrom:
       case iTo:
       case iCc:
       case iSender:
       case iRecips:
       case iInit:
+      if (env)  
 	get_addr_data(env, type, buf, maxlen);
-	break;
+      break;
 
-      case iRoleNick:
-	if(role && role->nick){
-	    strncpy(buf, role->nick, maxlen);
-	    buf[maxlen] = '\0';
+     case iFolder:
+	if(ps_global->cur_folder){
+	  strncpy(buf,ps_global->cur_folder, maxlen);
+	  buf[maxlen] = '\0';
+	}
+      break;
+
+     case iCollection:
+	if(ps_global->context_current->nickname){
+	  strncpy(buf,ps_global->context_current->nickname, maxlen);
+	  buf[maxlen] = '\0';
+	}
+      break;
+      
+     case iFlag:
+        {MAILSTREAM *stream = ps_global->mail_stream;
+	 MSGNO_S *msgmap = NULL;
+         long msgno;
+         MESSAGECACHE *mc = NULL;
+	 strncpy(buf, "_FLAG_", maxlen);	/* default value */
+	 if (stream){
+	     msgmap = sp_msgmap(stream);
+	     msgno =  mn_m2raw(msgmap, rules_cursor_pos(stream));
+	     if (msgno > 0L) mc = stream ? mail_elt(stream,  msgno) : NULL;
+	     if (mc)
+	        sprintf(buf,"%s%s%s%s",mc->flagged ? "*" : "",
+                mc->recent   ? (mc->seen ? "R" : "N") : (mc->seen) ? "R" : "U",
+                mc->answered ? "A" : "",
+                mc->deleted  ? "D" : "" );
+	 }
+	 buf[maxlen] = '\0';
+        }
+        break;
+
+     case iAltAddress:
+	if(ps_global->VAR_ALT_ADDRS != NULL 
+		&& ps_global->VAR_ALT_ADDRS[0] != NULL){
+	   size_t len;
+	   int i, j;
+
+	   for(i = 0, len = 0; len < maxlen && ps_global->VAR_ALT_ADDRS[i]; i++){
+	      for(j = 0; len < maxlen && ps_global->VAR_ALT_ADDRS[i][j] != '\0'; j++){
+		if(ps_global->VAR_ALT_ADDRS[i][j] == ';')
+		  buf[len++] = '\\';
+		buf[len++] = ps_global->VAR_ALT_ADDRS[i][j];
+	      }
+	      if(len < maxlen){
+		if(ps_global->VAR_ALT_ADDRS[i+1] != NULL)
+		  buf[len++] = ';';
+		else
+		  buf[len++] = '\0';
+	      }
+	   }
+	   buf[maxlen] = '\0';
+	}
+	break;
+         
+     case iNick:
+     case iFccFrom:
+     case iFccSender:
+	if (env){
+	   ADDRESS *tmp_adr;
+
+	   switch(type){
+	     case iNick: 
+		tmp_adr = env->from ? copyaddr(env->from)
+ 			 : env->sender ? copyaddr(env->sender) : NULL;
+		break;
+	     case iFccFrom:
+	   	tmp_adr = env->from ? copyaddr(env->from) : NULL;
+		break;
+	     case iFccSender:
+		tmp_adr = env->sender ? copyaddr(env->sender) : NULL;
+		break;
+	     default: alpine_panic("Unhandled Rules case (01)");
+	   }
+	   if(type == iNick)
+	     get_nickname_from_addr(tmp_adr, buf, maxlen);
+	   else
+	     get_fcc_from_addr(tmp_adr, buf, maxlen);
+	   mail_free_address(&tmp_adr);
 	}
 	break;
 
+     case iAddressSender:
+     case iAddressCc:
+     case iAddressRecip:
+     case iAddressTo:
+     case iFadd:
+     {
+     int plen = 0; 	/* partial length */
+     ADDRESS *sparep2 = (type == iAddressTo || type == iAddressRecip) 
+			? ((env && env->to) 
+			   ? copyaddrlist(env->to)
+			   : NULL)
+			: (type == iAddressCc)
+			    ? ((env && env->cc) 
+				? copyaddrlist(env->cc)
+				: NULL)
+			    : (type == iAddressSender)
+				? ((env && env->sender)
+				    ? copyaddr(env->sender)
+				    : NULL)
+				: ((env && env->sparep) 
+				   ? copyaddr((ADDRESS *)env->sparep)
+				   : NULL);
+      ADDRESS *sparep;
+
+      if (type == iAddressRecip){
+	  ADDRESS *last_to = NULL;
+
+	for(last_to = sparep2;last_to && last_to->next; last_to= last_to->next);
+	
+	/* Make the end of To list point to cc list */
+	if(last_to)
+	  last_to->next = (env && env->cc ? copyaddrlist(env->cc) : NULL);
+
+      }
+      sparep = sparep2;
+      for(; sparep ; sparep = sparep->next)
+	if(sparep && sparep->mailbox && sparep->mailbox[0] &&
+	   (plen ? plen + 1 : plen) + strlen(sparep->mailbox) <= maxlen){
+	   if (plen == 0)
+	       strcpy(buf, sparep->mailbox);
+	   else{
+	       strcat(buf, " ");
+	       strcat(buf, sparep->mailbox);
+	   }
+	   if(sparep->host &&
+        	   sparep->host[0] &&
+		   sparep->host[0] != '.' &&
+		   strlen(buf) + strlen(sparep->host) + 1 <= maxlen){
+	      strcat(buf, "@");
+              strcat(buf, sparep->host);
+           }
+	   plen = strlen(buf);
+	}
+	 mail_free_address(&sparep2);
+     }
+         
+      break;  
+
       case iNewLine:
 	if(maxlen >= strlen(NEWLINE)){
 	    strncpy(buf, NEWLINE, maxlen);
@@ -1937,6 +2153,11 @@ get_reply_data(ENVELOPE *env, ACTION_S *
 
 	break;
 
+      case iLcc:	/* fake it, there are not enough spare pointers */
+	if (env && env->date)
+	  sprintf(buf,"%s",env->date);
+	break;
+
       case iNews:
       case iCurNews:
 	get_news_data(env, type, buf, maxlen);
@@ -1986,6 +2207,14 @@ get_reply_data(ENVELOPE *env, ACTION_S *
 
 	break;
 
+      case iOpeningText:
+      case iOpeningTextNQ:
+	if(env && env->sparep){
+	   strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen);
+	   buf[maxlen] = '\0';
+	}
+	break;
+
       case iSubject:
       case iShortSubject:
 	if(env && env->subject){
@@ -2048,7 +2277,18 @@ reply_delimiter(ENVELOPE *env, ACTION_S
     if(!env)
       return;
 
-    strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
+    { RULE_RESULT *rule;
+	rule = get_result_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO, env);
+	if(rule){
+	   strncpy(buf, rule->result, MAX_DELIM);
+	   if (rule->result)
+	      fs_give((void **)&rule->result);
+	   fs_give((void **)&rule);
+	}
+	else
+	  strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
+    }
+
     buf[MAX_DELIM] = '\0';
     /* preserve exact default behavior from before */
     if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
@@ -2307,6 +2547,7 @@ forward_subject(ENVELOPE *env, int flags
 {
     size_t l;
     char  *p, buftmp[MAILTMPLEN];
+    RULE_RESULT *rule;
     
     if(!env)
       return(NULL);
@@ -2314,9 +2555,19 @@ forward_subject(ENVELOPE *env, int flags
     dprint((9, "checking subject: \"%s\"\n",
 	       env->subject ? env->subject : "NULL"));
 
-    if(env->subject && env->subject[0]){		/* add (fwd)? */
-	snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
-	buftmp[sizeof(buftmp)-1] = '\0';
+    buftmp[0] = '\0';
+    ps_global->procid = cpystr("fwd-subject");
+    if ((rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)) != NULL){
+       snprintf(buftmp, sizeof(buftmp), "%s", rule->result);
+       fs_give((void **)&rule->result);
+       fs_give((void **)&rule);
+    }
+    else if(env->subject)
+       snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
+    buftmp[sizeof(buftmp)-1] = '\0';
+    fs_give((void **)&ps_global->procid);
+
+    if(buftmp[0]){            /* add (fwd)? */
 	/* decode any 8bit (copy to the temp buffer if decoding doesn't) */
 	if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
 				  SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp)
Index: alpine-2.26/pith/rules.c
===================================================================
--- /dev/null
+++ alpine-2.26/pith/rules.c
@@ -0,0 +1,1566 @@
+/* This module was written by
+ *
+ * Eduardo Chappa (chappa@washington.edu)
+ * http://alpine.x10host.com/alpine/
+ *
+ *  Original Version: November 1999
+ *  Last Modified   : November 24, 2018
+ *
+ * Send bug reports about this module to the address above.
+ */
+
+#include "../pith/headers.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/copyaddr.h"
+#include "../pith/mailindx.h"
+#include "../pith/rules.h"
+
+#define CSEP_C	('\001')
+#define CSEP_S	("\001")
+
+/* Internal Prototypes */
+
+int   test_condition  (CONDITION_S *, int, ENVELOPE *);
+int   test_in         (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   test_ni         (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   test_not_in     (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   test_not_ni     (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   test_eq         (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   test_not_eq     (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+int   isolate_condition (char *, char **, int *);
+int   sanity_check_condition (char *);
+char  *test_rule      (RULELIST *, int, ENVELOPE *, int *);
+char  *trim	      (RULEACTION_S *, int, ENVELOPE *);
+char  *rextrim	      (RULEACTION_S *, int, ENVELOPE *);
+char  *rexsub	      (RULEACTION_S *, int, ENVELOPE *);
+char  *do_rextrim     (char *, TOKEN_VALUE *);
+char  *do_rexsub      (char *, TOKEN_VALUE *);
+char  *raw_value      (RULEACTION_S *, int, ENVELOPE *);
+char  *extended_value (RULEACTION_S *, int, ENVELOPE *);
+char  *exec_fcn	      (RULEACTION_S *, int, ENVELOPE *);
+char  *expand         (char *, char *);
+char  *get_name_token (char *);
+char  *advance_to_char (char *, char, int, int *);
+char  **functions_for_token (char *);
+char  *canonicalize_condition (char *, int *);
+void  free_rexsub (REXSUB_S **);
+void  free_void_token_value (void **, int);
+void  free_token_value (TOKEN_VALUE **);
+void  free_condition  (CONDITION_S **);
+void  free_condition_value (CONDVALUE_S **);
+void  free_ruleaction (RULEACTION_S **);
+void  free_rule       (RULE_S **);
+void  free_rule_list  (RULELIST **);
+void  *rule_alloc_mem (size_t);
+void  add_rule        (int, int);
+void  set_rule_list    (struct variable *);
+void  free_parsed_value(TOKEN_VALUE **value);
+RULE_S   *parse_rule  (char *, int);
+RULELIST *get_rule_list (char **, int, int);
+TOKEN_VALUE *parse_group_data (char *,int *);
+TOKEN_VALUE *copy_parsed_value (TOKEN_VALUE *, int, ENVELOPE *);
+TOKEN_VALUE *parse_action_to_char(char *);
+TOKEN_VALUE *parse_rexsub_action(char *);
+CONDVALUE_S *fill_condition_value (char *);
+CONDITION_S *fill_condition (char *);
+CONDITION_S *parse_condition (char *, int *);
+PRULELIST_S *add_prule        (PRULELIST_S *, PRULELIST_S *);
+RULEACTION_S *parse_action (char *, int);
+
+REL_TOKEN rel_rules_test[] = {
+   {EQ_REL,     Equal,          test_eq},
+   {IN_REL,     Subset,         test_in},
+   {NI_REL,     Includes,       test_ni},
+   {NOT_EQ_REL, NotEqual,       test_not_eq},
+   {NOT_IN_REL, NotSubset,      test_not_in},
+   {NOT_NI_REL, NotIncludes,    test_not_ni},
+   {NULL,       EndTypes,       NULL}
+};
+
+#define NREL  (sizeof(rel_rules_test)/sizeof(rel_rules_test[0]) - 1)
+
+RULE_FCN rule_fcns[] = {
+{COPY_FCN,	6,	1,	CHAR_TYPE,	parse_action_to_char,	extended_value,	FOR_SAVE|FOR_COMPOSE},
+{REXTRIM_FCN,	9,	1,	CHAR_TYPE,	parse_action_to_char,	rextrim,	FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+{REXSUB_FCN,	8,	1,	REXSUB_TYPE,	parse_rexsub_action,	rexsub,		FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+{EXEC_FCN,	6,	1,	CHAR_TYPE,	parse_action_to_char,	exec_fcn,	FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+{TRIM_FCN,	6,	1,	CHAR_TYPE,	parse_action_to_char,	trim,		FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+{REPLACE_FCN,	7,	1,	CHAR_TYPE,	parse_action_to_char,	extended_value,	FOR_REPLACE},
+{SAVE_FCN,	6,	0,	UNDEFINED_TYPE,	NULL,			extended_value,	FOR_SAVE},
+{REPLY_FCN,	7,	0,	UNDEFINED_TYPE,	NULL,			extended_value,	FOR_REPLY_INTRO},
+{SORT_FCN,	6,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_SORT},
+{INDEX_FCN,	7,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_INDEX},
+{COMMAND_FCN,	9,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_KEY},
+{REPLYSTR_FCN, 10,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_COMPOSE},
+{SIGNATURE_FCN,11,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_COMPOSE},
+{RESUB_FCN,	7,	0,	UNDEFINED_TYPE,	NULL,			extended_value,	FOR_RESUB},
+{STARTUP_FCN,	9,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_STARTUP},
+{THRDSTYLE_FCN,11,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_THREAD},
+{THRDINDEX_FCN,11,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_THREAD},
+{SMTP_FCN,	6,	0,	UNDEFINED_TYPE,	NULL,			raw_value,	FOR_COMPOSE},
+{NULL,		0,	0,	UNDEFINED_TYPE,	0,			0,		FOR_NOTHING}
+};
+
+char* token_rules[] = {
+   FROM_TOKEN,
+   NICK_TOKEN,
+   FCCF_TOKEN,
+   FCCS_TOKEN,
+   OTEXT_TOKEN,
+   OTEXTNQ_TOKEN,
+   ROLE_TOKEN,
+   FOLDER_TOKEN,
+   SUBJ_TOKEN,
+   PROCID_TOKEN,
+   THDDSPSTY_TOKEN,
+   THDNDXSTY_TOKEN,
+   FLAG_TOKEN,
+   COLLECT_TOKEN,
+   THDDSPSTY_TOKEN,
+   ADDR_TOKEN,
+   TO_TOKEN,
+   ADDTO_TOKEN,
+   ADDCC_TOKEN,
+   ADDRECIP_TOKEN,
+   SCREEN_TOKEN,
+   KEY_TOKEN,
+   SEND_TOKEN,
+   CC_TOKEN,
+   LCC_TOKEN,
+   BCC_TOKEN,
+   FFROM_TOKEN,
+   FADDRESS_TOKEN,
+   NULL
+};
+
+#define NTOKENS  (sizeof(token_rules)/sizeof(token_rules[0]) - 1)
+#define NFCN    (sizeof(rule_fcns)/sizeof(rule_fcns[0]) - 1)
+
+char *subj_fcn[]    = {SUBJ_TOKEN,    REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+char *from_fcn[]    = {FROM_TOKEN,    REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+char *otext_fcn[]   = {OTEXT_TOKEN,   REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+char *otextnq_fcn[] = {OTEXTNQ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+
+char *adto_fcn[] = {ADDTO_TOKEN, EXEC_FCN, NULL, NULL, NULL};
+
+char **fcns_for_index[] = {subj_fcn, from_fcn, otext_fcn, otextnq_fcn};
+
+#define NFCNFI    (sizeof(fcns_for_index)/sizeof(fcns_for_index[0])) /*for idx*/
+#define NFPT      (sizeof(fcns_for_index[0])) /* functions pert token */
+
+SPAREP_S *
+get_sparep_for_rule(char *value, int flag)
+{
+  SPAREP_S *rv;
+  rv = (SPAREP_S *) rule_alloc_mem(sizeof(SPAREP_S));
+  rv->flag = flag;
+  rv->value = value ? cpystr(value) : NULL;
+  return rv;
+}
+
+void
+free_sparep_for_rule(void **sparep)
+{
+  SPAREP_S *spare = (SPAREP_S *) *sparep;
+  if(!spare) return;
+  if(spare->value)
+     fs_give((void **)&spare->value);
+  fs_give((void **)sparep);
+}
+
+int
+context_for_function(char *name)
+{
+  int i;
+  for (i = 0; i < NFCN && strcmp(rule_fcns[i].name, name); i++);
+  return i == NFCN ? 0 : rule_fcns[i].what_for;
+
+}
+
+char **
+functions_for_token(char *name)
+{
+  int i;
+  for (i = 0; i < NFCNFI && strcmp(fcns_for_index[i][0], name); i++);
+  return i == NFCNFI ? NULL : fcns_for_index[i];
+}
+
+void
+free_rexsub (REXSUB_S **rexsub)
+{
+   if(rexsub == NULL || *rexsub == NULL)
+      return;
+
+   if((*rexsub)->pattern) fs_give((void **) &(*rexsub)->pattern);
+   if((*rexsub)->text) fs_give((void **) &(*rexsub)->text);
+   fs_give((void **) rexsub);
+}
+
+void 
+free_void_token_value(void **voidptr, int voidtype)
+{
+  switch(voidtype){
+      case CHAR_TYPE:	fs_give(voidptr);
+			break;
+      case REXSUB_TYPE: free_rexsub ((REXSUB_S **) voidptr);
+			break;
+      default: break;	/* do nothing */
+  }
+}
+
+void 
+free_token_value(TOKEN_VALUE **token)
+{
+   if(token && *token){
+     if ((*token)->testxt)
+	fs_give((void **)&(*token)->testxt);
+     if ((*token)->voidtxt)
+	free_void_token_value((void **) &(*token)->voidtxt, (*token)->voidtype);
+     if((*token)->next)
+	free_token_value(&(*token)->next);
+     fs_give((void **)token);
+   }
+}
+
+void
+free_condition_value(CONDVALUE_S **cvalue)
+{
+  if(cvalue && *cvalue){
+    if ((*cvalue)->tname)
+	fs_give((void **)&(*cvalue)->tname);
+    if ((*cvalue)->value)
+	free_token_value(&(*cvalue)->value);
+    fs_give((void **)cvalue);
+  }
+}
+
+void 
+free_condition(CONDITION_S **condition)
+{
+   if(condition && *condition){
+     if((*condition)->cndtype ==  Condition)
+	free_condition_value((CONDVALUE_S **)&(*condition)->cndrule);
+     else if((*condition)->cndtype ==  ParOpen || (*condition)->cndtype ==  ParClose)
+	fs_give(&(*condition)->cndrule);
+     if((*condition)->next)
+	free_condition(&(*condition)->next);
+     fs_give((void **)condition);
+   }
+}
+
+void
+free_ruleaction(RULEACTION_S **raction)
+{
+   if(raction && *raction){
+     if ((*raction)->token)
+	fs_give((void **)&(*raction)->token);
+     if ((*raction)->function)
+	fs_give((void **)&(*raction)->function);
+     if ((*raction)->value)
+	free_token_value(&(*raction)->value);
+     fs_give((void **)raction);
+   }
+}
+
+void
+free_rule(RULE_S **rule)
+{
+   if(rule && *rule){
+     free_condition(&(*rule)->condition);
+     free_ruleaction(&(*rule)->action);
+     fs_give((void **)rule);
+   }
+}
+
+void
+free_rule_list(RULELIST **rule)
+{
+  if(!*rule)
+    return;
+
+  if((*rule)->next)
+    free_rule_list(&(*rule)->next);
+
+  if((*rule)->prule)
+    free_rule(&(*rule)->prule);
+
+  fs_give((void **)rule);
+}
+
+void
+free_parsed_rule_list(PRULELIST_S **rule)
+{
+  if(!*rule)
+    return;
+
+  if((*rule)->next)
+    free_parsed_rule_list(&(*rule)->next);
+
+  if((*rule)->rlist)
+    free_rule_list(&(*rule)->rlist);
+
+  fs_give((void **)rule);
+}
+
+void *
+rule_alloc_mem (size_t amount)
+{
+   void *genmem;
+   memset(genmem = fs_get(amount), 0, amount);
+   return genmem;
+}
+
+int
+isolate_condition (char *data, char **cvalue, int  *len)
+{
+  char *p = data;
+  int done = 0, error = 0, next_condition = 0, l;
+
+  if(*p == '"' && p[strlen(p) - 1] == '"'){
+    p[strlen(p) - 1] = '\0';
+    p++;
+  }
+  *cvalue = NULL;
+  while (*p && !done){
+	switch (*p){
+	   case '_': *cvalue = advance_to_char(p,'}', STRICTLY, NULL);
+		     if(*cvalue){
+			strcat(*cvalue,"}");
+			p += strlen(*cvalue);
+		     }
+		     else
+			error++;
+		     done++;
+	   case ' ': p++;
+		     break;
+	   case '&': 
+	   case '|': if (*(p+1) == *p){	/* looking for && or ||*/
+			p += 2;
+			next_condition++;
+		     }
+		     else{
+			error++;
+			done++;
+		     }
+		     break;
+	   case '=': /* looking for => or -> */
+	   case '-': if (*(p+1) != '>' || next_condition)
+			error++;
+		     done++;
+		     break;
+	   default : done++;
+		     error++;
+		     break;
+	}
+  }
+  *len = p - data;
+  return error ? -1 : (*cvalue ? 1 : 0);
+}
+
+TOKEN_VALUE *
+parse_group_data (char *data, int *error)
+{
+  TOKEN_VALUE *rvalue;
+  char *p, *d;
+  int offset, err = 0, freeme = 0;
+
+  if(error)
+    *error = 0;
+
+  if (!data)
+     return (TOKEN_VALUE *) NULL;
+
+  if(*data == '_'){
+    d = detoken_src(data, FOR_RULE, NULL, NULL, NULL, NULL);
+    freeme++;
+  }
+  else
+    d = data;
+
+  rvalue = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+  if ((p = advance_to_char(d,';', STRICTLY, &offset)) != NULL){
+      rvalue->testxt = p;
+      rvalue->next   = parse_group_data(d + strlen(p) + 1 + offset, error);
+  }
+  else if ((p = advance_to_char(d,'}', STRICTLY, NULL)) != NULL)
+      rvalue->testxt = p;
+  else if (d && *d == '}')
+      rvalue->testxt = cpystr("");
+  else{
+      err++;
+      free_token_value(&rvalue);
+  }
+  if (error)
+    *error += err;
+  if(freeme != 0 && d != NULL)
+    fs_give((void **)&d);
+  return(rvalue);
+}
+
+CONDVALUE_S *
+fill_condition_value(char *data)
+{
+  CONDVALUE_S *condition;
+  int i, done, error = 0;
+  char *group;
+
+  for (i = 0, done = 0; done == 0 && token_rules[i] != NULL; i++)
+      done = strncmp(data,token_rules[i], strlen(token_rules[i])) ? 0 : 1;
+  if (done){
+     condition = rule_alloc_mem(sizeof(CONDVALUE_S));
+     condition->tname = cpystr(token_rules[--i]);
+     data += strlen(token_rules[i]);
+  }
+  else if (*data == '_') {
+      INDEX_PARSE_T *token;
+      char *itokname;
+      for (i = 0, done = 0; 
+	   done == 0 && (token = itoken(i)) != NULL && (itokname = token->name) != NULL; i++)
+	 done = strncmp(data+1, itokname, strlen(itokname))
+			? 0 : data[strlen(itokname) + 1] == '_';
+      if (done){
+	 condition = (CONDVALUE_S *) rule_alloc_mem(sizeof(CONDVALUE_S));
+	 condition->tname = fs_get(strlen(itokname) + 3);
+	 sprintf(condition->tname, "_%s_", itokname);
+	 data += strlen(itokname) + 2;
+      }
+      else 
+	return NULL;
+  } 
+  else
+     return NULL;
+
+  for (; *data && *data == ' '; data++);
+  if (*data){
+     for (i = 0, done = 0; done == 0 && rel_rules_test[i].value != NULL; i++)
+       done = strncmp(data, rel_rules_test[i].value, 2) ? 0 : 1;
+     if (done)
+       condition->ttype = rel_rules_test[--i].ttype;
+     else{
+	 free_condition_value(&condition);
+	 return NULL;
+     }
+  }
+  else{
+    free_condition_value(&condition);
+    return  NULL;
+  }
+
+  data += 2;
+  for (; *data && *data == ' '; data++);
+  if (*data++ != '{'){
+     free_condition_value(&condition);
+     return NULL;
+  }
+  group = advance_to_char(data,'}', STRICTLY, &error); 
+  if (group || (!group &&  error < 0)){
+     condition->value = parse_group_data(data, &error);
+     if(group && error)
+	free_condition_value(&condition);
+     if(group)
+        fs_give((void **) &group);
+  }
+  else
+     free_condition_value(&condition);
+  return condition;
+}
+
+char *
+canonicalize_condition(char *data, int *eoc)
+{
+  char *p = data, *s, *t, c;
+  char *q = fs_get((5*strlen(data)+1)*sizeof(char));
+  char tmp[10];
+  int level, done, error, i;
+
+  if(eoc) *eoc = -1; 	/* assume error */
+  *q = '\0';
+  if(*p == '"'){
+     if(p[strlen(p) - 1] == '"')
+	p[strlen(p) - 1] = '\0';
+     p++;
+  }
+  for(level = done = error = 0; *p && !done && !error; ){
+     switch(*p){
+	case ' ' : p++; break;
+	case '(' : strcat(q, CSEP_S); strcat(q, "(");
+		   sprintf(tmp, "%d ", level++);
+		   strcat(q, tmp);
+		   p++;
+		   break;
+	case ')' : strcat(q, CSEP_S); strcat(q, ")");
+		   sprintf(tmp, "%d ", --level);
+		   strcat(q, tmp);
+		   p++;
+		   if(level < 0) error++;
+		   break;
+	case '_' : for (s = p+1; *s >= 'A' && *s <= 'Z'; s++)
+			;
+		   for (i = 0; token_rules[i] != NULL; i++)
+		      if(!strncmp(token_rules[i], p, s-p))
+			break;
+		   if(token_rules[i] == NULL)
+		     error++;
+		   else if(*s++ == '_'){
+		     for(; *s == ' '; s++);
+		     if(*s && *(s+1)){
+			for(i = 0; rel_rules_test[i].value != NULL; i++)
+			   if(!strncmp(rel_rules_test[i].value, s, 2))
+			      break;
+			if (rel_rules_test[i].value == NULL)
+			   error++;
+			else{
+			   s += 2;
+			   for(; *s == ' '; s++);
+			   if(*s == '{'){
+			     if(*(s+1) != '}')
+			       t = advance_to_char(s+1,'}', STRICTLY, NULL);
+			     else
+			       t = cpystr("");
+			     if(t != NULL){
+			        for(i = 0; t[i] != '\0' && t[i] != CSEP_C; i++);
+				if(t[i] == CSEP_C) error++;
+			        if(error == 0){
+				   strcat(q, CSEP_S); strcat(q, "C[");
+				   s += strlen(t) + 1;	/* get past '{' */
+				   *s = '\0';
+				   strcat(q, p);
+				   strcat(q, "}] ");
+				   *s++ = '}';
+				   p = s;
+			        }
+				fs_give((void **) &t);
+			     }
+			     else error++;
+			   }
+			   else
+			     error++;
+			}
+		     }
+		   }
+		   else error++;
+		   break;
+	case '|':
+	case '&': if((*(p+1) = *p) != '\0'){
+			strcat(q, CSEP_S); strcat(q, *p == '|' ? "OR " : "AND ");
+			p += 2;
+		  } else error++;
+		  break;
+	case '-':
+	case '=': if (*(p+1) == '>'){
+		    if(eoc) *eoc = p - data;
+		    done++;
+		  }
+		  else
+		    error++;
+		  break;
+	default : error++;
+		  break;
+     }
+  }
+  if(error || level > 0)	/*simplistic approach by now */
+    fs_give((void **)&q);
+  else
+    q[strlen(q)-1] = '\0';
+  return q;
+}
+
+/* for a canonical condition, return if it is constructed according
+ * to logical rules such as AND or OR between conditions, etc. We assume
+ * we already canonicalized data, or else this will not work.
+ */
+int
+sanity_check_condition(char *data)
+{
+  int i, error;
+  char *s, *t, *d;
+
+  if(data == NULL || *data == '\0')	/* no data in, no data out */
+    return 0;
+
+  d = fs_get((strlen(data)+1)*sizeof(char));
+  for(s = data,i = 0; (t = strchr(s, CSEP_C))!= NULL && (d[i] = *(t+1)); s = t+1, i++);
+  d[i] = '\0';
+  for(i = 0, error = 0; d[i] != '\0' && error == 0; i++){
+     switch(d[i]){
+	case 'C': if((d[i+1] != '\0' && (d[i+1] == '(' || d[i+1] == 'C'))
+			|| (i == 0 && d[1] != 'A' && d[1] != 'O' && d[1] != '\0'))
+		     error++;
+		  break;
+	case ')': if(i == 0 || (d[i+1] != '\0' && (d[i+1] == 'C' || d[i+1] == '(')))
+		     error++;
+		  break;
+	case '(': if(d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O')
+		     error++;
+		  break;
+	case 'O':
+	case 'A': if(i == 0 || d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O')
+		     error++;
+		  break;
+	default : error++;
+     }
+  }
+  if(d) fs_give((void **)&d);
+  return error ? 0 : 1;
+}
+
+/* given a parsed data that satisfies sanity checks, parse it
+ * into a condition we can check later on.
+ */
+CONDITION_S *
+fill_condition(char *data)
+{
+  char *s, *t, *u;
+  CONDITION_S *rv = NULL;
+  CONDVALUE_S *cvalue;
+  int *i;
+
+  if(data == NULL || *data == '\0' || (s = strchr(data, CSEP_C)) == NULL)
+    return NULL;
+
+  rv = (CONDITION_S *) rule_alloc_mem(sizeof(CONDITION_S));
+  switch(*++s){
+     case ')':
+     case '(':	 i = fs_get(sizeof(int));
+		*i = atoi(s+1);
+		rv->cndrule = (void *) i;
+		rv->cndtype = *s == '(' ? ParOpen : ParClose;
+		break;
+
+     case 'C':	if((u = strchr(s+2, CSEP_C)) != NULL){
+		   *u = '\0';
+		   t = strrchr(s, ']');
+		   t = '\0';
+		   *u = CSEP_C;
+		} else
+		   s[strlen(s) - 1] = '\0';
+		rv->cndrule = (void *) fill_condition_value(s+2);
+		rv->cndtype = Condition;
+		break;
+
+     case 'A':
+     case 'O':  rv->cndtype = *s == 'A' ? And : Or;
+		break;
+
+     default : fs_give((void **)&rv);
+	       break;
+  }
+  rv->next = fill_condition(strchr(s, CSEP_C));
+
+  return rv;
+} 
+
+/* eoc = end of condition, equal to -1 on error */
+CONDITION_S *
+parse_condition (char *data, int *eoc)
+{
+  CONDITION_S *condition = NULL;
+  char *pvalue;
+  
+  if((pvalue = canonicalize_condition(data, eoc)) != NULL
+	&& sanity_check_condition(pvalue) > 0)
+    condition = fill_condition(pvalue);
+
+  if(pvalue)
+    fs_give((void **)&pvalue);
+
+  if (condition == NULL && eoc)
+    *eoc = -1;
+
+  return condition;
+}
+
+RULEACTION_S *
+parse_action (char *data, int context)
+{
+  int i, done, is_save;
+  RULEACTION_S *raction = NULL;
+  char *function, *p = data;
+
+  if (p == NULL || *p == '\0')
+     return NULL;
+
+  is_save = *p == '-';
+  p += 2;
+  for (; *p == ' '; p++);
+
+  if (is_save){	/* got "->", a save-rule separator */
+     raction = (RULEACTION_S *) rule_alloc_mem(sizeof(RULEACTION_S));
+     raction->function = cpystr("_SAVE_");
+     raction->value    = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+     raction->context |= FOR_SAVE;
+     raction->exec     = extended_value;
+     raction->value->testxt = cpystr(p);
+     return raction;
+  }
+  for (i = 0, done = 0; !done && (i < NFCN); i++)
+       done = (strstr(p,rule_fcns[i].name) == p);
+  p += done ? strlen(rule_fcns[--i].name) + 1 : 0;
+  if(!*p || (rule_fcns[i].what_for && !(rule_fcns[i].what_for & context)))
+     return NULL;
+  if (done){
+     raction = rule_alloc_mem(sizeof(RULEACTION_S));
+	/* We assign raction->token to be subject. This is not necessary for
+	   most rules. It is done only for rules that need it and will not
+	   make any difference in rules that do not need it. It will hopefully
+	   reduce complexity in the language
+	 */
+     raction->token    = cpystr(SUBJ_TOKEN);
+     raction->function = cpystr(rule_fcns[i].name);
+     raction->context  = rule_fcns[i].what_for;
+     raction->exec     = rule_fcns[i].execute;
+     raction->value    = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+     raction->value->testxt = advance_to_char(p,'}', STRICTLY, NULL);
+     if(!raction->value->testxt)
+       free_ruleaction(&raction);
+     return raction;
+  }
+
+  raction = (RULEACTION_S *) rule_alloc_mem(sizeof(RULEACTION_S));
+  raction->token = get_name_token(p);
+
+  p += strlen(raction->token) + 1;
+  for (; *p && *p == ' '; p++);
+  if (!strncmp(p, ":=", 2))
+      p += 2;
+  else{
+      free_ruleaction(&raction);
+      return NULL;
+  }
+
+  for (; *p && *p == ' '; p++);
+
+  for(i = 0; i < NFCN && strncmp(p, rule_fcns[i].name, rule_fcns[i].len); i++);
+
+  if (!rule_fcns[i].is_assignment){
+     free_ruleaction(&raction);
+     return NULL;
+  }
+
+  raction->function = cpystr(rule_fcns[i].name);
+  raction->context  = rule_fcns[i].what_for;
+  raction->exec     = rule_fcns[i].execute;
+  p += rule_fcns[i].len;
+
+  if(*p++ != '{'){
+     free_ruleaction(&raction);
+     return NULL;
+  }
+
+  if(rule_fcns[i].parse_action_value){
+    raction->value = (rule_fcns[i].parse_action_value)(p);
+    if(raction->value)
+       raction->value->voidtype = rule_fcns[i].return_type;
+  }
+
+  if(raction->value == NULL
+	|| (raction->value->testxt == NULL && raction->value->voidtxt == NULL))
+      free_ruleaction(&raction);
+
+  return raction;
+}
+
+TOKEN_VALUE *
+parse_action_to_char(char *p)
+{
+  return parse_group_data(p, NULL);
+}
+
+TOKEN_VALUE *
+parse_rexsub_action(char *data)
+{
+  TOKEN_VALUE *rv;
+  REXSUB_S *rsv = NULL;		/* rexsub value */
+  char *d = NULL, *p, *t, *n;	/* data, pattern, text, number */
+  long number;
+  int offset, error;
+
+  if (data == NULL || *data == '\0')
+     return NULL;
+
+  rv  = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+
+  number = 0;
+  p = t = n = NULL;
+
+  p = advance_to_char(data,'}', STRICTLY, NULL);
+
+  error = (p == NULL) ? 1 : 0;
+  if(!error){
+     offset = strlen(p) + 1;
+     d = data + offset;
+     if(*d++ != '{') error++;
+  }
+
+  if(!error){
+     n = advance_to_char(d,'}', STRICTLY, NULL);
+     if(n == NULL && *d == '}')
+	n = cpystr("");
+     if(n == NULL) error++;
+  }
+
+  if(!error){
+     t = detoken_src(n, FOR_RULE, NULL, NULL, NULL, NULL);
+     offset += strlen(n) + 2;
+     d = data + offset;
+     if(*d == '\0') number = 1;
+     else if(*d++ != '{') error++;
+  }
+
+  if(!error && number == 0){
+     fs_give((void **) &n);
+     n = advance_to_char(d,'}', STRICTLY, NULL);
+     if(n == NULL){
+	if(*d == '\0')
+	   number = 1;
+	else
+	   error++;
+     }
+     else if(strcmp(n, "g") == 0)
+        number = -1;
+     else{
+        char *eon;
+	number = strtol(n, &eon, 10);
+	if((eon != NULL && *eon != '\0') || number <= 0) error++;
+     }
+  }
+
+  if(error){
+     if (p) fs_give((void **) &p);
+     if (t) fs_give((void **) &t);
+  }
+  if (n) fs_give((void **) &n);
+
+  if(!error){
+     rsv = fs_get(sizeof(REXSUB_S));
+     rsv->pattern = p;
+     rsv->text    = t;
+     rsv->times   = number;
+  }
+
+  rv->voidtxt = (void *) rsv;
+  return rv;
+}
+
+RULE_S *
+parse_rule (char *data, int context)
+{
+  RULE_S *prule;	/*parsed rule */
+  int len = 0;
+  
+  if (!(prule = (RULE_S *) rule_alloc_mem(sizeof(RULE_S))) ||
+	!(prule->condition = parse_condition(data, &len)) ||
+	!(prule->action = parse_action(data+len, context)))
+      free_rule(&prule);
+
+  return prule;
+}
+
+RULELIST *
+get_rule_list(char **list, int context, int i)
+{
+  RULE_S *rule;
+  RULELIST *trulelist = NULL;
+
+  if (list[i] && *list[i]){
+     if((rule = parse_rule(list[i], context)) != NULL){
+	trulelist  = (RULELIST *) rule_alloc_mem(sizeof(RULELIST));
+	trulelist->prule = rule;
+	trulelist->next = get_rule_list(list, context, i+1);
+     }
+     else
+	trulelist = get_rule_list(list, context, i+1);
+  }
+  return trulelist;
+}
+
+/* add parsed rule */
+PRULELIST_S *
+add_prule(PRULELIST_S *rule_list, PRULELIST_S *rule)
+{
+   PRULELIST_S *rlist;
+
+   for(rlist = rule_list; rlist && rlist->next; rlist = rlist->next);
+
+   if(rlist)
+      rlist->next = rule;
+   else
+      rule_list = rule;
+
+   return rule_list;
+}  
+
+void
+add_rule(int code, int context)
+{
+  char **list = ps_global->vars[code].current_val.l;
+  PRULELIST_S *prulelist, *trulelist, *orulelist;
+
+  if (list && *list && **list){
+     trulelist = (PRULELIST_S *) rule_alloc_mem(sizeof(PRULELIST_S));
+     trulelist->varnum = code;
+     if ((trulelist->rlist = get_rule_list(list, context, 0)) != NULL)
+        ps_global->rule_list = add_prule(ps_global->rule_list, trulelist);
+     else
+	free_parsed_rule_list(&trulelist);
+  }
+}
+
+/* see create_rule_list below */
+void
+set_rule_list(struct variable *vars)
+{
+    set_current_val(&vars[V_THREAD_DISP_STYLE_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_THREAD_INDEX_STYLE_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_COMPOSE_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_FORWARD_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_INDEX_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_KEY_RULES], FALSE, TRUE);
+    set_current_val(&vars[V_REPLACE_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_REPLY_INDENT_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_REPLY_LEADIN_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_RESUB_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_SAVE_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_SMTP_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_SORT_RULES], TRUE, TRUE);
+    set_current_val(&vars[V_STARTUP_RULES], TRUE, TRUE);
+}
+
+/* see set_rule_list above */
+void
+create_rule_list(struct variable *vars)
+{
+  set_rule_list(vars);
+  add_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD);
+  add_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD);
+  add_rule(V_COMPOSE_RULES, FOR_COMPOSE);
+  add_rule(V_FORWARD_RULES, FOR_COMPOSE);
+  add_rule(V_INDEX_RULES, FOR_INDEX);
+  add_rule(V_KEY_RULES, FOR_KEY);
+  add_rule(V_REPLACE_RULES, FOR_REPLACE);
+  add_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE);
+  add_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO);
+  add_rule(V_RESUB_RULES, FOR_RESUB|FOR_TRIM);
+  add_rule(V_SAVE_RULES, FOR_SAVE);
+  add_rule(V_SMTP_RULES, FOR_COMPOSE);
+  add_rule(V_SORT_RULES, FOR_SORT);
+  add_rule(V_STARTUP_RULES, FOR_STARTUP);
+}
+
+int
+condition_contains_token(CONDITION_S *condition, char *token)
+{
+  while(condition && condition->cndtype != Condition)
+      condition =  condition->next;
+
+  return condition 
+	  ? (!strcmp(COND(condition)->tname, token) 
+		? 1
+		: condition_contains_token(condition->next, token)) 
+	  : 0;
+}
+
+RULELIST *
+get_rulelist_from_code(int code, PRULELIST_S *list)
+{
+  return list ? (list->varnum == code ? list->rlist 
+			      : get_rulelist_from_code(code, list->next))
+	      : (RULELIST *) NULL;
+}   
+
+char *
+test_rule(RULELIST *rlist, int ctxt, ENVELOPE *env, int *n)
+{
+  char *result;
+
+  if(!rlist)
+     return NULL;
+
+  if ((result = process_rule(rlist->prule, ctxt, env)) != NULL)
+      return result;
+  else{
+       (*n)++;
+       return test_rule(rlist->next, ctxt, env, n);
+  } 
+}
+
+RULE_S *
+get_rule (RULELIST *rule, int n)
+{
+  return rule ? (n ? get_rule(rule->next, n-1) : rule->prule) 
+	      : NULL;
+}
+
+/* get_result_rule:
+ * Parameters: list: the list of rules to be passed to the function to check
+ *             rule_context: context of the rule
+ *             env : envelope used to check the rule, if needed.
+ *
+ * Returns: The value of the first rule that is satisfied in the list, or
+ *          NULL if not. This function should be called in the following 
+ *          way (notice that memory is freed by caller).
+ *
+ * You should use this function to obtain the result of a rule. You can
+ * also call directly "process_rule", but I advice to use this function if
+ * there's no difference on which function to call.
+
+   RULE_RESULT *rule;
+
+   rule = (RULE_RESULT *) 
+           get_result_rule(V_SOME_RULE, context, envelope);
+
+   if (rule){ 
+       assign the value of rule->result;
+       if (rule->result)
+          fs_give((void **)&rule->result);
+       fs_give((void **)&rule);
+   }
+ */
+
+RULE_RESULT *
+get_result_rule(int code, int rule_context, ENVELOPE *env)
+{
+    char  *rule_result;
+    RULE_RESULT *rule = NULL;
+    RULELIST *rlist;
+    int n = 0;
+
+    if(!(rule_context & FOR_RULE))
+      rule_context |= FOR_RULE;
+    rlist = get_rulelist_from_code(code, ps_global->rule_list);
+    if (rlist){
+       rule_result = test_rule(rlist, rule_context, env, &n);
+       if (rule_result && *rule_result){
+          rule = (RULE_RESULT *) fs_get (sizeof(RULE_RESULT));
+          rule->result = rule_result;
+          rule->number = n;
+       }
+    }
+    return rule;
+}
+
+char *
+get_rule_result(int rule_context, char *newfolder, int code)
+{   
+    char        *rule_result = NULL;
+    ENVELOPE    *news_envelope;
+    RULE_RESULT *rule;
+
+    if (IS_NEWS(ps_global->mail_stream)){
+       news_envelope = mail_newenvelope();
+       news_envelope->newsgroups = cpystr(newfolder);
+    }
+    else
+       news_envelope = NULL;
+
+    rule = get_result_rule(code, rule_context, news_envelope);
+
+    if (news_envelope)
+        mail_free_envelope (&news_envelope);
+
+    if (rule){
+        rule_result = cpystr(rule->result);
+        if (rule->result)
+          fs_give((void **)&rule->result);
+        fs_give((void **)&rule);
+    }
+    return rule_result;
+}
+
+/* process_rule:
+   Parameters:  prule, a processed rule, ready to be tested
+		rule_context: context of the rule, and
+		env: An envelope if needed.
+
+   Returns   :  The value of the processed rule_data if the processing was 
+		successful and matches context and possibly the envelope, or
+		NULL if there's no match
+ */
+
+char *
+process_rule (RULE_S *prule, int rule_context, ENVELOPE *env)
+{
+   if(!prule)
+     return NULL;
+
+   if(!(rule_context & FOR_RULE))
+      rule_context |= FOR_RULE;
+
+   return test_condition(prule->condition, rule_context, env)
+	    ? (prule->action->exec)(prule->action, rule_context, env)
+	    : NULL;
+}
+
+TOKEN_VALUE *
+copy_parsed_value(TOKEN_VALUE *value, int ctxt, ENVELOPE *env)
+{
+   TOKEN_VALUE *tval = NULL;
+
+   if(!value)
+      return NULL;
+
+   if(value->testxt){
+     tval = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+     tval->testxt = detoken_src(value->testxt, ctxt, env, NULL, NULL, NULL);
+     tval->voidtxt = value->voidtxt;
+     tval->codefcn = value->codefcn;     
+   }
+   if(value->next)
+     tval->next = copy_parsed_value(value->next, ctxt, env);
+
+   return tval;
+}
+
+void
+free_parsed_value(TOKEN_VALUE **value)
+{
+   TOKEN_VALUE *tval = NULL;
+
+   if(!*value)
+      return;
+
+   if((*value)->testxt)
+     fs_give((void **)&(*value)->testxt);
+
+   if((*value)->next)
+     free_parsed_value(&(*value)->next);
+
+    fs_give((void **)value);
+}
+
+int
+test_condition_work(CONDITION_S *bc, CONDITION_S *ec, int rcntxt, ENVELOPE *env)
+{
+   int rv,level;
+   TOKEN_VALUE *group;
+   CONDITION_S *cend;
+
+   switch(bc->cndtype){
+	case Condition:	group = copy_parsed_value(COND(bc)->value, rcntxt, env);
+			rv = (*rel_rules_test[COND(bc)->ttype].execute)(bc, group, env, rcntxt);
+			free_parsed_value(&group);
+			if(bc == ec)
+			  return rv;
+			if(bc->next == NULL)
+			  return rv;
+			else
+			  switch(bc->next->cndtype){
+			    case And: return rv ? test_condition_work(bc->next->next, ec, rcntxt, env) : 0;
+				  break;
+			    case Or : return rv ? 1 : test_condition_work(bc->next->next, ec, rcntxt, env);
+				  break;
+			    case ParClose: return rv;
+			    default : rv = 0; break;  /* fail, we should not be here */
+			  }
+			break;
+
+	case ParOpen:	level = ((int *)bc->cndrule)[0];
+			for(cend = bc; cend->next && (cend->next->cndtype != ParClose
+				  	|| ((int *)cend->next->cndrule)[0] != level); 
+					cend = cend->next);
+			rv = test_condition_work(bc->next, cend, rcntxt, env);
+			cend = cend->next;	/* here we are at ')' */
+			if(cend->next == NULL)
+			  return rv;
+			else{
+			  switch(cend->next->cndtype){
+			    case And: return rv ? test_condition_work(cend->next->next, ec, rcntxt, env) : 0;
+				  break;
+			    case Or : return rv ? 1 : test_condition_work(cend->next->next, ec, rcntxt, env);
+				  break;
+			    default : rv = 0; break;	/* fail, we should not be here */
+			  }
+			}
+			break;
+	     default:	rv = 0; break; 	/* fail, we should not be here */
+   }
+   return rv;  /* we never ever get here */
+}
+
+
+int
+test_condition(CONDITION_S *condition, int rcntxt, ENVELOPE *env)
+{
+   return test_condition_work(condition, NULL, rcntxt, env);
+}
+
+/* returns the name of the token it found or NULL if there is no token, the
+ * real value of the token is obtained by calling the detoken_src function.
+ */ 
+char *
+get_name_token (char *condition)
+{
+  char *p, *q, c, *rv;
+
+  if (condition == NULL)
+     return NULL;
+
+  for(p = condition; *p != '\0' && (*p == ' '|| *p == '\t'); p++);
+
+  if (*p != '_') return NULL;
+
+  for (q = p+1; *q != '\0' && *q != '_'; q++);
+
+  if (*q != '_') return NULL;
+
+  c = *(q+1);
+  *(q+1) = '\0';
+  rv = cpystr(p);
+  *(q+1) = c;
+  return rv;
+}
+
+/* This function tests if a string contained in the variable "group" is
+ * in the "condition"
+ */
+int
+test_in (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, 
+		int context)
+{
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+	? cpystr(((SPAREP_S *)env->sparep)->value)
+	: detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+    while (rv == 0 && test_group){
+       if(!*test || strstr(test_group->testxt, test))
+	  rv++;
+       else
+	  test_group = test_group->next;
+    }
+    fs_give((void **)&test);
+ }
+ return rv;
+}
+
+int
+test_ni (CONDITION_S *condition, TOKEN_VALUE *group, 
+		ENVELOPE *env, int context)
+{
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+	? cpystr(((SPAREP_S *)env->sparep)->value)
+	: detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+    if(!test_group)
+      rv++;
+    while (rv == 0 && test_group){
+       if(!*test_group->testxt || strstr(test, test_group->testxt))
+	  rv++;
+       else
+	  test_group = test_group->next;
+    }
+    fs_give((void **)&test);
+ }
+ return rv;
+}
+
+int
+test_not_in (CONDITION_S *condition, TOKEN_VALUE *group, 
+		ENVELOPE *env, int context)
+{
+ return !test_in(condition, group, env, context);
+}
+
+int
+test_not_ni (CONDITION_S *condition, TOKEN_VALUE *group, 
+		ENVELOPE *env, int context)
+{
+ return !test_ni(condition, group, env, context);
+}
+
+int
+test_eq (CONDITION_S *condition, TOKEN_VALUE *group, 
+		ENVELOPE *env, int context)
+{
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+	? cpystr(((SPAREP_S *)env->sparep)->value)
+	: detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+    while (rv == 0 && test_group){
+       if((!*test && !*test_group->testxt) || !strcmp(test_group->testxt, test))
+	  rv++;
+       else
+	  test_group = test_group->next;
+    }
+    fs_give((void **)&test);
+ }
+ return rv;
+}
+
+int
+test_not_eq (CONDITION_S *condition, TOKEN_VALUE *group, 
+		ENVELOPE *env, int context)
+{
+ return !test_eq(condition, group, env, context);
+}
+
+char *
+do_trim (char *test, TOKEN_VALUE *tval)
+{
+   char *begin_text;
+   int offset = 0;
+
+   if (!tval)
+      return test;
+
+   while((begin_text = strstr(test+offset,tval->testxt)) != NULL){
+      memmove(begin_text, begin_text+strlen(tval->testxt), strlen(begin_text) - strlen(tval->testxt) + 1);
+      offset = begin_text - test;
+   }
+
+   return do_trim(test, tval->next);
+}
+
+char *
+trim (RULEACTION_S *action, int context, ENVELOPE *env)
+{
+ char *begin_text, *test;
+ RULEACTION_S *taction = action;
+ int offset;
+
+ if (taction->context & context){
+    if ((test = detoken_src(taction->token, context, env, NULL, NULL, NULL)) != NULL)
+       test = do_trim(test, taction->value);
+    return test;
+ }
+ return NULL;
+}
+
+char *
+do_rextrim (char *test, TOKEN_VALUE *tval)
+{
+   char *begin_text, *trim_text;
+   int i, offset = 0;
+   size_t len;
+
+   if (!tval)
+      return test;
+
+   if((trim_text = expand(test, tval->testxt)) != NULL){
+      while((begin_text = strstr(test+offset, trim_text)) != NULL){
+          memmove(begin_text, begin_text+strlen(trim_text), strlen(begin_text) - strlen(trim_text) + 1);
+          offset = begin_text - test;
+      }
+   }
+
+   return do_rextrim(test, tval->next);
+}
+
+char *
+rextrim (RULEACTION_S *action, int context, ENVELOPE *env)
+{
+ char *test = NULL;
+ RULEACTION_S *taction = action;
+
+ if ((taction->context & context) &&
+    (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)))
+	test = do_rextrim(test, taction->value);
+ return test;
+}
+
+char *
+do_rexsub (char *test, TOKEN_VALUE *tval)
+{
+   REXSUB_S *rsv;
+   regmatch_t pmatch;
+   regex_t preg;
+   char *ret_string = cpystr(test ? test : "");
+
+   if (!tval)
+      return ret_string;
+
+   rsv = (REXSUB_S *) tval->voidtxt;
+
+   if (rsv == NULL 		/* this should not happen */
+	|| rsv->pattern == NULL /* neither should this */
+	|| rsv->text == NULL    /* neither should this */
+	|| regcomp(&preg, rsv->pattern, REG_EXTENDED) != 0) /* but this could */
+      return ret_string;
+
+   /* can't use expand() to do the job here. This is because there
+    * is a variable number of hits in rsv->times. */
+
+   if(regexec((regex_t *) &preg, test, 1, &pmatch, 0) == 0){
+     int offset = 0, n;
+
+     n = rsv->times > 0 ? rsv->times : strlen(test);
+     fs_resize((void **) &ret_string, strlen(test) + n*strlen(rsv->text) + 1);
+     *ret_string = '\0';
+
+     do {
+	strncat(ret_string, test + offset, pmatch.rm_so);
+	strcat(ret_string, rsv->text);
+	offset += pmatch.rm_eo;
+     } while (--n && regexec(&preg, test + offset, 1, &pmatch, REG_NOTBOL) == 0);
+     strcat(ret_string, test + offset);
+   }
+   fs_resize((void **) &ret_string, strlen(ret_string) + 1);
+   regfree(&preg);
+
+   return ret_string;
+}
+
+char *
+rexsub (RULEACTION_S *action, int context, ENVELOPE *env)
+{
+ char *test = NULL, *sub = NULL;
+ RULEACTION_S *taction = action;
+
+ if ((taction->context & context) &&
+    (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)))
+	sub = do_rexsub(test, taction->value);
+ if (test) fs_give((void **) &test);
+ return sub;
+}
+
+char *
+raw_value (RULEACTION_S *action, int context, ENVELOPE *env)
+{
+return (action->context & context) ? cpystr(action->value->testxt) : NULL;
+}
+
+char *
+extended_value (RULEACTION_S *action, int ctxt, ENVELOPE *env)
+{
+return (action->context & ctxt) 
+	? detoken_src(action->value->testxt, ctxt, env, NULL, NULL, NULL)
+	: NULL;
+}
+
+/* advances given_string until it finds given_char, memory freed by caller  */
+char *
+advance_to_char(char *given_string, char given_char, int flag, int *error) 
+{
+   char *b, *s, c;
+   int i, err  = 0, quoted ;
+
+   if (error)
+      *error = 0;
+
+   if (!given_string || !*given_string)
+       return NULL;
+
+   b = s = cpystr(given_string);
+   for(i = 0, quoted = 0, c = *s; c ; c = *++s){
+      if(c == '\\'){
+	 quoted++;
+	 continue;
+      }
+      if(quoted){
+	quoted = 0;
+	if (c == given_char){
+	  err += (flag & STRICTLY) ? 0 : 1;
+	  err++;
+	  break;
+	}
+	b[i++] = '\\';
+      }
+      if(c == given_char){
+	 err += (flag & STRICTLY) ? 0 : 1;
+	 break;
+      }
+      b[i++] = c;
+   }
+   b[i] = '\0';
+   if (b && (strlen(b) == strlen(given_string)) && (flag & STRICTLY)){
+      fs_give((void **)&b);
+      return NULL;   /* character not found */
+   }
+
+   if(b && !*b){
+     fs_give((void **)&b);
+     err = -1;
+   }
+
+   if (error)
+      *error = err;
+
+   return b;
+}
+
+/* Regular Expressions Support */
+char *
+expand (char *string, char *pattern)
+{
+  char c, *ret_string = NULL;
+  regmatch_t pmatch;
+  regex_t preg;
+
+  if(pattern == NULL || regcomp(&preg, pattern, REG_EXTENDED) != 0)
+     return NULL;
+
+  if(regexec((regex_t *) &preg, string , 1, &pmatch, 0) == 0 
+	&& pmatch.rm_so < pmatch.rm_eo){
+      c = string[pmatch.rm_eo];
+      string[pmatch.rm_eo] = '\0';
+      ret_string = cpystr(string+pmatch.rm_so);
+      string[pmatch.rm_eo] = c;
+  }
+  regfree(&preg);
+
+  return ret_string;
+}
+
+char *
+exec_fcn (RULEACTION_S *action, int ctxt, ENVELOPE *env)
+{
+  STORE_S *output_so;
+  gf_io_t	gc, pc;
+  char *status, *rv, *cmd, *test;
+
+  if(!(action->context & ctxt))
+    return NULL;
+
+  if((test = detoken_src(action->token, ctxt, env, NULL, NULL, NULL)) != NULL)
+    gf_set_readc(&gc, test, (unsigned long)strlen(test), CharStar, 0);
+
+  if((output_so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
+     gf_set_so_writec(&pc, output_so);
+
+  cmd = (char *)fs_get((strlen(action->value->testxt) + strlen("_TMPFILE_") + 2)*sizeof(char));
+  sprintf(cmd,"%s _TMPFILE_", action->value->testxt);
+  status = (*ps_global->tools.exec_rule)(cmd, gc, pc);
+
+  so_seek(output_so, 0L, 0);
+  rv = cpystr(output_so->dp);
+  gf_clear_so_writec(output_so);
+  so_give(&output_so);
+  if(test)
+    fs_give((void **)&test);
+
+  return status ? NULL : rv;
+}
+
+ENVELOPE *
+rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear)
+{
+  ENVELOPE *env;
+
+  if (idata->no_fetch){
+    if (we_clear) *we_clear = 1;
+    env = mail_newenvelope();
+    env->from     = copyaddrlist(idata->from);
+    env->to       = copyaddrlist(idata->to);
+    env->cc       = copyaddrlist(idata->cc);
+    env->sender   = copyaddrlist(idata->sender);
+    env->subject  = cpystr(idata->subject);
+    env->date     = cpystr((unsigned char *) idata->date);
+    env->newsgroups = cpystr(idata->newsgroups);
+    return env;
+  }
+  if (we_clear) *we_clear = 0;
+  env = pine_mail_fetchenvelope(idata->stream, idata->rawno);
+  return env;
+}
Index: alpine-2.26/pith/rules.h
===================================================================
--- /dev/null
+++ alpine-2.26/pith/rules.h
@@ -0,0 +1,154 @@
+/* Included file rules.h */
+
+#ifndef PITH_RULES_INCLUDED
+#define PITH_RULES_INCLUDED
+
+#include "../pith/conftype.h"
+#include "../pith/detoken.h"
+#include "../pith/indxtype.h"
+#include "../pith/rulestype.h"
+
+/* Exported prototypes */
+
+void  create_rule_list (struct variable *);
+SPAREP_S *get_sparep_for_rule(char *, int);
+void  free_sparep_for_rule(void **);
+void  free_parsed_rule_list (PRULELIST_S **);
+RULE_RESULT *get_result_rule (int, int, ENVELOPE *);
+char  *get_rule_result (int , char *, int);
+char  *process_rule   (RULE_S *, int, ENVELOPE *);
+char  **functions_for_token (char *);
+RULELIST *get_rulelist_from_code (int, PRULELIST_S *);
+RULE_S   *get_rule  (RULELIST *, int);
+int   condition_contains_token (CONDITION_S *, char *);
+int   context_for_function (char *);
+ENVELOPE *rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear);
+
+/* Separators:
+ *
+ * A separator is a string that separates the rule condition with the rule
+ * action. Below is the list of separators
+ *
+ */
+
+#define  SAVE_TO_SEP  "->"
+#define  APPLY_SEP    "=>"
+
+/*------- Definitions of tokens -------*/
+/*------ Keep the list alphabetically sorted, thanks -------*/
+
+#define ADDR_TOKEN	"_ADDRESS_"
+#define ADDCC_TOKEN	"_ADDRESSCC_"
+#define ADDRECIP_TOKEN	"_ADDRESSRECIPS_"
+#define ADDTO_TOKEN	"_ADDRESSTO_"
+#define BCC_TOKEN	"_BCC_"
+#define CC_TOKEN	"_CC_"
+#define COLLECT_TOKEN	"_COLLECTION_"
+#define FCCF_TOKEN	"_FCCFROM_"
+#define FCCS_TOKEN	"_FCCSENDER_"
+#define FLAG_TOKEN	"_FLAG_"
+#define FOLDER_TOKEN	"_FOLDER_"
+#define FADDRESS_TOKEN	"_FORWARDADDRESS_"
+#define FFROM_TOKEN	"_FORWARDFROM_"
+#define FROM_TOKEN	"_FROM_"
+#define KEY_TOKEN	"_PKEY_"
+#define LCC_TOKEN	"_LCC_"
+#define NICK_TOKEN	"_NICK_"
+#define OTEXT_TOKEN	"_OPENINGTEXT_"
+#define OTEXTNQ_TOKEN	"_OPENINGTEXTNQ_"
+#define PROCID_TOKEN	"_PROCID_"
+#define ROLE_TOKEN	"_ROLE_"
+#define SCREEN_TOKEN	"_SCREEN_"
+#define SEND_TOKEN	"_SENDER_"
+#define SUBJ_TOKEN	"_SUBJECT_"
+#define THDDSPSTY_TOKEN	"_THREADSTYLE_"
+#define THDNDXSTY_TOKEN	"_THREADINDEX_"
+#define TO_TOKEN	"_TO_"
+
+/*------ Definitions of relational operands -------------*/
+
+typedef struct {
+        char       *value;
+	TestType    ttype;
+        int        (*execute)();
+} REL_TOKEN;
+
+/* Relational Operands */
+#define AND_REL     "&&"        /* For putting more than one condition  */
+#define IN_REL      "<<"        /* For belonging relation */
+#define NI_REL      ">>"        /* For contain relation   */
+#define NOT_IN_REL  "!<"        /* Negation of IN_REL   */
+#define NOT_NI_REL  "!>"        /* Negation of NI_REL   */
+#define EQ_REL      "=="        /* Test of equality     */
+#define NOT_EQ_REL  "!="        /* Test of inequality   */
+#define OPEN_SET    "{"         /* Braces to open a set */
+#define CLOSE_SET   "}"         /* Braces to close a set*/
+
+/*--- Context in which these variables can be used ---*/
+
+typedef struct use_context {
+    char        *name;
+    int          what_for;
+} USE_IN_CONTEXT;
+
+static USE_IN_CONTEXT tokens_use[] = {
+    {NICK_TOKEN,	FOR_SAVE},
+    {FROM_TOKEN,	FOR_SAVE},
+    {OTEXT_TOKEN,	FOR_SAVE|FOR_FOLDER},
+    {OTEXTNQ_TOKEN,	FOR_SAVE|FOR_FOLDER},
+    {ROLE_TOKEN,	FOR_COMPOSE},
+    {FOLDER_TOKEN,	FOR_SAVE|FOR_FOLDER|FOR_THREAD|FOR_COMPOSE},
+    {SUBJ_TOKEN,	FOR_SAVE|FOR_FOLDER|FOR_COMPOSE},
+    {FLAG_TOKEN,	FOR_SAVE|FOR_FLAG},
+    {COLLECT_TOKEN,	FOR_SAVE|FOR_COMPOSE|FOR_FOLDER|FOR_THREAD},
+    {THDDSPSTY_TOKEN,	FOR_THREAD},
+    {THDNDXSTY_TOKEN,	FOR_THREAD},
+    {ADDR_TOKEN,	FOR_SAVE|FOR_FOLDER},
+    {TO_TOKEN,		FOR_SAVE},
+    {ADDTO_TOKEN,	FOR_SAVE|FOR_COMPOSE},
+    {ADDCC_TOKEN,	FOR_SAVE|FOR_COMPOSE},
+    {ADDRECIP_TOKEN,	FOR_SAVE|FOR_COMPOSE},
+    {SCREEN_TOKEN,	FOR_KEY},
+    {KEY_TOKEN,		FOR_KEY},
+    {SEND_TOKEN,	FOR_SAVE},
+    {CC_TOKEN,		FOR_SAVE},
+    {BCC_TOKEN,		FOR_COMPOSE},
+    {LCC_TOKEN,		FOR_COMPOSE},
+    {FFROM_TOKEN,	FOR_COMPOSE},
+    {FADDRESS_TOKEN,	FOR_COMPOSE},
+    {NULL,		FOR_NOTHING}
+};
+
+typedef struct {
+  char	 *name;
+  size_t  len;
+  unsigned is_assignment:1; /* has structure _TOKEN_ := _FUNCTION_{} */
+  int return_type; /* the type of data returned by the parser, when it fills voidtxt */
+  TOKEN_VALUE *(*parse_action_value)(char *);
+  char	*(*execute)();
+  int	what_for;
+} RULE_FCN;
+
+#define COMMAND_FCN	"_COMMAND_"
+#define COPY_FCN	"_COPY_"
+#define EXEC_FCN	"_EXEC_"
+#define INDEX_FCN       "_INDEX_"
+#define REPLACE_FCN     "_REPLACE_"
+#define REPLYSTR_FCN    "_RESTR_"
+#define REPLY_FCN       "_REPLY_"
+#define RESUB_FCN       "_RESUB_"
+#define REXSUB_FCN	"_REXSUB_"
+#define REXTRIM_FCN	"_REXTRIM_"
+#define SAVE_FCN        "_SAVE_"
+#define SIGNATURE_FCN   "_SIGNATURE_"
+#define SMTP_FCN        "_SMTP_"
+#define SORT_FCN        "_SORT_"
+#define STARTUP_FCN     "_STARTUP_"
+#define THRDSTYLE_FCN   "_THREADSTYLE_"
+#define THRDINDEX_FCN   "_THREADINDEX_"
+#define TRIM_FCN        "_TRIM_"
+
+#define STRICTLY  0x1
+#define RELAXED 0x2
+
+#endif 	/* PITH_RULES_INCLUDED */
Index: alpine-2.26/pith/rulestype.h
===================================================================
--- /dev/null
+++ alpine-2.26/pith/rulestype.h
@@ -0,0 +1,94 @@
+#ifndef PITH_RULESTYPE_INCLUDED
+#define PITH_RULESTYPE_INCLUDED
+
+typedef struct rule {
+	char *result;	/* The result of the rule */
+	int number;	/* The number of the rule that succeded, -1 if not */
+} RULE_RESULT;
+
+typedef struct {
+        char    *value;
+        int     type;
+} RULE_ACTION;
+
+
+#define TOKEN_VALUE	struct tokenvalue_s
+#define CONDITION_S	struct condition_s
+#define REXSUB_S	struct rexsub_s
+#define RULEACTION_S	struct ruleaction_s
+#define RULE_S		struct rule_s
+#define RULELIST	struct rulelist_s
+#define PRULELIST_S	struct parsedrulelist_s
+
+#define FREEREGEX	1
+
+#define UNDEFINED_TYPE	0
+#define CHAR_TYPE	1
+#define REXSUB_TYPE	2
+
+TOKEN_VALUE {
+	char	*testxt;
+	void	*voidtxt;
+	int	voidtype;
+	int	codefcn;
+	TOKEN_VALUE *next;
+};
+
+typedef enum {Equal, Subset, Includes, NotEqual, NotSubset, NotIncludes, EndTypes} TestType;
+
+typedef enum {And, Or, ParOpen, ParClose, Condition} CondType;
+
+typedef struct condvalue_s {
+    char	*tname;		/* tname ttype {value} */
+    TestType	ttype;		/* type of rule */
+    TOKEN_VALUE	*value;		/* value to check against */
+} CONDVALUE_S;
+
+CONDITION_S {
+    void     *cndrule;		/* text in condition */
+    CondType  cndtype;		/* type of object    */
+    CONDITION_S	*next;		/* next condition to test */
+};
+
+#define COND(C)  ((CONDVALUE_S *)((C)->cndrule))
+
+RULEACTION_S {
+   char *token;		/* token := function{value} or token = null  */
+   char *function;	/* token := function{value} or simply function{value}*/
+   TOKEN_VALUE  *value; /* token := function{value} or simply function{value}*/
+   int   context;	/* context in which this rule can be used */
+   char* (*exec)();
+};
+
+REXSUB_S {
+   char *pattern;	/* pattern to use in the replacement	     */
+   char *text;		/* text to replace			     */
+   int   times;		/* how many times to repeat the substitution */
+};
+
+RULE_S {
+  CONDITION_S  *condition;
+  RULEACTION_S *action;
+};
+
+RULELIST {
+   RULE_S *prule;
+   RULELIST *next;
+};
+
+PRULELIST_S {
+   int varnum;		/* number associated to the variable */
+   RULELIST *rlist;
+   PRULELIST_S *next;
+};
+
+#define USE_RAW_SP      0x001
+#define PROCESS_SP      0x010
+
+typedef struct sparep {
+   int flag;
+   char *value;
+} SPAREP_S;
+
+
+#endif	/* PITH_RULESTYPE_INCLUDED */
Index: alpine-2.26/pith/save.c
===================================================================
--- alpine-2.26.orig/pith/save.c
+++ alpine-2.26/pith/save.c
@@ -951,7 +951,7 @@ save(struct pine *state, MAILSTREAM *str
 	      *date = '\0';
 
 	    rv = save_fetch_append(stream, mn_m2raw(msgmap, i),
-				   NULL, save_stream, save_folder, context,
+				   NULL, save_stream, folder, context,
 				   mc ? mc->rfc822_size : 0L, flags, date, so);
 
 	    if(flags)
Index: alpine-2.26/pith/send.c
===================================================================
--- alpine-2.26.orig/pith/send.c
+++ alpine-2.26/pith/send.c
@@ -40,6 +40,7 @@
 #include "../pith/ablookup.h"
 #include "../pith/sort.h"
 #include "../pith/smime.h"
+#include "../pith/rules.h"
 
 #include "../c-client/smtp.h"
 #include "../c-client/nntp.h"
@@ -1691,9 +1692,9 @@ call_mailer(METAENV *header, struct mail
     char         error_buf[200], *error_mess = NULL, *postcmd;
     ADDRESS     *a;
     ENVELOPE	*fake_env = NULL;
-    int          addr_error_count, we_cancel = 0;
+    int          addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1;
     long	 smtp_opts = 0L;
-    char	*verbose_file = NULL;
+    char	*verbose_file = NULL, **smtp_list;
     BODY	*bp = NULL;
     PINEFIELD	*pf;
     BODY	*origBody = body;
@@ -1848,20 +1849,49 @@ call_mailer(METAENV *header, struct mail
      * OK, who posts what?  We tried an mta_handoff above, but there
      * was either none specified or we decided not to use it.  So,
      * if there's an smtp-server defined anywhere, 
+     * First we check for rules and make a list using the rules.
      */
-    if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){
-	/*---------- SMTP ----------*/
-	dprint((4, "call_mailer: via TCP (%s)\n",
-		alt_smtp_servers[0]));
-	TIME_STAMP("smtp-open start (tcp)", 1);
-	sending_stream = smtp_open(alt_smtp_servers, smtp_opts);
+    if(ps_global->VAR_SMTP_RULES && ps_global->VAR_SMTP_RULES[0]
+        && ps_global->VAR_SMTP_RULES[0][0])
+        while (ps_global->VAR_SMTP_RULES[num_rules]) num_rules++;
+
+    if(num_rules){
+	int i, j;
+
+        added_rules = 0;
+        smtp_list = (char **) fs_get ((num_rules + 1)*sizeof(char*));
+        for (i = 0, j = 0; i < num_rules; i++){
+	   RULELIST *rule = get_rulelist_from_code(V_SMTP_RULES,
+                                                      ps_global->rule_list);
+	   RULE_S *prule = get_rule(rule, i);
+           if(prule){
+	     char *rule_result = process_rule(prule, FOR_COMPOSE, header->env);
+	     if(rule_result && *rule_result){
+		smtp_list[j++] = cpystr(rule_result);
+		added_rules++;
+	     }
+	   }
+	}
+    }
+
+    if (added_rules < 0){
+	smtp_list = (char **) fs_get (sizeof(char*));
+	added_rules = 0;
     }
-    else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
-	    && ps_global->VAR_SMTP_SERVER[0][0]){
-	/*---------- SMTP ----------*/
-	dprint((4, "call_mailer: via TCP\n"));
-	TIME_STAMP("smtp-open start (tcp)", 1);
-	sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
+    smtp_list[added_rules] = NULL;
+
+    choice = smtp_list && smtp_list[0] && smtp_list[0][0] ? 3 :
+	(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0] ? 2 :
+	(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] 
+		&& ps_global->VAR_SMTP_SERVER[0][0] ? 1 : -1));
+
+    if(choice > 0){
+        /*---------- SMTP ----------*/
+       dprint((4, "call_mailer: via TCP (%s)\n",smtp_list[0]));
+        TIME_STAMP("smtp-open start (tcp)", 1);
+        sending_stream = smtp_open(choice == 3 ? smtp_list
+				: (choice == 2 ? alt_smtp_servers
+				: ps_global->VAR_SMTP_SERVER), smtp_opts);
     }
     else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
 	char *cmdlist[2];
Index: alpine-2.26/pith/sort.c
===================================================================
--- alpine-2.26.orig/pith/sort.c
+++ alpine-2.26/pith/sort.c
@@ -26,7 +26,7 @@
 #include "../pith/signal.h"
 #include "../pith/busy.h"
 #include "../pith/icache.h"
-
+#include "../pith/rules.h"
 
 /*
  * global place to store mail_sort and mail_thread results
@@ -691,7 +691,21 @@ reset_sort_order(unsigned int flags)
     PAT_S        *pat;
     SortOrder	  the_sort_order;
     int           sort_is_rev;
-
+    char       *rule_result;
+    SortOrder   new_sort = EndofList;
+    int               is_rev;
+
+   rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES);
+   if (rule_result && *rule_result){
+      new_sort  = (SortOrder) translate(rule_result, 1);
+      is_rev    = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1;
+      fs_give((void **)&rule_result);
+   }
+   if (new_sort != EndofList){
+       the_sort_order = new_sort;
+       sort_is_rev    = is_rev;
+   }
+   else{
     /* set default order */
     the_sort_order = ps_global->def_sort;
     sort_is_rev    = the_sort_order == SortThread
@@ -714,6 +728,7 @@ reset_sort_order(unsigned int flags)
 				: pat->action->revsort;
 	}
     }
+   }
 
     if(the_sort_order == SortThread && !(flags & SRT_MAN))
       ps_global->thread_cur_sort = ps_global->thread_def_sort;
@@ -721,3 +736,39 @@ reset_sort_order(unsigned int flags)
     sort_folder(ps_global->mail_stream, ps_global->msgmap,
 		the_sort_order, sort_is_rev, flags, 1);
 }
+
+SortOrder translate(char *order, int is_rev)
+{
+   int rev = 0;
+     if (!strncmp(order,"tHread", 6)
+                || (rev = !strncmp(order,"Reverse tHread", 14)))
+        return is_rev || rev ? SortThread : EndofList;
+     if (!strncmp(order,"OrderedSubj", 11)
+                || (rev = !strncmp(order,"Reverse OrderedSubj", 19)))
+        return is_rev || rev  ? SortSubject2 : EndofList;
+     if (!strncmp(order,"Subject", 7)
+                || (rev = !strncmp(order,"Reverse SortSubject", 15)))
+        return is_rev || rev  ?  SortSubject : EndofList;
+     if (!strncmp(order,"Arrival", 7)
+                || (rev = !strncmp(order,"Reverse Arrival", 15)))
+        return is_rev || rev  ?  SortArrival : EndofList;
+     if (!strncmp(order,"From", 4)
+                || (rev = !strncmp(order,"Reverse From", 12)))
+        return is_rev || rev  ?  SortFrom : EndofList;
+     if (!strncmp(order,"To", 2)
+                || (rev = !strncmp(order,"Reverse To", 10)))
+        return is_rev || rev  ?  SortTo : EndofList;
+     if (!strncmp(order,"Cc", 2)
+                || (rev = !strncmp(order,"Reverse Cc", 10)))
+        return is_rev || rev  ?  SortCc : EndofList;
+     if (!strncmp(order,"Date", 4)
+                || (rev = !strncmp(order,"Reverse Date", 12)))
+        return is_rev || rev  ?  SortDate : EndofList;
+     if (!strncmp(order,"siZe", 4)
+                || (rev = !strncmp(order,"Reverse siZe", 12)))
+        return is_rev || rev  ?  SortSize : EndofList;
+     if (!strncmp(order,"scorE", 5)
+                || (rev = !strncmp(order,"Reverse scorE", 13)))
+        return is_rev || rev  ?  SortScore : EndofList;
+   return EndofList;
+}
Index: alpine-2.26/pith/sort.h
===================================================================
--- alpine-2.26.orig/pith/sort.h
+++ alpine-2.26/pith/sort.h
@@ -45,6 +45,6 @@ char	*sort_name(SortOrder);
 void	 sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int);
 int	 decode_sort(char *, SortOrder *, int *, int);
 void	 reset_sort_order(unsigned);
-
+SortOrder translate(char *, int);
 
 #endif /* PITH_SORT_INCLUDED */
Index: alpine-2.26/pith/state.c
===================================================================
--- alpine-2.26.orig/pith/state.c
+++ alpine-2.26/pith/state.c
@@ -31,6 +31,7 @@
 #include "../pith/smime.h"
 #include "../pith/ical.h"
 #include "../pith/bldaddr.h"
+#include "../pith/rules.h"
 
 /*
  * Globals referenced throughout pine...
@@ -254,6 +255,9 @@ free_pine_struct(struct pine **pps)
     if((*pps)->id)
       free_id(&(*pps)->id);
     
+    if((*pps)->rule_list)
+	free_parsed_rule_list(&(*pps)->rule_list);
+
     free_vars(*pps);
 
     fs_give((void **) pps);
Index: alpine-2.26/pith/state.h
===================================================================
--- alpine-2.26.orig/pith/state.h
+++ alpine-2.26/pith/state.h
@@ -33,7 +33,7 @@
 #include "../pith/stream.h"
 #include "../pith/color.h"
 #include "../pith/user.h"
-
+#include "../pith/rulestype.h"
 
 /*
  * Printing control structure
@@ -112,6 +112,11 @@ struct pine {
     MAILSTREAM  *mail_stream;		/* ptr to current folder stream */
     MSGNO_S	*msgmap;		/* ptr to current message map   */
 
+    char	screen_name[16];	/* name of current screen */
+    char	*role;			/* role used when composing */
+    char	*procid;		/* procedure id when needed */
+    int		exiting;
+
     unsigned     read_predicted:1;
 
     char         cur_folder[MAXPATH+1];
@@ -371,6 +376,7 @@ struct pine {
     struct {
         char	*(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *);
         char	*(*display_filter_trigger)(BODY *, char *, size_t);
+	char	*(*exec_rule)(char *, gf_io_t, gf_io_t);
     } tools;
 
     KEYWORD_S   *keywords;
@@ -381,6 +387,9 @@ struct pine {
     char	 last_error[500];
     INIT_ERR_S  *init_errs;
 
+    PRULELIST_S *rule_list;
+    char	*pressed_key;
+
     PRINT_S	*print;
 
 #ifdef SMIME
Index: alpine-2.26/pith/string.c
===================================================================
--- alpine-2.26.orig/pith/string.c
+++ alpine-2.26/pith/string.c
@@ -16,6 +16,7 @@
     string.c
     Misc extra and useful string functions
       - rplstr         replace a substring with another string
+      - collspaces     consecutive spaces are reduced to one space.
       - sqzspaces      Squeeze out the extra blanks in a string
       - sqznewlines    Squeeze out \n and \r.
       - removing_trailing_white_space 
@@ -127,6 +128,31 @@ rplstr(char *os, size_t oslen, int dl, c
     return(x3);
 }
 
+/*----------------------------------------------------------------------
+     collapse blank space
+  ----------------------------------------------------------------------*/
+void
+collspaces(char *string)
+{
+    char *p = string;
+    int only_one_space = 0;
+
+    if(!string)
+      return;
+
+    for(;isspace(*p); p++);
+
+    while((*string = *p++) != '\0')
+      if(!isspace((unsigned char)*string)){
+	only_one_space = 0;
+	string++;
+      }
+      else if(!only_one_space){
+		string++;
+		only_one_space++;
+	   }
+    *string = '\0';
+}
 
 
 /*----------------------------------------------------------------------
@@ -3041,3 +3067,35 @@ remove_quotes(unsigned char *name)
      }
   }
 }
+
+
+void
+removing_extra_stuff(string)
+    char *string;
+{
+    char *p = NULL;
+    int change = 0, length = 0;
+
+
+    if(!string)
+      return;
+
+    for(; *string; string++, length++)
+      p = ((unsigned char)*string != ',') ? NULL : (!p) ? string : p;
+
+    if(p)
+      *p = '\0';
+
+    string -= length;
+        for (; *string; string++){
+           if (change){
+              *string = ' ';
+              change = 0;
+           }
+           if ((((unsigned char)*string == ' ') ||
+                ((unsigned char)*string == ',')) &&
+                ((unsigned char)*(string + 1) == ','))
+            change++;
+        }
+}
+
Index: alpine-2.26/pith/string.h
===================================================================
--- alpine-2.26.orig/pith/string.h
+++ alpine-2.26/pith/string.h
@@ -87,6 +87,7 @@ struct date {
 
 /* exported prototypes */
 char	   *rplstr(char *, size_t, int, char *);
+void	    collspaces(char *);
 void	    sqzspaces(char *);
 void	    sqznewlines(char *);
 void	    removing_leading_white_space(char *);
@@ -94,6 +95,7 @@ void	    removing_trailing_white_space(c
 void	    replace_tabs_by_space(char *);
 void	    removing_leading_and_trailing_white_space(char *);
 int 	    removing_double_quotes(char *);
+void	    removing_extra_stuff (char *);
 char	   *skip_white_space(char *);
 char	   *skip_to_white_space(char *);
 char	   *removing_quotes(char *);