alpine/chappa-rules.patch

6555 lines
225 KiB
Diff

---
alpine/adrbkcmd.c | 3
alpine/alpine.c | 4
alpine/confscroll.c | 33
alpine/dispfilt.c | 60 +
alpine/dispfilt.h | 2
alpine/folder.c | 3
alpine/mailcmd.c | 18
alpine/mailindx.c | 6
alpine/mailpart.c | 12
alpine/mailview.c | 4
alpine/osdep/termin.gen.c | 41 +
alpine/reply.c | 102 ++
alpine/roleconf.c | 5
alpine/send.c | 40 +
pith/Makefile.am | 2
pith/Makefile.in | 5
pith/adrbklib.c | 10
pith/conf.c | 90 ++
pith/conf.h | 40 +
pith/conftype.h | 15
pith/detoken.c | 33
pith/indxtype.h | 34
pith/mailcmd.c | 435 +++++++++---
pith/mailcmd.h | 8
pith/mailindx.c | 340 +++++++--
pith/mailindx.h | 3
pith/makefile.wnt | 5
pith/pine.hlp | 1150 +++++++++++++++++++++++++++++++++
pith/reply.c | 277 +++++++-
pith/rules.c | 1565 ++++++++++++++++++++++++++++++++++++++++++++++
pith/rules.h | 154 ++++
pith/rulestype.h | 94 ++
pith/save.c | 2
pith/send.c | 58 +
pith/sort.c | 54 +
pith/sort.h | 2
pith/state.c | 4
pith/state.h | 11
pith/string.c | 58 +
pith/string.h | 2
40 files changed, 4487 insertions(+), 297 deletions(-)
Index: alpine-2.23.2/alpine/adrbkcmd.c
===================================================================
*** alpine-2.23.2.orig/alpine/adrbkcmd.c
--- alpine-2.23.2/alpine/adrbkcmd.c
*************** ab_compose_internal(BuildTo bldto, int a
*** 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{
*************** ab_compose_internal(BuildTo bldto, int a
*** 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);
Index: alpine-2.23.2/alpine/alpine.c
===================================================================
*** alpine-2.23.2.orig/alpine/alpine.c
--- alpine-2.23.2/alpine/alpine.c
*************** main(int argc, char **argv)
*** 506,511 ****
--- 506,512 ----
/* 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){
*************** goodnight_gracey(struct pine *pine_state
*** 3274,3279 ****
--- 3275,3283 ----
extern KBESC_T *kbesc;
dprint((2, "goodnight_gracey:\n"));
+ strncpy(pine_state->cur_folder, pine_state->inbox_name,
+ sizeof(pine_state->cur_folder));
+ pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0';
/* We want to do this here before we close up the streams */
trim_remote_adrbks();
Index: alpine-2.23.2/alpine/confscroll.c
===================================================================
*** alpine-2.23.2.orig/alpine/confscroll.c
--- alpine-2.23.2/alpine/confscroll.c
*************** static char rcsid[] = "$Id: confscroll.c
*** 52,57 ****
--- 52,58 ----
#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")
*************** delete:
*** 2465,2470 ****
--- 2466,2474 ----
* 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);
*************** fix_side_effects(struct pine *ps, struct
*** 5262,5267 ****
--- 5266,5300 ----
var == &ps->vars[V_ABOOK_FORMATS]){
addrbook_reset();
}
+ else if(var == &ps->vars[V_INDEX_RULES]){
+ if(ps_global->rule_list)
+ free_parsed_rule_list(&ps_global->rule_list);
+ create_rule_list(ps->vars);
+ reset_index_format();
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if(var == &ps->vars[V_COMPOSE_RULES] ||
+ var == &ps->vars[V_FORWARD_RULES] ||
+ var == &ps->vars[V_KEY_RULES] ||
+ var == &ps->vars[V_REPLACE_RULES] ||
+ var == &ps->vars[V_REPLY_INDENT_RULES] ||
+ var == &ps->vars[V_REPLY_LEADIN_RULES] ||
+ var == &ps->vars[V_RESUB_RULES] ||
+ var == &ps->vars[V_SAVE_RULES] ||
+ var == &ps->vars[V_SMTP_RULES] ||
+ var == &ps->vars[V_SORT_RULES] ||
+ var == &ps->vars[V_STARTUP_RULES] ||
+ var == &ps->vars[V_THREAD_DISP_STYLE_RULES] ||
+ var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){
+ if(ps_global->rule_list)
+ free_parsed_rule_list(&ps_global->rule_list);
+ create_rule_list(ps->vars);
+ if(var == &ps->vars[V_REPLACE_RULES] ||
+ var == &ps->vars[V_RESUB_RULES]){
+ reset_index_format();
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ }
else if(var == &ps->vars[V_INDEX_FORMAT]){
reset_index_format();
clear_index_cache(ps->mail_stream, 0);
Index: alpine-2.23.2/alpine/dispfilt.c
===================================================================
*** alpine-2.23.2.orig/alpine/dispfilt.c
--- alpine-2.23.2/alpine/dispfilt.c
*************** df_valid_test(struct mail_bodystruct *bo
*** 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);
+ }
Index: alpine-2.23.2/alpine/dispfilt.h
===================================================================
*** alpine-2.23.2.orig/alpine/dispfilt.h
--- alpine-2.23.2/alpine/dispfilt.h
*************** char *dfilter_trigger(BODY *, char *, si
*** 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 */
Index: alpine-2.23.2/alpine/folder.c
===================================================================
*** alpine-2.23.2.orig/alpine/folder.c
--- alpine-2.23.2/alpine/folder.c
*************** folder_screen(struct pine *ps)
*** 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;
*************** folder_screen(struct pine *ps)
*** 345,350 ****
--- 345,351 ----
pine_mail_close(*fs.cache_streamp);
ps->prev_screen = folder_screen;
+ strcpy(ps->screen_name, "unknown");
}
Index: alpine-2.23.2/alpine/mailcmd.c
===================================================================
*** alpine-2.23.2.orig/alpine/mailcmd.c
--- alpine-2.23.2/alpine/mailcmd.c
*************** static char rcsid[] = "$Id: mailcmd.c 12
*** 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
*************** role_compose(struct pine *state)
*** 2721,2726 ****
--- 2722,2730 ----
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':
*************** save_prompt(struct pine *state, CONTEXT_
*** 2771,2782 ****
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;
--- 2775,2786 ----
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;
*************** save_prompt(struct pine *state, CONTEXT_
*** 2784,2789 ****
--- 2788,2794 ----
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");
*************** save_prompt(struct pine *state, CONTEXT_
*** 2793,2798 ****
--- 2798,2812 ----
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))
Index: alpine-2.23.2/alpine/mailindx.c
===================================================================
*** alpine-2.23.2.orig/alpine/mailindx.c
--- alpine-2.23.2/alpine/mailindx.c
*************** mail_index_screen(struct pine *state)
*** 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
*************** mail_index_screen(struct pine *state)
*** 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");
}
Index: alpine-2.23.2/alpine/mailpart.c
===================================================================
*** alpine-2.23.2.orig/alpine/mailpart.c
--- alpine-2.23.2/alpine/mailpart.c
*************** attachment_screen(struct pine *ps)
*** 182,188 ****
maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
last_type = TYPEOTHER;
long msgno;
! char *q, *last_subtype = NULL, backtag[64], *utf8str;
OtherMenu what;
ATTACH_S *a;
ATDISP_S *current = NULL, *ctmp = NULL;
--- 182,188 ----
maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
last_type = TYPEOTHER;
long msgno;
! char *q, *last_subtype = NULL, backtag[64], *utf8str, *screen_name;
OtherMenu what;
ATTACH_S *a;
ATDISP_S *current = NULL, *ctmp = NULL;
*************** attachment_screen(struct pine *ps)
*** 191,196 ****
--- 191,200 ----
ps->prev_screen = attachment_screen;
ps->next_screen = SCREEN_FUN_NULL;
+ screen_name = ps->screen_name[0] ? cpystr(ps->screen_name) : NULL;
+ strncpy(ps->screen_name, "attachment", sizeof(ps->screen_name));
+ ps->screen_name[sizeof(ps->screen_name)-1] = '\0';
+
ps->some_quoting_was_suppressed = 0;
if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
*************** attachment_screen(struct pine *ps)
*** 911,916 ****
--- 915,926 ----
if(screen.titlecolor)
free_color_pair(&screen.titlecolor);
+
+ if(screen_name){
+ strncpy(ps->screen_name, screen_name, sizeof(ps->screen_name));
+ ps->screen_name[sizeof(ps->screen_name)-1] = '\0';
+ fs_give((void **) &screen_name);
+ }
}
Index: alpine-2.23.2/alpine/mailview.c
===================================================================
*** alpine-2.23.2.orig/alpine/mailview.c
--- alpine-2.23.2/alpine/mailview.c
*************** mail_view_screen(struct pine *ps)
*** 252,257 ****
--- 252,259 ----
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"));
*************** mail_view_screen(struct pine *ps)
*** 499,504 ****
--- 501,508 ----
}
while(ps->next_screen == SCREEN_FUN_NULL);
+ strcpy(ps->screen_name, "unknown");
+
if (prefix && *prefix)
fs_give((void **)&prefix);
if(we_cancel)
Index: alpine-2.23.2/alpine/osdep/termin.gen.c
===================================================================
*** alpine-2.23.2.orig/alpine/osdep/termin.gen.c
--- alpine-2.23.2/alpine/osdep/termin.gen.c
*************** static char rcsid[] = "$Id: termin.gen.c
*** 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"
*************** int pcpine_oe_cursor(int, long);
*** 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
*************** read_command(char **utf8str)
*** 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();
*************** process_config_input(UCS *ch)
*** 1158,1163 ****
--- 1196,1202 ----
if(ps_global->initial_cmds && !*ps_global->initial_cmds && ps_global->free_initial_cmds){
fs_give((void **) &ps_global->free_initial_cmds);
ps_global->initial_cmds = NULL;
+ firsttime = (char) 1;
}
return(ret);
Index: alpine-2.23.2/alpine/reply.c
===================================================================
*** alpine-2.23.2.orig/alpine/reply.c
--- alpine-2.23.2/alpine/reply.c
*************** The evolution continues...
*** 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
*************** reply(struct pine *pine_state, ACTION_S
*** 110,120 ****
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
--- 111,122 ----
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
*************** reply(struct pine *pine_state, ACTION_S
*** 140,145 ****
--- 142,210 ----
&& 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...
*************** reply(struct pine *pine_state, ACTION_S
*** 288,295 ****
outgoing->subject = cpystr("Re: several messages");
}
}
! else
! outgoing->subject = reply_subject(env->subject, NULL, 0);
}
/* fill reply header */
--- 353,370 ----
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 */
*************** reply(struct pine *pine_state, ACTION_S
*** 308,320 ****
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 */
--- 383,389 ----
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 */
*************** reply(struct pine *pine_state, ACTION_S
*** 725,730 ****
--- 794,802 ----
if(prefix)
fs_give((void **)&prefix);
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+
if(fcc)
fs_give((void **) &fcc);
*************** forward(struct pine *ps, ACTION_S *role_
*** 1598,1606 ****
}
}
! if(role)
q_status_message1(SM_ORDER, 3, 4,
_("Forwarding using role \"%s\""), role->nick);
outgoing->message_id = generate_message_id(role);
--- 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);
+ }
outgoing->message_id = generate_message_id(role);
*************** forward(struct pine *ps, ACTION_S *role_
*** 1834,1839 ****
--- 1911,1917 ----
#if defined(DOS) && !defined(_WINDOWS)
free((void *)reserve);
#endif
+ outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL;
pine_send(outgoing, &body, "FORWARD MESSAGE",
role, NULL, &reply, redraft_pos,
NULL, NULL, 0);
Index: alpine-2.23.2/alpine/roleconf.c
===================================================================
*** alpine-2.23.2.orig/alpine/roleconf.c
--- alpine-2.23.2/alpine/roleconf.c
*************** role_text_tool_inick(struct pine *ps, in
*** 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));
Index: alpine-2.23.2/alpine/send.c
===================================================================
*** alpine-2.23.2.orig/alpine/send.c
--- alpine-2.23.2/alpine/send.c
*************** static char rcsid[] = "$Id: send.c 1142
*** 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;
*************** alt_compose_screen(struct pine *pine_sta
*** 236,241 ****
--- 236,246 ----
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);
*************** compose_mail(char *given_to, char *fcc_a
*** 445,452 ****
ps_global->next_screen = prev_screen;
ps_global->redrawer = redraw;
! if(role)
role = combine_inherited_role(role);
}
break;
--- 450,461 ----
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;
*************** compose_mail(char *given_to, char *fcc_a
*** 640,648 ****
}
}
! if(role)
q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
role->nick);
/*
* set ps_global->hostname to something sensible, if possible,
--- 649,662 ----
}
}
! 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);
+ }
/*
* set ps_global->hostname to something sensible, if possible,
*************** pine_send(ENVELOPE *outgoing, struct mai
*** 2504,2509 ****
--- 2518,2543 ----
removing_trailing_white_space(pf->textbuf);
(void)removing_double_quotes(pf->textbuf);
build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ if (!strncmp(pf->name,"Lcc",3) && addr && *addr){
+ RULE_RESULT *rule;
+
+ outgoing->date = (unsigned char *) cpystr(addr);
+ ps_global->procid = cpystr("fwd-lcc");
+ rule = get_result_rule(V_FORWARD_RULES,
+ FOR_COMPOSE|FOR_TRIM, outgoing);
+ if (rule){
+ addr = cpystr(rule->result);
+ removing_trailing_white_space(addr);
+ (void)removing_extra_stuff(addr);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ fs_give((void **)&ps_global->procid);
+ if (outgoing->date)
+ fs_give((void **)&outgoing->date);
+ }
+
rfc822_parse_adrlist(pf->addr, addr,
ps_global->maildomain);
fs_give((void **)&addr);
Index: alpine-2.23.2/pith/Makefile.am
===================================================================
*** alpine-2.23.2.orig/pith/Makefile.am
--- alpine-2.23.2/pith/Makefile.am
*************** libpith_a_SOURCES = ablookup.c abdlc.c a
*** 26,32 ****
filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
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 ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
! readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \
state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
Index: alpine-2.23.2/pith/Makefile.in
===================================================================
*** alpine-2.23.2.orig/pith/Makefile.in
--- alpine-2.23.2/pith/Makefile.in
*************** am_libpith_a_OBJECTS = ablookup.$(OBJEXT
*** 142,148 ****
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) \
--- 142,148 ----
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) \
*************** libpith_a_SOURCES = ablookup.c abdlc.c a
*** 441,447 ****
filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
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
--- 441,447 ----
filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
! readfile.c remote.c reply.c rfc2231.c 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
*************** distclean-compile:
*** 575,580 ****
--- 575,581 ----
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
Index: alpine-2.23.2/pith/adrbklib.c
===================================================================
*** alpine-2.23.2.orig/pith/adrbklib.c
--- alpine-2.23.2/pith/adrbklib.c
*************** init_addrbooks(OpenStatus want_status, i
*** 5138,5145 ****
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;
--- 5138,5151 ----
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;
Index: alpine-2.23.2/pith/conf.c
===================================================================
*** alpine-2.23.2.orig/pith/conf.c
--- alpine-2.23.2/pith/conf.c
*************** static char rcsid[] = "$Id: conf.c 1266
*** 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"
*************** CONF_TXT_T cf_text_unk_character_set[] =
*** 225,230 ****
--- 226,261 ----
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
*************** static struct variable variables[] = {
*** 570,575 ****
--- 601,634 ----
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},
*************** init_vars(struct pine *ps, void (*cmds_f
*** 2718,2723 ****
--- 2777,2783 ----
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 */
*************** feature_list(int index)
*** 3174,3179 ****
--- 3234,3241 ----
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,
*************** config_help(int var, int feature)
*** 7842,7847 ****
--- 7904,7937 ----
return(h_config_ab_sort_rule);
case V_FLD_SORT_RULE :
return(h_config_fld_sort_rule);
+ case V_THREAD_DISP_STYLE_RULES:
+ return(h_config_thread_display_style_rule);
+ case V_THREAD_INDEX_STYLE_RULES:
+ return(h_config_thread_index_style_rule);
+ case V_COMPOSE_RULES:
+ return(h_config_compose_rules);
+ case V_FORWARD_RULES:
+ return(h_config_forward_rules);
+ case V_INDEX_RULES:
+ return(h_config_index_rules);
+ case V_KEY_RULES:
+ return(h_config_key_macro_rules);
+ case V_REPLACE_RULES:
+ return(h_config_replace_rules);
+ case V_REPLY_INDENT_RULES:
+ return(h_config_reply_indent_rules);
+ case V_REPLY_LEADIN_RULES:
+ return(h_config_reply_leadin_rules);
+ case V_RESUB_RULES:
+ return(h_config_resub_rules);
+ case V_SAVE_RULES:
+ return(h_config_save_rules);
+ case V_SMTP_RULES:
+ return(h_config_smtp_rules);
+ case V_SORT_RULES:
+ return(h_config_sort_rules);
+ case V_STARTUP_RULES:
+ return(h_config_startup_rules);
case V_POST_CHAR_SET :
return(h_config_post_char_set);
case V_UNK_CHAR_SET :
Index: alpine-2.23.2/pith/conf.h
===================================================================
*** alpine-2.23.2.orig/pith/conf.h
--- alpine-2.23.2/pith/conf.h
***************
*** 157,162 ****
--- 157,202 ----
#define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p
#define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p
#define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p
+ #define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l
+ #define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l
+ #define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l
+ #define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l
+ #define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l
+ #define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l
+ #define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l
+ #define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l
+ #define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l
+ #define VAR_KEY_RULES vars[V_KEY_RULES].current_val.l
+ #define GLO_KEY_RULES vars[V_KEY_RULES].global_val.l
+ #define USR_KEY_RULES vars[V_KEY_RULES].user_val.l
+ #define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l
+ #define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l
+ #define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l
+ #define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l
+ #define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l
+ #define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l
+ #define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l
+ #define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l
+ #define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l
+ #define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l
+ #define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l
+ #define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l
+ #define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l
+ #define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l
+ #define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l
+ #define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l
+ #define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l
+ #define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l
+ #define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l
+ #define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l
+ #define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l
+ #define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l
+ #define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l
+ #define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l
+ #define USR_SORT_RULES vars[V_SORT_RULES].user_val.l
+ #define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l
+ #define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l
+ #define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l
#ifndef _WINDOWS
#define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p
#define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p
Index: alpine-2.23.2/pith/conftype.h
===================================================================
*** alpine-2.23.2.orig/pith/conftype.h
--- alpine-2.23.2/pith/conftype.h
*************** typedef enum { V_PERSONAL_NAME = 0
*** 71,76 ****
--- 71,90 ----
, 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
*************** typedef enum {
*** 348,353 ****
--- 362,368 ----
F_FULL_AUTO_EXPUNGE,
F_EXPUNGE_MANUALLY,
F_AUTO_READ_MSGS,
+ F_AUTO_READ_MSGS_RULES,
F_AUTO_FCC_ONLY,
F_READ_IN_NEWSRC_ORDER,
F_SELECT_WO_CONFIRM,
Index: alpine-2.23.2/pith/detoken.c
===================================================================
*** alpine-2.23.2.orig/pith/detoken.c
--- alpine-2.23.2/pith/detoken.c
*************** static char rcsid[] = "$Id: detoken.c 76
*** 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
*************** detoken(ACTION_S *role, ENVELOPE *env, i
*** 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;
*************** detoken(ACTION_S *role, ENVELOPE *env, i
*** 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;
*************** top:
*** 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);
Index: alpine-2.23.2/pith/indxtype.h
===================================================================
*** alpine-2.23.2.orig/pith/indxtype.h
--- alpine-2.23.2/pith/indxtype.h
*************** typedef enum {iNothing, iStatus, iFStatu
*** 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,
*************** typedef struct index_parse_tokens {
*** 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"
Index: alpine-2.23.2/pith/mailcmd.c
===================================================================
*** alpine-2.23.2.orig/pith/mailcmd.c
--- alpine-2.23.2/pith/mailcmd.c
*************** static char rcsid[] = "$Id: mailcmd.c 11
*** 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"
*************** do_broach_folder(char *newfolder, CONTEX
*** 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! */
*************** do_broach_folder(char *newfolder, CONTEX
*** 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
*************** do_broach_folder(char *newfolder, CONTEX
*** 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)
*************** do_broach_folder(char *newfolder, CONTEX
*** 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)){
/*
*************** expunge_and_close(MAILSTREAM *stream, ch
*** 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 */
*************** move_read_msgs(MAILSTREAM *stream, char
*** 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;
*************** move_read_msgs(MAILSTREAM *stream, char
*** 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';
*************** move_read_incoming(MAILSTREAM *stream, C
*** 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;
*************** get_uname(char *mailbox, char *target, i
*** 2761,2763 ****
--- 2658,2952 ----
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;
+
+ }
+ }
Index: alpine-2.23.2/pith/mailcmd.h
===================================================================
*** alpine-2.23.2.orig/pith/mailcmd.h
--- alpine-2.23.2/pith/mailcmd.h
***************
*** 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
*************** int do_broach_folder(char *, CONTEXT_
*** 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 *);
Index: alpine-2.23.2/pith/mailindx.c
===================================================================
*** alpine-2.23.2.orig/pith/mailindx.c
--- alpine-2.23.2/pith/mailindx.c
*************** static char rcsid[] = "$Id: mailindx.c 1
*** 41,46 ****
--- 41,47 ----
#include "../pith/send.h"
#include "../pith/options.h"
#include "../pith/ablookup.h"
+ #include "../pith/rules.h"
#ifdef _WINDOWS
#include "../pico/osdep/mswin.h"
#endif
*************** reset_index_format(void)
*** 379,384 ****
--- 380,392 ----
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)){
*************** free_hdrtok(HEADER_TOK_S **hdrtok)
*** 452,458 ****
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},
--- 460,466 ----
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},
*************** static INDEX_PARSE_T itokens[] = {
*** 460,466 ****
{"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},
--- 468,474 ----
{"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},
*************** static INDEX_PARSE_T itokens[] = {
*** 472,527 ****
{"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},
--- 480,539 ----
{"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},
*************** static INDEX_PARSE_T itokens[] = {
*** 530,585 ****
{"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}
};
--- 542,612 ----
{"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}
};
*************** format_index_index_line(INDEXDATA_S *ida
*** 2486,2491 ****
--- 2513,2536 ----
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"
*************** try_again:
*** 3880,3886 ****
--- 3925,3941 ----
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));
*************** subj_str(INDEXDATA_S *idata, char *str,
*** 5459,5468 ****
{
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;
--- 5514,5523 ----
{
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;
*************** subj_str(INDEXDATA_S *idata, char *str,
*** 5518,5523 ****
--- 5573,5586 ----
* 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);
*************** subj_str(INDEXDATA_S *idata, char *str,
*** 5956,5961 ****
--- 6019,6027 ----
if(free_subj)
fs_give((void **) &free_subj);
+
+ if (we_clear && origsubj)
+ fs_give((void **)&origsubj);
}
*************** from_str(IndexColType ctype, INDEXDATA_S
*** 6321,6336 ****
? "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;
}
--- 6387,6419 ----
? "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;
}
*************** from_str(IndexColType ctype, INDEXDATA_S
*** 6343,6349 ****
break;
case iFrom:
! set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
break;
case iAddress:
--- 6426,6440 ----
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:
*************** set_print_format(IELEM_S *ielem, int wid
*** 6641,6643 ****
--- 6732,6795 ----
}
}
}
+
+ 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;
+ }
Index: alpine-2.23.2/pith/mailindx.h
===================================================================
*** alpine-2.23.2.orig/pith/mailindx.h
--- alpine-2.23.2/pith/mailindx.h
*************** extern void (*setup_header_widths)(MAIL
*** 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 *);
Index: alpine-2.23.2/pith/makefile.wnt
===================================================================
*** alpine-2.23.2.orig/pith/makefile.wnt
--- alpine-2.23.2/pith/makefile.wnt
*************** HFILES= ../include/system.h ../include/g
*** 46,52 ****
init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \
mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \
options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \
! rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h smime.h smkeys.h sort.h sorttype.h \
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
--- 46,53 ----
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 rulestype.h save.h savetype.h search.h send.h sequence.h signal.h \
! smime.h smkeys.h sort.h sorttype.h \
state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \
thread.h url.h user.h util.h
*************** OFILES= ablookup.obj abdlc.obj addrbook.
*** 56,62 ****
ical.obj imap.obj init.obj \
keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \
margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \
! readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj \
smime.obj smkeys.obj sort.obj state.obj status.obj store.obj stream.obj string.obj strlst.obj \
takeaddr.obj tempfile.obj text.obj thread.obj adjtime.obj url.obj util.obj
--- 57,63 ----
ical.obj imap.obj init.obj \
keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \
margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \
! readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj \
smime.obj smkeys.obj sort.obj state.obj status.obj store.obj stream.obj string.obj strlst.obj \
takeaddr.obj tempfile.obj text.obj thread.obj adjtime.obj url.obj util.obj
Index: alpine-2.23.2/pith/pine.hlp
===================================================================
*** alpine-2.23.2.orig/pith/pine.hlp
--- alpine-2.23.2/pith/pine.hlp
*************** There are also additional details on
*** 4433,4438 ****
--- 4433,4439 ----
<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>
*************** This set of special tokens may be used i
*** 19930,19935 ****
--- 19931,19937 ----
<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.
*************** and in the target of Filter Rules.
*** 19942,19948 ****
<P>
<P>
! <H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1>
<DL>
<DT>SUBJECT</DT>
--- 19944,19950 ----
<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>
*************** email address, never the personal name.
*** 19976,19981 ****
--- 19978,19999 ----
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
*************** are unavailable) of the persons specifie
*** 20023,20028 ****
--- 20041,20055 ----
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
*************** message's &quot;To:&quot; header field a
*** 20031,20036 ****
--- 20058,20071 ----
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
*************** This is an end of line marker.
*** 21163,21168 ****
--- 21198,21307 ----
</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>
*************** character sets Alpine knows about by usi
*** 24560,24565 ****
--- 24699,25620 ----
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+ ====== h_config_procid =====
+ <HTML>
+ <HEAD>
+ <TITLE>Token: PROCID</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>TOKEN: PROCID explained</H1>
+
+ <P>
+ The PROCID token is a way in which the user and the program can differentiate
+ between different parts of a program. It allows the user to tell the
+ program when to use a specific rule, and only use it at that specific
+ moment.
+
+ <P> The normal way in which this is done is by adding a new configuration
+ variable. The idea behind the PROCID token is that instead of adding a new
+ configuration variable (which means the user has to go through more
+ configuration variables just to tune the program to his liking), we reuse
+ an old variable and let the user look inside that variable for the desired
+ behavior, which is actually set by setting the PROCID token.
+
+ <P>
+ Consider the following examples for forward-rules:
+
+ <P>
+ _ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+ <P>
+ and
+
+ <P>
+ _ROLE_ == {work} =&gt; _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+ <P>
+ both are triggered by the same condition. Since both are configured in the
+ same variable, only one of them will be executed all the time (whichever
+ is first). Therefore in order to differentiate, we add a _PROCID_ token.
+ So, for example, the first example above will be executed only when we are
+ determining the subject. In this case, the following rule will accomplish
+ this task
+
+ <P>
+ _PROCID_ == {fwd-subject} && _ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+ <P>
+ In this case, this rule will be tested fully only when we are determining
+ the subject line of a forwarded message, not otherwise.
+
+ <P>
+ It is wise to add the _PROCID_ token as the first condition in a rule, so
+ that other conditions will not be tested in a long list of rules.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_compose_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_compose-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_compose-rules"--></H1>
+
+ <P> At this time, this option is used to generate values for signature
+ files that is not possible to do with the use of
+ <A HREF="h_rules_roles">roles</A>.
+
+ <P> For example, you can have a rule like:<BR>
+ _TO_ >> {Peter Flintstones} => _SIGNATURE_{~/.petersignature}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_forward_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_forward-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_forward-rules"--></H1>
+
+ <P> This option has several uses. This feature uses the PROCID function
+ to identify different features of forwarding. You can read more about PROCID
+ by following <A HREF="h_config_procid">this link</A>.
+
+ <P> If you want to edit the subject of a forwarded message, use the
+ PROCID <I>fwd-subject</I>. For example you could have a rule like
+
+ <P>
+ _ROLE_ == {admin} && _SUBJECT_ !&gt; {[tag] } =&gt; _COPY_{[tag] _SUBJECT_}
+
+ <P> Another way in which this option can be used, is to trim the values of
+ some fields. For this application the PROCID is <I>fwd-lcc</I>. For
+ example it can be used in the following way:
+
+ <P>
+ _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+ <P> Other functions that can be used in this option are _EXEC_, _REXTRIM_ and
+ _REXSUB_.
+
+ <P> You can also use the _EXEC_ function. The documentation for this function
+ is in the
+ <A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+ help text.
+
+ <P> Another function that can be used is the _REXSUB_ function which does
+ a substitution. This function takes three parameters: a pattern to search
+ for, the text to be substituted for when the pattern is matched, and the
+ number of times that the pattern will be replaced. Each of the parameters
+ is enclosed between &quot;{&quot; and &quot;}&quot;. For example, to
+ delete only one ocurrence of the string &quot;Re: &quot; in a subject we
+ would write a rule such as
+
+ <P>
+ _FOLDER_ >> {} => _SUBJECT_ := _REXSUB_{Re: }{}{1}
+
+ <P> The last parameter of the rexsub function is optional. In the sense
+ that its omission is understood as if the third parameter was
+ &quot;{1}&quot;. In order to make unlimited substitutions, use
+ &quot;{g}&quot; as the last parameter.
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_index_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_index-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_index-rules"--></H1>
+
+ <P> This option is used to supersede the value of the option <A
+ HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> for specific folders. In
+ this form you can have different index-formats for different folders. For
+ example an entry here may be:
+
+ <P>
+ _FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_pretty_command =====
+ <HTML>
+ <HEAD>
+ <TITLE>Pretty-Command Explained</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>Pretty Command Explained</H1>
+
+ <P> This text explains how to encode keys so that they will be recognized
+ by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the
+ same way. For example, the key ~ is recognized by the same character. The
+ issue is how control, or functions keys are recognized. The internal code
+ is most times easy to find out. If the key you want to use is not already
+ recognized by Alpine simply press it. Alpine will print its code. For example,
+ the return key is not recognized in this screen, so if you press it, you
+ will see the following message.
+
+ <P> [Command &quot;RETURN&quot; not defined for this screen. Use ? for help]
+
+ <P> from here you can guess that the code for the return command is
+ RETURN. You can try other commands, like Control-C, the TAB key, F4, etc.
+ to see their codes.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_key_macro_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_key-definition-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_key-definition-rules"--></H1>
+
+ <P> This option can be used to define macros, that is, to define a key that
+ when pressed executes a group of predetermined keystrokes. Since Alpine is
+ a menu driven program, sometimes the same key may have different meanings
+ in different screens, so a global redefinition of a key although possible
+ is not advisable.
+
+ <P> <B>Always use the _SCREEN_ token as defined below.</B>. You have been
+ warned!
+
+ <P> In each screen, every time you press a recognized key, a command is
+ activated. In order to understand this feature, think of commands instead
+ of keystrokes. For example, you can think of the sort by thread command.
+ This command is associated to the keystrokes $ and h. You may want to
+ associate this command to a specific keystroke, like ~, so every time you
+ press the ~ key, Alpine understand the $ and h keystrokes, which activates
+ the sort by thread command.
+
+ <P> Therefore, in order to use this option you must think of three
+ components. The screen where you will use the macro, the keystroke you
+ want to use and the set of keystrokes used by Alpine to accomplish the task
+ you want to accomplish. We will talk about these three components in what
+ follows.
+
+ <P> First you must decide in which screen the macro will be used. This
+ feature is currently only available for the screen where your messages are
+ listed in index form (<A HREF="h_mail_index">MESSAGE INDEX</A>), the
+ screen where your message is displayed (<A HREF="h_mail_view">MESSAGE
+ TEXT</A>) the screen where the list of folders is displayed (<A
+ HREF="h_folder_maint">FOLDER LIST</A>) and the attachment index screen (<A
+ HREF="h_attachment_screen">ATTACHMENT INDEX</A>). The internal names of
+ these screens for this patch are &quot;index&quot;, &quot;text&quot;,
+ &quot;folder&quot;, and &quot;attachment&quot;, respectively. Please note
+ that the internal names are all in lowercase and are case sensitive.
+
+ <P> In order to define the screen, you use the _SCREEN_ token, so for
+ example, you can write _SCREEN_ == {index}.
+
+ <P> Second you must think of which key you will use to activate the macro.
+ Here you can use any key of your choice. The token you use to designate a
+ key is the _PKEY_ token (PKEY stands for &quot;pressed key&quot;). For
+ example you could use _PKEY_ == {~}, to designate the &quot;~&quot;
+ key to activate the command. Some keystrokes (like control, or
+ function keys) are encoded in special ways. You should read the
+ <A HREF="h_config_pretty_command">full explanation</A> on how to find
+ out the encoding for each keystroke.
+
+ <P> Last, you must think of the list of keys you will use to accomplish
+ the task you want Alpine to perform. Say for example you want to have the
+ folder sorted by thread. That means you want Aline to execute the keys
+ &quot;$&quot; and &quot;h&quot;. You use the _COMMAND_ function to specify
+ this. The syntax in this case is _COMMAND_{$,h}.
+
+ <P> Observe that in the above example the different inputs are separated
+ by commas. This is the standard way in which the
+ <A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A> command works from
+ the command line. Due to restrictions in the way Alpine works, a comma is a
+ special character, which when added to a configuration option like this
+ will cause the configuration to split into several lines in the
+ configuration screen. This has the effect of producing several
+ configuration options, all of which are incorrect. This is undesirable
+ because what you want is to have it all in one line. In order to force the
+ configuration into one line you must quote the comma. The best way to
+ accomplish this is by quoting the full definition of the rule. For
+ example.
+
+ <P>
+ &quot;_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}&quot;
+
+ <P> Another way to accomplish the same effect is by quoting the command and
+ not using quotes for the full command, nor commas to separate the
+ keystrokes in the command, for example
+
+ <P>
+ _SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{&quot;$h&quot;}
+
+ <P> For more information on how to define the argument of the _COMMAND_
+ token see the help of
+ <A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A>.
+
+ <P> Because the $ command can also be used as the first character in the
+ definition of an environemnt variable, no expansion of environment variables
+ is done when parsing this variable. The $ character does not need quoting
+ and quoting it will make Alpine fail to produce the correct result.
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_replace_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_replace-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_replace-rules"--></H1>
+
+ <P> This option is used to have Alpine print different values for specific
+ tokens in the <A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>. For example you
+ can replace strings like "To: newsgroup" by your name.
+
+ <P> Here are examples of possible rules:
+
+ <P>_FOLDER_ != {sent-mail} && _NICK_ != {} =&gt; _FROM_ := _REPLACE_{_FROM_ (_NICK_)}
+
+ <P> or if you receive messages with tags that contain arbitrary numbers, and
+ you want them removed from the index (but not from the subject), use a rule
+ like the following
+
+ <P>_FOLDER_ == {INBOX} =&gt; _SUBJECT_ := _REXTRIM_{&#92;&#91;some-tag-here &#35;&#91;0-9&#93;.*&#92;&#93;}
+
+ <P> You can also use this configuration option to remove specific strings of
+ the index display screen, so that you can trim unnecessary information in
+ your index, like the reply leadin string in the OPENINGTEXTNQ token of the index.<BR>
+
+ <P>_FOLDER_ == {some-folder} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: }
+
+ <P> You can also use the _EXEC_ function. The documentation for this function
+ is in the
+ <A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+ help text.
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_reply_leadin_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></H1>
+
+ <P> This option is used to have Alpine generate a different
+ <A HREF="h_config_reply_intro"><!--#echo var="VAR_reply-leadin"--></A> string dependent either on
+ the person you are replying to, or the folder where the message is being
+ replied is in, or both.
+
+ <P> Here there are examples of how this can be used. One can use the definition
+ below to post to newsgroups and the pine-info mailing list, say:
+ <P>
+ _FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):}
+
+ <P> Here there is an example that one can use to change the reply indent string
+ to reply people that speak spanish.
+ <P>
+ _FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribi&oacute; _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_resub_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_reply-subject-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_reply-subject-rules"--></H1>
+
+ <P> This option is used to have Alpine generate a different subject when
+ replying rather than the one Alpine would generate automatically.
+
+ <P> Here there are a couple of examples about how to use this
+ configuration option:
+
+ <P> In order to have messages with empty subject to be replied with the message
+ "your message" use the rule<BR>
+ <center>_SUBJECT_ == {} => _RESUB_{Re: your message}</center>
+
+ <P> If you want to trim some parts of the subject when you reply use the
+ rule<BR>
+ <center>_SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}</center>
+
+ <P>this rule removes the brackets "[" and "]" whenever the string "[one]"
+ appears in it, it also removes the word "two" from it.
+
+ <P>Another example where you may want to use this rule is when you
+ correspond with people that change the reply string from &quot;Re:&quot;
+ to &quot;AW:&quot; or &quot;Sv:&quot;. In this case a rule like<BR>
+ <center>_SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }</center>
+ <P>
+ would eliminate undesired strings in replies.
+
+ <P> Another interesting use of this option is the use of the _EXEC_ function.
+ This function takes as an argument a program or a script. This program
+ must take as the input a file, and write its output to that file. For example,
+ below is a sample of a script that removes the letter &quot;a&quot; of a file.
+
+ <PRE>
+ #!/bin/sh
+ sed 's/a//g' $1 > /tmp/mytest
+ mv /tmp/mytest $1
+ </PRE>
+
+ <P>
+ As you can see this script took &quot;$1&quot; as input file, the sed program
+ wrote its output to /tmp/mytest, and then the move program moved the file
+ /tmp/mytest to the input file &quot;$1&quot;. This is the kind of behavior
+ that your program is expected to have.
+
+ <P>
+ The content of the input file (&quot;$1&quot; above) is the value of a token
+ like _SUBJECT_. In order to indicate this, we use the notation
+
+ <P>
+ _SUBJECT_ := _EXEC_{/path/to/script}
+
+ <P> for the action. So for example
+
+ <P>
+ _FOLDER_ := {sent-mail} =&gt; _SUBJECT_ := _EXEC_{/path/to/script}
+
+ <P> is a valid rule.
+
+ <P> You can also use this configuration option to customize reply subjects
+ according to the sender of the message.
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_sort_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_sort-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_sort-rules"--></H1>
+
+ <P> This option is used to have Alpine sort different folders in different orders
+ and thus override the value already set in the
+ <A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A> configuration option.
+
+ <P> Here's an example of the way it can be used. In this case all incoming
+ folders are mailing lists, except for INBOX, so we sort INBOX by arrival
+ (which is the default type of sort), but we want all the rest of mailing
+ lists and newsgroups to be sorted by thread.
+
+ <P>
+ _COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread}
+
+ <P> Another example could be<BR>
+ _FOLDER_ == {Mailing List} => _SORT_{Reverse tHread}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+
+ </BODY>
+ </HTML>
+ ====== h_config_save_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_save-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_save-rules"--></H1>
+
+ <P> This option is used to specify which folder should be used to save a
+ message depending either on the folder the message is in, who the message
+ is from, or text that the message contains in specific headers (Cc:,
+ Subject:, etc).
+
+ <P> If this option is set and the
+ <A HREF="h_config_auto_read_msgs"><!--#echo var="FEAT_auto-move-read-msgs"--></A> configuration
+ option is also enabled then these definitions will be used to move messages
+ from your INBOX when exiting Alpine.
+
+ <P>Here there are some examples<BR>
+ _FLAG_ >> {D} -> Trash<BR>
+ _FROM_ == {U2} -> Bono<BR>
+ _FOLDER_ == {comp.mail.pine} -> pine-stuff<BR>
+ _NICK_ != {} -> _NICK_/_NICK_<BR>
+ _DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+
+ </BODY>
+ </HTML>
+ ====== h_config_reply_indent_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_reply-indent-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_reply-indent-rules"--></H1>
+
+ <P> This option is used to specify which reply-indent-string is to be used
+ when replying to an e-mail. If none of the rules are successful, the result in
+ the variable <a href="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></a>
+ is used.
+
+ <P> The associated function to this configuration option is called "RESTR" (for
+ REply STRing). Some examples of its use are:<BR>
+ _FROM_ == {Your Boss} => _RESTR_{"> "}<BR>
+ _FROM_ == {My Wife} => _RESTR_{":* "}<BR>
+ _FROM_ == {Perter Flintstone;Wilma Flintstone} => _RESTR_{"_INIT_ > "}<BR>
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+
+ </BODY>
+ </HTML>
+ ====== h_config_smtp_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_smtp-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_smtp-rules"--></H1>
+
+ <P> This option is used to specify which SMTP server should be used when
+ sending a message, if this rule is not defined, or the execution of the rule
+ results in no server selected, then Alpine will look for
+ the value from the role that is being used to compose the message. If no smtp
+ server is defined in that role or you are not using a role, then Alpine will get
+ the name of the server from the
+ <A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A> configuration
+ option according to the rules used in that variable.
+
+ <P> The function associated to this configuration option is _SMTP_, an example
+ of the use of this function is<BR>
+ _ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+
+ </BODY>
+ </HTML>
+ ====== h_config_startup_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: <!--#echo var="VAR_startup-rules"--></TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: <!--#echo var="VAR_startup-rules"--></H1>
+
+ <P> This option is used when a folder is being opened. You can use it to specify its <A
+ HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A> and override
+ Alpine's global value set for all folders.
+
+ <P> An example of the usage of this option is:<BR>
+ _FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen}
+
+ <P> This configuration option is just one of many that allow you to
+ override the value of some global configurations within Alpine. There is a
+ help text explaining how to define all of them, which you can read by
+ following this <A HREF="h_config_new_rules">link</A>.
+
+ <P>&lt;End of help on this topic&gt;
+
+ </BODY>
+ </HTML>
+ ====== h_config_new_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>OPTION: New Rules Explained</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>OPTION: New Rules Explained</H1>
+
+ This is a quite powerful option. Here you can define rules that override
+ the values of any other option you have set in Alpine.
+
+ <P>
+ For example, you can set your folders to be sorted in a certain way when
+ you open them (say by Arrival). You may want, however, your newsgroups to
+ be sorted by thread. The set of &quot;rules&quot; options allows you to
+ configure this and many other options, including the index-format for
+ specific folders, the way the subject is displayed in the index screen or
+ the reply-leadin-string, to name a few.
+
+ <P>
+ Every rule has three parts: a condition, a separator and an action. The
+ action is what will happen if the condition of the rule is satisfied.
+
+ <P>
+ Here is an example:
+
+ <P>
+ _FROM_ == {Fred Flintstone} =&gt; _SAVE_{Fred}
+
+ <P>
+ Here the separator is "=&gt;". Whatever is to the left of the separator
+ is the condition (that is _FROM_ == {Fred Flintstone}) and to the right is
+ the action (_SAVE_{Fred}). The condition means that the rule will be
+ applied only if the message that you are reading is from &quot;Fred
+ Flintstone&quot;, and the action will be that you will be offered to save
+ it in the folder &quot;Fred&quot;, whenever you press the letter
+ &quot;S&quot; to save a message.
+
+ <P>
+ The separator is always &quot;=&gt;&quot;, with one exception to be seen
+ later. But for the most part this will be the only one you will ever need.
+
+ <P>
+ Now let us see how to do it. There are 14 functions already defined for
+ you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_,
+ _SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _REXSUB_, _THREADSTYLE and
+ _THREADINDEX_. The parameter of a function has to be enclosed between
+ &quot;{&quot; and &quot;}&quot;, so for example you can specify
+ _SAVE_{saved-messages} as a valid sentence.
+
+ <P>
+ Later in the document you will find examples. Here is a short
+ description of what each function does:
+
+ <P>
+ <UL>
+ <LI> _EXEC_ : This function takes as an argument a program. This program
+ gets as the input a file and must rewrite its output to that file, which
+ is then taken as the value to replace from the contents of that file. You
+ can use this function with
+ <A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>,
+ <A HREF="h_config_replace_rules"><!--#echo var="VAR_replace-rules"--></A> and
+ <A HREF="h_config_forward_rules"><!--#echo var="VAR_forward-rules"--></A>.
+ See the help of those options for examples of how to use this function
+ and configure these rules.
+ <BR>&nbsp;<BR>
+ <LI> _INDEX_ : This function takes as an argument an index-format, and
+ makes that the index-format for the specified folder.
+ <BR>&nbsp;<BR>
+ <LI> _REPLACE_ : This function replaces the subject/from of the given e-mail by
+ another subject/from only when displaying the index.
+ <BR>&nbsp;<BR>
+ <LI> _REPLY_ : This function takes as an argument a definition of a
+ reply-leadin-string and makes this the reply-leading-string of the
+ specified folder or person.
+ <BR>&nbsp;<BR>
+ <LI> _RESTR_ : This function takes as an argument the value of the
+ reply-indent-string to be used to answer the message being replied to.
+ <BR>&nbsp;<BR>
+ <LI> _RESUB_ : This function replaces the subject of the given e-mail by
+ another subject only when replying to a message.
+ <BR>&nbsp;<BR>
+ <LI> _SAVE_ : The save function takes as an argument the name of a
+ possibly non existing folder, whenever you want to save a message, that
+ folder will be offered for you to save.
+ <BR>&nbsp;<BR>
+ <LI> _SIGNATURE_ : This function takes as an argument a signature file and
+ uses that file as the signature for the message you are about to
+ compose/reply/forward.
+ <BR>&nbsp;<BR>
+ <LI> _SMTP_ : This function takes as an argument the definition of a
+ SMTP server.
+ <BR>&nbsp;<BR>
+ <LI> _SORT_ : This function takes as an argument a Sort Style, and sorts a
+ specified folder in that sort order.
+ <BR>&nbsp;<BR>
+ <LI> _TRIM_ : This function takes as an argument a list of strings that
+ you want removed from another string. At this time this only works for
+ _FROM_ and _SUBJECT_.
+ <BR>&nbsp;<BR>
+ <LI> _REXTRIM_ : Same as _TRIM_ but its argument is one and
+ only one extended regular expression.
+ <BR>&nbsp;<BR>
+ <LI> _REXSUB_ : This function takes three arguments. The structure
+ is _REXSUB_{pattern}{text}{number of times}. The &quot;pattern&quot;
+ to match is an extended regular expression. If the pattern is matched,
+ it will be replaced by &quot;text&quot; as many times as the &quot;number
+ of times&quot; argument specifies. If the last argument is
+ omitted (that is, there are only two arguments) then the last argument
+ is considered as &quot;{1}&quot;.
+ <BR>&nbsp;<BR>
+ <LI> _STARTUP_ : This function takes as an argument an
+ incoming-startup-rule, and open an specified folder using that rule.
+ <BR>&nbsp;<BR>
+ <LI> _THREADSTYLE_ : This function takes as an argument a
+ threading-display-style and uses it to display threads in a folder.
+ <BR>&nbsp;<BR>
+ <LI> _THREADINDEX_ : This function takes as an argument a
+ threading-index-style and uses it to display threads in a folder.
+ </UL>
+
+ <P>
+ You must me wondering how to define the person/folder over who to apply
+ the action. This is done in the condition. When you specify a rule, the
+ rule is only executed if the condition is satisfied. In another words for
+ the rule:
+
+ <P>
+ _FROM_ == {Fred Flintstone} =&gt; _SAVE_{Fred}
+
+ <P> it will only be applied if the from is &quot;Fred Flintstone&quot;. If
+ the From is &quot;Wilma Flintstone&quot; the rule will be skipped.
+
+ <P> In order to test a condition you can use the following tokens (in
+ alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE,
+ _SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what
+ it is between &quot;{&quot; and &quot;}&quot; in the condition, this part
+ of the condition is called the &quot;condition set&quot;. The definition
+ of each token can be found <A HREF="h_index_tokens">here</A>.
+
+ <P> A special testing token called _PROCID_ can be used to differentiate
+ inside a rule, between two rules that are triggered by the same condition.
+ A full explanation of the _PROCID_ token can be found in
+ <A HREF="h_config_procid">this link</A>.
+
+ <P> There are two more tokens related to the option
+ <A HREF="h_config_key_macro_rules">key-definition-rules</A>. Those tokens
+ are only specific to that option, and hence are not explained here.
+
+ <P> You can also test in different ways, you can use the following
+ &quot;test operands&quot;: &lt;&lt;, !&lt;, &gt;&gt;, !&gt;, == and !=.
+ All of them are two characters long. Here is the meaning of them:
+
+ <P>
+ <UL>
+ <LI> &lt;&lt; : It tests if the value of the token is contained in
+ the condition set. Here for example if the condition set were equal to
+ &quot;Freddy&quot;, then the condition: _NICK_ &lt;&lt; {Freddy}, would be true if
+ the value of _NICK_ were &quot;Fred&quot;, &quot;red&quot; or &quot;Freddy&quot;. You are just looking
+ for substrings here.
+ <LI> &gt;&gt; : It tests if the value of the token contains the value of
+ the condition set. Here for example if the condittion set were equal to
+ &quot;Fred&quot;, then the condition: _FROM_ &gt;&gt; {Fred}, would be true if
+ the value of _FROM_ were &quot;Fred Flintstone&quot; or &quot;Fred P. Flintstone&quot; or &quot;Freddy&quot;.
+ <LI> == : It tests if the value of the token is exactly equal to the value
+ of the set condition. For example _NICK_ == {Fred} will be false if the value
+ of _NICK_ is &quot;Freddy&quot; or &quot;red&quot;.
+ <LI> !&lt; : This is true only when &lt;&lt; is false and vice versa.
+ <LI> !&gt; : This is true only when &gt;&gt; is false and vice versa.
+ <LI> != : This is true only when == is false and vice versa.
+ </UL>
+
+ <P>
+ Now let us say that you want the same action to be applied to more than
+ one person or folder, say you want &quot;folder1&quot; and &quot;folder2&quot; to be sorted by
+ Ordered Subject upon entering. Then you can list them all of them in the
+ condition part separting them by a &quot;;&quot;. Here is the way to do it.
+
+ <P>
+ _FOLDER_ &lt;&lt; {folder1; folder2} =&gt; _SORT_{OrderedSubj}
+
+ <P>
+ Here is the first subtlety about these definitions. Notice that the
+ following rule:
+
+ <P>
+ _FOLDER_ == {folder1; folder2} =&gt; _SORT_{Reverse OrderedSubj}
+
+ <P> works only for &quot;folder1&quot; but not for &quot;folder2&quot;. This is because the
+ comparison of the name of the folder is done with whatever is in between
+ &quot;{&quot;, &quot;;&quot; or &quot;}&quot;, so in the above rule you would be testing <BR>
+ &quot;folder2&quot; == &quot; folder2&quot;. The extra space makes the difference.
+ The reason why the first rule does not fail is because
+ &quot;folder2&quot; &lt;&lt; &quot; folder2&quot; is actually
+ true. If something ever fails this may be something to look into.
+
+ <P>
+ Here are a few examples of what we have talked about before.
+
+ <P>
+ _NICK_ == {lisa;kika} =&gt; _SAVE_{_NICK_/_NICK_} <BR>
+ This means that if the nick is lisa, it will
+ save the message in the folder &quot;lisa/lisa&quot;, and if the nick
+ is &quot;kika&quot;, it will save the message in the folder &quot;kika/kika&quot;
+
+ <P>
+ _FOLDER_ == {Lynx} -&gt; lynx <BR>
+ This, is an abbreviation of the following rule:<BR>
+ _FOLDER_ == {Lynx} =&gt; _SAVE_{lynx} <BR>
+ (note the change in separator from &quot;=&gt;&quot; to &quot;-&gt;&quot;). In the future
+ I will use that abbreviation.
+
+ <P> _FOLDER_ &lt;&lt; {comp.mail.pine; pine-info; pine-alpha} -&gt; pine <BR>
+ Any message in the folders &quot;comp.mail.pine&quot;, &quot;pine-info&quot; or &quot;pine-alpha&quot;
+ will be saved to the folder &quot;pine&quot;.
+
+ <P> _FROM_ &lt;&lt; {Pine Master} -&gt; pine <BR>
+ Any message whose From field contains
+ &quot;Pine Master&quot; will be saved in the folder pine.
+
+ <P> _FOLDER_ &lt;&lt; {Lynx; pine-info; comp.mail.pine} =&gt;
+ _INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)} <BR> Use a
+ different index-format for the folders &quot;Lynx&quot;, &quot;pine-info&quot; and
+ &quot;comp.mail.pine&quot;, where the size is not present.
+
+ <P> _FOLDER_ == {Lynx;pine-info} =&gt; _REPLY_{*** _FROM_ (_ADDRESS_)
+ wrote in the _FOLDER_ list _SMARTDATE_(&quot;Today&quot; &quot;today&quot; &quot;on
+ _LONGDATE_&quot;):}<BR> If a message is in one of the incoming folders &quot;Lynx&quot;
+ or &quot;pine-info&quot;, create a reply-leadin-string that acknowledges that. Note
+ the absence of &quot;,&quot; in the function _SMARTDATE_. For example answering to a
+ message in the pine-info list would look like:
+
+ <P>
+ *** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today:
+
+ <P>
+ However replying for a message in the Lynx list would look:
+
+ <P>
+ *** mattack@area.com (mattack@area.com) wrote in the Lynx list today:
+
+ <P>
+ If you write in more than one language you can use this feature to create
+ Reply-leadin-strings in different languages.
+
+ <P> Note that at least for people you can create particular
+ reply-leadin-string using the role features, but it does not work as this
+ one does. This seems to be the right way to do it.
+
+ <P> _FOLDER_ &lt;&lt; {Lynx; comp.mail.pine; pine_info; pine-alpha} =&gt;
+ _SORT_{OrderedSubj}<BR> This means upon opening, sort the folders &quot;Lynx&quot;,
+ &quot;comp.mail.pine&quot;, etc in ordered subject. All the others use the default
+ sort order. You can not sort in reverse in this form. The possible
+ arguments of this function are listed in the definition of the
+ default-sort-rule (Arrival, scorE, siZe, etc).
+
+ <P> The last examples use the function _TRIM_ which has a special form.
+ This function can only be used in the index list.
+
+ <P> _FOLDER_ &lt;&lt; {Lynx} =&gt; _SUBJECT_ := _TRIM_{lynx-dev }<BR> In
+ the folder &quot;Lynx&quot; eliminate from the subject the string &quot;lynx-dev &quot; (with
+ the space at the end). For example a message whose subject is &quot;Re:
+ lynx-dev unvisited Visited Links&quot;, would be shown in the index with
+ subject: &quot;Re: unvisited Visited Links&quot;, making the subject shorter and
+ giving the same information.
+
+ <P> _FROM_ &gt;&gt; {Name (Comment)} =&gt; _FROM_ :=
+ _TRIM_{ (Comment)}<BR> Remove the part &quot; (Comment)&quot;
+ from the _FROM_, so when displaying in the index the real From &quot;Name&quot;
+ will appear.
+
+ <P> _SUBJECT_ == {} =&gt; _RESUB_{Re: your mail without subject}
+ If there is no subject in the message, use the subject &quot;Re: your mail
+ wiyhout subject&quot; as a subject for the reply message.
+
+ <P> You can add more complexity to your rules by checking more than one
+ conditions before a rule is executed. More than one condition can be
+ checked by separating different conditions by the && (and) separator,
+ or using the || (or) separator. For example we could have a rule that
+ saves all
+ messages in inbox from Rubye, to the Personal folder, as
+
+ <P> _FOLDER_ == {INBOX} && _FROM_ >> {Rubye} => _SAVE_{Personal}
+
+ <P> We could also have a rule that is triggered by an &quot;or&quot;
+ condition by, sat for messages from Andres or messages in the index
+ to trigger a specific reply leadin string.
+
+ <P> _FOLDER_ == {INBOX} || _FROM_ >> {Andres} =&gt; _REPLY_{You wrote:}
+
+ <P>Observe that the construction
+
+ <P> _TOKEN_ == {value1} || _TOKEN_ == {value2}
+
+ <P>can be shortened to
+
+ <P> _TOKEN_ == {value1;value2}
+
+ <P> Round parentheses can be used to group some conditions, for example
+
+ <P> (_FROM_ &gt;&gt; {Andres} && _FOLDER_ == {INBOX}) || _FROM_ &gt;&gt; {Rubye}
+
+
+ <P> You can also list your index by nick, in the following way:<BR>
+ _NICK_ != {} => _FROM_ := _REPLACE_{_NICK_}
+
+ <P>
+ If you want to open the folder &quot;pine-info&quot; in the first non-read message
+ use the rule:<BR>
+ _FOLDER_ == {pine-info} => _STARTUP_{first-unseen}
+
+ <P>
+ If you want to move your deleted messages to a folder, called &quot;Trash&quot;, use
+ the following rule:<BR>
+ _FLAG_ >> {D} -> Trash
+
+ <P>
+ The reason why the above test is not &quot;_FLAG_ == {D}&quot; is because that would mean
+ that this is the only flag set in the message. It's better to test by containment in this case.
+
+ <P> If you want to use a specific signature when you are in a specific collection
+ use the following rule:<BR>
+ _COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature}
+
+ <P> Finally about the question of which rule will be executed. Only the
+ first rule that matches will be executed. It is important to notice though
+ that &quot;saving&quot; rules do not compete with &quot;sorting&quot; rules. So the first
+ &quot;saving&quot; rule that matches will be executed in the case of saving and so
+ on.
+
+ <P>
+ <UL>
+ <LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+ </UL><P>
+ &lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
====== h_config_char_set =====
<HTML>
<HEAD>
*************** the From field is used to show the relat
*** 28312,28317 ****
--- 29367,29442 ----
&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>
*************** automatically transfer all read messages
*** 32003,32008 ****
--- 33128,33156 ----
them as deleted in the INBOX. Messages in the INBOX marked with an
&quot;N&quot; (meaning New, or unseen) are not affected.
<P>
+ <UL>
+ <LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+ </UL><P>
+ &lt;End of help on this topic&gt;
+ </BODY>
+ </HTML>
+ ====== h_config_auto_read_msgs_rules =====
+ <HTML>
+ <HEAD>
+ <TITLE>FEATURE: auto-move-read-msgs-using-rules</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>FEATURE: auto-move-read-msgs-using-rules</H1>
+ This feature controls an aspect of Alpine's behavior upon quitting. If set,
+ and the
+ <A HREF="h_config_read_message_folder">&quot;<!--#echo var="VAR_read-message-folder"-->&quot;</A>
+ option is also set, then Alpine will automatically transfer all read
+ messages to the designated folder using the rules that you have defined in
+ your
+ <A HREF="h_config_save_rules">&quot;<!--#echo var="VAR_save-rules"-->&quot;</A> and mark
+ them as deleted in the INBOX. Messages in the INBOX marked with an
+ &quot;N&quot; (meaning New, or unseen) are not affected.
+ <P>
<UL>
<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
</UL><P>
Index: alpine-2.23.2/pith/reply.c
===================================================================
*** alpine-2.23.2.orig/pith/reply.c
--- alpine-2.23.2/pith/reply.c
*************** static char rcsid[] = "$Id: reply.c 1074
*** 47,52 ****
--- 47,54 ----
#include "../pith/mailcmd.h"
#include "../pith/margin.h"
#include "../pith/smime.h"
+ #include "../pith/copyaddr.h"
+ #include "../pith/rules.h"
/*
*************** char *
*** 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 */
*************** reply_quote_str(ENVELOPE *env)
*** 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)));
}
*************** get_addr_data(ENVELOPE *env, IndexColTyp
*** 1486,1491 ****
--- 1526,1535 ----
buf[0] = '\0';
switch(type){
+ case iFfrom:
+ addr = env && env->sparep ? env->sparep : NULL;
+ break;
+
case iFrom:
addr = env ? env->from : NULL;
break;
*************** get_reply_data(ENVELOPE *env, ACTION_S *
*** 1898,1919 ****
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);
--- 1942,2135 ----
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);
*************** get_reply_data(ENVELOPE *env, ACTION_S *
*** 1941,1946 ****
--- 2157,2167 ----
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);
*************** get_reply_data(ENVELOPE *env, ACTION_S *
*** 1990,1995 ****
--- 2211,2224 ----
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){
*************** reply_delimiter(ENVELOPE *env, ACTION_S
*** 2052,2058 ****
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)){
--- 2281,2298 ----
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)){
*************** forward_subject(ENVELOPE *env, int flags
*** 2311,2316 ****
--- 2551,2557 ----
{
size_t l;
char *p, buftmp[MAILTMPLEN];
+ RULE_RESULT *rule;
if(!env)
return(NULL);
*************** forward_subject(ENVELOPE *env, int flags
*** 2318,2326 ****
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)
--- 2559,2577 ----
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)
Index: alpine-2.23.2/pith/rules.c
===================================================================
*** /dev/null
--- alpine-2.23.2/pith/rules.c
***************
*** 0 ****
--- 1,1565 ----
+ /* This module was written by
+ *
+ * Eduardo Chappa (chappa@washington.edu)
+ * http://alpine.x10host.com/alpine/
+ *
+ * Original Version: November 1999
+ * Last Modified : November 24, 2018
+ *
+ * Send bug reports about this module to the address above.
+ */
+
+ #include "../pith/headers.h"
+ #include "../pith/state.h"
+ #include "../pith/conf.h"
+ #include "../pith/copyaddr.h"
+ #include "../pith/mailindx.h"
+ #include "../pith/rules.h"
+
+ #define CSEP_C ('\001')
+ #define CSEP_S ("\001")
+
+ /* Internal Prototypes */
+
+ int test_condition (CONDITION_S *, int, ENVELOPE *);
+ int test_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int test_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int test_not_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int test_not_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int test_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int test_not_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int);
+ int isolate_condition (char *, char **, int *);
+ int sanity_check_condition (char *);
+ char *test_rule (RULELIST *, int, ENVELOPE *, int *);
+ char *trim (RULEACTION_S *, int, ENVELOPE *);
+ char *rextrim (RULEACTION_S *, int, ENVELOPE *);
+ char *rexsub (RULEACTION_S *, int, ENVELOPE *);
+ char *do_rextrim (char *, TOKEN_VALUE *);
+ char *do_rexsub (char *, TOKEN_VALUE *);
+ char *raw_value (RULEACTION_S *, int, ENVELOPE *);
+ char *extended_value (RULEACTION_S *, int, ENVELOPE *);
+ char *exec_fcn (RULEACTION_S *, int, ENVELOPE *);
+ char *expand (char *, char *);
+ char *get_name_token (char *);
+ char *advance_to_char (char *, char, int, int *);
+ char **functions_for_token (char *);
+ char *canonicalize_condition (char *, int *);
+ void free_rexsub (REXSUB_S **);
+ void free_void_token_value (void **, int);
+ void free_token_value (TOKEN_VALUE **);
+ void free_condition (CONDITION_S **);
+ void free_condition_value (CONDVALUE_S **);
+ void free_ruleaction (RULEACTION_S **);
+ void free_rule (RULE_S **);
+ void free_rule_list (RULELIST **);
+ void *rule_alloc_mem (size_t);
+ void add_rule (int, int);
+ void set_rule_list (struct variable *);
+ void free_parsed_value(TOKEN_VALUE **value);
+ RULE_S *parse_rule (char *, int);
+ RULELIST *get_rule_list (char **, int, int);
+ TOKEN_VALUE *parse_group_data (char *,int *);
+ TOKEN_VALUE *copy_parsed_value (TOKEN_VALUE *, int, ENVELOPE *);
+ TOKEN_VALUE *parse_action_to_char(char *);
+ TOKEN_VALUE *parse_rexsub_action(char *);
+ CONDVALUE_S *fill_condition_value (char *);
+ CONDITION_S *fill_condition (char *);
+ CONDITION_S *parse_condition (char *, int *);
+ PRULELIST_S *add_prule (PRULELIST_S *, PRULELIST_S *);
+ RULEACTION_S *parse_action (char *, int);
+
+ REL_TOKEN rel_rules_test[] = {
+ {EQ_REL, Equal, test_eq},
+ {IN_REL, Subset, test_in},
+ {NI_REL, Includes, test_ni},
+ {NOT_EQ_REL, NotEqual, test_not_eq},
+ {NOT_IN_REL, NotSubset, test_not_in},
+ {NOT_NI_REL, NotIncludes, test_not_ni},
+ {NULL, EndTypes, NULL}
+ };
+
+ #define NREL (sizeof(rel_rules_test)/sizeof(rel_rules_test[0]) - 1)
+
+ RULE_FCN rule_fcns[] = {
+ {COPY_FCN, 6, 1, CHAR_TYPE, parse_action_to_char, extended_value, FOR_SAVE|FOR_COMPOSE},
+ {REXTRIM_FCN, 9, 1, CHAR_TYPE, parse_action_to_char, rextrim, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {REXSUB_FCN, 8, 1, REXSUB_TYPE, parse_rexsub_action, rexsub, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {EXEC_FCN, 6, 1, CHAR_TYPE, parse_action_to_char, exec_fcn, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {TRIM_FCN, 6, 1, CHAR_TYPE, parse_action_to_char, trim, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE},
+ {REPLACE_FCN, 7, 1, CHAR_TYPE, parse_action_to_char, extended_value, FOR_REPLACE},
+ {SAVE_FCN, 6, 0, UNDEFINED_TYPE, NULL, extended_value, FOR_SAVE},
+ {REPLY_FCN, 7, 0, UNDEFINED_TYPE, NULL, extended_value, FOR_REPLY_INTRO},
+ {SORT_FCN, 6, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_SORT},
+ {INDEX_FCN, 7, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_INDEX},
+ {COMMAND_FCN, 9, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_KEY},
+ {REPLYSTR_FCN, 10, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_COMPOSE},
+ {SIGNATURE_FCN,11, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_COMPOSE},
+ {RESUB_FCN, 7, 0, UNDEFINED_TYPE, NULL, extended_value, FOR_RESUB},
+ {STARTUP_FCN, 9, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_STARTUP},
+ {THRDSTYLE_FCN,11, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_THREAD},
+ {THRDINDEX_FCN,11, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_THREAD},
+ {SMTP_FCN, 6, 0, UNDEFINED_TYPE, NULL, raw_value, FOR_COMPOSE},
+ {NULL, 0, 0, UNDEFINED_TYPE, 0, 0, FOR_NOTHING}
+ };
+
+ char* token_rules[] = {
+ FROM_TOKEN,
+ NICK_TOKEN,
+ FCCF_TOKEN,
+ FCCS_TOKEN,
+ OTEXT_TOKEN,
+ OTEXTNQ_TOKEN,
+ ROLE_TOKEN,
+ FOLDER_TOKEN,
+ SUBJ_TOKEN,
+ PROCID_TOKEN,
+ THDDSPSTY_TOKEN,
+ THDNDXSTY_TOKEN,
+ FLAG_TOKEN,
+ COLLECT_TOKEN,
+ THDDSPSTY_TOKEN,
+ ADDR_TOKEN,
+ TO_TOKEN,
+ ADDTO_TOKEN,
+ ADDCC_TOKEN,
+ ADDRECIP_TOKEN,
+ SCREEN_TOKEN,
+ KEY_TOKEN,
+ SEND_TOKEN,
+ CC_TOKEN,
+ LCC_TOKEN,
+ BCC_TOKEN,
+ FFROM_TOKEN,
+ FADDRESS_TOKEN,
+ NULL
+ };
+
+ #define NTOKENS (sizeof(token_rules)/sizeof(token_rules[0]) - 1)
+ #define NFCN (sizeof(rule_fcns)/sizeof(rule_fcns[0]) - 1)
+
+ char *subj_fcn[] = {SUBJ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+ char *from_fcn[] = {FROM_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+ char *otext_fcn[] = {OTEXT_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+ char *otextnq_fcn[] = {OTEXTNQ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, REXSUB_FCN, EXEC_FCN};
+
+ char *adto_fcn[] = {ADDTO_TOKEN, EXEC_FCN, NULL, NULL, NULL};
+
+ char **fcns_for_index[] = {subj_fcn, from_fcn, otext_fcn, otextnq_fcn};
+
+ #define NFCNFI (sizeof(fcns_for_index)/sizeof(fcns_for_index[0])) /*for idx*/
+ #define NFPT (sizeof(fcns_for_index[0])) /* functions pert token */
+
+ SPAREP_S *
+ get_sparep_for_rule(char *value, int flag)
+ {
+ SPAREP_S *rv;
+ rv = (SPAREP_S *) rule_alloc_mem(sizeof(SPAREP_S));
+ rv->flag = flag;
+ rv->value = value ? cpystr(value) : NULL;
+ return rv;
+ }
+
+ void
+ free_sparep_for_rule(void **sparep)
+ {
+ SPAREP_S *spare = (SPAREP_S *) *sparep;
+ if(!spare) return;
+ if(spare->value)
+ fs_give((void **)&spare->value);
+ fs_give((void **)sparep);
+ }
+
+ int
+ context_for_function(char *name)
+ {
+ int i;
+ for (i = 0; i < NFCN && strcmp(rule_fcns[i].name, name); i++);
+ return i == NFCN ? 0 : rule_fcns[i].what_for;
+
+ }
+
+ char **
+ functions_for_token(char *name)
+ {
+ int i;
+ for (i = 0; i < NFCNFI && strcmp(fcns_for_index[i][0], name); i++);
+ return i == NFCNFI ? NULL : fcns_for_index[i];
+ }
+
+ void
+ free_rexsub (REXSUB_S **rexsub)
+ {
+ if(rexsub == NULL || *rexsub == NULL)
+ return;
+
+ if((*rexsub)->pattern) fs_give((void **) &(*rexsub)->pattern);
+ if((*rexsub)->text) fs_give((void **) &(*rexsub)->text);
+ fs_give((void **) rexsub);
+ }
+
+ void
+ free_void_token_value(void **voidptr, int voidtype)
+ {
+ switch(voidtype){
+ case CHAR_TYPE: fs_give(voidptr);
+ break;
+ case REXSUB_TYPE: free_rexsub ((REXSUB_S **) voidptr);
+ break;
+ default: break; /* do nothing */
+ }
+ }
+
+ void
+ free_token_value(TOKEN_VALUE **token)
+ {
+ if(token && *token){
+ if ((*token)->testxt)
+ fs_give((void **)&(*token)->testxt);
+ if ((*token)->voidtxt)
+ free_void_token_value((void **) &(*token)->voidtxt, (*token)->voidtype);
+ if((*token)->next)
+ free_token_value(&(*token)->next);
+ fs_give((void **)token);
+ }
+ }
+
+ void
+ free_condition_value(CONDVALUE_S **cvalue)
+ {
+ if(cvalue && *cvalue){
+ if ((*cvalue)->tname)
+ fs_give((void **)&(*cvalue)->tname);
+ if ((*cvalue)->value)
+ free_token_value(&(*cvalue)->value);
+ fs_give((void **)cvalue);
+ }
+ }
+
+ void
+ free_condition(CONDITION_S **condition)
+ {
+ if(condition && *condition){
+ if((*condition)->cndtype == Condition)
+ free_condition_value((CONDVALUE_S **)&(*condition)->cndrule);
+ else if((*condition)->cndtype == ParOpen || (*condition)->cndtype == ParClose)
+ fs_give(&(*condition)->cndrule);
+ if((*condition)->next)
+ free_condition(&(*condition)->next);
+ fs_give((void **)condition);
+ }
+ }
+
+ void
+ free_ruleaction(RULEACTION_S **raction)
+ {
+ if(raction && *raction){
+ if ((*raction)->token)
+ fs_give((void **)&(*raction)->token);
+ if ((*raction)->function)
+ fs_give((void **)&(*raction)->function);
+ if ((*raction)->value)
+ free_token_value(&(*raction)->value);
+ fs_give((void **)raction);
+ }
+ }
+
+ void
+ free_rule(RULE_S **rule)
+ {
+ if(rule && *rule){
+ free_condition(&(*rule)->condition);
+ free_ruleaction(&(*rule)->action);
+ fs_give((void **)rule);
+ }
+ }
+
+ void
+ free_rule_list(RULELIST **rule)
+ {
+ if(!*rule)
+ return;
+
+ if((*rule)->next)
+ free_rule_list(&(*rule)->next);
+
+ if((*rule)->prule)
+ free_rule(&(*rule)->prule);
+
+ fs_give((void **)rule);
+ }
+
+ void
+ free_parsed_rule_list(PRULELIST_S **rule)
+ {
+ if(!*rule)
+ return;
+
+ if((*rule)->next)
+ free_parsed_rule_list(&(*rule)->next);
+
+ if((*rule)->rlist)
+ free_rule_list(&(*rule)->rlist);
+
+ fs_give((void **)rule);
+ }
+
+ void *
+ rule_alloc_mem (size_t amount)
+ {
+ void *genmem;
+ memset(genmem = fs_get(amount), 0, amount);
+ return genmem;
+ }
+
+ int
+ isolate_condition (char *data, char **cvalue, int *len)
+ {
+ char *p = data;
+ int done = 0, error = 0, next_condition = 0, l;
+
+ if(*p == '"' && p[strlen(p) - 1] == '"'){
+ p[strlen(p) - 1] = '\0';
+ p++;
+ }
+ *cvalue = NULL;
+ while (*p && !done){
+ switch (*p){
+ case '_': *cvalue = advance_to_char(p,'}', STRICTLY, NULL);
+ if(*cvalue){
+ strcat(*cvalue,"}");
+ p += strlen(*cvalue);
+ }
+ else
+ error++;
+ done++;
+ case ' ': p++;
+ break;
+ case '&':
+ case '|': if (*(p+1) == *p){ /* looking for && or ||*/
+ p += 2;
+ next_condition++;
+ }
+ else{
+ error++;
+ done++;
+ }
+ break;
+ case '=': /* looking for => or -> */
+ case '-': if (*(p+1) != '>' || next_condition)
+ error++;
+ done++;
+ break;
+ default : done++;
+ error++;
+ break;
+ }
+ }
+ *len = p - data;
+ return error ? -1 : (*cvalue ? 1 : 0);
+ }
+
+ TOKEN_VALUE *
+ parse_group_data (char *data, int *error)
+ {
+ TOKEN_VALUE *rvalue;
+ char *p, *d;
+ int offset, err = 0, freeme = 0;
+
+ if(error)
+ *error = 0;
+
+ if (!data)
+ return (TOKEN_VALUE *) NULL;
+
+ if(*data == '_'){
+ d = detoken_src(data, FOR_RULE, NULL, NULL, NULL, NULL);
+ freeme++;
+ }
+ else
+ d = data;
+
+ rvalue = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+ if (p = advance_to_char(d,';', STRICTLY, &offset)){
+ 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 = rule_alloc_mem(sizeof(CONDVALUE_S));
+ condition->tname = cpystr(token_rules[--i]);
+ data += strlen(token_rules[i]);
+ }
+ else if (*data == '_') {
+ INDEX_PARSE_T *token;
+ char *itokname;
+ for (i = 0, done = 0;
+ done == 0 && (token = itoken(i)) != NULL && (itokname = token->name) != NULL; i++)
+ done = strncmp(data+1, itokname, strlen(itokname))
+ ? 0 : data[strlen(itokname) + 1] == '_';
+ if (done){
+ condition = (CONDVALUE_S *) rule_alloc_mem(sizeof(CONDVALUE_S));
+ condition->tname = fs_get(strlen(itokname) + 3);
+ sprintf(condition->tname, "_%s_", itokname);
+ data += strlen(itokname) + 2;
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+
+ for (; *data && *data == ' '; data++);
+ if (*data){
+ for (i = 0, done = 0; done == 0 && rel_rules_test[i].value != NULL; i++)
+ done = strncmp(data, rel_rules_test[i].value, 2) ? 0 : 1;
+ if (done)
+ condition->ttype = rel_rules_test[--i].ttype;
+ else{
+ free_condition_value(&condition);
+ return NULL;
+ }
+ }
+ else{
+ free_condition_value(&condition);
+ return NULL;
+ }
+
+ data += 2;
+ for (; *data && *data == ' '; data++);
+ if (*data++ != '{'){
+ free_condition_value(&condition);
+ return NULL;
+ }
+ group = advance_to_char(data,'}', STRICTLY, &error);
+ if (group || (!group && error < 0)){
+ condition->value = parse_group_data(data, &error);
+ if(group && error)
+ free_condition_value(&condition);
+ if(group)
+ fs_give((void **) &group);
+ }
+ else
+ free_condition_value(&condition);
+ return condition;
+ }
+
+ char *
+ canonicalize_condition(char *data, int *eoc)
+ {
+ char *p = data, *s, *t, c;
+ char *q = fs_get((5*strlen(data)+1)*sizeof(char));
+ char tmp[10];
+ int level, done, error, i;
+
+ if(eoc) *eoc = -1; /* assume error */
+ *q = '\0';
+ if(*p == '"'){
+ if(p[strlen(p) - 1] == '"')
+ p[strlen(p) - 1] = '\0';
+ p++;
+ }
+ for(level = done = error = 0; *p && !done && !error; ){
+ switch(*p){
+ case ' ' : p++; break;
+ case '(' : strcat(q, CSEP_S); strcat(q, "(");
+ sprintf(tmp, "%d ", level++);
+ strcat(q, tmp);
+ p++;
+ break;
+ case ')' : strcat(q, CSEP_S); strcat(q, ")");
+ sprintf(tmp, "%d ", --level);
+ strcat(q, tmp);
+ p++;
+ if(level < 0) error++;
+ break;
+ case '_' : for(s = p+1; *s >= 'A' && *s <= 'Z'; s++);
+ for(i = 0; token_rules[i] != NULL; i++)
+ if(!strncmp(token_rules[i], p, s-p))
+ break;
+ if(token_rules[i] == NULL)
+ error++;
+ else if(*s++ == '_'){
+ for(; *s == ' '; s++);
+ if(*s && *(s+1)){
+ for(i = 0; rel_rules_test[i].value != NULL; i++)
+ if(!strncmp(rel_rules_test[i].value, s, 2))
+ break;
+ if (rel_rules_test[i].value == NULL)
+ error++;
+ else{
+ s += 2;
+ for(; *s == ' '; s++);
+ if(*s == '{'){
+ if(*(s+1) != '}')
+ t = advance_to_char(s+1,'}', STRICTLY, NULL);
+ else
+ t = cpystr("");
+ if(t != NULL){
+ for(i = 0; t[i] != '\0' && t[i] != CSEP_C; i++);
+ if(t[i] == CSEP_C) error++;
+ if(error == 0){
+ strcat(q, CSEP_S); strcat(q, "C[");
+ s += strlen(t) + 1; /* get past '{' */
+ *s = '\0';
+ strcat(q, p);
+ strcat(q, "}] ");
+ *s++ = '}';
+ p = s;
+ }
+ fs_give((void **) &t);
+ }
+ else error++;
+ }
+ else
+ error++;
+ }
+ }
+ }
+ else error++;
+ break;
+ case '|':
+ case '&': if(*(p+1) = *p){
+ strcat(q, CSEP_S); strcat(q, *p == '|' ? "OR " : "AND ");
+ p += 2;
+ } else error++;
+ break;
+ case '-':
+ case '=': if (*(p+1) == '>'){
+ if(eoc) *eoc = p - data;
+ done++;
+ }
+ else
+ error++;
+ break;
+ default : error++;
+ break;
+ }
+ }
+ if(error || level > 0) /*simplistic approach by now */
+ fs_give((void **)&q);
+ else
+ q[strlen(q)-1] = '\0';
+ return q;
+ }
+
+ /* for a canonical condition, return if it is constructed according
+ * to logical rules such as AND or OR between conditions, etc. We assume
+ * we already canonicalized data, or else this will not work.
+ */
+ int
+ sanity_check_condition(char *data)
+ {
+ int i, error;
+ char *s, *t, *d;
+
+ if(data == NULL || *data == '\0') /* no data in, no data out */
+ return 0;
+
+ d = fs_get((strlen(data)+1)*sizeof(char));
+ for(s = data,i = 0; (t = strchr(s, CSEP_C))!= NULL && (d[i] = *(t+1)); s = t+1, i++);
+ d[i] = '\0';
+ for(i = 0, error = 0; d[i] != '\0' && error == 0; i++){
+ switch(d[i]){
+ case 'C': if((d[i+1] != '\0' && (d[i+1] == '(' || d[i+1] == 'C'))
+ || (i == 0 && d[1] != 'A' && d[1] != 'O' && d[1] != '\0'))
+ error++;
+ break;
+ case ')': if(i == 0 || (d[i+1] != '\0' && (d[i+1] == 'C' || d[i+1] == '(')))
+ error++;
+ break;
+ case '(': if(d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O')
+ error++;
+ break;
+ case 'O':
+ case 'A': if(i == 0 || d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O')
+ error++;
+ break;
+ default : error++;
+ }
+ }
+ if(d) fs_give((void **)&d);
+ return error ? 0 : 1;
+ }
+
+ /* given a parsed data that satisfies sanity checks, parse it
+ * into a condition we can check later on.
+ */
+ CONDITION_S *
+ fill_condition(char *data)
+ {
+ char *s, *t, *u;
+ CONDITION_S *rv = NULL;
+ CONDVALUE_S *cvalue;
+ int *i;
+
+ if(data == NULL || *data == '\0' || (s = strchr(data, CSEP_C)) == NULL)
+ return NULL;
+
+ rv = (CONDITION_S *) rule_alloc_mem(sizeof(CONDITION_S));
+ switch(*++s){
+ case ')':
+ case '(': i = fs_get(sizeof(int));
+ *i = atoi(s+1);
+ rv->cndrule = (void *) i;
+ rv->cndtype = *s == '(' ? ParOpen : ParClose;
+ break;
+
+ case 'C': if((u = strchr(s+2, CSEP_C)) != NULL){
+ *u = '\0';
+ t = strrchr(s, ']');
+ t = '\0';
+ *u = CSEP_C;
+ } else
+ s[strlen(s) - 1] = '\0';
+ rv->cndrule = (void *) fill_condition_value(s+2);
+ rv->cndtype = Condition;
+ break;
+
+ case 'A':
+ case 'O': rv->cndtype = *s == 'A' ? And : Or;
+ break;
+
+ default : fs_give((void **)&rv);
+ break;
+ }
+ rv->next = fill_condition(strchr(s, CSEP_C));
+
+ return rv;
+ }
+
+ /* eoc = end of condition, equal to -1 on error */
+ CONDITION_S *
+ parse_condition (char *data, int *eoc)
+ {
+ CONDITION_S *condition = NULL;
+ char *pvalue;
+
+ if((pvalue = canonicalize_condition(data, eoc)) != NULL
+ && sanity_check_condition(pvalue) > 0)
+ condition = fill_condition(pvalue);
+
+ if(pvalue)
+ fs_give((void **)&pvalue);
+
+ if (condition == NULL && eoc)
+ *eoc = -1;
+
+ return condition;
+ }
+
+ RULEACTION_S *
+ parse_action (char *data, int context)
+ {
+ int i, done, is_save;
+ RULEACTION_S *raction = NULL;
+ char *function, *p = data;
+
+ if (p == NULL || *p == '\0')
+ return NULL;
+
+ is_save = *p == '-';
+ p += 2;
+ for (; *p == ' '; p++);
+
+ if (is_save){ /* got "->", a save-rule separator */
+ raction = (RULEACTION_S *) rule_alloc_mem(sizeof(RULEACTION_S));
+ raction->function = cpystr("_SAVE_");
+ raction->value = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+ raction->context |= FOR_SAVE;
+ raction->exec = extended_value;
+ raction->value->testxt = cpystr(p);
+ return raction;
+ }
+ for (i = 0, done = 0; !done && (i < NFCN); i++)
+ done = (strstr(p,rule_fcns[i].name) == p);
+ p += done ? strlen(rule_fcns[--i].name) + 1 : 0;
+ if(!*p || (rule_fcns[i].what_for && !(rule_fcns[i].what_for & context)))
+ return NULL;
+ if (done){
+ raction = rule_alloc_mem(sizeof(RULEACTION_S));
+ /* We assign raction->token to be subject. This is not necessary for
+ most rules. It is done only for rules that need it and will not
+ make any difference in rules that do not need it. It will hopefully
+ reduce complexity in the language
+ */
+ raction->token = cpystr(SUBJ_TOKEN);
+ raction->function = cpystr(rule_fcns[i].name);
+ raction->context = rule_fcns[i].what_for;
+ raction->exec = rule_fcns[i].execute;
+ raction->value = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+ raction->value->testxt = advance_to_char(p,'}', STRICTLY, NULL);
+ if(!raction->value->testxt)
+ free_ruleaction(&raction);
+ return raction;
+ }
+
+ raction = (RULEACTION_S *) rule_alloc_mem(sizeof(RULEACTION_S));
+ raction->token = get_name_token(p);
+
+ p += strlen(raction->token) + 1;
+ for (; *p && *p == ' '; p++);
+ if (!strncmp(p, ":=", 2))
+ p += 2;
+ else{
+ free_ruleaction(&raction);
+ return NULL;
+ }
+
+ for (; *p && *p == ' '; p++);
+
+ for(i = 0; i < NFCN && strncmp(p, rule_fcns[i].name, rule_fcns[i].len); i++);
+
+ if (!rule_fcns[i].is_assignment){
+ free_ruleaction(&raction);
+ return NULL;
+ }
+
+ raction->function = cpystr(rule_fcns[i].name);
+ raction->context = rule_fcns[i].what_for;
+ raction->exec = rule_fcns[i].execute;
+ p += rule_fcns[i].len;
+
+ if(*p++ != '{'){
+ free_ruleaction(&raction);
+ return NULL;
+ }
+
+ if(rule_fcns[i].parse_action_value){
+ raction->value = (rule_fcns[i].parse_action_value)(p);
+ if(raction->value)
+ raction->value->voidtype = rule_fcns[i].return_type;
+ }
+
+ if(raction->value == NULL
+ || (raction->value->testxt == NULL && raction->value->voidtxt == NULL))
+ free_ruleaction(&raction);
+
+ return raction;
+ }
+
+ TOKEN_VALUE *
+ parse_action_to_char(char *p)
+ {
+ return parse_group_data(p, NULL);
+ }
+
+ TOKEN_VALUE *
+ parse_rexsub_action(char *data)
+ {
+ TOKEN_VALUE *rv;
+ REXSUB_S *rsv = NULL; /* rexsub value */
+ char *d, *p, *t, *n; /* data, pattern, text, number */
+ long number;
+ int offset, error;
+
+ if (data == NULL || *data == '\0')
+ return NULL;
+
+ rv = (TOKEN_VALUE *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+
+ number = 0;
+ p = t = n = NULL;
+
+ p = advance_to_char(data,'}', STRICTLY, NULL);
+
+ error = (p == NULL) ? 1 : 0;
+ if(!error){
+ offset = strlen(p) + 1;
+ d = data + offset;
+ if(*d++ != '{') error++;
+ }
+
+ if(!error){
+ n = advance_to_char(d,'}', STRICTLY, NULL);
+ if(n == NULL && *d == '}')
+ n = cpystr("");
+ if(n == NULL) error++;
+ }
+
+ if(!error){
+ t = detoken_src(n, FOR_RULE, NULL, NULL, NULL, NULL);
+ offset += strlen(n) + 2;
+ d = data + offset;
+ if(*d == '\0') number = 1;
+ else if(*d++ != '{') error++;
+ }
+
+ if(!error && number == 0){
+ fs_give((void **) &n);
+ n = advance_to_char(d,'}', STRICTLY, NULL);
+ if(n == NULL){
+ if(*d == '\0')
+ number = 1;
+ else
+ error++;
+ }
+ else if(strcmp(n, "g") == 0)
+ number = -1;
+ else{
+ char *eon;
+ number = strtol(n, &eon, 10);
+ if((eon != NULL && *eon != '\0') || number <= 0) error++;
+ }
+ }
+
+ if(error){
+ if (p) fs_give((void **) &p);
+ if (t) fs_give((void **) &t);
+ }
+ if (n) fs_give((void **) &n);
+
+ if(!error){
+ rsv = fs_get(sizeof(REXSUB_S));
+ rsv->pattern = p;
+ rsv->text = t;
+ rsv->times = number;
+ }
+
+ rv->voidtxt = (void *) rsv;
+ return rv;
+ }
+
+ RULE_S *
+ parse_rule (char *data, int context)
+ {
+ RULE_S *prule; /*parsed rule */
+ int len = 0;
+
+ if (!(prule = (RULE_S *) rule_alloc_mem(sizeof(RULE_S))) ||
+ !(prule->condition = parse_condition(data, &len)) ||
+ !(prule->action = parse_action(data+len, context)))
+ free_rule(&prule);
+
+ return prule;
+ }
+
+ RULELIST *
+ get_rule_list(char **list, int context, int i)
+ {
+ RULE_S *rule;
+ RULELIST *trulelist = NULL;
+
+ if (list[i] && *list[i]){
+ if(rule = parse_rule(list[i], context)){
+ trulelist = (RULELIST *) rule_alloc_mem(sizeof(RULELIST));
+ trulelist->prule = rule;
+ trulelist->next = get_rule_list(list, context, i+1);
+ }
+ else
+ trulelist = get_rule_list(list, context, i+1);
+ }
+ return trulelist;
+ }
+
+ /* add parsed rule */
+ PRULELIST_S *
+ add_prule(PRULELIST_S *rule_list, PRULELIST_S *rule)
+ {
+ PRULELIST_S *rlist;
+
+ for(rlist = rule_list; rlist && rlist->next; rlist = rlist->next);
+
+ if(rlist)
+ rlist->next = rule;
+ else
+ rule_list = rule;
+
+ return rule_list;
+ }
+
+ void
+ add_rule(int code, int context)
+ {
+ char **list = ps_global->vars[code].current_val.l;
+ PRULELIST_S *prulelist, *trulelist, *orulelist;
+
+ if (list && *list && **list){
+ trulelist = (PRULELIST_S *) rule_alloc_mem(sizeof(PRULELIST_S));
+ trulelist->varnum = code;
+ if (trulelist->rlist = get_rule_list(list, context, 0))
+ 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 *) rule_alloc_mem(sizeof(TOKEN_VALUE));
+ tval->testxt = detoken_src(value->testxt, ctxt, env, NULL, NULL, NULL);
+ tval->voidtxt = value->voidtxt;
+ tval->codefcn = value->codefcn;
+ }
+ if(value->next)
+ tval->next = copy_parsed_value(value->next, ctxt, env);
+
+ return tval;
+ }
+
+ void
+ free_parsed_value(TOKEN_VALUE **value)
+ {
+ TOKEN_VALUE *tval = NULL;
+
+ if(!*value)
+ return;
+
+ if((*value)->testxt)
+ fs_give((void **)&(*value)->testxt);
+
+ if((*value)->next)
+ free_parsed_value(&(*value)->next);
+
+ fs_give((void **)value);
+ }
+
+ int
+ test_condition_work(CONDITION_S *bc, CONDITION_S *ec, int rcntxt, ENVELOPE *env)
+ {
+ int rv,level;
+ TOKEN_VALUE *group;
+ CONDITION_S *cend;
+
+ switch(bc->cndtype){
+ case Condition: group = copy_parsed_value(COND(bc)->value, rcntxt, env);
+ rv = (*rel_rules_test[COND(bc)->ttype].execute)(bc, group, env, rcntxt);
+ free_parsed_value(&group);
+ if(bc == ec)
+ return rv;
+ if(bc->next == NULL)
+ return rv;
+ else
+ switch(bc->next->cndtype){
+ case And: return rv ? test_condition_work(bc->next->next, ec, rcntxt, env) : 0;
+ break;
+ case Or : return rv ? 1 : test_condition_work(bc->next->next, ec, rcntxt, env);
+ break;
+ case ParClose: return rv;
+ default : rv = 0; break; /* fail, we should not be here */
+ }
+ break;
+
+ case ParOpen: level = ((int *)bc->cndrule)[0];
+ for(cend = bc; cend->next && (cend->next->cndtype != ParClose
+ || ((int *)cend->next->cndrule)[0] != level);
+ cend = cend->next);
+ rv = test_condition_work(bc->next, cend, rcntxt, env);
+ cend = cend->next; /* here we are at ')' */
+ if(cend->next == NULL)
+ return rv;
+ else{
+ switch(cend->next->cndtype){
+ case And: return rv ? test_condition_work(cend->next->next, ec, rcntxt, env) : 0;
+ break;
+ case Or : return rv ? 1 : test_condition_work(cend->next->next, ec, rcntxt, env);
+ break;
+ default : rv = 0; break; /* fail, we should not be here */
+ }
+ }
+ break;
+ default: rv = 0; break; /* fail, we should not be here */
+ }
+ return rv; /* we never ever get here */
+ }
+
+
+ int
+ test_condition(CONDITION_S *condition, int rcntxt, ENVELOPE *env)
+ {
+ return test_condition_work(condition, NULL, rcntxt, env);
+ }
+
+ /* returns the name of the token it found or NULL if there is no token, the
+ * real value of the token is obtained by calling the detoken_src function.
+ */
+ char *
+ get_name_token (char *condition)
+ {
+ char *p, *q, c, *rv;
+
+ if (condition == NULL)
+ return NULL;
+
+ for(p = condition; *p != '\0' && (*p == ' '|| *p == '\t'); p++);
+
+ if (*p != '_') return NULL;
+
+ for (q = p+1; *q != '\0' && *q != '_'; q++);
+
+ if (*q != '_') return NULL;
+
+ c = *(q+1);
+ *(q+1) = '\0';
+ rv = cpystr(p);
+ *(q+1) = c;
+ return rv;
+ }
+
+ /* This function tests if a string contained in the variable "group" is
+ * in the "condition"
+ */
+ int
+ test_in (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env,
+ int context)
+ {
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+ ? cpystr(((SPAREP_S *)env->sparep)->value)
+ : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+ while (rv == 0 && test_group){
+ if(!*test || strstr(test_group->testxt, test))
+ rv++;
+ else
+ test_group = test_group->next;
+ }
+ fs_give((void **)&test);
+ }
+ return rv;
+ }
+
+ int
+ test_ni (CONDITION_S *condition, TOKEN_VALUE *group,
+ ENVELOPE *env, int context)
+ {
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+ ? cpystr(((SPAREP_S *)env->sparep)->value)
+ : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+ if(!test_group)
+ rv++;
+ while (rv == 0 && test_group){
+ if(!*test_group->testxt || strstr(test, test_group->testxt))
+ rv++;
+ else
+ test_group = test_group->next;
+ }
+ fs_give((void **)&test);
+ }
+ return rv;
+ }
+
+ int
+ test_not_in (CONDITION_S *condition, TOKEN_VALUE *group,
+ ENVELOPE *env, int context)
+ {
+ return !test_in(condition, group, env, context);
+ }
+
+ int
+ test_not_ni (CONDITION_S *condition, TOKEN_VALUE *group,
+ ENVELOPE *env, int context)
+ {
+ return !test_ni(condition, group, env, context);
+ }
+
+ int
+ test_eq (CONDITION_S *condition, TOKEN_VALUE *group,
+ ENVELOPE *env, int context)
+ {
+ int rv = 0;
+ char *test;
+ TOKEN_VALUE *test_group = group;
+
+ test = env && env->sparep && (((SPAREP_S *)env->sparep)->flag & USE_RAW_SP)
+ ? cpystr(((SPAREP_S *)env->sparep)->value)
+ : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL);
+ if (test){
+ while (rv == 0 && test_group){
+ if((!*test && !*test_group->testxt) || !strcmp(test_group->testxt, test))
+ rv++;
+ else
+ test_group = test_group->next;
+ }
+ fs_give((void **)&test);
+ }
+ return rv;
+ }
+
+ int
+ test_not_eq (CONDITION_S *condition, TOKEN_VALUE *group,
+ ENVELOPE *env, int context)
+ {
+ return !test_eq(condition, group, env, context);
+ }
+
+ char *
+ do_trim (char *test, TOKEN_VALUE *tval)
+ {
+ char *begin_text;
+ int offset = 0;
+
+ if (!tval)
+ return test;
+
+ while(begin_text = strstr(test+offset,tval->testxt)){
+ memmove(begin_text, begin_text+strlen(tval->testxt), strlen(begin_text) - strlen(tval->testxt) + 1);
+ offset = begin_text - test;
+ }
+
+ return do_trim(test, tval->next);
+ }
+
+ char *
+ trim (RULEACTION_S *action, int context, ENVELOPE *env)
+ {
+ char *begin_text, *test;
+ RULEACTION_S *taction = action;
+ int offset;
+
+ if (taction->context & context){
+ if (test = detoken_src(taction->token, context, env, NULL, NULL, NULL))
+ test = do_trim(test, taction->value);
+ return test;
+ }
+ return NULL;
+ }
+
+ char *
+ do_rextrim (char *test, TOKEN_VALUE *tval)
+ {
+ char *begin_text, *trim_text;
+ int i, offset = 0;
+ size_t len;
+
+ if (!tval)
+ return test;
+
+ if((trim_text = expand(test, tval->testxt)) != NULL){
+ while(begin_text = strstr(test+offset, trim_text)){
+ memmove(begin_text, begin_text+strlen(trim_text), strlen(begin_text) - strlen(trim_text) + 1);
+ offset = begin_text - test;
+ }
+ }
+
+ return do_rextrim(test, tval->next);
+ }
+
+ char *
+ rextrim (RULEACTION_S *action, int context, ENVELOPE *env)
+ {
+ char *test = NULL;
+ RULEACTION_S *taction = action;
+
+ if ((taction->context & context) &&
+ (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)))
+ test = do_rextrim(test, taction->value);
+ return test;
+ }
+
+ char *
+ do_rexsub (char *test, TOKEN_VALUE *tval)
+ {
+ REXSUB_S *rsv;
+ regmatch_t pmatch;
+ regex_t preg;
+ char *ret_string = cpystr(test ? test : "");
+
+ if (!tval)
+ return ret_string;
+
+ rsv = (REXSUB_S *) tval->voidtxt;
+
+ if (rsv == NULL /* this should not happen */
+ || rsv->pattern == NULL /* neither should this */
+ || rsv->text == NULL /* neither should this */
+ || regcomp(&preg, rsv->pattern, REG_EXTENDED) != 0) /* but this could */
+ return ret_string;
+
+ /* can't use expand() to do the job here. This is because there
+ * is a variable number of hits in rsv->times. */
+
+ if(regexec((regex_t *) &preg, test, 1, &pmatch, 0) == 0){
+ int offset = 0, n;
+
+ n = rsv->times > 0 ? rsv->times : strlen(test);
+ fs_resize((void **) &ret_string, strlen(test) + n*strlen(rsv->text) + 1);
+ *ret_string = '\0';
+
+ do {
+ strncat(ret_string, test + offset, pmatch.rm_so);
+ strcat(ret_string, rsv->text);
+ offset += pmatch.rm_eo;
+ } while (--n && regexec(&preg, test + offset, 1, &pmatch, REG_NOTBOL) == 0);
+ strcat(ret_string, test + offset);
+ }
+ fs_resize((void **) &ret_string, strlen(ret_string) + 1);
+ regfree(&preg);
+
+ return ret_string;
+ }
+
+ char *
+ rexsub (RULEACTION_S *action, int context, ENVELOPE *env)
+ {
+ char *test = NULL, *sub = NULL;
+ RULEACTION_S *taction = action;
+
+ if ((taction->context & context) &&
+ (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)))
+ sub = do_rexsub(test, taction->value);
+ if (test) fs_give((void **) &test);
+ return sub;
+ }
+
+ char *
+ raw_value (RULEACTION_S *action, int context, ENVELOPE *env)
+ {
+ return (action->context & context) ? cpystr(action->value->testxt) : NULL;
+ }
+
+ char *
+ extended_value (RULEACTION_S *action, int ctxt, ENVELOPE *env)
+ {
+ return (action->context & ctxt)
+ ? detoken_src(action->value->testxt, ctxt, env, NULL, NULL, NULL)
+ : NULL;
+ }
+
+ /* advances given_string until it finds given_char, memory freed by caller */
+ char *
+ advance_to_char(char *given_string, char given_char, int flag, int *error)
+ {
+ char *b, *s, c;
+ int i, err = 0, quoted ;
+
+ if (error)
+ *error = 0;
+
+ if (!given_string || !*given_string)
+ return NULL;
+
+ b = s = cpystr(given_string);
+ for(i = 0, quoted = 0, c = *s; c ; c = *++s){
+ if(c == '\\'){
+ quoted++;
+ continue;
+ }
+ if(quoted){
+ quoted = 0;
+ if (c == given_char){
+ err += (flag & STRICTLY) ? 0 : 1;
+ err++;
+ break;
+ }
+ b[i++] = '\\';
+ }
+ if(c == given_char){
+ err += (flag & STRICTLY) ? 0 : 1;
+ break;
+ }
+ b[i++] = c;
+ }
+ b[i] = '\0';
+ if (b && (strlen(b) == strlen(given_string)) && (flag & STRICTLY)){
+ fs_give((void **)&b);
+ return NULL; /* character not found */
+ }
+
+ if(b && !*b){
+ fs_give((void **)&b);
+ err = -1;
+ }
+
+ if (error)
+ *error = err;
+
+ return b;
+ }
+
+ /* Regular Expressions Support */
+ char *
+ expand (char *string, char *pattern)
+ {
+ char c, *ret_string = NULL;
+ regmatch_t pmatch;
+ regex_t preg;
+
+ if(pattern == NULL || regcomp(&preg, pattern, REG_EXTENDED) != 0)
+ return NULL;
+
+ if(regexec((regex_t *) &preg, string , 1, &pmatch, 0) == 0
+ && pmatch.rm_so < pmatch.rm_eo){
+ c = string[pmatch.rm_eo];
+ string[pmatch.rm_eo] = '\0';
+ ret_string = cpystr(string+pmatch.rm_so);
+ string[pmatch.rm_eo] = c;
+ }
+ regfree(&preg);
+
+ return ret_string;
+ }
+
+ char *
+ exec_fcn (RULEACTION_S *action, int ctxt, ENVELOPE *env)
+ {
+ STORE_S *output_so;
+ gf_io_t gc, pc;
+ char *status, *rv, *cmd, *test;
+
+ if(!(action->context & ctxt))
+ return NULL;
+
+ if((test = detoken_src(action->token, ctxt, env, NULL, NULL, NULL)) != NULL)
+ gf_set_readc(&gc, test, (unsigned long)strlen(test), CharStar, 0);
+
+ if((output_so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
+ gf_set_so_writec(&pc, output_so);
+
+ cmd = (char *)fs_get((strlen(action->value->testxt) + strlen("_TMPFILE_") + 2)*sizeof(char));
+ sprintf(cmd,"%s _TMPFILE_", action->value->testxt);
+ status = (*ps_global->tools.exec_rule)(cmd, gc, pc);
+
+ so_seek(output_so, 0L, 0);
+ rv = cpystr(output_so->dp);
+ gf_clear_so_writec(output_so);
+ so_give(&output_so);
+ if(test)
+ fs_give((void **)&test);
+
+ return status ? NULL : rv;
+ }
+
+ ENVELOPE *
+ rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear)
+ {
+ ENVELOPE *env;
+
+ if (idata->no_fetch){
+ if (we_clear) *we_clear = 1;
+ env = mail_newenvelope();
+ env->from = copyaddrlist(idata->from);
+ env->to = copyaddrlist(idata->to);
+ env->cc = copyaddrlist(idata->cc);
+ env->sender = copyaddrlist(idata->sender);
+ env->subject = cpystr(idata->subject);
+ env->date = cpystr((unsigned char *) idata->date);
+ env->newsgroups = cpystr(idata->newsgroups);
+ return env;
+ }
+ if (we_clear) *we_clear = 0;
+ env = pine_mail_fetchenvelope(idata->stream, idata->rawno);
+ return env;
+ }
Index: alpine-2.23.2/pith/rules.h
===================================================================
*** /dev/null
--- alpine-2.23.2/pith/rules.h
***************
*** 0 ****
--- 1,154 ----
+ /* Included file rules.h */
+
+ #ifndef PITH_RULES_INCLUDED
+ #define PITH_RULES_INCLUDED
+
+ #include "../pith/conftype.h"
+ #include "../pith/detoken.h"
+ #include "../pith/indxtype.h"
+ #include "../pith/rulestype.h"
+
+ /* Exported prototypes */
+
+ void create_rule_list (struct variable *);
+ SPAREP_S *get_sparep_for_rule(char *, int);
+ void free_sparep_for_rule(void **);
+ void free_parsed_rule_list (PRULELIST_S **);
+ RULE_RESULT *get_result_rule (int, int, ENVELOPE *);
+ char *get_rule_result (int , char *, int);
+ char *process_rule (RULE_S *, int, ENVELOPE *);
+ char **functions_for_token (char *);
+ RULELIST *get_rulelist_from_code (int, PRULELIST_S *);
+ RULE_S *get_rule (RULELIST *, int);
+ int condition_contains_token (CONDITION_S *, char *);
+ int context_for_function (char *);
+ ENVELOPE *rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear);
+
+ /* Separators:
+ *
+ * A separator is a string that separates the rule condition with the rule
+ * action. Below is the list of separators
+ *
+ */
+
+ #define SAVE_TO_SEP "->"
+ #define APPLY_SEP "=>"
+
+ /*------- Definitions of tokens -------*/
+ /*------ Keep the list alphabetically sorted, thanks -------*/
+
+ #define ADDR_TOKEN "_ADDRESS_"
+ #define ADDCC_TOKEN "_ADDRESSCC_"
+ #define ADDRECIP_TOKEN "_ADDRESSRECIPS_"
+ #define ADDTO_TOKEN "_ADDRESSTO_"
+ #define BCC_TOKEN "_BCC_"
+ #define CC_TOKEN "_CC_"
+ #define COLLECT_TOKEN "_COLLECTION_"
+ #define FCCF_TOKEN "_FCCFROM_"
+ #define FCCS_TOKEN "_FCCSENDER_"
+ #define FLAG_TOKEN "_FLAG_"
+ #define FOLDER_TOKEN "_FOLDER_"
+ #define FADDRESS_TOKEN "_FORWARDADDRESS_"
+ #define FFROM_TOKEN "_FORWARDFROM_"
+ #define FROM_TOKEN "_FROM_"
+ #define KEY_TOKEN "_PKEY_"
+ #define LCC_TOKEN "_LCC_"
+ #define NICK_TOKEN "_NICK_"
+ #define OTEXT_TOKEN "_OPENINGTEXT_"
+ #define OTEXTNQ_TOKEN "_OPENINGTEXTNQ_"
+ #define PROCID_TOKEN "_PROCID_"
+ #define ROLE_TOKEN "_ROLE_"
+ #define SCREEN_TOKEN "_SCREEN_"
+ #define SEND_TOKEN "_SENDER_"
+ #define SUBJ_TOKEN "_SUBJECT_"
+ #define THDDSPSTY_TOKEN "_THREADSTYLE_"
+ #define THDNDXSTY_TOKEN "_THREADINDEX_"
+ #define TO_TOKEN "_TO_"
+
+ /*------ Definitions of relational operands -------------*/
+
+ typedef struct {
+ char *value;
+ TestType ttype;
+ int (*execute)();
+ } REL_TOKEN;
+
+ /* Relational Operands */
+ #define AND_REL "&&" /* For putting more than one condition */
+ #define IN_REL "<<" /* For belonging relation */
+ #define NI_REL ">>" /* For contain relation */
+ #define NOT_IN_REL "!<" /* Negation of IN_REL */
+ #define NOT_NI_REL "!>" /* Negation of NI_REL */
+ #define EQ_REL "==" /* Test of equality */
+ #define NOT_EQ_REL "!=" /* Test of inequality */
+ #define OPEN_SET "{" /* Braces to open a set */
+ #define CLOSE_SET "}" /* Braces to close a set*/
+
+ /*--- Context in which these variables can be used ---*/
+
+ typedef struct use_context {
+ char *name;
+ int what_for;
+ } USE_IN_CONTEXT;
+
+ static USE_IN_CONTEXT tokens_use[] = {
+ {NICK_TOKEN, FOR_SAVE},
+ {FROM_TOKEN, FOR_SAVE},
+ {OTEXT_TOKEN, FOR_SAVE|FOR_FOLDER},
+ {OTEXTNQ_TOKEN, FOR_SAVE|FOR_FOLDER},
+ {ROLE_TOKEN, FOR_COMPOSE},
+ {FOLDER_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_THREAD|FOR_COMPOSE},
+ {SUBJ_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_COMPOSE},
+ {FLAG_TOKEN, FOR_SAVE|FOR_FLAG},
+ {COLLECT_TOKEN, FOR_SAVE|FOR_COMPOSE|FOR_FOLDER|FOR_THREAD},
+ {THDDSPSTY_TOKEN, FOR_THREAD},
+ {THDNDXSTY_TOKEN, FOR_THREAD},
+ {ADDR_TOKEN, FOR_SAVE|FOR_FOLDER},
+ {TO_TOKEN, FOR_SAVE},
+ {ADDTO_TOKEN, FOR_SAVE|FOR_COMPOSE},
+ {ADDCC_TOKEN, FOR_SAVE|FOR_COMPOSE},
+ {ADDRECIP_TOKEN, FOR_SAVE|FOR_COMPOSE},
+ {SCREEN_TOKEN, FOR_KEY},
+ {KEY_TOKEN, FOR_KEY},
+ {SEND_TOKEN, FOR_SAVE},
+ {CC_TOKEN, FOR_SAVE},
+ {BCC_TOKEN, FOR_COMPOSE},
+ {LCC_TOKEN, FOR_COMPOSE},
+ {FFROM_TOKEN, FOR_COMPOSE},
+ {FADDRESS_TOKEN, FOR_COMPOSE},
+ {NULL, FOR_NOTHING}
+ };
+
+ typedef struct {
+ char *name;
+ size_t len;
+ unsigned is_assignment:1; /* has structure _TOKEN_ := _FUNCTION_{} */
+ int return_type; /* the type of data returned by the parser, when it fills voidtxt */
+ TOKEN_VALUE *(*parse_action_value)(char *);
+ char *(*execute)();
+ int what_for;
+ } RULE_FCN;
+
+ #define COMMAND_FCN "_COMMAND_"
+ #define COPY_FCN "_COPY_"
+ #define EXEC_FCN "_EXEC_"
+ #define INDEX_FCN "_INDEX_"
+ #define REPLACE_FCN "_REPLACE_"
+ #define REPLYSTR_FCN "_RESTR_"
+ #define REPLY_FCN "_REPLY_"
+ #define RESUB_FCN "_RESUB_"
+ #define REXSUB_FCN "_REXSUB_"
+ #define REXTRIM_FCN "_REXTRIM_"
+ #define SAVE_FCN "_SAVE_"
+ #define SIGNATURE_FCN "_SIGNATURE_"
+ #define SMTP_FCN "_SMTP_"
+ #define SORT_FCN "_SORT_"
+ #define STARTUP_FCN "_STARTUP_"
+ #define THRDSTYLE_FCN "_THREADSTYLE_"
+ #define THRDINDEX_FCN "_THREADINDEX_"
+ #define TRIM_FCN "_TRIM_"
+
+ #define STRICTLY 0x1
+ #define RELAXED 0x2
+
+ #endif /* PITH_RULES_INCLUDED */
Index: alpine-2.23.2/pith/rulestype.h
===================================================================
*** /dev/null
--- alpine-2.23.2/pith/rulestype.h
***************
*** 0 ****
--- 1,94 ----
+ #ifndef PITH_RULESTYPE_INCLUDED
+ #define PITH_RULESTYPE_INCLUDED
+
+ typedef struct rule {
+ char *result; /* The result of the rule */
+ int number; /* The number of the rule that succeded, -1 if not */
+ } RULE_RESULT;
+
+ typedef struct {
+ char *value;
+ int type;
+ } RULE_ACTION;
+
+
+ #define TOKEN_VALUE struct tokenvalue_s
+ #define CONDITION_S struct condition_s
+ #define REXSUB_S struct rexsub_s
+ #define RULEACTION_S struct ruleaction_s
+ #define RULE_S struct rule_s
+ #define RULELIST struct rulelist_s
+ #define PRULELIST_S struct parsedrulelist_s
+
+ #define FREEREGEX 1
+
+ #define UNDEFINED_TYPE 0
+ #define CHAR_TYPE 1
+ #define REXSUB_TYPE 2
+
+ TOKEN_VALUE {
+ char *testxt;
+ void *voidtxt;
+ int voidtype;
+ int codefcn;
+ TOKEN_VALUE *next;
+ };
+
+ typedef enum {Equal, Subset, Includes, NotEqual, NotSubset, NotIncludes, EndTypes} TestType;
+
+ typedef enum {And, Or, ParOpen, ParClose, Condition} CondType;
+
+ typedef struct condvalue_s {
+ char *tname; /* tname ttype {value} */
+ TestType ttype; /* type of rule */
+ TOKEN_VALUE *value; /* value to check against */
+ } CONDVALUE_S;
+
+ CONDITION_S {
+ void *cndrule; /* text in condition */
+ CondType cndtype; /* type of object */
+ CONDITION_S *next; /* next condition to test */
+ };
+
+ #define COND(C) ((CONDVALUE_S *)((C)->cndrule))
+
+ RULEACTION_S {
+ char *token; /* token := function{value} or token = null */
+ char *function; /* token := function{value} or simply function{value}*/
+ TOKEN_VALUE *value; /* token := function{value} or simply function{value}*/
+ int context; /* context in which this rule can be used */
+ char* (*exec)();
+ };
+
+ REXSUB_S {
+ char *pattern; /* pattern to use in the replacement */
+ char *text; /* text to replace */
+ int times; /* how many times to repeat the substitution */
+ };
+
+ RULE_S {
+ CONDITION_S *condition;
+ RULEACTION_S *action;
+ };
+
+ RULELIST {
+ RULE_S *prule;
+ RULELIST *next;
+ };
+
+ PRULELIST_S {
+ int varnum; /* number associated to the variable */
+ RULELIST *rlist;
+ PRULELIST_S *next;
+ };
+
+ #define USE_RAW_SP 0x001
+ #define PROCESS_SP 0x010
+
+ typedef struct sparep {
+ int flag;
+ char *value;
+ } SPAREP_S;
+
+
+ #endif /* PITH_RULESTYPE_INCLUDED */
Index: alpine-2.23.2/pith/save.c
===================================================================
*** alpine-2.23.2.orig/pith/save.c
--- alpine-2.23.2/pith/save.c
*************** save(struct pine *state, MAILSTREAM *str
*** 955,961 ****
*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)
--- 955,961 ----
*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)
Index: alpine-2.23.2/pith/send.c
===================================================================
*** alpine-2.23.2.orig/pith/send.c
--- alpine-2.23.2/pith/send.c
*************** static char rcsid[] = "$Id: send.c 1204
*** 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"
*************** call_mailer(METAENV *header, struct mail
*** 1695,1703 ****
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;
--- 1696,1704 ----
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;
*************** call_mailer(METAENV *header, struct mail
*** 1852,1871 ****
* 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];
--- 1853,1901 ----
* 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];
Index: alpine-2.23.2/pith/sort.c
===================================================================
*** alpine-2.23.2.orig/pith/sort.c
--- alpine-2.23.2/pith/sort.c
*************** static char rcsid[] = "$Id: sort.c 1142
*** 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
*************** pine_compare_scores(const qsort_t *a, co
*** 686,691 ****
--- 686,726 ----
: ((mdiff = *mess_a - *mess_b) ? ((mdiff > 0) ? 1 : -1) : 0));
}
+ 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;
+ }
void
reset_sort_order(unsigned int flags)
*************** reset_sort_order(unsigned int flags)
*** 695,701 ****
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
--- 730,750 ----
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
*************** reset_sort_order(unsigned int flags)
*** 721,726 ****
--- 770,776 ----
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);
Index: alpine-2.23.2/pith/sort.h
===================================================================
*** alpine-2.23.2.orig/pith/sort.h
--- alpine-2.23.2/pith/sort.h
*************** char *sort_name(SortOrder);
*** 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 */
Index: alpine-2.23.2/pith/state.c
===================================================================
*** alpine-2.23.2.orig/pith/state.c
--- alpine-2.23.2/pith/state.c
*************** static char rcsid[] = "$Id: state.c 1074
*** 35,40 ****
--- 35,41 ----
#include "../pith/smime.h"
#include "../pith/ical.h"
#include "../pith/bldaddr.h"
+ #include "../pith/rules.h"
/*
* Globals referenced throughout pine...
*************** free_pine_struct(struct pine **pps)
*** 255,260 ****
--- 256,264 ----
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);
Index: alpine-2.23.2/pith/state.h
===================================================================
*** alpine-2.23.2.orig/pith/state.h
--- alpine-2.23.2/pith/state.h
***************
*** 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
*************** struct pine {
*** 112,117 ****
--- 112,122 ----
MAILSTREAM *mail_stream; /* ptr to current folder stream */
MSGNO_S *msgmap; /* ptr to current message map */
+ char screen_name[16]; /* name of current screen */
+ char *role; /* role used when composing */
+ char *procid; /* procedure id when needed */
+ int exiting;
+
unsigned read_predicted:1;
char cur_folder[MAXPATH+1];
*************** struct pine {
*** 367,372 ****
--- 372,378 ----
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;
*************** struct pine {
*** 377,382 ****
--- 383,391 ----
char last_error[500];
INIT_ERR_S *init_errs;
+ PRULELIST_S *rule_list;
+ char *pressed_key;
+
PRINT_S *print;
#ifdef SMIME
Index: alpine-2.23.2/pith/string.c
===================================================================
*** alpine-2.23.2.orig/pith/string.c
--- alpine-2.23.2/pith/string.c
*************** static char rcsid[] = "$Id: string.c 910
*** 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
*************** rplstr(char *os, size_t oslen, int dl, c
*** 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';
+ }
/*----------------------------------------------------------------------
*************** convert_decimal_to_alpha (char *rn, size
*** 2997,2999 ****
--- 3023,3057 ----
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++;
+ }
+ }
+
Index: alpine-2.23.2/pith/string.h
===================================================================
*** alpine-2.23.2.orig/pith/string.h
--- alpine-2.23.2/pith/string.h
*************** struct date {
*** 87,92 ****
--- 87,93 ----
/* exported prototypes */
char *rplstr(char *, size_t, int, char *);
+ void collspaces(char *);
void sqzspaces(char *);
void sqznewlines(char *);
void removing_leading_white_space(char *);
*************** void removing_trailing_white_space(c
*** 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 *);