File chappa-rules.patch of Package alpine

diff -rc alpine-2.21/alpine/adrbkcmd.c alpine-2.21.rules/alpine/adrbkcmd.c
*** alpine-2.21/alpine/adrbkcmd.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/adrbkcmd.c	Sun Feb  5 16:15:21 2017
***************
*** 4128,4133 ****
--- 4128,4135 ----
  	 * 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{
***************
*** 4135,4140 ****
--- 4137,4143 ----
  	    memset((void *)role, 0, sizeof(*role));
  	    role->nick = cpystr("Default Role");
  	}
+ 	ps_global->role = cpystr(role->nick);
      }
  
      compose_mail(addr, fcc, role, NULL, NULL);
diff -rc alpine-2.21/alpine/alpine.c alpine-2.21.rules/alpine/alpine.c
*** alpine-2.21/alpine/alpine.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/alpine.c	Sun Feb  5 16:15:21 2017
***************
*** 485,490 ****
--- 485,491 ----
      /* 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){
***************
*** 3162,3167 ****
--- 3163,3171 ----
      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();
diff -rc alpine-2.21/alpine/confscroll.c alpine-2.21.rules/alpine/confscroll.c
*** alpine-2.21/alpine/confscroll.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/confscroll.c	Sun Feb  5 16:15:21 2017
***************
*** 51,56 ****
--- 51,57 ----
  #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")
***************
*** 2436,2441 ****
--- 2437,2445 ----
  	 * 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);
  
***************
*** 5200,5205 ****
--- 5204,5233 ----
  	    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);
+     }
      else if(var == &ps->vars[V_INDEX_FORMAT]){
  	reset_index_format();
  	clear_index_cache(ps->mail_stream, 0);
diff -rc alpine-2.21/alpine/dispfilt.c alpine-2.21.rules/alpine/dispfilt.c
*** alpine-2.21/alpine/dispfilt.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/dispfilt.c	Sun Feb  5 16:15:21 2017
***************
*** 461,463 ****
--- 461,523 ----
  
      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);
+ }
diff -rc alpine-2.21/alpine/dispfilt.h alpine-2.21.rules/alpine/dispfilt.h
*** alpine-2.21/alpine/dispfilt.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/dispfilt.h	Sun Feb  5 16:15:21 2017
***************
*** 25,31 ****
  char	*expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *);
  char	*filter_session_key(void);
  char	*filter_data_file(int);
! 
  
  
  #endif /* PINE_DISPFILT_INCLUDED */
--- 25,31 ----
  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 */
diff -rc alpine-2.21/alpine/folder.c alpine-2.21.rules/alpine/folder.c
*** alpine-2.21/alpine/folder.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/folder.c	Sun Feb  5 16:15:21 2017
***************
*** 248,254 ****
      dprint((1, "=== folder_screen called ====\n"));
      mailcap_free(); /* free resources we won't be using for a while */
      ps->next_screen = SCREEN_FUN_NULL;
! 
      /* Initialize folder state and dispatches */
      memset(&fs, 0, sizeof(FSTATE_S));
      fs.context		= cntxt;
--- 248,254 ----
      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;
***************
*** 345,350 ****
--- 345,351 ----
        pine_mail_close(*fs.cache_streamp);
  
      ps->prev_screen = folder_screen;
+     strcpy(ps->screen_name, "unknown");
  }
  
  
diff -rc alpine-2.21/alpine/mailcmd.c alpine-2.21.rules/alpine/mailcmd.c
*** alpine-2.21/alpine/mailcmd.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/mailcmd.c	Sun Feb  5 16:15:21 2017
***************
*** 73,78 ****
--- 73,79 ----
  #include "../pith/tempfile.h"
  #include "../pith/search.h"
  #include "../pith/margin.h"
+ #include "../pith/rules.h"
  #ifdef _WINDOWS
  #include "../pico/osdep/mswin.h"
  #endif
***************
*** 2657,2662 ****
--- 2658,2666 ----
  		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':
***************
*** 2707,2718 ****
  	    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		      delindex, preindex, r;
      char	      prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN];
      char              *buf = tmp_20k_buf;
      char              shortbuf[200];
!     char              *folder;
      HelpType	      help;
      SaveDel           del = DontAsk;
      SavePreserveOrder pre = DontAskPreserve;
--- 2711,2722 ----
  	    char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section,
  	    SaveDel *dela, SavePreserveOrder *prea)
  {
!     int		      rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0;
      int		      delindex, preindex, r;
      char	      prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN];
      char              *buf = tmp_20k_buf;
      char              shortbuf[200];
!     char              *folder, folder2[MAXPATH];
      HelpType	      help;
      SaveDel           del = DontAsk;
      SavePreserveOrder pre = DontAskPreserve;
***************
*** 2720,2725 ****
--- 2724,2730 ----
      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");
***************
*** 2729,2734 ****
--- 2734,2748 ----
      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)){
+        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))
diff -rc alpine-2.21/alpine/mailindx.c alpine-2.21.rules/alpine/mailindx.c
*** alpine-2.21/alpine/mailindx.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/mailindx.c	Sun Feb  5 16:15:21 2017
***************
*** 229,234 ****
--- 229,236 ----
      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
***************
*** 240,249 ****
--- 242,255 ----
  
      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");
  }
  
  
diff -rc alpine-2.21/alpine/mailview.c alpine-2.21.rules/alpine/mailview.c
*** alpine-2.21/alpine/mailview.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/mailview.c	Sun Feb  5 16:15:21 2017
***************
*** 243,248 ****
--- 243,250 ----
      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"));
***************
*** 479,484 ****
--- 481,488 ----
      }
      while(ps->next_screen == SCREEN_FUN_NULL);
  
+     strcpy(ps->screen_name, "unknown");
+ 
      if (prefix && *prefix)
         fs_give((void **)&prefix);
      if(we_cancel)
diff -rc alpine-2.21/alpine/osdep/termin.gen.c alpine-2.21.rules/alpine/osdep/termin.gen.c
*** alpine-2.21/alpine/osdep/termin.gen.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/osdep/termin.gen.c	Sun Feb  5 16:15:21 2017
***************
*** 33,38 ****
--- 33,40 ----
  #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"
***************
*** 72,78 ****
   *     Generic tty input routines
   */
  
! 
  /*----------------------------------------------------------------------
          Read a character from keyboard with timeout
   Input:  none
--- 74,81 ----
   *     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
***************
*** 114,119 ****
--- 117,157 ----
        *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();
  
***************
*** 1158,1163 ****
--- 1196,1202 ----
  	if(!*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);
diff -rc alpine-2.21/alpine/reply.c alpine-2.21.rules/alpine/reply.c
*** alpine-2.21/alpine/reply.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/reply.c	Sun Feb  5 16:15:21 2017
***************
*** 62,68 ****
  #include "../pith/tempfile.h"
  #include "../pith/busy.h"
  #include "../pith/ablookup.h"
! 
  
  /*
   * Internal Prototypes
--- 62,69 ----
  #include "../pith/tempfile.h"
  #include "../pith/busy.h"
  #include "../pith/ablookup.h"
! #include "../pith/copyaddr.h"
! #include "../pith/rules.h"
  
  /*
   * Internal Prototypes
***************
*** 109,119 ****
      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;
      gf_io_t     pc;
      PAT_STATE   dummy;
      REDRAFT_POS_S *redraft_pos = NULL;
      ACTION_S   *role = NULL, *nrole;
  #if	defined(DOS) && !defined(_WINDOWS)
      char *reserve;
  #endif
--- 110,121 ----
      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, 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
***************
*** 139,144 ****
--- 141,209 ----
         && 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)){
+ 	        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...
***************
*** 287,294 ****
  		outgoing->subject = cpystr("Re: several messages");
  	    }
  	}
! 	else
! 	  outgoing->subject = reply_subject(env->subject, NULL, 0);
      }
  
      /* fill reply header */
--- 352,369 ----
  		outgoing->subject = cpystr("Re: several messages");
  	    }
  	}
! 	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 */
***************
*** 307,319 ****
      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){
  	rflags = ROLE_REPLY;
  	if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){
  	    /* setup default role */
--- 382,388 ----
      if(sp_expunge_count(pine_state->mail_stream))	/* cur msg expunged */
        goto done_early;
  
!     if(!do_role_early){
  	rflags = ROLE_REPLY;
  	if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){
  	    /* setup default role */
***************
*** 724,729 ****
--- 793,801 ----
      if(prefix)
        fs_give((void **)&prefix);
  
+     if (ps_global->role)
+ 	fs_give((void **)&ps_global->role);
+ 
      if(fcc)
        fs_give((void **) &fcc);
  
***************
*** 1598,1606 ****
  	}
      }
  
!     if(role)
        q_status_message1(SM_ORDER, 3, 4,
  			_("Forwarding using role \"%s\""), role->nick);
  
      if(role && role->template){
  	char *filtered;
--- 1670,1683 ----
  	}
      }
  
!     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);
+     }
  
      if(role && role->template){
  	char *filtered;
***************
*** 1832,1837 ****
--- 1909,1915 ----
  #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);
diff -rc alpine-2.21/alpine/roleconf.c alpine-2.21.rules/alpine/roleconf.c
*** alpine-2.21/alpine/roleconf.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/roleconf.c	Sun Feb  5 16:15:21 2017
***************
*** 7706,7711 ****
--- 7706,7716 ----
  	    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));
  
diff -rc alpine-2.21/alpine/send.c alpine-2.21.rules/alpine/send.c
*** alpine-2.21/alpine/send.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/alpine/send.c	Sun Feb  5 16:15:21 2017
***************
*** 63,69 ****
  #include "../pith/mimetype.h"
  #include "../pith/send.h"
  #include "../pith/smime.h"
! 
  
  typedef struct body_particulars {
      unsigned short     type, encoding, had_csp;
--- 63,69 ----
  #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;
***************
*** 240,245 ****
--- 240,250 ----
  	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);
***************
*** 449,456 ****
  
  	      ps_global->next_screen = prev_screen;
  	      ps_global->redrawer = redraw;
! 	      if(role)
  		role = combine_inherited_role(role);
  	    }
  	    break;
  	  
--- 454,465 ----
  
  	      ps_global->next_screen = prev_screen;
  	      ps_global->redrawer = redraw;
! 	      if (ps_global->role)
! 		  fs_give((void **)&ps_global->role);  
! 	      if(role){
  		role = combine_inherited_role(role);
+ 		ps_global->role = cpystr(role->nick);
+ 	      }
  	    }
  	    break;
  	  
***************
*** 645,653 ****
  	    }
  	}
  
! 	if(role)
  	  q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
  			    role->nick);
  
  	/*
  	 * The type of storage object allocated below is vitally
--- 654,667 ----
  	    }
  	}
  
! 	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);
+ 	}
  
  	/*
  	 * The type of storage object allocated below is vitally
***************
*** 2476,2481 ****
--- 2490,2515 ----
  		    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);
diff -rc alpine-2.21/pith/Makefile.am alpine-2.21.rules/pith/Makefile.am
*** alpine-2.21/pith/Makefile.am	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/Makefile.am	Sun Feb  5 16:15:21 2017
***************
*** 26,32 ****
  	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.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 \
  	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
  
--- 26,32 ----
  	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.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 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
  
diff -rc alpine-2.21/pith/Makefile.in alpine-2.21.rules/pith/Makefile.in
*** alpine-2.21/pith/Makefile.in	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/Makefile.in	Sun Feb  5 16:15:21 2017
***************
*** 147,153 ****
  	margin.$(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) search.$(OBJEXT) sequence.$(OBJEXT) \
  	send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
  	store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \
--- 147,153 ----
  	margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \
  	msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \
  	pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \
! 	remote.$(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) \
***************
*** 442,448 ****
  	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.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 \
  	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
  
--- 442,448 ----
  	filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.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 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
  
***************
*** 574,579 ****
--- 574,580 ----
  @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 $@ $<
diff -rc alpine-2.21/pith/adrbklib.c alpine-2.21.rules/pith/adrbklib.c
*** alpine-2.21/pith/adrbklib.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/adrbklib.c	Sun Feb  5 16:15:21 2017
***************
*** 5136,5143 ****
  	    if(as.cur >= as.how_many_personals)
  	      pab->type |= GLOBAL;
  
! 	    pab->access = adrbk_access(pab);
! 
  	    /* global address books are forced readonly */
  	    if(pab->type & GLOBAL && pab->access != NoAccess)
  	      pab->access = ReadOnly;
--- 5136,5149 ----
  	    if(as.cur >= as.how_many_personals)
  	      pab->type |= GLOBAL;
  
! 	    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;
diff -rc alpine-2.21/pith/conf.c alpine-2.21.rules/pith/conf.c
*** alpine-2.21/pith/conf.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/conf.c	Sun Feb  5 16:15:21 2017
***************
*** 29,34 ****
--- 29,35 ----
  #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"
***************
*** 222,227 ****
--- 223,258 ----
  
  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
***************
*** 550,555 ****
--- 581,614 ----
  	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},
***************
*** 2635,2640 ****
--- 2694,2700 ----
        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 */
***************
*** 3081,3086 ****
--- 3141,3148 ----
  	 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,
***************
*** 7699,7704 ****
--- 7761,7794 ----
  	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 :
diff -rc alpine-2.21/pith/conf.h alpine-2.21.rules/pith/conf.h
*** alpine-2.21/pith/conf.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/conf.h	Sun Feb  5 16:15:21 2017
***************
*** 148,153 ****
--- 148,193 ----
  #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
diff -rc alpine-2.21/pith/conftype.h alpine-2.21.rules/pith/conftype.h
*** alpine-2.21/pith/conftype.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/conftype.h	Sun Feb  5 16:15:21 2017
***************
*** 70,75 ****
--- 70,89 ----
  		, 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
***************
*** 332,337 ****
--- 346,352 ----
  	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,
diff -rc alpine-2.21/pith/detoken.c alpine-2.21.rules/pith/detoken.c
*** alpine-2.21/pith/detoken.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/detoken.c	Sun Feb  5 16:15:21 2017
***************
*** 25,31 ****
  #include "../pith/reply.h"
  #include "../pith/mailindx.h"
  #include "../pith/options.h"
! 
  
  /*
   * Hook to read signature from local file
--- 25,31 ----
  #include "../pith/reply.h"
  #include "../pith/mailindx.h"
  #include "../pith/options.h"
! #include "../pith/rules.h"
  
  /*
   * Hook to read signature from local file
***************
*** 91,96 ****
--- 91,98 ----
  
      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;
***************
*** 104,117 ****
  	 * 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;
      }
      else if(role && role->template)
        sigfile = role->template;
--- 106,130 ----
  	 * there is no reason to mix them, so we don't provide support to
  	 * do so.
  	 */
!         { 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;
***************
*** 302,308 ****
  			}
  		    }
  		}
! 		else if(pt->what_for & FOR_REPLY_INTRO)
  		  repl = get_reply_data(env, role, pt->ctype,
  					subbuf, sizeof(subbuf)-1);
  
--- 315,321 ----
  			}
  		    }
  		}
! 		else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE))
  		  repl = get_reply_data(env, role, pt->ctype,
  					subbuf, sizeof(subbuf)-1);
  
diff -rc alpine-2.21/pith/indxtype.h alpine-2.21.rules/pith/indxtype.h
*** alpine-2.21/pith/indxtype.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/indxtype.h	Sun Feb  5 16:15:21 2017
***************
*** 84,89 ****
--- 84,94 ----
  	      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,119 ****
  
  
  /* 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 DEFAULT_REPLY_INTRO "default"
  
--- 110,135 ----
  
  
  /* these are flags for the what_for field in INDEX_PARSE_T */
! #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"
  
diff -rc alpine-2.21/pith/mailcmd.c alpine-2.21.rules/pith/mailcmd.c
*** alpine-2.21/pith/mailcmd.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/mailcmd.c	Sun Feb  5 16:15:21 2017
***************
*** 39,44 ****
--- 39,45 ----
  #include "../pith/ablookup.h"
  #include "../pith/search.h"
  #include "../pith/charconv/utf8.h"
+ #include "../pith/rules.h"
  
  #ifdef _WINDOWS
  #include "../pico/osdep/mswin.h"
***************
*** 665,670 ****
--- 666,672 ----
  	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! */
***************
*** 991,996 ****
--- 993,999 ----
  
      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
***************
*** 1114,1120 ****
  
      if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){
  
! 	perfolder_startup_rule = 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)
--- 1117,1126 ----
  
      if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){
  
! 	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)
***************
*** 1136,1259 ****
  	    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;
! 
! 	    }
  	}
  	else if(IS_NEWS(ps_global->mail_stream)){
  	    /*
--- 1142,1148 ----
  	    else
  	      use_this_startup_rule = ps_global->inc_startup_rule;
  
! 	    find_startup_position(use_this_startup_rule, m, pc);
  	}
  	else if(IS_NEWS(ps_global->mail_stream)){
  	    /*
***************
*** 1431,1439 ****
  	    /* 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))){
  
  		if(F_ON(F_AUTO_READ_MSGS,ps_global)
  		   || (pith_opt_read_msg_prompt
  		       && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER)))
  		  /* move inbox's read messages */
--- 1320,1330 ----
  	    /* Save read messages? */
  	    if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0]
  	       && sp_flagged(stream, SP_INBOX)
! 	       && (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 */
***************
*** 1716,1721 ****
--- 1607,1615 ----
      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;
***************
*** 1755,1762 ****
  	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)
  	  strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */
  
  	buf[buflen-1] = '\0';
--- 1649,1657 ----
  	snprintf(buf, buflen, "Moving %s read message%s to \"%s\"",
  		comatose(searched), plural(searched), dstfldr);
  	we_cancel = busy_cue(buf, NULL, 0);
! 	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';
***************
*** 1794,1800 ****
         && ((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))){
  
  	for(; f && *archive; archive++){
  	    char *p;
--- 1689,1697 ----
         && ((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))
! 	   || (F_ON(F_AUTO_READ_MSGS,ps_global) &&
! 	       F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){
  
  	for(; f && *archive; archive++){
  	    char *p;
***************
*** 2755,2757 ****
--- 2652,2946 ----
  
      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); 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); 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;
+ 
+   }
+ }
diff -rc alpine-2.21/pith/mailcmd.h alpine-2.21.rules/pith/mailcmd.h
*** alpine-2.21/pith/mailcmd.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/mailcmd.h	Sun Feb  5 16:15:21 2017
***************
*** 42,47 ****
--- 42,49 ----
  #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,69 ****
--- 65,77 ----
  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 *);
diff -rc alpine-2.21/pith/mailindx.c alpine-2.21.rules/pith/mailindx.c
*** alpine-2.21/pith/mailindx.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/mailindx.c	Sun Feb  5 16:15:21 2017
***************
*** 40,45 ****
--- 40,46 ----
  #include "../pith/send.h"
  #include "../pith/options.h"
  #include "../pith/ablookup.h"
+ #include "../pith/rules.h"
  #ifdef _WINDOWS
  #include "../pico/osdep/mswin.h"
  #endif
***************
*** 377,382 ****
--- 378,390 ----
      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)){
+       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)){
***************
*** 450,464 ****
  static INDEX_PARSE_T itokens[] = {
      {"STATUS",		iStatus,	FOR_INDEX},
      {"MSGNO",		iMessNo,	FOR_INDEX},
!     {"DATE",		iDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
      {"FROMORTO",	iFromTo,	FOR_INDEX},
      {"FROMORTONOTNEWS",	iFromToNotNews,	FOR_INDEX},
      {"SIZE",		iSize,		FOR_INDEX},
      {"SIZECOMMA",	iSizeComma,	FOR_INDEX},
      {"SIZETHREAD",	iSizeThread,	FOR_INDEX},
      {"SIZENARROW",	iSizeNarrow,	FOR_INDEX},
      {"KSIZE",		iKSize,		FOR_INDEX},
!     {"SUBJECT",		iSubject,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
      {"SHORTSUBJECT",	iShortSubject,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
      {"FULLSTATUS",	iFStatus,	FOR_INDEX},
      {"IMAPSTATUS",	iIStatus,	FOR_INDEX},
--- 458,472 ----
  static INDEX_PARSE_T itokens[] = {
      {"STATUS",		iStatus,	FOR_INDEX},
      {"MSGNO",		iMessNo,	FOR_INDEX},
!     {"DATE",		iDate,		FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
      {"FROMORTO",	iFromTo,	FOR_INDEX},
      {"FROMORTONOTNEWS",	iFromToNotNews,	FOR_INDEX},
      {"SIZE",		iSize,		FOR_INDEX},
      {"SIZECOMMA",	iSizeComma,	FOR_INDEX},
      {"SIZETHREAD",	iSizeThread,	FOR_INDEX},
      {"SIZENARROW",	iSizeNarrow,	FOR_INDEX},
      {"KSIZE",		iKSize,		FOR_INDEX},
!     {"SUBJECT",		iSubject,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM},
      {"SHORTSUBJECT",	iShortSubject,	FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
      {"FULLSTATUS",	iFStatus,	FOR_INDEX},
      {"IMAPSTATUS",	iIStatus,	FOR_INDEX},
***************
*** 469,524 ****
      {"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},
      {"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},
      {"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},
      {"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},
--- 477,536 ----
      {"SUBJECTTEXT",	iSubjectText,	FOR_INDEX},
      {"SUBJKEYTEXT",	iSubjKeyText,	FOR_INDEX},
      {"SUBJKEYINITTEXT", iSubjKeyInitText, 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|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|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},
***************
*** 527,582 ****
      {"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},
      {"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},
      {"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},
      {"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},
      {"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},
      {"HEADER",		iHeader,	FOR_INDEX},
      {"TEXT",		iText,		FOR_INDEX},
      {"ARROW",		iArrow,		FOR_INDEX},
      {"NEWLINE",		iNewLine,	FOR_REPLY_INTRO},
      {"CURSORPOS",	iCursorPos,	FOR_TEMPLATE},
      {NULL,		iNothing,	FOR_NOTHING}
  };
  
--- 539,609 ----
      {"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|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|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|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|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|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}
  };
  
***************
*** 2483,2488 ****
--- 2510,2533 ----
  		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"
***************
*** 3833,3839 ****
--- 3878,3894 ----
  
  			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)){
+ 			      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));
***************
*** 5407,5416 ****
  {
      char          *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
      char          *p, *border, *q = NULL, *free_subj = NULL;
!     char	  *sp;
      size_t         len;
      int            width = -1;
!     int            depth = 0, mult = 2;
      int            save;
      int            do_subj = 0, truncated_tree = 0;
      PINETHRD_S    *thd, *thdorig;
--- 5462,5471 ----
  {
      char          *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
      char          *p, *border, *q = NULL, *free_subj = NULL;
!     char	  *sp, *rule_result;
      size_t         len;
      int            width = -1;
!     int            depth = 0, mult = 2, collapsed, i, we_clear = 0;
      int            save;
      int            do_subj = 0, truncated_tree = 0;
      PINETHRD_S    *thd, *thdorig;
***************
*** 5466,5471 ****
--- 5521,5534 ----
       * to free it at the end of this routine.
       */
  
+     if (rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)){
+ 	if(origsubj)
+ 	  fs_give((void **)&origsubj);
+ 	we_clear++;
+ 	origsubj = cpystr(rule_result);
+ 	fs_give((void **)&rule_result);
+     }
+ 
      if(shorten)
         shorten_subject(origsubj);
  
***************
*** 5908,5913 ****
--- 5971,5979 ----
  
      if(free_subj)
        fs_give((void **) &free_subj);
+ 
+     if (we_clear && origsubj)
+       fs_give((void **)&origsubj);
  }
  
  
***************
*** 6276,6291 ****
  				 ? "To"
  				 : (addr = fetch_cc(idata))
  				 ? "Cc"
! 				 : NULL))
! 		       && set_index_addr(idata, field, addr, "To: ",
! 					 strsize-1, fptr))
! 		      break;
  
  		    if(ctype == iFromTo &&
  		       (newsgroups = fetch_newsgroups(idata)) &&
  		       *newsgroups){
! 			snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4),
! 				newsgroups);
  			break;
  		    }
  
--- 6342,6374 ----
  				 ? "To"
  				 : (addr = fetch_cc(idata))
  				 ? "Cc"
! 				 : 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", strsize-1, strsize-1, 
! 								rule_result);
! 			    fs_give((void **)&rule_result);
! 			  }
  
+ 		      break;
+ 		    }
  		    if(ctype == iFromTo &&
  		       (newsgroups = fetch_newsgroups(idata)) &&
  		       *newsgroups){
! 			   char *rule_result;
! 			   rule_result = find_value("_FROM_", NULL, 0, idata, 1);
! 			   if (!rule_result)
! 				sprintf(str, "To: %-*.*s", strsize-1-4, 
! 					strsize-1-4, newsgroups);
! 				else{
! 				  sprintf(str, "%-*.*s", strsize-1, strsize-1,
! 							 rule_result);
! 				  fs_give((void **)&rule_result);
! 				}
  			break;
  		    }
  
***************
*** 6298,6304 ****
  	      break;
  
  	  case iFrom:
! 	    set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
  	    break;
  
  	  case iAddress:
--- 6381,6395 ----
  	      break;
  
  	  case iFrom:
! 	   { 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", strsize-1, strsize-1, rule_result);
! 		fs_give((void **)&rule_result);
! 	     }
! 	   }
  	    break;
  
  	  case iAddress:
***************
*** 6596,6598 ****
--- 6687,6750 ----
  	}
      }
  }
+ 
+ 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); 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)){
+     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;
+ }
diff -rc alpine-2.21/pith/mailindx.h alpine-2.21.rules/pith/mailindx.h
*** alpine-2.21/pith/mailindx.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/mailindx.h	Sun Feb  5 16:15:21 2017
***************
*** 30,35 ****
--- 30,38 ----
  
  
  /* 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 *);
diff -rc alpine-2.21/pith/makefile.wnt alpine-2.21.rules/pith/makefile.wnt
*** alpine-2.21/pith/makefile.wnt	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/makefile.wnt	Sun Feb  5 16:15:21 2017
***************
*** 45,51 ****
  	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 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
  
--- 45,51 ----
  	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 rules.h save.h savetype.h search.h send.h sequence.h signal.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
  
***************
*** 54,60 ****
  	filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.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 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
  
--- 54,60 ----
  	filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.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 rules.obj save.obj search.obj sequence.obj send.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
  
diff -rc alpine-2.21/pith/pine.hlp alpine-2.21.rules/pith/pine.hlp
*** alpine-2.21/pith/pine.hlp	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/pine.hlp	Sun Feb  5 16:15:21 2017
***************
*** 3855,3860 ****
--- 3855,3861 ----
  <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>
***************
*** 19045,19050 ****
--- 19046,19052 ----
  <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.
***************
*** 19057,19063 ****
  <P>
  <P>
  
! <H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1>
  
  <DL>
  <DT>SUBJECT</DT>
--- 19059,19065 ----
  <P>
  <P>
  
! <H1><EM>Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)</EM></H1>
  
  <DL>
  <DT>SUBJECT</DT>
***************
*** 19091,19096 ****
--- 19093,19114 ----
  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 
***************
*** 19138,19143 ****
--- 19156,19170 ----
  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
***************
*** 19146,19151 ****
--- 19173,19186 ----
  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
***************
*** 20270,20275 ****
--- 20305,20414 ----
  </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>
***************
*** 23397,23402 ****
--- 23536,24432 ----
  &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_ and _REXTRIM_.
+ 
+ <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_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>) and the screen where the list of
+ folders is displayed (<A HREF="h_folder_maint">FOLDER LIST</A>). The 
+ internal names of these screens for this patch are &quot;index&quot;, 
+ &quot;text&quot; and 
+ &quot;folder&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 13 functions already defined for 
+ you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_, 
+ _SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _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> _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>
***************
*** 27005,27010 ****
--- 28035,28110 ----
  &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>
***************
*** 30595,30600 ****
--- 31695,31723 ----
  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>
diff -rc alpine-2.21/pith/reply.c alpine-2.21.rules/pith/reply.c
*** alpine-2.21/pith/reply.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/reply.c	Sun Feb  5 16:15:22 2017
***************
*** 47,52 ****
--- 47,54 ----
  #include "../pith/mailcmd.h"
  #include "../pith/margin.h"
  #include "../pith/smime.h"
+ #include "../pith/copyaddr.h"
+ #include "../pith/rules.h"
  
  
  /*
***************
*** 864,871 ****
  reply_quote_str(ENVELOPE *env)
  {
      char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
  
!     strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
      buf[sizeof(buf)-1] = '\0';
  
      /* set up the prefix to quote included text */
--- 866,892 ----
  reply_quote_str(ENVELOPE *env)
  {
      char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
+     char reply_string[MAX_PREFIX+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,"> ",sizeof("> "));
!     }
! 
!     strncpy(buf, reply_string, sizeof(buf)-1);
      buf[sizeof(buf)-1] = '\0';
  
      /* set up the prefix to quote included text */
***************
*** 917,926 ****
  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)));
  }
  
  
--- 938,966 ----
  int
  reply_quote_str_contains_tokens(void)
  {
!    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)));
  }
  
  
***************
*** 1385,1390 ****
--- 1425,1434 ----
      buf[0] = '\0';
  
      switch(type){
+       case iFfrom:
+ 	addr = env && env->sparep ? env->sparep : NULL;
+ 	break;
+ 
        case iFrom:
  	addr = env ? env->from : NULL;
  	break;
***************
*** 1797,1818 ****
  
  	break;
  
        case iFrom:
        case iTo:
        case iCc:
        case iSender:
        case iRecips:
        case iInit:
  	get_addr_data(env, type, buf, maxlen);
  	break;
  
!       case iRoleNick:
! 	if(role && role->nick){
! 	    strncpy(buf, role->nick, maxlen);
! 	    buf[maxlen] = '\0';
  	}
  	break;
  
        case iNewLine:
  	if(maxlen >= strlen(NEWLINE)){
  	    strncpy(buf, NEWLINE, maxlen);
--- 1841,2034 ----
  
  	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;
  
!      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;
! 	 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);
***************
*** 1840,1845 ****
--- 2056,2066 ----
  
  	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);
***************
*** 1889,1894 ****
--- 2110,2123 ----
  
  	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){
***************
*** 1951,1957 ****
      if(!env)
        return;
  
!     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)){
--- 2180,2197 ----
      if(!env)
        return;
  
!     { 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)){
***************
*** 2210,2215 ****
--- 2450,2456 ----
  {
      size_t l;
      char  *p, buftmp[MAILTMPLEN];
+     RULE_RESULT *rule;
      
      if(!env)
        return(NULL);
***************
*** 2217,2225 ****
      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';
  	/* 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)
--- 2458,2476 ----
      dprint((9, "checking subject: \"%s\"\n",
  	       env->subject ? env->subject : "NULL"));
  
!     buftmp[0] = '\0';
!     ps_global->procid = cpystr("fwd-subject");
!     if (rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)){
!        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)
diff -rc alpine-2.21/pith/rules.c alpine-2.21.rules/pith/rules.c
*** alpine-2.21/pith/rules.c	Sun Feb  5 16:15:22 2017
--- alpine-2.21.rules/pith/rules.c	Sun Feb  5 16:15:22 2017
***************
*** 0 ****
--- 1,1416 ----
+ /* This module was written by
+  *
+  * Eduardo Chappa (chappa@washington.edu)
+  * http://patches.freeiz.com/alpine/
+  *
+  *  Original Version: November 1999
+  *  Last Modified   : September 14, 2013
+  *
+  * 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  *raw_value      (RULEACTION_S *, int, ENVELOPE *);
+ char  *extended_value (RULEACTION_S *, int, ENVELOPE *);
+ char  *exec_fcn	      (RULEACTION_S *, int, ENVELOPE *);
+ char  *expand         (char *, void *);
+ 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_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  free_alloc_rule (void **, int);
+ void  *alloc_mem      (size_t);
+ void  add_rule        (int, int);
+ void  set_rule_list    (struct variable *);
+ void  parse_patterns_into_action(TOKEN_VALUE **);
+ 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 *);
+ 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,      extended_value, FOR_SAVE|FOR_COMPOSE},
+ {SAVE_FCN,      extended_value, FOR_SAVE},
+ {EXEC_FCN,	exec_fcn,	FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {REPLY_FCN,     extended_value, FOR_REPLY_INTRO},
+ {TRIM_FCN,      trim,           FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {REPLACE_FCN,   extended_value, FOR_REPLACE},
+ {SORT_FCN,      raw_value,      FOR_SORT},
+ {INDEX_FCN,     raw_value,      FOR_INDEX},
+ {COMMAND_FCN,   raw_value,      FOR_KEY},
+ {REPLYSTR_FCN,  raw_value,      FOR_COMPOSE},
+ {SIGNATURE_FCN, raw_value,      FOR_COMPOSE},
+ {RESUB_FCN,     extended_value, FOR_RESUB},
+ {STARTUP_FCN,   raw_value,      FOR_STARTUP},
+ {REXTRIM_FCN,   rextrim,        FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {THRDSTYLE_FCN, raw_value,      FOR_THREAD},
+ {THRDINDEX_FCN, raw_value,      FOR_THREAD},
+ {SMTP_FCN,      raw_value,      FOR_COMPOSE},
+ {NULL,          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, EXEC_FCN};
+ char *from_fcn[]    = {FROM_TOKEN,    REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN};
+ char *otext_fcn[]   = {OTEXT_TOKEN,   REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN};
+ char *otextnq_fcn[] = {OTEXTNQ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_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 *) 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, j;
+   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_alloc_rule (void **voidtext, int code)
+ {
+   switch(code){
+      case FREEREGEX : regfree((regex_t *)*voidtext);
+ 			break;
+ 	default: break;
+   }
+ }
+ 
+ 
+ 
+ void free_token_value(TOKEN_VALUE **token)
+ {
+    if(token && *token){
+      if ((*token)->testxt)
+ 	fs_give((void **)&(*token)->testxt);
+      free_alloc_rule (&(*token)->voidtxt, (*token)->codefcn);
+      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 *
+ alloc_mem (size_t amount)
+ {
+    void *genmem;
+    memset(genmem = fs_get(amount), 0, amount);
+    return genmem;
+ }
+ 
+ 
+ void
+ parse_patterns_into_action(TOKEN_VALUE **tokenp)
+ {
+   if(!*tokenp)
+     return;
+ 
+   if((*tokenp)->testxt){
+       regex_t preg;
+ 
+       (*tokenp)->voidtxt = NULL;
+       (*tokenp)->voidtxt = fs_get(sizeof(regex_t));
+       if (regcomp((regex_t *)(*tokenp)->voidtxt, 
+ 			(*tokenp)->testxt, REG_EXTENDED) != 0){
+          regfree((regex_t *)(*tokenp)->voidtxt);
+ 	 (*tokenp)->voidtxt = NULL;
+       }
+   }
+   if((*tokenp)->voidtxt)
+      (*tokenp)->codefcn = FREEREGEX;
+   if((*tokenp)->next)
+      parse_patterns_into_action(&(*tokenp)->next);
+ }
+ 
+ 
+ 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 *) alloc_mem(sizeof(TOKEN_VALUE));
+   if (p = advance_to_char(d,';', STRICTLY, &offset)){
+       rvalue->testxt = p;
+       rvalue->next   = parse_group_data(d + strlen(p) + 1 + offset, error);
+   }
+   else if (p = advance_to_char(d,'}', STRICTLY, 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 = alloc_mem(sizeof(CONDVALUE_S));
+      condition->tname = cpystr(token_rules[--i]);
+      data += strlen(token_rules[i]);
+   }
+   else if (*data == '_') {
+       char *itokname;
+       for (i = 0, done = 0; done == 0 && (itokname = itoken(i)->name) != NULL; i++)
+ 	 done = strncmp(data+1, itokname, strlen(itokname))
+ 			? 0 : data[strlen(itokname) + 1] == '_';
+       if (done){
+ 	 condition = (CONDVALUE_S *) 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){
+ 			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 *) 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 *) alloc_mem(sizeof(RULEACTION_S));
+      raction->function = cpystr("_SAVE_");
+      raction->value    = (TOKEN_VALUE *) 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 = 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 *) alloc_mem(sizeof(TOKEN_VALUE));
+      raction->value->testxt = advance_to_char(p,'}', STRICTLY, NULL);
+      if(!raction->value->testxt)
+        free_ruleaction(&raction);
+      return raction;
+   }
+ 
+   done = (((function = strstr(p, "_TRIM_")) != NULL)
+ 	  ? 1 : ((function = strstr(p, "_COPY_")) != NULL)
+ 	  ? 2 : ((function = strstr(p, "_EXEC_")) != NULL)
+ 	  ? 3 : ((function = strstr(p, "_REXTRIM_")) != NULL)
+ 	  ? 4 : ((function = strstr(p, "_REPLACE_")) != NULL)
+ 	  ? 5 : 0);
+ 
+   if(!function)
+      return (RULEACTION_S *) NULL;
+ 
+   *function = '\0';
+    raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S));
+    raction->token = get_name_token(p);
+   *function = '_';
+    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++);
+    if (p != function){
+       free_ruleaction(&raction);
+       return NULL;
+    }
+    p += done <= 3 ? 6 : 9; /* 6 = strlen("_EXEC_"), 9 = strlen("_REPLACE_") */
+    if (*p != '{'){
+       free_ruleaction(&raction);
+       return NULL;
+    }
+    *p = '\0';
+    for(i = 0; i < NFCN && strcmp(function, rule_fcns[i].name);i++);
+    raction->function   = cpystr(function);
+    raction->is_trim    = strcmp(function,"_TRIM_")    ? 0 : 1;
+    raction->is_rextrim = strcmp(function,"_REXTRIM_") ? 0 : 1;
+    raction->is_replace = strcmp(function,"_REPLACE_") ? 0 : 1;
+    raction->context    = rule_fcns[i].what_for;
+    raction->exec       = rule_fcns[i].execute;
+    *p++ = '{';
+    if((raction->value = parse_group_data(p, NULL)) == NULL 
+ 	|| raction->value->testxt == NULL)
+       free_ruleaction(&raction);
+    if(raction && raction->is_rextrim)
+       parse_patterns_into_action(&raction->value);
+    return raction;
+ }
+ 
+ RULE_S *
+ parse_rule (char *data, int context)
+ {
+   RULE_S *prule;	/*parsed rule */
+   int len = 0;
+   
+   if (!(prule = (RULE_S *) 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)){
+ 	trulelist  = (RULELIST *)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;
+ }
+ 
+ PRULELIST_S *
+ add_prule(PRULELIST_S *rule_list, PRULELIST_S *rule)
+ {
+    if (!rule_list)
+       rule_list = (PRULELIST_S *) alloc_mem(sizeof(PRULELIST_S));
+ 
+    if(rule_list->next)
+      rule_list->next = add_prule(rule_list->next, rule);
+    else{
+      if (rule_list->rlist)
+ 	rule_list->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 *)alloc_mem(sizeof(PRULELIST_S));
+      trulelist->varnum = code;
+      if (trulelist->rlist = get_rule_list(list, context, 0))
+         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))
+       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 *) 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 ')': 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 = NULL, *q, *s;
+ 
+     if ((q = strchr(condition,'_')) && (s = strchr(q+1,'_'))){
+ 	char c = *++s;
+ 	*s = '\0';
+ 	 p = cpystr(q);
+ 	*s = c;
+     }
+     return p;
+ }
+ 
+ /* 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)){
+       memmove(begin_text, begin_text+strlen(tval->testxt), strlen(begin_text) - strlen(tval->testxt));
+       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))
+        test = do_trim(test, taction->value);
+     return test;
+  }
+  return NULL;
+ }
+ 
+ 
+ char *
+ do_rextrim (char *test, TOKEN_VALUE *tval)
+ {
+    char *begin_text, *trim_text;
+    int offset = 0;
+ 
+    if (!tval)
+       return test;
+ 
+    trim_text = expand(test, tval->voidtxt);
+    while(trim_text && (begin_text = strstr(test+offset,trim_text))){
+       strcpy(begin_text, begin_text+strlen(trim_text));
+       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 *
+ 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, void *pattern)
+ {
+  char c, *ret_string = NULL;
+  regmatch_t pmatch;
+  
+   if((regex_t *)pattern == NULL)
+      return NULL;
+ 
+   if(regexec((regex_t *)pattern, 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;
+   }
+   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;
+ }
diff -rc alpine-2.21/pith/rules.h alpine-2.21.rules/pith/rules.h
*** alpine-2.21/pith/rules.h	Sun Feb  5 16:15:22 2017
--- alpine-2.21.rules/pith/rules.h	Sun Feb  5 16:15:22 2017
***************
*** 0 ****
--- 1,151 ----
+ /* 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;
+         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 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 */
diff -rc alpine-2.21/pith/rulestype.h alpine-2.21.rules/pith/rulestype.h
*** alpine-2.21/pith/rulestype.h	Sun Feb  5 16:15:22 2017
--- alpine-2.21.rules/pith/rulestype.h	Sun Feb  5 16:15:22 2017
***************
*** 0 ****
--- 1,85 ----
+ #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 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
+ 
+ TOKEN_VALUE {
+ 	char	*testxt;
+ 	void	*voidtxt;
+ 	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)();
+    unsigned int is_trim:1;
+    unsigned int is_rextrim:1;
+    unsigned int is_replace:1;
+ };
+ 
+ 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 */
diff -rc alpine-2.21/pith/save.c alpine-2.21.rules/pith/save.c
*** alpine-2.21/pith/save.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/save.c	Sun Feb  5 16:15:22 2017
***************
*** 954,960 ****
  	      *date = '\0';
  
  	    rv = save_fetch_append(stream, mn_m2raw(msgmap, i),
! 				   NULL, save_stream, save_folder, context,
  				   mc ? mc->rfc822_size : 0L, flags, date, so);
  
  	    if(flags)
--- 954,960 ----
  	      *date = '\0';
  
  	    rv = save_fetch_append(stream, mn_m2raw(msgmap, i),
! 				   NULL, save_stream, folder, context,
  				   mc ? mc->rfc822_size : 0L, flags, date, so);
  
  	    if(flags)
diff -rc alpine-2.21/pith/send.c alpine-2.21.rules/pith/send.c
*** alpine-2.21/pith/send.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/send.c	Sun Feb  5 16:15:22 2017
***************
*** 44,49 ****
--- 44,50 ----
  #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"
***************
*** 1742,1750 ****
      char         error_buf[200], *error_mess = NULL, *postcmd;
      ADDRESS     *a;
      ENVELOPE	*fake_env = NULL;
!     int          addr_error_count, we_cancel = 0;
      long	 smtp_opts = 0L;
!     char	*verbose_file = NULL;
      BODY	*bp = NULL;
      PINEFIELD	*pf;
      BODY	*origBody = body;
--- 1743,1751 ----
      char         error_buf[200], *error_mess = NULL, *postcmd;
      ADDRESS     *a;
      ENVELOPE	*fake_env = NULL;
!     int          addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1;
      long	 smtp_opts = 0L;
!     char	*verbose_file = NULL, **smtp_list;
      BODY	*bp = NULL;
      PINEFIELD	*pf;
      BODY	*origBody = body;
***************
*** 1899,1918 ****
       * 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, 
       */
!     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);
      }
!     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);
      }
      else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
  	char *cmdlist[2];
--- 1900,1948 ----
       * 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(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;
      }
!     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];
diff -rc alpine-2.21/pith/sort.c alpine-2.21.rules/pith/sort.c
*** alpine-2.21/pith/sort.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/sort.c	Sun Feb  5 16:15:22 2017
***************
*** 30,36 ****
  #include "../pith/signal.h"
  #include "../pith/busy.h"
  #include "../pith/icache.h"
! 
  
  /*
   * global place to store mail_sort and mail_thread results
--- 30,36 ----
  #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
***************
*** 686,692 ****
      PAT_S        *pat;
      SortOrder	  the_sort_order;
      int           sort_is_rev;
! 
      /* set default order */
      the_sort_order = ps_global->def_sort;
      sort_is_rev    = the_sort_order == SortThread
--- 686,706 ----
      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
***************
*** 704,713 ****
  				: pat->action->revsort;
  	}
      }
! 
      if(the_sort_order == SortThread && !(flags & SRT_MAN))
        ps_global->thread_cur_sort = ps_global->thread_def_sort;

      sort_folder(ps_global->mail_stream, ps_global->msgmap,
  		the_sort_order, sort_is_rev, flags, 1);
  }
--- 718,763 ----
  				: pat->action->revsort;
  	}
      }
!    }
      if(the_sort_order == SortThread && !(flags & SRT_MAN))
        ps_global->thread_cur_sort = ps_global->thread_def_sort;

      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;
+ }
diff -rc alpine-2.21/pith/sort.h alpine-2.21.rules/pith/sort.h
*** alpine-2.21/pith/sort.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/sort.h	Sun Feb  5 16:15:22 2017
***************
*** 45,50 ****
  void	 sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int);
  int	 decode_sort(char *, SortOrder *, int *, int);
  void	 reset_sort_order(unsigned);
! 
  
  #endif /* PITH_SORT_INCLUDED */
--- 45,50 ----
  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 */
diff -rc alpine-2.21/pith/state.c alpine-2.21.rules/pith/state.c
*** alpine-2.21/pith/state.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/state.c	Sun Feb  5 16:15:22 2017
***************
*** 33,39 ****
  #include "../pith/remote.h"
  #include "../pith/list.h"
  #include "../pith/smime.h"
! 
  
  /*
   * Globals referenced throughout pine...
--- 33,39 ----
  #include "../pith/remote.h"
  #include "../pith/list.h"
  #include "../pith/smime.h"
! #include "../pith/rules.h"
  
  /*
   * Globals referenced throughout pine...
***************
*** 239,244 ****
--- 239,247 ----
      if((*pps)->msgmap)
        msgno_give(&(*pps)->msgmap);
      
+     if((*pps)->rule_list)
+ 	free_parsed_rule_list(&(*pps)->rule_list);
+ 
      free_vars(*pps);
  
      fs_give((void **) pps);
diff -rc alpine-2.21/pith/state.h alpine-2.21.rules/pith/state.h
*** alpine-2.21/pith/state.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/state.h	Sun Feb  5 16:15:22 2017
***************
*** 33,39 ****
  #include "../pith/stream.h"
  #include "../pith/color.h"
  #include "../pith/user.h"
! 
  
  /*
   * Printing control structure
--- 33,39 ----
  #include "../pith/stream.h"
  #include "../pith/color.h"
  #include "../pith/user.h"
! #include "../pith/rulestype.h"
  
  /*
   * Printing control structure
***************
*** 105,110 ****
--- 105,115 ----
      MAILSTREAM  *mail_stream;		/* ptr to current folder stream */
      MSGNO_S	*msgmap;		/* ptr to current message map   */
  
+     char	screen_name[10];	/* 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];
***************
*** 349,354 ****
--- 354,360 ----
      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;
***************
*** 359,364 ****
--- 365,373 ----
      char	 last_error[500];
      INIT_ERR_S  *init_errs;
  
+     PRULELIST_S *rule_list;
+     char	*pressed_key;
+ 
      PRINT_S	*print;
  
  #ifdef SMIME
diff -rc alpine-2.21/pith/string.c alpine-2.21.rules/pith/string.c
*** alpine-2.21/pith/string.c	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/string.c	Sun Feb  5 16:15:22 2017
***************
*** 20,25 ****
--- 20,26 ----
      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 
***************
*** 131,136 ****
--- 132,162 ----
      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++)
+       if(!isspace((unsigned char)*string)){
+ 	only_one_space = 0;
+ 	string++;
+       }
+       else if(!only_one_space){
+ 		string++;
+ 		only_one_space++;
+ 	   }
+     *string = '\0';
+ }
  
  
  /*----------------------------------------------------------------------
***************
*** 2983,2985 ****
--- 3009,3043 ----
    if(i < len) rn[i] = '\0';
    rn[len-1] = '\0';
  }
+ 
+ 
+ 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++;
+         }
+ }
+ 
diff -rc alpine-2.21/pith/string.h alpine-2.21.rules/pith/string.h
*** alpine-2.21/pith/string.h	Sun Feb  5 16:02:36 2017
--- alpine-2.21.rules/pith/string.h	Sun Feb  5 16:15:22 2017
***************
*** 87,92 ****
--- 87,93 ----
  
  /* exported protoypes */
  char	   *rplstr(char *, size_t, int, char *);
+ void	    collspaces(char *);
  void	    sqzspaces(char *);
  void	    sqznewlines(char *);
  void	    removing_leading_white_space(char *);
***************
*** 94,99 ****
--- 95,101 ----
  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 *);