diff --git a/_service b/_service new file mode 100644 index 0000000..5bfc424 --- /dev/null +++ b/_service @@ -0,0 +1,25 @@ + + + + git + https://repo.or.cz/alpine.git + v2.23.2 + 2.23.2 + *.dll + *.exe + *.lib + + + *.tar + xz + + diff --git a/alpine-2.23.2.tar.xz b/alpine-2.23.2.tar.xz new file mode 100644 index 0000000..fdeee28 --- /dev/null +++ b/alpine-2.23.2.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:927e170473bb060ccb11a37e30d44bb10aaca50c4aafa974d5741ae593a95694 +size 5151416 diff --git a/alpine-2.23.tar.xz b/alpine-2.23.tar.xz deleted file mode 100644 index ec73d11..0000000 --- a/alpine-2.23.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:793a61215c005b5fcffb48f642f125915276b7ec7827508dd9e83d4c4da91f7b -size 6330784 diff --git a/alpine.changes b/alpine.changes index 9c42941..1c12f20 100644 --- a/alpine.changes +++ b/alpine.changes @@ -1,3 +1,19 @@ +------------------------------------------------------------------- +Fri Oct 2 20:43:57 UTC 2020 - Jan Engelhardt + +- Update to release 2.23.2 + * Expansion of the configuration screen for XOAUTH2 to include + username, and tenant. + * Alpine uses the domain in the From: header of a message + to generate a message-id and suppresses all information + about Alpine, version, revision, and time of generation + of the message-id from this header. + * When messages are selected, pressing the ';' command to + broaden or narrow a search, now offers the possibility to + completely replace the search, and is almost equivalent to + being a shortcut to "unselect all messages, and select + again". + ------------------------------------------------------------------- Mon Sep 7 13:44:51 UTC 2020 - Jan Engelhardt diff --git a/alpine.spec b/alpine.spec index a30196c..1aad30e 100644 --- a/alpine.spec +++ b/alpine.spec @@ -19,15 +19,16 @@ Name: alpine # For debugging only: %define build_vanilla 0 -Version: 2.23 +Version: 2.23.2 Release: 0 Summary: Mail User Agent License: Apache-2.0 Group: Productivity/Networking/Email/Clients URL: http://alpine.x10host.com/alpine/ +#Git-Clone: https://repo.or.cz/alpine.git -# direct download does not work for openSUSE:Factory -Source: http://alpine.x10host.com/alpine/release/src/%name-%version.tar.xz +#Source: http://alpine.x10host.com/alpine/release/src/%name-%version.tar.xz +Source: %name-%version.tar.xz Source1: %name.png Source2: %name.desktop Source9: UPDATING.txt diff --git a/chappa-fancy.patch b/chappa-fancy.patch index a3e06d9..3700211 100644 --- a/chappa-fancy.patch +++ b/chappa-fancy.patch @@ -1,7 +1,8 @@ -diff -rc alpine-2.23/alpine/arg.c alpine-2.23.fancy/alpine/arg.c -*** alpine-2.23/alpine/arg.c 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/arg.c 2020-06-18 16:08:07.149575591 -0600 -*************** +Index: alpine-2.23.2/alpine/arg.c +=================================================================== +*** alpine-2.23.2.orig/alpine/arg.c +--- alpine-2.23.2/alpine/arg.c +*************** static char args_err_non_abs_pwdcertdir[ *** 68,73 **** --- 68,74 ---- #endif /* SMIME inside PASSFILE */ @@ -11,9 +12,9 @@ diff -rc alpine-2.23/alpine/arg.c alpine-2.23.fancy/alpine/arg.c static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\""); static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\""); static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\""); -*************** -*** 114,119 **** ---- 115,121 ---- +*************** N_(" -k \t\tKeys - Force use of function +*** 116,121 **** +--- 117,123 ---- N_(" -z \t\tSuspend - allow use of ^Z suspension"), N_(" -r \t\tRestricted - can only send mail to oneself"), N_(" -sort \tSort - Specify sort order of folder:"), @@ -21,19 +22,19 @@ diff -rc alpine-2.23/alpine/arg.c alpine-2.23.fancy/alpine/arg.c N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"), N_("\t\t\tfrom, size, score, to, cc, /reverse"), N_(" -i\t\tIndex - Go directly to index, bypassing main menu"), -*************** -*** 215,220 **** ---- 217,223 ---- +*************** pine_args(struct pine *pine_state, int a +*** 217,222 **** +--- 219,225 ---- char *cmd_list = NULL; char *debug_str = NULL; char *sort = NULL; + char *threadsort = NULL; char *pinerc_file = NULL; char *lc = NULL; - char *xoauth2_server = NULL; -*************** -*** 439,444 **** ---- 442,458 ---- + XOAUTH2_INFO_S x; +*************** Loop: while(--ac > 0) +*** 440,445 **** +--- 443,459 ---- goto Loop; } @@ -51,11 +52,12 @@ diff -rc alpine-2.23/alpine/arg.c alpine-2.23.fancy/alpine/arg.c else if(strcmp(*av, "url") == 0){ if(args->action == aaFolder && !args->data.folder){ args->action = aaURL; -diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c -*** alpine-2.23/alpine/confscroll.c 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/confscroll.c 2020-06-18 16:08:07.149575591 -0600 -*************** -*** 139,145 **** +Index: alpine-2.23.2/alpine/confscroll.c +=================================================================== +*** alpine-2.23.2.orig/alpine/confscroll.c +--- alpine-2.23.2/alpine/confscroll.c +*************** char *yesno_pretty_value(struct pine +*** 140,146 **** char *radio_pretty_value(struct pine *, CONF_S *); char *sigfile_pretty_value(struct pine *, CONF_S *); char *color_pretty_value(struct pine *, CONF_S *); @@ -63,7 +65,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c int longest_feature_name(void); COLOR_PAIR *sample_color(struct pine *, struct variable *); COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); ---- 139,145 ---- +--- 140,146 ---- char *radio_pretty_value(struct pine *, CONF_S *); char *sigfile_pretty_value(struct pine *, CONF_S *); char *color_pretty_value(struct pine *, CONF_S *); @@ -71,8 +73,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c int longest_feature_name(void); COLOR_PAIR *sample_color(struct pine *, struct variable *); COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); -*************** -*** 287,293 **** +*************** set_radio_pretty_vals(struct pine *ps, C +*** 288,294 **** CONF_S *ctmp; if(!(cl && *cl && @@ -80,7 +82,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr))) return; ---- 287,294 ---- +--- 288,295 ---- CONF_S *ctmp; if(!(cl && *cl && @@ -89,8 +91,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr))) return; -*************** -*** 2937,2943 **** +*************** radiobutton_tool(struct pine *ps, int cm +*** 2960,2966 **** } set_current_val((*cl)->var, TRUE, TRUE); @@ -98,7 +100,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c ps->def_sort = def_sort; ps->def_sort_rev = def_sort_rev; } ---- 2938,2944 ---- +--- 2961,2967 ---- } set_current_val((*cl)->var, TRUE, TRUE); @@ -106,9 +108,9 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c ps->def_sort = def_sort; ps->def_sort_rev = def_sort_rev; } -*************** -*** 2946,2951 **** ---- 2947,2983 ---- +*************** radiobutton_tool(struct pine *ps, int cm +*** 2969,2974 **** +--- 2970,3006 ---- ps->mangled_body = 1; /* BUG: redraw it all for now? */ rv = 1; } @@ -146,8 +148,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c else q_status_message(SM_ORDER | SM_DING, 3, 6, "Programmer botch! Unknown radiobutton type."); -*************** -*** 3809,3815 **** +*************** pretty_value(struct pine *ps, CONF_S *cl +*** 3832,3838 **** else if(standard_radio_var(ps, v) || v == startup_ptr) return(radio_pretty_value(ps, cl)); else if(v == &ps->vars[V_SORT_KEY]) @@ -155,7 +157,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c else if(v == &ps->vars[V_SIGNATURE_FILE]) return(sigfile_pretty_value(ps, cl)); else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) ---- 3841,3849 ---- +--- 3864,3872 ---- else if(standard_radio_var(ps, v) || v == startup_ptr) return(radio_pretty_value(ps, cl)); else if(v == &ps->vars[V_SORT_KEY]) @@ -165,8 +167,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c else if(v == &ps->vars[V_SIGNATURE_FILE]) return(sigfile_pretty_value(ps, cl)); else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) -*************** -*** 4340,4353 **** +*************** color_pretty_value(struct pine *ps, CONF +*** 4363,4376 **** char * @@ -181,7 +183,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c { char tmp[6*MAXPATH]; char *pvalnorm, *pvalexc, *pval; ---- 4374,4387 ---- +--- 4397,4410 ---- char * @@ -196,8 +198,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c { char tmp[6*MAXPATH]; char *pvalnorm, *pvalexc, *pval; -*************** -*** 4397,4403 **** +*************** generalized_sort_pretty_value(struct pin +*** 4420,4426 **** } else if(fixed){ pval = v->fixed_val.p; @@ -205,7 +207,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", ---- 4431,4437 ---- +--- 4454,4460 ---- } else if(fixed){ pval = v->fixed_val.p; @@ -213,8 +215,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", -*************** -*** 4408,4416 **** +*************** generalized_sort_pretty_value(struct pin +*** 4431,4439 **** is_the_one ? " (value is fixed)" : ""); } else if(is_set_for_this_level){ @@ -224,7 +226,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c the_exc_one = (editing_normal_which_isnt_except && pvalexc && exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", ---- 4442,4450 ---- +--- 4465,4473 ---- is_the_one ? " (value is fixed)" : ""); } else if(is_set_for_this_level){ @@ -234,8 +236,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c the_exc_one = (editing_normal_which_isnt_except && pvalexc && exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", -*************** -*** 4428,4434 **** +*************** generalized_sort_pretty_value(struct pin +*** 4451,4457 **** } else{ if(pvalexc){ @@ -243,7 +245,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = (exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", ---- 4462,4468 ---- +--- 4485,4491 ---- } else{ if(pvalexc){ @@ -251,8 +253,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = (exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", -*************** -*** 4439,4445 **** +*************** generalized_sort_pretty_value(struct pin +*** 4462,4468 **** } else{ pval = v->current_val.p; @@ -260,7 +262,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = ((pval || default_ok) && var_sort_rev == line_sort_rev && var_sort == line_sort); ---- 4473,4479 ---- +--- 4496,4502 ---- } else{ pval = v->current_val.p; @@ -268,8 +270,8 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c is_the_one = ((pval || default_ok) && var_sort_rev == line_sort_rev && var_sort == line_sort); -*************** -*** 5595,5603 **** +*************** fix_side_effects(struct pine *ps, struct +*** 5621,5629 **** else if(revert && var == &ps->vars[V_SORT_KEY]){ int def_sort_rev; @@ -279,7 +281,7 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c else if(var == &ps->vars[V_THREAD_MORE_CHAR] || var == &ps->vars[V_THREAD_EXP_CHAR] || var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ ---- 5629,5643 ---- +--- 5655,5669 ---- else if(revert && var == &ps->vars[V_SORT_KEY]){ int def_sort_rev; @@ -295,10 +297,11 @@ diff -rc alpine-2.23/alpine/confscroll.c alpine-2.23.fancy/alpine/confscroll.c else if(var == &ps->vars[V_THREAD_MORE_CHAR] || var == &ps->vars[V_THREAD_EXP_CHAR] || var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ -diff -rc alpine-2.23/alpine/confscroll.h alpine-2.23.fancy/alpine/confscroll.h -*** alpine-2.23/alpine/confscroll.h 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/confscroll.h 2020-06-18 16:08:07.149575591 -0600 -*************** +Index: alpine-2.23.2/alpine/confscroll.h +=================================================================== +*** alpine-2.23.2.orig/alpine/confscroll.h +--- alpine-2.23.2/alpine/confscroll.h +*************** int checkbox_tool(struct pine *, int, C *** 97,103 **** int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); int yesno_tool(struct pine *, int, CONF_S **, unsigned); @@ -315,10 +318,11 @@ diff -rc alpine-2.23/alpine/confscroll.h alpine-2.23.fancy/alpine/confscroll.h int exclude_config_var(struct pine *, struct variable *, int); int config_exit_cmd(unsigned); int simple_exit_cmd(unsigned); -diff -rc alpine-2.23/alpine/keymenu.c alpine-2.23.fancy/alpine/keymenu.c -*** alpine-2.23/alpine/keymenu.c 2020-06-18 15:19:23.469318992 -0600 ---- alpine-2.23.fancy/alpine/keymenu.c 2020-06-18 16:08:07.149575591 -0600 -*************** +Index: alpine-2.23.2/alpine/keymenu.c +=================================================================== +*** alpine-2.23.2.orig/alpine/keymenu.c +--- alpine-2.23.2/alpine/keymenu.c +*************** struct key index_keys[] = *** 679,688 **** RCOMPOSE_MENU, HOMEKEY_MENU, @@ -356,7 +360,7 @@ diff -rc alpine-2.23/alpine/keymenu.c alpine-2.23.fancy/alpine/keymenu.c {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, NULL_MENU}; INST_KEY_MENU(index_keymenu, index_keys); -*************** +*************** struct key thread_keys[] = *** 757,765 **** RCOMPOSE_MENU, HOMEKEY_MENU, @@ -390,7 +394,7 @@ diff -rc alpine-2.23/alpine/keymenu.c alpine-2.23.fancy/alpine/keymenu.c NULL_MENU}; INST_KEY_MENU(thread_keymenu, thread_keys); -*************** +*************** struct key view_keys[] = *** 927,933 **** NULL_MENU, NULL_MENU, @@ -420,35 +424,49 @@ diff -rc alpine-2.23/alpine/keymenu.c alpine-2.23.fancy/alpine/keymenu.c INST_KEY_MENU(view_keymenu, view_keys); -diff -rc alpine-2.23/alpine/keymenu.h alpine-2.23.fancy/alpine/keymenu.h -*** alpine-2.23/alpine/keymenu.h 2020-06-18 15:19:23.469318992 -0600 ---- alpine-2.23.fancy/alpine/keymenu.h 2020-06-18 16:08:07.149575591 -0600 -*************** -*** 217,222 **** ---- 217,235 ---- +Index: alpine-2.23.2/alpine/keymenu.h +=================================================================== +*** alpine-2.23.2.orig/alpine/keymenu.h +--- alpine-2.23.2/alpine/keymenu.h +*************** struct key_menu { +*** 217,225 **** #define MC_ADDHEADER 804 #define MC_XOAUTH2 805 #define MC_EXTERNAL 806 -+ #define MC_DELTHREAD 807 -+ #define MC_UNDTHREAD 808 -+ #define MC_SELTHREAD 809 -+ #define MC_SSUTHREAD 810 -+ #define MC_DSUTHREAD 811 -+ #define MC_USUTHREAD 812 -+ #define MC_SORTHREAD 813 -+ #define MC_NEXTHREAD 814 -+ #define MC_KOLAPSE 815 -+ #define MC_EXPTHREAD 816 -+ #define MC_PRETHREAD 817 -+ #define MC_CTHREAD 818 -+ #define MC_OTHREAD 819 - +! #define MC_XSADD 807 +! #define MC_XSDELETE 808 +! #define MC_XSHELP 809 /* Commands for S/MIME screens */ -diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c -*** alpine-2.23/alpine/mailcmd.c 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/mailcmd.c 2020-06-18 16:08:07.153575601 -0600 -*************** + #define MC_TRUST 900 +--- 217,238 ---- + #define MC_ADDHEADER 804 + #define MC_XOAUTH2 805 + #define MC_EXTERNAL 806 +! #define MC_DELTHREAD 807 +! #define MC_UNDTHREAD 808 +! #define MC_SELTHREAD 809 +! #define MC_SSUTHREAD 810 +! #define MC_DSUTHREAD 811 +! #define MC_USUTHREAD 812 +! #define MC_SORTHREAD 813 +! #define MC_NEXTHREAD 814 +! #define MC_KOLAPSE 815 +! #define MC_EXPTHREAD 816 +! #define MC_PRETHREAD 817 +! #define MC_CTHREAD 818 +! #define MC_OTHREAD 819 +! #define MC_XSADD 820 +! #define MC_XSDELETE 821 +! #define MC_XSHELP 822 + + /* Commands for S/MIME screens */ + #define MC_TRUST 900 +Index: alpine-2.23.2/alpine/mailcmd.c +=================================================================== +*** alpine-2.23.2.orig/alpine/mailcmd.c +--- alpine-2.23.2/alpine/mailcmd.c +*************** int select_by_thread(MAILSTREAM *, MSG *** 114,120 **** char *choose_a_rule(int); int select_by_keyword(MAILSTREAM *, SEARCHSET **); @@ -465,8 +483,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c int print_index(struct pine *, MSGNO_S *, int); /* -*************** -*** 1434,1440 **** +*************** get_out: +*** 1435,1441 **** if(any_messages(msgmap, NULL, NULL)){ if(any_lflagged(msgmap, MN_SLCT) > 0L){ if(apply_command(state, stream, msgmap, 0, @@ -474,7 +492,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c if(F_ON(F_AUTO_UNSELECT, state)){ agg_select_all(stream, msgmap, NULL, 0); unzoom_index(state, stream, msgmap); ---- 1434,1440 ---- +--- 1435,1441 ---- if(any_messages(msgmap, NULL, NULL)){ if(any_lflagged(msgmap, MN_SLCT) > 0L){ if(apply_command(state, stream, msgmap, 0, @@ -482,8 +500,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c if(F_ON(F_AUTO_UNSELECT, state)){ agg_select_all(stream, msgmap, NULL, 0); unzoom_index(state, stream, msgmap); -*************** -*** 1452,1474 **** +*************** get_out: +*** 1453,1475 **** /*-------- Sort command -------*/ case MC_SORT : @@ -507,7 +525,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } state->mangled_footer = 1; ---- 1452,1486 ---- +--- 1453,1487 ---- /*-------- Sort command -------*/ case MC_SORT : @@ -543,9 +561,9 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } state->mangled_footer = 1; -*************** -*** 3313,3318 **** ---- 3325,3334 ---- +*************** cmd_expunge(struct pine *state, MAILSTRE +*** 3314,3319 **** +--- 3326,3335 ---- if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); @@ -556,9 +574,9 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c state->mangled_body = 1; state->mangled_header = 1; q_status_message2(SM_ORDER, 0, 4, -*************** -*** 3414,3419 **** ---- 3430,3438 ---- +*************** cmd_expunge(struct pine *state, MAILSTRE +*** 3415,3420 **** +--- 3431,3439 ---- */ if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); @@ -568,8 +586,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } else{ if(del_count){ -*************** -*** 7307,7313 **** +*************** select_by_current(struct pine *state, MS +*** 7324,7330 **** * Maybe it makes sense to zoom after a select but not after a colon * command even though they are very similar. */ @@ -577,7 +595,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } else{ if((all_selected = ---- 7326,7332 ---- +--- 7343,7349 ---- * Maybe it makes sense to zoom after a select but not after a colon * command even though they are very similar. */ @@ -585,8 +603,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } else{ if((all_selected = -*************** -*** 7363,7369 **** +*************** select_by_current(struct pine *state, MS +*** 7380,7386 **** ----*/ int apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, @@ -594,7 +612,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c { int i = 8, /* number of static entries in sel_opts3 */ rv = 0, ---- 7382,7388 ---- +--- 7399,7405 ---- ----*/ int apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, @@ -602,8 +620,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c { int i = 8, /* number of static entries in sel_opts3 */ rv = 0, -*************** -*** 7530,7538 **** +*************** apply_command(struct pine *state, MAILST +*** 7547,7555 **** collapse_or_expand(state, stream, msgmap, F_ON(F_SLASH_COLL_ENTIRE, ps_global) ? 0L @@ -613,7 +631,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c case ':' : select_thread_stmp(state, stream, msgmap); break; ---- 7549,7567 ---- +--- 7566,7584 ---- collapse_or_expand(state, stream, msgmap, F_ON(F_SLASH_COLL_ENTIRE, ps_global) ? 0L @@ -633,8 +651,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c case ':' : select_thread_stmp(state, stream, msgmap); break; -*************** -*** 9578,9587 **** +*************** Args: state -- pine state pointer +*** 9595,9604 **** Returns 0 if it was cancelled, 1 otherwise. ----*/ int @@ -645,7 +663,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c int deefault = 'a', retval = 1; HelpType help; ESCKEY_S sorts[14]; ---- 9607,9616 ---- +--- 9624,9633 ---- Returns 0 if it was cancelled, 1 otherwise. ----*/ int @@ -656,8 +674,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c int deefault = 'a', retval = 1; HelpType help; ESCKEY_S sorts[14]; -*************** -*** 9614,9630 **** +*************** select_sort(struct pine *state, int ql, +*** 9631,9647 **** strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), sizeof(prompt)); @@ -675,7 +693,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } sorts[i].ch = 'r'; ---- 9643,9668 ---- +--- 9660,9685 ---- strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), sizeof(prompt)); @@ -702,8 +720,8 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c } sorts[i].ch = 'r'; -*************** -*** 9648,9655 **** +*************** select_sort(struct pine *state, int ql, +*** 9665,9672 **** state->mangled_body = 1; /* signal screen's changed */ if(s == 'r') *rev = !mn_get_revsort(state->msgmap); @@ -712,7 +730,7 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c if(F_ON(F_SHOW_SORT, ps_global)) ps_global->mangled_header = 1; ---- 9686,9702 ---- +--- 9703,9719 ---- state->mangled_body = 1; /* signal screen's changed */ if(s == 'r') *rev = !mn_get_revsort(state->msgmap); @@ -730,9 +748,9 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c if(F_ON(F_SHOW_SORT, ps_global)) ps_global->mangled_header = 1; -*************** -*** 10033,10035 **** ---- 10080,10457 ---- +*************** flag_submenu(mc) +*** 10050,10052 **** +--- 10097,10474 ---- } #endif /* _WINDOWS */ @@ -1111,10 +1129,11 @@ diff -rc alpine-2.23/alpine/mailcmd.c alpine-2.23.fancy/alpine/mailcmd.c + expand_thread(state, stream, msgmap, 0); + } + -diff -rc alpine-2.23/alpine/mailcmd.h alpine-2.23.fancy/alpine/mailcmd.h -*** alpine-2.23/alpine/mailcmd.h 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/mailcmd.h 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/mailcmd.h +=================================================================== +*** alpine-2.23.2.orig/alpine/mailcmd.h +--- alpine-2.23.2/alpine/mailcmd.h +*************** char *broach_folder(int, int, int *, *** 90,96 **** int ask_mailbox_reopen(struct pine *, int *); void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); @@ -1131,7 +1150,7 @@ diff -rc alpine-2.23/alpine/mailcmd.h alpine-2.23.fancy/alpine/mailcmd.h char **choose_list_of_keywords(void); char *choose_a_charset(int); char **choose_list_of_charsets(void); -*************** +*************** int any_selected_callback(int, long) *** 108,113 **** int flag_callback(int, long); MPopup *flag_submenu(MESSAGECACHE *); @@ -1155,10 +1174,11 @@ diff -rc alpine-2.23/alpine/mailcmd.h alpine-2.23.fancy/alpine/mailcmd.h ! int expand_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); #endif /* PINE_MAILCMD_INCLUDED */ -diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c -*** alpine-2.23/alpine/mailindx.c 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/mailindx.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/mailindx.c +=================================================================== +*** alpine-2.23.2.orig/alpine/mailindx.c +--- alpine-2.23.2/alpine/mailindx.c +*************** index_lister(struct pine *state, CONTEXT *** 564,569 **** --- 564,570 ---- @@ -1168,7 +1188,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c (void) process_cmd(state, stream, msgmap, MC_PREVITEM, (style == MsgIndex || style == MultiMsgIndex -*************** +*************** index_lister(struct pine *state, CONTEXT *** 581,586 **** --- 582,588 ---- @@ -1178,7 +1198,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c /* * Special Page framing handling here. If we * did something that should scroll-by-a-line, frame -*************** +*************** view_a_thread: *** 798,803 **** --- 800,806 ---- @@ -1188,7 +1208,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c if(any_lflagged(msgmap, MN_SLCT)){ PINETHRD_S *thrd, *topthrd; for(i = 1L; i > 0L && i <= mn_get_total(msgmap);){ -*************** +*************** view_a_thread: *** 863,869 **** && mp.col == id.plus_col && style != ThreadIndex){ @@ -1205,7 +1225,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c } else if (mp.doubleclick){ if(mp.button == M_BUTTON_LEFT){ -*************** +*************** view_a_thread: *** 972,980 **** @@ -1322,7 +1342,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c case MC_DELETE : case MC_UNDELETE : case MC_REPLY : -*************** +*************** view_a_thread: *** 995,1007 **** if(rawno) thrd = fetch_thread(stream, rawno); @@ -1350,7 +1370,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c /* increment current */ if(cmd == MC_DELETE){ advance_cur_after_delete(state, stream, msgmap, -*************** +*************** top_ent_calc(MAILSTREAM *stream, MSGNO_S *** 2692,2697 **** --- 2790,2796 ---- n = mn_raw2m(msgs, thrd->rawno); @@ -1360,7 +1380,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c if(!msgline_hidden(stream, msgs, n, 0) && (++m % lines_per_page) == 1L) t = n; -*************** +*************** top_ent_calc(MAILSTREAM *stream, MSGNO_S *** 2760,2770 **** /* n is the end of this thread */ @@ -1386,7 +1406,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c else thrd = NULL; } -*************** +*************** warn_other_cmds(void) *** 2872,2878 **** void @@ -1403,7 +1423,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c { PINETHRD_S *thrd = NULL; unsigned long rawno, save_branch; -*************** +*************** thread_command(struct pine *state, MAILS *** 2921,2927 **** cancel_busy_cue(0); @@ -1420,7 +1440,7 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c /* restore the original flags */ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); -*************** +*************** index_sort_callback(set, order) *** 3438,3444 **** if(set){ sort_folder(ps_global->mail_stream, ps_global->msgmap, @@ -1437,10 +1457,11 @@ diff -rc alpine-2.23/alpine/mailindx.c alpine-2.23.fancy/alpine/mailindx.c mswin_beginupdate(); update_titlebar_message(); update_titlebar_status(); -diff -rc alpine-2.23/alpine/mailindx.h alpine-2.23.fancy/alpine/mailindx.h -*** alpine-2.23/alpine/mailindx.h 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/mailindx.h 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/mailindx.h +=================================================================== +*** alpine-2.23.2.orig/alpine/mailindx.h +--- alpine-2.23.2/alpine/mailindx.h +*************** int truncate_subj_and_from_strings(voi *** 103,109 **** void paint_index_hline(MAILSTREAM *, long, ICE_S *); void setup_index_state(int); @@ -1457,10 +1478,11 @@ diff -rc alpine-2.23/alpine/mailindx.h alpine-2.23.fancy/alpine/mailindx.h COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); #ifdef _WINDOWS int index_sort_callback(int, long); -diff -rc alpine-2.23/alpine/mailview.c alpine-2.23.fancy/alpine/mailview.c -*** alpine-2.23/alpine/mailview.c 2020-06-18 15:19:23.469318992 -0600 ---- alpine-2.23.fancy/alpine/mailview.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/mailview.c +=================================================================== +*** alpine-2.23.2.orig/alpine/mailview.c +--- alpine-2.23.2/alpine/mailview.c +*************** scrolltool(SCROLL_S *sparms) *** 3495,3500 **** --- 3495,3546 ---- print_to_printer(sparms); @@ -1515,10 +1537,11 @@ diff -rc alpine-2.23/alpine/mailview.c alpine-2.23.fancy/alpine/mailview.c /* ------- First handle on Line ------ */ case MC_GOTOBOL : -diff -rc alpine-2.23/alpine/roleconf.c alpine-2.23.fancy/alpine/roleconf.c -*** alpine-2.23/alpine/roleconf.c 2020-06-18 15:19:23.469318992 -0600 ---- alpine-2.23.fancy/alpine/roleconf.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/roleconf.c +=================================================================== +*** alpine-2.23.2.orig/alpine/roleconf.c +--- alpine-2.23.2/alpine/roleconf.c +*************** role_config_edit_screen(struct pine *ps, *** 4478,4488 **** ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; @@ -1543,7 +1566,7 @@ diff -rc alpine-2.23/alpine/roleconf.c alpine-2.23.fancy/alpine/roleconf.c /* allow user to set their default sort order */ new_confline(&ctmp)->var = &sort_act_var; -*************** +*************** role_config_edit_screen(struct pine *ps, *** 4492,4498 **** ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; @@ -1560,7 +1583,7 @@ diff -rc alpine-2.23/alpine/roleconf.c alpine-2.23.fancy/alpine/roleconf.c for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ -*************** +*************** role_config_edit_screen(struct pine *ps, *** 4504,4510 **** ctmp->valoffset = rindent; ctmp->varmem = i + (j * EndofList); @@ -1577,7 +1600,7 @@ diff -rc alpine-2.23/alpine/roleconf.c alpine-2.23.fancy/alpine/roleconf.c } } -*************** +*************** role_config_edit_screen(struct pine *ps, *** 5437,5443 **** (*result)->patgrp->stat_boy = PAT_STAT_EITHER; @@ -1594,10 +1617,11 @@ diff -rc alpine-2.23/alpine/roleconf.c alpine-2.23.fancy/alpine/roleconf.c (*result)->action->sort_is_set = 1; (*result)->action->sortorder = def_sort; (*result)->action->revsort = (def_sort_rev ? 1 : 0); -diff -rc alpine-2.23/alpine/setup.c alpine-2.23.fancy/alpine/setup.c -*** alpine-2.23/alpine/setup.c 2020-06-18 15:19:23.465318999 -0600 ---- alpine-2.23.fancy/alpine/setup.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/alpine/setup.c +=================================================================== +*** alpine-2.23.2.orig/alpine/setup.c +--- alpine-2.23.2/alpine/setup.c +*************** option_screen(struct pine *ps, int edit_ *** 262,268 **** ctmpa->flags |= CF_NOSELECT; ctmpa->value = cpystr("--- ----------------------"); @@ -1614,7 +1638,7 @@ diff -rc alpine-2.23/alpine/setup.c alpine-2.23.fancy/alpine/setup.c for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ -*************** +*************** option_screen(struct pine *ps, int edit_ *** 277,282 **** --- 277,331 ---- } @@ -1672,7 +1696,7 @@ diff -rc alpine-2.23/alpine/setup.c alpine-2.23.fancy/alpine/setup.c else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */ ctmpa->keymenu = &config_yesno_keymenu; ctmpa->tool = yesno_tool; -*************** +*************** option_screen(struct pine *ps, int edit_ *** 469,474 **** --- 518,532 ---- } @@ -1690,10 +1714,11 @@ diff -rc alpine-2.23/alpine/setup.c alpine-2.23.fancy/alpine/setup.c treat_color_vars_as_text = 0; free_saved_config(ps, &vsave, expose_hidden_config); #ifdef _WINDOWS -diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c -*** alpine-2.23/pith/conf.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/conf.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/pith/conf.c +=================================================================== +*** alpine-2.23.2.orig/pith/conf.c +--- alpine-2.23.2/pith/conf.c +*************** CONF_TXT_T cf_text_fcc_name_rule[] = "De *** 207,212 **** --- 207,214 ---- @@ -1704,9 +1729,9 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\"."; CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\"."; -*************** -*** 539,544 **** ---- 541,548 ---- +*************** static struct variable variables[] = { +*** 541,546 **** +--- 543,550 ---- NULL, cf_text_fcc_name_rule}, {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_sort_key}, @@ -1715,8 +1740,8 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c {"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, "Address Book Sort Rule", cf_text_addrbook_sort_rule}, {"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -*************** -*** 1607,1613 **** +*************** init_vars(struct pine *ps, void (*cmds_f +*** 1613,1619 **** register struct variable *vars = ps->vars; int obs_header_in_reply = 0, /* the obs_ variables are to */ obs_old_style_reply = 0, /* support backwards compatibility */ @@ -1724,7 +1749,7 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; ---- 1611,1617 ---- +--- 1617,1623 ---- register struct variable *vars = ps->vars; int obs_header_in_reply = 0, /* the obs_ variables are to */ obs_old_style_reply = 0, /* support backwards compatibility */ @@ -1732,9 +1757,9 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; -*************** -*** 1635,1640 **** ---- 1639,1645 ---- +*************** init_vars(struct pine *ps, void (*cmds_f +*** 1641,1646 **** +--- 1645,1651 ---- GLO_FEATURE_LEVEL = cpystr("sappling"); GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); GLO_SORT_KEY = cpystr(DF_SORT_KEY); @@ -1742,8 +1767,8 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE); GLO_FCC_RULE = cpystr(DF_FCC_RULE); GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE); -*************** -*** 2586,2592 **** +*************** init_vars(struct pine *ps, void (*cmds_f +*** 2594,2600 **** set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); @@ -1751,7 +1776,7 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ps->def_sort = SortArrival; ---- 2591,2597 ---- +--- 2599,2605 ---- set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); @@ -1759,9 +1784,9 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ps->def_sort = SortArrival; -*************** -*** 2595,2600 **** ---- 2600,2616 ---- +*************** init_vars(struct pine *ps, void (*cmds_f +*** 2603,2608 **** +--- 2608,2624 ---- else ps->def_sort_rev = def_sort_rev; @@ -1779,9 +1804,9 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE); {NAMEVAL_S *v; int i; for(i = 0; (v = save_msg_rules(i)); i++) -*************** -*** 3020,3025 **** ---- 3036,3043 ---- +*************** feature_list(int index) +*** 3028,3033 **** +--- 3044,3051 ---- F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0}, {"thread-sorts-by-arrival", "Thread Sorts by Arrival", F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0}, @@ -1790,9 +1815,9 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c /* Viewer prefs */ {"enable-msg-view-addresses", "Enable Message View Address Links", -*************** -*** 7783,7788 **** ---- 7801,7808 ---- +*************** config_help(int var, int feature) +*** 7792,7797 **** +--- 7810,7817 ---- return(h_config_fcc_rule); case V_SORT_KEY : return(h_config_sort_key); @@ -1801,9 +1826,10 @@ diff -rc alpine-2.23/pith/conf.c alpine-2.23.fancy/pith/conf.c case V_AB_SORT_RULE : return(h_config_ab_sort_rule); case V_FLD_SORT_RULE : -diff -rc alpine-2.23/pith/conf.h alpine-2.23.fancy/pith/conf.h -*** alpine-2.23/pith/conf.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/conf.h 2020-06-18 16:08:07.153575601 -0600 +Index: alpine-2.23.2/pith/conf.h +=================================================================== +*** alpine-2.23.2.orig/pith/conf.h +--- alpine-2.23.2/pith/conf.h *************** *** 150,155 **** --- 150,158 ---- @@ -1816,10 +1842,11 @@ diff -rc alpine-2.23/pith/conf.h alpine-2.23.fancy/pith/conf.h #define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p #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 -diff -rc alpine-2.23/pith/conftype.h alpine-2.23.fancy/pith/conftype.h -*** alpine-2.23/pith/conftype.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/conftype.h 2020-06-18 16:08:07.153575601 -0600 -*************** +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 *** 59,64 **** --- 59,65 ---- , V_SAVED_MSG_NAME_RULE @@ -1829,9 +1856,9 @@ diff -rc alpine-2.23/pith/conftype.h alpine-2.23.fancy/pith/conftype.h , V_AB_SORT_RULE , V_FLD_SORT_RULE , V_GOTO_DEFAULT_RULE -*************** -*** 526,531 **** ---- 527,533 ---- +*************** typedef enum { +*** 529,534 **** +--- 530,536 ---- F_QUELL_TIMEZONE, F_QUELL_USERAGENT, F_COLOR_LINE_IMPORTANT, @@ -1839,19 +1866,20 @@ diff -rc alpine-2.23/pith/conftype.h alpine-2.23.fancy/pith/conftype.h F_SLASH_COLL_ENTIRE, F_ENABLE_FULL_HDR_AND_TEXT, F_QUELL_FULL_HDR_RESET, -*************** -*** 791,795 **** ---- 793,798 ---- +*************** typedef struct smime_stuff { +*** 794,798 **** +--- 796,801 ---- /* exported prototypes */ + #define DF_THREAD_SORT_KEY "thread" #endif /* PITH_CONFTYPE_INCLUDED */ -diff -rc alpine-2.23/pith/flag.c alpine-2.23.fancy/pith/flag.c -*** alpine-2.23/pith/flag.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/flag.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/pith/flag.c +=================================================================== +*** alpine-2.23.2.orig/pith/flag.c +--- alpine-2.23.2/pith/flag.c +*************** set_lflag(MAILSTREAM *stream, MSGNO_S *m *** 594,607 **** was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; @@ -1884,10 +1912,11 @@ diff -rc alpine-2.23/pith/flag.c alpine-2.23.fancy/pith/flag.c } if(topthrd){ -diff -rc alpine-2.23/pith/indxtype.h alpine-2.23.fancy/pith/indxtype.h -*** alpine-2.23/pith/indxtype.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/indxtype.h 2020-06-18 16:08:07.153575601 -0600 -*************** +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 *** 78,84 **** iKey, iKeyInit, iPrefDate, iPrefTime, iPrefDateTime, @@ -1904,10 +1933,11 @@ diff -rc alpine-2.23/pith/indxtype.h alpine-2.23.fancy/pith/indxtype.h iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, iCurNews, iArrow, -diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c -*** alpine-2.23/pith/mailindx.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/mailindx.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/pith/mailindx.c +=================================================================== +*** alpine-2.23.2.orig/pith/mailindx.c +--- alpine-2.23.2/pith/mailindx.c +*************** init_index_format(char *format, INDEX_CO *** 229,234 **** --- 229,235 ---- case iSTime: @@ -1917,7 +1947,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c case iPrioAlpha: (*answer)[column].req_width = 7; break; -*************** +*************** static INDEX_PARSE_T itokens[] = { *** 456,461 **** --- 457,463 ---- {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, @@ -1927,7 +1957,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -*************** +*************** static IndexColType fixed_ctypes[] = { *** 954,960 **** iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, iSDateTimeIso24, iSDateTimeIsoS24, @@ -1944,7 +1974,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c iPrio, iPrioBang, iPrioAlpha, iInit, iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek -*************** +*************** setup_index_header_widths(MAILSTREAM *st *** 1147,1152 **** --- 1149,1155 ---- case iTime12: @@ -1954,7 +1984,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c cdesc->actual_length = 7; cdesc->adjustment = Right; break; -*************** +*************** setup_index_header_widths(MAILSTREAM *st *** 1241,1247 **** cdesc->ctype != iNothing; cdesc++) @@ -1971,7 +2001,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ if(cdesc->actual_length == 0){ if((fix=cdesc->width) > 0){ /* had this reserved */ -*************** +*************** build_header_work(struct pine *state, MA *** 1627,1636 **** /* find next thread which is visible */ @@ -1996,7 +2026,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c else thrd = NULL; } while(thrd -*************** +*************** format_index_index_line(INDEXDATA_S *ida *** 2042,2054 **** */ ice = copy_ice(ice); @@ -2022,7 +2052,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c /* calculate contents of the required fields */ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) -*************** +*************** format_index_index_line(INDEXDATA_S *ida *** 2550,2556 **** --- 2552,2581 ---- @@ -2055,7 +2085,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c /* 0 ... 9999 */ if((l = fetch_size(idata)) < 10*1000L) snprintf(str, sizeof(str), "(%lu)", l); -*************** +*************** subj_str(INDEXDATA_S *idata, char *str, *** 5582,5591 **** if(pith_opt_condense_thread_cue) @@ -2076,7 +2106,7 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c /* * width is < available strsize and -*************** +*************** from_str(IndexColType ctype, INDEXDATA_S *** 6211,6221 **** border = str + width; if(pith_opt_condense_thread_cue) @@ -2098,10 +2128,11 @@ diff -rc alpine-2.23/pith/mailindx.c alpine-2.23.fancy/pith/mailindx.c fptr = str; if(thd) -diff -rc alpine-2.23/pith/pattern.c alpine-2.23.fancy/pith/pattern.c -*** alpine-2.23/pith/pattern.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/pattern.c 2020-06-18 16:08:07.153575601 -0600 -*************** +Index: alpine-2.23.2/pith/pattern.c +=================================================================== +*** alpine-2.23.2.orig/pith/pattern.c +--- alpine-2.23.2/pith/pattern.c +*************** parse_action_slash(char *str, ACTION_S * *** 1756,1762 **** SortOrder def_sort; int def_sort_rev; @@ -2118,12 +2149,13 @@ diff -rc alpine-2.23/pith/pattern.c alpine-2.23.fancy/pith/pattern.c action->sort_is_set = 1; action->sortorder = def_sort; action->revsort = (def_sort_rev ? 1 : 0); -diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp -*** alpine-2.23/pith/pine.hlp 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/pine.hlp 2020-06-18 16:08:07.157575611 -0600 -*************** -*** 4714,4719 **** ---- 4714,4720 ---- +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 +*** 4839,4844 **** +--- 4839,4845 ----
  • OPTION:
  • OPTION:
  • OPTION: @@ -2131,9 +2163,9 @@ diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp
  • OPTION:
  • OPTION:
  • OPTION: -*************** -*** 6655,6660 **** ---- 6656,6818 ---- +*************** the names of the carbon copy addresses o +*** 6780,6785 **** +--- 6781,6943 ---- <End of help on this topic> @@ -2297,9 +2329,9 @@ diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp ======= h_index_cmd_whereis ======= -*************** -*** 20153,20158 **** ---- 20311,20324 ---- +*************** The progression of sizes used looks like +*** 20325,20330 **** +--- 20483,20496 ----

    @@ -2314,9 +2346,9 @@ diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp

    SIZENARROW
    This token represents the total size, in bytes, of the message. -*************** -*** 23776,23781 **** ---- 23942,23986 ---- +*************** command, then it will not be re-sorted u +*** 23948,23953 **** +--- 24114,24158 ---- <End of help on this topic> @@ -2362,9 +2394,9 @@ diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp ====== h_config_other_startup ===== -*************** -*** 31850,31855 **** ---- 32055,32077 ---- +*************** Reply Use, Forward Use, and Compose Use. +*** 32059,32064 **** +--- 32264,32286 ---- <End of help on this topic> @@ -2388,10 +2420,11 @@ diff -rc alpine-2.23/pith/pine.hlp alpine-2.23.fancy/pith/pine.hlp ====== h_config_news_cross_deletes ===== -diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c -*** alpine-2.23/pith/sort.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/sort.c 2020-06-18 16:08:07.157575611 -0600 -*************** +Index: alpine-2.23.2/pith/sort.c +=================================================================== +*** alpine-2.23.2.orig/pith/sort.c +--- alpine-2.23.2/pith/sort.c +*************** Args: msgmap -- *** 91,97 **** ----*/ void @@ -2408,7 +2441,7 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c { long raw_current, i, j; unsigned long *sort = NULL; -*************** +*************** sort_folder(MAILSTREAM *stream, MSGNO_S *** 101,106 **** --- 101,115 ---- int current_rev; @@ -2426,7 +2459,7 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c dprint((2, "Sorting by %s%s\n", sort_name(new_sort), new_rev ? "/reverse" : "")); -*************** +*************** percent_sorted(void) *** 530,549 **** * argument also means arrival/reverse. */ @@ -2469,7 +2502,7 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c *def_sort_rev = 1; return(0); } -*************** +*************** decode_sort(char *sort_spec, SortOrder * *** 572,578 **** if(ps_global->sort_types[x] == EndofList) return(-1); @@ -2486,7 +2519,7 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c *def_sort_rev = reverse; return(0); } -*************** +*************** reset_sort_order(unsigned int flags) *** 689,695 **** /* set default order */ @@ -2505,7 +2538,7 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ -*************** +*************** reset_sort_order(unsigned int flags) *** 702,710 **** && pat->action->sort_is_set){ the_sort_order = pat->action->sortorder; @@ -2532,9 +2565,10 @@ diff -rc alpine-2.23/pith/sort.c alpine-2.23.fancy/pith/sort.c sort_folder(ps_global->mail_stream, ps_global->msgmap, ! the_sort_order, sort_is_rev, flags, 1); } -diff -rc alpine-2.23/pith/sort.h alpine-2.23.fancy/pith/sort.h -*** alpine-2.23/pith/sort.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/sort.h 2020-06-18 16:08:07.157575611 -0600 +Index: alpine-2.23.2/pith/sort.h +=================================================================== +*** alpine-2.23.2.orig/pith/sort.h +--- alpine-2.23.2/pith/sort.h *************** *** 23,29 **** @@ -2552,7 +2586,7 @@ diff -rc alpine-2.23/pith/sort.h alpine-2.23.fancy/pith/sort.h struct global_sort_data { MSGNO_S *msgmap; -*************** +*************** extern struct global_sort_data g_sort; *** 42,49 **** /* exported prototypes */ @@ -2571,10 +2605,11 @@ diff -rc alpine-2.23/pith/sort.h alpine-2.23.fancy/pith/sort.h void reset_sort_order(unsigned); -diff -rc alpine-2.23/pith/state.c alpine-2.23.fancy/pith/state.c -*** alpine-2.23/pith/state.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/state.c 2020-06-18 16:08:07.157575611 -0600 -*************** +Index: alpine-2.23.2/pith/state.c +=================================================================== +*** alpine-2.23.2.orig/pith/state.c +--- alpine-2.23.2/pith/state.c +*************** new_pine_struct(void) *** 75,80 **** --- 75,81 ---- @@ -2584,10 +2619,11 @@ diff -rc alpine-2.23/pith/state.c alpine-2.23.fancy/pith/state.c p->def_sort = SortArrival; p->sort_types[0] = SortSubject; p->sort_types[1] = SortArrival; -diff -rc alpine-2.23/pith/state.h alpine-2.23.fancy/pith/state.h -*** alpine-2.23/pith/state.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/state.h 2020-06-18 16:08:07.157575611 -0600 -*************** +Index: alpine-2.23.2/pith/state.h +=================================================================== +*** alpine-2.23.2.orig/pith/state.h +--- alpine-2.23.2/pith/state.h +*************** struct pine { *** 148,153 **** --- 148,155 ---- unsigned unseen_in_view:1; @@ -2598,7 +2634,7 @@ diff -rc alpine-2.23/pith/state.h alpine-2.23.fancy/pith/state.h unsigned restricted:1; unsigned tcptimeout:1; /* a tcp timeout is in progress */ -*************** +*************** struct pine { *** 311,316 **** --- 313,321 ---- EditWhich ew_for_srch_take; @@ -2610,10 +2646,11 @@ diff -rc alpine-2.23/pith/state.h alpine-2.23.fancy/pith/state.h sort_types[22]; int last_expire_year, last_expire_month; -diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c -*** alpine-2.23/pith/thread.c 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/thread.c 2020-06-18 16:08:07.157575611 -0600 -*************** +Index: alpine-2.23.2/pith/thread.c +=================================================================== +*** alpine-2.23.2.orig/pith/thread.c +--- alpine-2.23.2/pith/thread.c +*************** static char rcsid[] = "$Id: thread.c 942 *** 30,41 **** #include "../pith/mailcmd.h" #include "../pith/ablookup.h" @@ -2646,7 +2683,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); THREADNODE *collapse_threadnode_tree(THREADNODE *); THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); -*************** +*************** THREADNODE *sort_threads_and_collapse( *** 43,48 **** --- 49,55 ---- THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *); @@ -2656,7 +2693,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c PINETHRD_S * -*************** +*************** void *** 95,114 **** set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) { @@ -2701,7 +2738,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(bthrd) set_flags_for_thread(stream, msgmap, f, bthrd, v); } -*************** +*************** erase_threading_info(MAILSTREAM *stream, *** 122,128 **** MESSAGECACHE *mc; PINELT_S *peltp; @@ -2718,7 +2755,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c return; ps_global->view_skipped_index = 0; -*************** +*************** sort_thread_callback(MAILSTREAM *stream, *** 155,161 **** PINETHRD_S *thrd = NULL; unsigned long msgno, rawno; @@ -2735,7 +2772,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c char *dup_chk = NULL; -*************** +*************** sort_thread_callback(MAILSTREAM *stream, *** 168,177 **** * way. If the dummy node is at the top-level, then its children are * promoted to the top-level as separate threads. @@ -2759,7 +2796,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c /* dup_chk is like sort with an origin of 1 */ dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); -*************** +*************** sort_thread_callback(MAILSTREAM *stream, *** 182,188 **** (void) sort_thread_flatten(collapsed_tree, stream, &g_sort.msgmap->sort[1], @@ -2776,7 +2813,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c /* reset the inverse array */ msgno_reset_isort(g_sort.msgmap); -*************** +*************** sort_thread_callback(MAILSTREAM *stream, *** 340,351 **** else{ thrd = fetch_head_thread(stream); @@ -2805,7 +2842,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(thrd->next){ PINETHRD_S *nthrd; -*************** +*************** sort_thread_callback(MAILSTREAM *stream, *** 359,367 **** MN_COLL)); } @@ -2827,7 +2864,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c thrd = NULL; } } -*************** +*************** make_thrdflags_consistent(MAILSTREAM *st *** 412,418 **** int a_parent_is_collapsed) { @@ -2844,7 +2881,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(!thrd) return; -*************** +*************** make_thrdflags_consistent(MAILSTREAM *st *** 430,437 **** set_lflag(stream, msgmap, msgno, MN_CHID, 0); } @@ -2863,7 +2900,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(nthrd) make_thrdflags_consistent(stream, msgmap, nthrd, a_parent_is_collapsed -*************** +*************** make_thrdflags_consistent(MAILSTREAM *st *** 440,447 **** MN_COLL)); } @@ -2882,7 +2919,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(bthrd) make_thrdflags_consistent(stream, msgmap, bthrd, a_parent_is_collapsed); -*************** +*************** calculate_visible_threads(MAILSTREAM *st *** 488,496 **** long * sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, @@ -2904,7 +2941,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(node){ if(node->num > 0L && node->num <= maxno){ /* holes happen */ -*************** +*************** sort_thread_flatten(THREADNODE *node, MA *** 498,503 **** --- 512,520 ---- *entry = node->num; @@ -2916,7 +2953,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c /* * Build a richer threading structure that will help us paint * and operate on threads and subthreads. -*************** +*************** sort_thread_flatten(THREADNODE *node, MA *** 506,525 **** if(newthrd){ entry++; @@ -2990,7 +3027,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c } return(entry); -*************** +*************** msgno_thread_info(MAILSTREAM *stream, lo *** 788,794 **** */ void @@ -3007,7 +3044,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c { int collapsed, adjust_current = 0; PINETHRD_S *thrd = NULL, *nthrd; -*************** +*************** collapse_or_expand(struct pine *state, M *** 841,847 **** if(!thrd) return; @@ -3024,7 +3061,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(collapsed){ msgno = mn_raw2m(msgmap, thrd->rawno); -*************** +*************** collapse_or_expand(struct pine *state, M *** 859,871 **** msgno = mn_raw2m(msgmap, thrd->rawno); if(msgno > 0L && msgno <= mn_get_total(msgmap)){ @@ -3053,7 +3090,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c q_status_message(SM_ORDER, 0, 1, _("No thread to collapse or expand on this line")); -*************** +*************** count_flags_in_thread(MAILSTREAM *stream *** 952,969 **** unsigned long count = 0; PINETHRD_S *nthrd, *bthrd; @@ -3093,7 +3130,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(bthrd) count += count_flags_in_thread(stream, bthrd, flags); } -*************** +*************** int *** 1051,1070 **** mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) { @@ -3137,7 +3174,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(bthrd) count += mark_msgs_in_thread(stream, bthrd, msgmap); } -*************** +*************** set_thread_lflags(MAILSTREAM *stream, PI *** 1098,1104 **** /* flags to set or clear */ /* set or clear? */ @@ -3154,7 +3191,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) -*************** +*************** set_thread_lflags(MAILSTREAM *stream, PI *** 1122,1135 **** if(msgno > 0L && flags == MN_CHID2 && v == 1) clear_index_cache_ent(stream, msgno, 0); @@ -3185,7 +3222,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(bthrd) set_thread_lflags(stream, bthrd, msgmap, flags, v); } -*************** +*************** to_us_symbol_for_thread(MAILSTREAM *stre *** 1218,1236 **** char to_us = ' '; char branch_to_us = ' '; @@ -3227,7 +3264,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c bthrd = fetch_thread(stream, thrd->branch); if(bthrd) branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); -*************** +*************** to_us_symbol_for_thread(MAILSTREAM *stre *** 1280,1286 **** break; } @@ -3244,7 +3281,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c to_us = '+'; if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) -*************** +*************** set_thread_subtree(MAILSTREAM *stream, P *** 1328,1334 **** set_lflag(stream, msgmap, msgno, flags, v); @@ -3262,7 +3299,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_subtree(stream, nthrd, msgmap, v, flags); -*************** +*************** view_thread(struct pine *state, MAILSTRE *** 1368,1375 **** if(rawno) thrd = fetch_thread(stream, rawno); @@ -3281,7 +3318,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(!thrd) return 0; -*************** +*************** unview_thread(struct pine *state, MAILST *** 1433,1439 **** thrd = fetch_thread(stream, rawno); @@ -3298,7 +3335,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(!topthrd) return 0; -*************** +*************** void *** 1539,1544 **** --- 1591,1597 ---- set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset) @@ -3308,7 +3345,7 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c if(!(stream && thrd)) return; -*************** +*************** set_search_bit_for_thread(MAILSTREAM *st *** 1547,1561 **** && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) mm_searched(stream, thrd->rawno); @@ -3948,10 +3985,11 @@ diff -rc alpine-2.23/pith/thread.c alpine-2.23.fancy/pith/thread.c + || sort == SortSize; + } + -diff -rc alpine-2.23/pith/thread.h alpine-2.23.fancy/pith/thread.h -*** alpine-2.23/pith/thread.h 2020-06-18 15:19:23.461319005 -0600 ---- alpine-2.23.fancy/pith/thread.h 2020-06-18 16:08:07.157575611 -0600 -*************** +Index: alpine-2.23.2/pith/thread.h +=================================================================== +*** alpine-2.23.2.orig/pith/thread.h +--- alpine-2.23.2/pith/thread.h +*************** typedef struct pine_thrd { *** 38,43 **** --- 38,44 ---- unsigned long nextthd; /* next thread, only tops have this */ @@ -3961,7 +3999,7 @@ diff -rc alpine-2.23/pith/thread.h alpine-2.23.fancy/pith/thread.h unsigned long head; /* head of the whole thread list */ } PINETHRD_S; -*************** +*************** void erase_threading_info(MAILSTRE *** 93,99 **** void sort_thread_callback(MAILSTREAM *, THREADNODE *); void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); @@ -3978,7 +4016,7 @@ diff -rc alpine-2.23/pith/thread.h alpine-2.23.fancy/pith/thread.h void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); -*************** +*************** int view_thread(struct pine *, MAI *** 107,112 **** int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); @@ -4011,10 +4049,11 @@ diff -rc alpine-2.23/pith/thread.h alpine-2.23.fancy/pith/thread.h ! int allowed_thread_key(SortOrder sort); #endif /* PITH_THREAD_INCLUDED */ -diff -rc alpine-2.23/web/src/alpined.d/alpined.c alpine-2.23.fancy/web/src/alpined.d/alpined.c -*** alpine-2.23/web/src/alpined.d/alpined.c 2020-06-18 15:19:23.477318978 -0600 ---- alpine-2.23.fancy/web/src/alpined.d/alpined.c 2020-06-18 16:08:07.161575622 -0600 -*************** +Index: alpine-2.23.2/web/src/alpined.d/alpined.c +=================================================================== +*** alpine-2.23.2.orig/web/src/alpined.d/alpined.c +--- alpine-2.23.2/web/src/alpined.d/alpined.c +*************** PEConfigCmd(ClientData clientData, Tcl_I *** 2755,2761 **** init_save_defaults(); break; @@ -4031,7 +4070,7 @@ diff -rc alpine-2.23/web/src/alpined.d/alpined.c alpine-2.23.fancy/web/src/alpin break; case V_VIEW_HDR_COLORS : set_custom_spec_colors(wps_global); -*************** +*************** PEMailboxCmd(ClientData clientData, Tcl_ *** 6331,6337 **** && mn_get_revsort(sp_msgmap(wps_global->mail_stream)) == reversed)) sort_folder(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), diff --git a/chappa-rules.patch b/chappa-rules.patch index f0c5f56..9b0e593 100644 --- a/chappa-rules.patch +++ b/chappa-rules.patch @@ -41,5907 +41,6514 @@ pith/string.h | 2 40 files changed, 4487 insertions(+), 297 deletions(-) -Index: alpine-2.23/alpine/adrbkcmd.c +Index: alpine-2.23.2/alpine/adrbkcmd.c =================================================================== ---- alpine-2.23.orig/alpine/adrbkcmd.c -+++ alpine-2.23/alpine/adrbkcmd.c -@@ -4128,6 +4128,8 @@ ab_compose_internal(BuildTo bldto, int a - * won't do anything, but will cause compose_mail to think there's - * already a role so that it won't try to confirm the default. - */ -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); - if(role) - role = copy_action(role); - else{ -@@ -4135,6 +4137,7 @@ ab_compose_internal(BuildTo bldto, int a - memset((void *)role, 0, sizeof(*role)); - role->nick = cpystr("Default Role"); - } -+ ps_global->role = cpystr(role->nick); - } - - compose_mail(addr, fcc, role, NULL, NULL); -Index: alpine-2.23/alpine/alpine.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.orig/alpine/alpine.c -+++ alpine-2.23/alpine/alpine.c -@@ -506,6 +506,7 @@ main(int argc, char **argv) - /* Set up optional for user-defined display filtering */ - pine_state->tools.display_filter = dfilter; - pine_state->tools.display_filter_trigger = dfilter_trigger; -+ pine_state->tools.exec_rule = exec_function_rule; - - #ifdef _WINDOWS - if(ps_global->install_flag){ -@@ -3274,6 +3275,9 @@ goodnight_gracey(struct pine *pine_state - extern KBESC_T *kbesc; - - dprint((2, "goodnight_gracey:\n")); -+ strncpy(pine_state->cur_folder, pine_state->inbox_name, -+ sizeof(pine_state->cur_folder)); -+ pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0'; - - /* We want to do this here before we close up the streams */ - trim_remote_adrbks(); -Index: alpine-2.23/alpine/confscroll.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.orig/alpine/confscroll.c -+++ alpine-2.23/alpine/confscroll.c -@@ -51,6 +51,7 @@ static char rcsid[] = "$Id: confscroll.c - #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") -@@ -2442,6 +2443,9 @@ delete: - * Now go and set the current_val based on user_val changes - * above. Turn off command line settings... - */ -+ set_current_val((*cl)->var, -+ (strcmp((*cl)->var->name,"key-definition-rules") ? TRUE : FALSE), -+ FALSE); - set_current_val((*cl)->var, TRUE, FALSE); - fix_side_effects(ps, (*cl)->var, 0); - -@@ -5239,6 +5243,35 @@ fix_side_effects(struct pine *ps, struct - var == &ps->vars[V_ABOOK_FORMATS]){ - addrbook_reset(); - } -+ else if(var == &ps->vars[V_INDEX_RULES]){ -+ if(ps_global->rule_list) -+ free_parsed_rule_list(&ps_global->rule_list); -+ create_rule_list(ps->vars); -+ reset_index_format(); -+ clear_index_cache(ps->mail_stream, 0); -+ } -+ else if(var == &ps->vars[V_COMPOSE_RULES] || -+ var == &ps->vars[V_FORWARD_RULES] || -+ var == &ps->vars[V_KEY_RULES] || -+ var == &ps->vars[V_REPLACE_RULES] || -+ var == &ps->vars[V_REPLY_INDENT_RULES] || -+ var == &ps->vars[V_REPLY_LEADIN_RULES] || -+ var == &ps->vars[V_RESUB_RULES] || -+ var == &ps->vars[V_SAVE_RULES] || -+ var == &ps->vars[V_SMTP_RULES] || -+ var == &ps->vars[V_SORT_RULES] || -+ var == &ps->vars[V_STARTUP_RULES] || -+ var == &ps->vars[V_THREAD_DISP_STYLE_RULES] || -+ var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){ -+ if(ps_global->rule_list) -+ free_parsed_rule_list(&ps_global->rule_list); -+ create_rule_list(ps->vars); -+ if(var == &ps->vars[V_REPLACE_RULES] || -+ var == &ps->vars[V_RESUB_RULES]){ -+ reset_index_format(); -+ clear_index_cache(ps->mail_stream, 0); -+ } -+ } - else if(var == &ps->vars[V_INDEX_FORMAT]){ - reset_index_format(); - clear_index_cache(ps->mail_stream, 0); -Index: alpine-2.23/alpine/dispfilt.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.orig/alpine/dispfilt.c -+++ alpine-2.23/alpine/dispfilt.c -@@ -461,3 +461,63 @@ df_valid_test(struct mail_bodystruct *bo - - return(passed); - } -+ -+char * -+exec_function_rule(char *rawcmd, gf_io_t input_gc, gf_io_t output_pc) -+{ -+ char *status = NULL, *cmd, *tmpfile = NULL; -+ -+ if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,NULL,NULL,NULL,NULL,NULL)) != NULL){ -+ suspend_busy_cue(); -+ ps_global->mangled_screen = 1; -+ if(tmpfile){ -+ PIPE_S *filter_pipe; -+ FILE *fp; -+ gf_io_t gc, pc; -+ STORE_S *tmpf_so; -+ -+ /* write the tmp file */ -+ if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){ -+ /* copy input to tmp file */ -+ gf_set_so_writec(&pc, tmpf_so); -+ gf_filter_init(); -+ status = gf_pipe(input_gc, pc); -+ gf_clear_so_writec(tmpf_so); -+ if(so_give(&tmpf_so) != 0 && status == NULL) -+ status = error_description(errno); -+ -+ /* prepare the terminal in case the filter uses it */ -+ if(status == NULL){ -+ if((filter_pipe = open_system_pipe(cmd, NULL, NULL, -+ PIPE_USER|PIPE_PROT|PIPE_NOSHELL|PIPE_SILENT, -+ 0, pipe_callback, NULL)) != NULL){ -+ if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){ -+ /* pull result out of tmp file */ -+ if((fp = our_fopen(tmpfile, "rb")) != NULL){ -+ gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE); -+ gf_filter_init(); -+ status = gf_pipe(gc, output_pc); -+ fclose(fp); -+ } -+ else -+ status = "Can't read result of EXEC command"; -+ } -+ else -+ status = "EXEC command command returned error."; -+ } -+ else -+ status = "Can't open pipe for EXEC command"; -+ } -+ -+ our_unlink(tmpfile); -+ } -+ else -+ status = "Can't open EXEC command tmp file"; -+ } -+ -+ resume_busy_cue(0); -+ fs_give((void **)&cmd); -+ } -+ -+ return(status); -+} -Index: alpine-2.23/alpine/dispfilt.h +*** 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.orig/alpine/dispfilt.h -+++ alpine-2.23/alpine/dispfilt.h -@@ -25,7 +25,7 @@ char *dfilter_trigger(BODY *, char *, si - char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *); - char *filter_session_key(void); - char *filter_data_file(int); -- -+char *exec_function_rule(char *, gf_io_t, gf_io_t); - - - #endif /* PINE_DISPFILT_INCLUDED */ -Index: alpine-2.23/alpine/folder.c +*** 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.orig/alpine/folder.c -+++ alpine-2.23/alpine/folder.c -@@ -248,7 +248,7 @@ folder_screen(struct pine *ps) - dprint((1, "=== folder_screen called ====\n")); - mailcap_free(); /* free resources we won't be using for a while */ - ps->next_screen = SCREEN_FUN_NULL; -- -+ strcpy(ps->screen_name, "folder"); - /* Initialize folder state and dispatches */ - memset(&fs, 0, sizeof(FSTATE_S)); - fs.context = cntxt; -@@ -345,6 +345,7 @@ folder_screen(struct pine *ps) - pine_mail_close(*fs.cache_streamp); - - ps->prev_screen = folder_screen; -+ strcpy(ps->screen_name, "unknown"); - } - - -Index: alpine-2.23/alpine/mailcmd.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.orig/alpine/mailcmd.c -+++ alpine-2.23/alpine/mailcmd.c -@@ -73,6 +73,7 @@ static char rcsid[] = "$Id: mailcmd.c 12 - #include "../pith/tempfile.h" - #include "../pith/search.h" - #include "../pith/margin.h" -+#include "../pith/rules.h" - #ifdef _WINDOWS - #include "../pico/osdep/mswin.h" - #endif -@@ -2720,6 +2721,9 @@ role_compose(struct pine *state) - role->nick = cpystr("Default Role"); - } - -+ if(state->role) -+ fs_give((void **)&state->role); -+ state->role = cpystr(role->nick); /* remember the role */ - state->redrawer = NULL; - switch(action){ - case 'c': -@@ -2770,12 +2774,12 @@ save_prompt(struct pine *state, CONTEXT_ - char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section, - SaveDel *dela, SavePreserveOrder *prea) - { -- int rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0; -+ int rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0; - int delindex, preindex, r; - char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; - char *buf = tmp_20k_buf; - char shortbuf[200]; -- char *folder; -+ char *folder, folder2[MAXPATH]; - HelpType help; - SaveDel del = DontAsk; - SavePreserveOrder pre = DontAskPreserve; -@@ -2783,6 +2787,7 @@ save_prompt(struct pine *state, CONTEXT_ - static HISTORY_S *history = NULL; - CONTEXT_S *tc; - ESCKEY_S ekey[10]; -+ RULE_RESULT *rule; - - if(!cntxt) - alpine_panic("no context ptr in save_prompt"); -@@ -2792,6 +2797,15 @@ save_prompt(struct pine *state, CONTEXT_ - if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt))) - return(0); /* message expunged! */ - -+ if (rule = get_result_rule(V_SAVE_RULES, FOR_SAVE, env)){ -+ 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/alpine/mailindx.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.orig/alpine/mailindx.c -+++ alpine-2.23/alpine/mailindx.c -@@ -229,6 +229,8 @@ mail_index_screen(struct pine *state) - state->prev_screen = mail_index_screen; - state->next_screen = SCREEN_FUN_NULL; - -+ setup_threading_display_style(); -+ - if(THRD_AUTO_VIEW() - && sp_viewing_a_thread(state->mail_stream) - && state->view_skipped_index -@@ -240,10 +242,14 @@ mail_index_screen(struct pine *state) - - adjust_cur_to_visible(state->mail_stream, state->msgmap); - -+ strcpy(state->screen_name,"index"); -+ - if(THRD_INDX()) - thread_index_screen(state); - else - index_index_screen(state); -+ -+ strcpy(state->screen_name,"unknown"); - } - - -Index: alpine-2.23/alpine/mailpart.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.orig/alpine/mailpart.c -+++ alpine-2.23/alpine/mailpart.c -@@ -182,7 +182,7 @@ attachment_screen(struct pine *ps) - maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits, - last_type = TYPEOTHER; - long msgno; -- char *q, *last_subtype = NULL, backtag[64], *utf8str; -+ char *q, *last_subtype = NULL, backtag[64], *utf8str, *screen_name; - OtherMenu what; - ATTACH_S *a; - ATDISP_S *current = NULL, *ctmp = NULL; -@@ -191,6 +191,10 @@ attachment_screen(struct pine *ps) - ps->prev_screen = attachment_screen; - ps->next_screen = SCREEN_FUN_NULL; - -+ screen_name = ps->screen_name[0] ? cpystr(ps->screen_name) : NULL; -+ strncpy(ps->screen_name, "attachment", sizeof(ps->screen_name)); -+ ps->screen_name[sizeof(ps->screen_name)-1] = '\0'; -+ - ps->some_quoting_was_suppressed = 0; - - if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){ -@@ -911,6 +915,12 @@ attachment_screen(struct pine *ps) - - if(screen.titlecolor) - free_color_pair(&screen.titlecolor); -+ -+ if(screen_name){ -+ strncpy(ps->screen_name, screen_name, sizeof(ps->screen_name)); -+ ps->screen_name[sizeof(ps->screen_name)-1] = '\0'; -+ fs_give((void **) &screen_name); -+ } - } - - -Index: alpine-2.23/alpine/mailview.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.orig/alpine/mailview.c -+++ alpine-2.23/alpine/mailview.c -@@ -252,6 +252,8 @@ mail_view_screen(struct pine *ps) - ps->prev_screen = mail_view_screen; - ps->force_prefer_plain = ps->force_no_prefer_plain = 0; - -+ strcpy(ps->screen_name, "text"); -+ - if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){ - q_status_message(SM_ORDER | SM_DING, 0, 3, - _("Screen too small to view message")); -@@ -499,6 +501,8 @@ mail_view_screen(struct pine *ps) - } - while(ps->next_screen == SCREEN_FUN_NULL); - -+ strcpy(ps->screen_name, "unknown"); -+ - if (prefix && *prefix) +*** 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(we_cancel) -Index: alpine-2.23/alpine/osdep/termin.gen.c + ++ 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.orig/alpine/osdep/termin.gen.c -+++ alpine-2.23/alpine/osdep/termin.gen.c -@@ -33,6 +33,8 @@ static char rcsid[] = "$Id: termin.gen.c - #include "../../pith/newmail.h" - #include "../../pith/conf.h" - #include "../../pith/busy.h" -+#include "../../pith/list.h" -+#include "../../pith/rules.h" - - #include "../../pico/estruct.h" - #include "../../pico/pico.h" -@@ -72,7 +74,8 @@ int pcpine_oe_cursor(int, long); - * Generic tty input routines - */ - -- -+void process_init_cmds(struct pine *, char **); -+void queue_init_errors(struct pine *); - /*---------------------------------------------------------------------- - Read a character from keyboard with timeout - Input: none -@@ -114,6 +117,41 @@ read_command(char **utf8str) - *utf8str = NULL; - - ucs = read_char(tm); -+ if(!ps_global->initial_cmds){ -+ RULE_RESULT *rule; -+ char **list = NULL, *error = NULL; -+ int commas = 0, k; /* From args.c */ -+ -+ ps_global->pressed_key = cpystr(pretty_command(ucs)); -+ rule = (RULE_RESULT *)get_result_rule(V_KEY_RULES, FOR_KEY, NULL); -+ if(ps_global->pressed_key) -+ fs_give((void **)&ps_global->pressed_key); +*** 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){ -+ 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); ++ } ++ } ++ } ++ ++ 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 ---- +
  • FEATURE: +
  • FEATURE: +
  • FEATURE: ++
  • FEATURE: +
  • FEATURE: +
  • FEATURE: +
  • FEATURE: +*************** This set of special tokens may be used i +*** 19930,19935 **** +--- 19931,19937 ---- + "" option, + in the "" option, + in signature files, ++ in the "new-rules" option, + in template files used in + "roles", and in the folder name + that is the target of a Filter Rule. +*************** and in the target of Filter Rules. +*** 19942,19948 **** +

    +

    + +!

    Tokens Available for all Cases (except Filter Rules)

    + +
    +
    SUBJECT
    +--- 19944,19950 ---- +

    +

    + +!

    Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)

    + +
    +
    SUBJECT
    +*************** email address, never the personal name. +*** 19976,19981 **** +--- 19978,19999 ---- + For example, "mailbox@domain". +
  • + ++
    ADDRESSTO
    ++
    ++ This is similar to the "TO" 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, "mailbox@domain" when ++ the e-mail message contains only one person in the To: field, or ++ "peter@flintstones.com president@world.com". ++
    ++ ++
    ADDRESSSENDER
    ++
    ++ This is similar to the "sender" token, only it is always the ++ email address of all person listed in the Sender: field of the message. ++ Example: "mailbox@domain". ++
    ++ +
    MAILBOX
    +
    + This is the same as the "ADDRESS" except that the +*************** are unavailable) of the persons specifie +*** 20023,20028 **** +--- 20041,20055 ---- + message's "Cc:" header field. +
    + ++
    ADDRESSCC
    ++
    ++ This is similar to the "CC" 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: "mailbox@domain" when ++ the e-mail message contains only one person in the Cc: field, or ++ "peter@flintstones.com president@world.com". ++
    ++ +
    RECIPS
    +
    + This token represents the personal names (or email addresses if the names +*************** message's "To:" header field a +*** 20031,20036 **** +--- 20058,20071 ---- + the message's "Cc:" header field. +
    + ++
    ADDRESSRECIPS
    ++
    ++ 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. ++
    ++ ++ +
    NEWSANDRECIPS
    +
    + This token represents the newsgroups from the +*************** This is an end of line marker. +*** 21163,21168 **** +--- 21198,21307 ---- + + +

    ++

    Tokens Available Only for New-Rules

    ++ ++
    ++
    FCCFROM
    ++
    ++ The Fcc: folder assigned to the email address in the From: field in the ++ addressbook. ++
    ++
    ++ ++
    ++
    FCCSENDER
    ++
    ++ The Fcc: folder assigned to the email address in the Sender: field in the ++ addressbook. ++
    ++
    ++ ++
    ++
    ALTADDRESS
    ++
    ++ The value of your ++ ++ variable. At this time, no expansion of regular expressions is supported. ++
    ++
    ++ ++
    ++
    NICK
    ++
    ++ Nickname of the person in the From field in your addressbook. ++
    ++
    ++ ++
    ++
    FOLDER
    ++
    ++ Name of the folder where the rule will be applied. ++
    ++
    ++ ++
    ++
    COLLECTION
    ++
    ++ Name of the collection list where the rule will be applied. ++
    ++
    ++ ++
    ++
    ROLE
    ++
    ++ Name of the Role used to reply a message. ++
    ++
    ++ ++
    ++
    BCC
    ++
    ++ Not implemented yet, but it will be implemented in future versions. It will ++ be used for compose ++ reply ++ forward ++ rules. ++
    ++
    ++ ++
    ++
    LCC
    ++
    ++ This is the value of the Lcc: field at the moment that you start the composition. ++
    ++
    ++ ++
    ++
    FORWARDFROM
    ++
    ++ 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. ++
    ++
    ++ ++
    ++
    FORWARDADDRESS
    ++
    ++ This is the address of the person that sent the message that you ++ are forwarding. ++
    ++
    ++ ++ ++ ++ ++
    ++
    FLAG
    ++
    ++ 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 ++ new rules explanation and example help. ++
    ++
    ++ ++

    +

    Token Available Only for Templates and Signatures

    + +
    +*************** character sets Alpine knows about by usi +*** 24560,24565 **** +--- 24699,25620 ---- + <End of help on this topic> + + ++ ====== h_config_procid ===== ++ ++ ++ Token: PROCID ++ ++ ++

    TOKEN: PROCID explained

    ++ ++

    ++ 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. ++ ++

    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. ++ ++

    ++ Consider the following examples for forward-rules: ++ ++

    ++ _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} ++ ++

    ++ and ++ ++

    ++ _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} ++ ++

    ++ 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 ++ ++

    ++ _PROCID_ == {fwd-subject} && _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} ++ ++

    ++ In this case, this rule will be tested fully only when we are determining ++ the subject line of a forwarded message, not otherwise. ++ ++

    ++ 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. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_compose_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_compose-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    At this time, this option is used to generate values for signature ++ files that is not possible to do with the use of ++ roles. ++ ++

    For example, you can have a rule like:
    ++ _TO_ >> {Peter Flintstones} => _SIGNATURE_{~/.petersignature} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_forward_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_forward-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    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 this link. ++ ++

    If you want to edit the subject of a forwarded message, use the ++ PROCID fwd-subject. For example you could have a rule like ++ ++

    ++ _ROLE_ == {admin} && _SUBJECT_ !> {[tag] } => _COPY_{[tag] _SUBJECT_} ++ ++

    Another way in which this option can be used, is to trim the values of ++ some fields. For this application the PROCID is fwd-lcc. For ++ example it can be used in the following way: ++ ++

    ++ _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} ++ ++

    Other functions that can be used in this option are _EXEC_, _REXTRIM_ and ++ _REXSUB_. ++ ++

    You can also use the _EXEC_ function. The documentation for this function ++ is in the ++ ++ help text. ++ ++

    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 "{" and "}". For example, to ++ delete only one ocurrence of the string "Re: " in a subject we ++ would write a rule such as ++ ++

    ++ _FOLDER_ >> {} => _SUBJECT_ := _REXSUB_{Re: }{}{1} ++ ++

    The last parameter of the rexsub function is optional. In the sense ++ that its omission is understood as if the third parameter was ++ "{1}". In order to make unlimited substitutions, use ++ "{g}" as the last parameter. ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_index_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_index-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used to supersede the value of the option for specific folders. In ++ this form you can have different index-formats for different folders. For ++ example an entry here may be: ++ ++

    ++ _FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_pretty_command ===== ++ ++ ++ Pretty-Command Explained ++ ++ ++

    Pretty Command Explained

    ++ ++

    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. ++ ++

    [Command "RETURN" not defined for this screen. Use ? for help] ++ ++

    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. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_key_macro_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_key-definition-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    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. ++ ++

    Always use the _SCREEN_ token as defined below.. You have been ++ warned! ++ ++

    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. ++ ++

    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. ++ ++

    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 (MESSAGE INDEX), the ++ screen where your message is displayed (MESSAGE ++ TEXT) the screen where the list of folders is displayed (FOLDER LIST) and the attachment index screen (ATTACHMENT INDEX). The internal names of ++ these screens for this patch are "index", "text", ++ "folder", and "attachment", respectively. Please note ++ that the internal names are all in lowercase and are case sensitive. ++ ++

    In order to define the screen, you use the _SCREEN_ token, so for ++ example, you can write _SCREEN_ == {index}. ++ ++

    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 "pressed key"). For ++ example you could use _PKEY_ == {~}, to designate the "~" ++ key to activate the command. Some keystrokes (like control, or ++ function keys) are encoded in special ways. You should read the ++ full explanation on how to find ++ out the encoding for each keystroke. ++ ++

    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 ++ "$" and "h". You use the _COMMAND_ function to specify ++ this. The syntax in this case is _COMMAND_{$,h}. ++ ++

    Observe that in the above example the different inputs are separated ++ by commas. This is the standard way in which the ++ 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. ++ ++

    ++ "_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}" ++ ++

    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 ++ ++

    ++ _SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{"$h"} ++ ++

    For more information on how to define the argument of the _COMMAND_ ++ token see the help of ++ . ++ ++

    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. ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_replace_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_replace-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used to have Alpine print different values for specific ++ tokens in the . For example you ++ can replace strings like "To: newsgroup" by your name. ++ ++

    Here are examples of possible rules: ++ ++

    _FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} ++ ++

    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 ++ ++

    _FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} ++ ++

    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.
    ++ ++

    _FOLDER_ == {some-folder} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: } ++ ++

    You can also use the _EXEC_ function. The documentation for this function ++ is in the ++ ++ help text. ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_reply_leadin_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_reply-leadin-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used to have Alpine generate a different ++ string dependent either on ++ the person you are replying to, or the folder where the message is being ++ replied is in, or both. ++ ++

    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: ++

    ++ _FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} ++ ++

    Here there is an example that one can use to change the reply indent string ++ to reply people that speak spanish. ++

    ++ _FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_resub_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_reply-subject-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used to have Alpine generate a different subject when ++ replying rather than the one Alpine would generate automatically. ++ ++

    Here there are a couple of examples about how to use this ++ configuration option: ++ ++

    In order to have messages with empty subject to be replied with the message ++ "your message" use the rule
    ++

    _SUBJECT_ == {} => _RESUB_{Re: your message}
    ++ ++

    If you want to trim some parts of the subject when you reply use the ++ rule
    ++

    _SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}
    ++ ++

    this rule removes the brackets "[" and "]" whenever the string "[one]" ++ appears in it, it also removes the word "two" from it. ++ ++

    Another example where you may want to use this rule is when you ++ correspond with people that change the reply string from "Re:" ++ to "AW:" or "Sv:". In this case a rule like
    ++

    _SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }
    ++

    ++ would eliminate undesired strings in replies. ++ ++

    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 "a" of a file. ++ ++

    ++ #!/bin/sh
    ++ sed 's/a//g' $1 > /tmp/mytest
    ++ mv /tmp/mytest $1
    ++ 
    ++ ++

    ++ As you can see this script took "$1" 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 "$1". This is the kind of behavior ++ that your program is expected to have. ++ ++

    ++ The content of the input file ("$1" above) is the value of a token ++ like _SUBJECT_. In order to indicate this, we use the notation ++ ++

    ++ _SUBJECT_ := _EXEC_{/path/to/script} ++ ++

    for the action. So for example ++ ++

    ++ _FOLDER_ := {sent-mail} => _SUBJECT_ := _EXEC_{/path/to/script} ++ ++

    is a valid rule. ++ ++

    You can also use this configuration option to customize reply subjects ++ according to the sender of the message. ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ====== h_config_sort_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_sort-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used to have Alpine sort different folders in different orders ++ and thus override the value already set in the ++ configuration option. ++ ++

    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. ++ ++

    ++ _COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} ++ ++

    Another example could be
    ++ _FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ++ ====== h_config_save_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_save-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    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). ++ ++

    If this option is set and the ++ configuration ++ option is also enabled then these definitions will be used to move messages ++ from your INBOX when exiting Alpine. ++ ++

    Here there are some examples
    ++ _FLAG_ >> {D} -> Trash
    ++ _FROM_ == {U2} -> Bono
    ++ _FOLDER_ == {comp.mail.pine} -> pine-stuff
    ++ _NICK_ != {} -> _NICK_/_NICK_
    ++ _DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ++ ====== h_config_reply_indent_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_reply-indent-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    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 ++ is used. ++ ++

    The associated function to this configuration option is called "RESTR" (for ++ REply STRing). Some examples of its use are:
    ++ _FROM_ == {Your Boss} => _RESTR_{"> "}
    ++ _FROM_ == {My Wife} => _RESTR_{":* "}
    ++ _FROM_ == {Perter Flintstone;Wilma Flintstone} => _RESTR_{"_INIT_ > "}
    ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ++ ====== h_config_smtp_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_smtp-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    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 ++ "" configuration ++ option according to the rules used in that variable. ++ ++

    The function associated to this configuration option is _SMTP_, an example ++ of the use of this function is
    ++ _ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ++ ====== h_config_startup_rules ===== ++ ++ ++ OPTION: <!--#echo var="VAR_startup-rules"--> ++ ++ ++

    OPTION:

    ++ ++

    This option is used when a folder is being opened. You can use it to specify its and override ++ Alpine's global value set for all folders. ++ ++

    An example of the usage of this option is:
    ++ _FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen} ++ ++

    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 link. ++ ++

    <End of help on this topic> ++ ++ ++ ++ ====== h_config_new_rules ===== ++ ++ ++ OPTION: New Rules Explained ++ ++ ++

    OPTION: New Rules Explained

    ++ ++ This is a quite powerful option. Here you can define rules that override ++ the values of any other option you have set in Alpine. ++ ++

    ++ 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 "rules" 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. ++ ++

    ++ 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. ++ ++

    ++ Here is an example: ++ ++

    ++ _FROM_ == {Fred Flintstone} => _SAVE_{Fred} ++ ++

    ++ Here the separator is "=>". 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 "Fred ++ Flintstone", and the action will be that you will be offered to save ++ it in the folder "Fred", whenever you press the letter ++ "S" to save a message. ++ ++

    ++ The separator is always "=>", with one exception to be seen ++ later. But for the most part this will be the only one you will ever need. ++ ++

    ++ 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 ++ "{" and "}", so for example you can specify ++ _SAVE_{saved-messages} as a valid sentence. ++ ++

    ++ Later in the document you will find examples. Here is a short ++ description of what each function does: ++ ++

    ++

      ++
    • _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 ++ , ++ and ++ . ++ See the help of those options for examples of how to use this function ++ and configure these rules. ++
       
      ++
    • _INDEX_ : This function takes as an argument an index-format, and ++ makes that the index-format for the specified folder. ++
       
      ++
    • _REPLACE_ : This function replaces the subject/from of the given e-mail by ++ another subject/from only when displaying the index. ++
       
      ++
    • _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. ++
       
      ++
    • _RESTR_ : This function takes as an argument the value of the ++ reply-indent-string to be used to answer the message being replied to. ++
       
      ++
    • _RESUB_ : This function replaces the subject of the given e-mail by ++ another subject only when replying to a message. ++
       
      ++
    • _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. ++
       
      ++
    • _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. ++
       
      ++
    • _SMTP_ : This function takes as an argument the definition of a ++ SMTP server. ++
       
      ++
    • _SORT_ : This function takes as an argument a Sort Style, and sorts a ++ specified folder in that sort order. ++
       
      ++
    • _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_. ++
       
      ++
    • _REXTRIM_ : Same as _TRIM_ but its argument is one and ++ only one extended regular expression. ++
       
      ++
    • _REXSUB_ : This function takes three arguments. The structure ++ is _REXSUB_{pattern}{text}{number of times}. The "pattern" ++ to match is an extended regular expression. If the pattern is matched, ++ it will be replaced by "text" as many times as the "number ++ of times" argument specifies. If the last argument is ++ omitted (that is, there are only two arguments) then the last argument ++ is considered as "{1}". ++
       
      ++
    • _STARTUP_ : This function takes as an argument an ++ incoming-startup-rule, and open an specified folder using that rule. ++
       
      ++
    • _THREADSTYLE_ : This function takes as an argument a ++ threading-display-style and uses it to display threads in a folder. ++
       
      ++
    • _THREADINDEX_ : This function takes as an argument a ++ threading-index-style and uses it to display threads in a folder. ++
    ++ ++

    ++ 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: ++ ++

    ++ _FROM_ == {Fred Flintstone} => _SAVE_{Fred} ++ ++

    it will only be applied if the from is "Fred Flintstone". If ++ the From is "Wilma Flintstone" the rule will be skipped. ++ ++

    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 "{" and "}" in the condition, this part ++ of the condition is called the "condition set". The definition ++ of each token can be found here. ++ ++

    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 ++ this link. ++ ++

    There are two more tokens related to the option ++ key-definition-rules. Those tokens ++ are only specific to that option, and hence are not explained here. ++ ++

    You can also test in different ways, you can use the following ++ "test operands": <<, !<, >>, !>, == and !=. ++ All of them are two characters long. Here is the meaning of them: ++ ++

    ++

      ++
    • << : It tests if the value of the token is contained in ++ the condition set. Here for example if the condition set were equal to ++ "Freddy", then the condition: _NICK_ << {Freddy}, would be true if ++ the value of _NICK_ were "Fred", "red" or "Freddy". You are just looking ++ for substrings here. ++
    • >> : 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 ++ "Fred", then the condition: _FROM_ >> {Fred}, would be true if ++ the value of _FROM_ were "Fred Flintstone" or "Fred P. Flintstone" or "Freddy". ++
    • == : 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 "Freddy" or "red". ++
    • !< : This is true only when << is false and vice versa. ++
    • !> : This is true only when >> is false and vice versa. ++
    • != : This is true only when == is false and vice versa. ++
    ++ ++

    ++ Now let us say that you want the same action to be applied to more than ++ one person or folder, say you want "folder1" and "folder2" to be sorted by ++ Ordered Subject upon entering. Then you can list them all of them in the ++ condition part separting them by a ";". Here is the way to do it. ++ ++

    ++ _FOLDER_ << {folder1; folder2} => _SORT_{OrderedSubj} ++ ++

    ++ Here is the first subtlety about these definitions. Notice that the ++ following rule: ++ ++

    ++ _FOLDER_ == {folder1; folder2} => _SORT_{Reverse OrderedSubj} ++ ++

    works only for "folder1" but not for "folder2". This is because the ++ comparison of the name of the folder is done with whatever is in between ++ "{", ";" or "}", so in the above rule you would be testing
    ++ "folder2" == " folder2". The extra space makes the difference. ++ The reason why the first rule does not fail is because ++ "folder2" << " folder2" is actually ++ true. If something ever fails this may be something to look into. ++ ++

    ++ Here are a few examples of what we have talked about before. ++ ++

    ++ _NICK_ == {lisa;kika} => _SAVE_{_NICK_/_NICK_}
    ++ This means that if the nick is lisa, it will ++ save the message in the folder "lisa/lisa", and if the nick ++ is "kika", it will save the message in the folder "kika/kika" ++ ++

    ++ _FOLDER_ == {Lynx} -> lynx
    ++ This, is an abbreviation of the following rule:
    ++ _FOLDER_ == {Lynx} => _SAVE_{lynx}
    ++ (note the change in separator from "=>" to "->"). In the future ++ I will use that abbreviation. ++ ++

    _FOLDER_ << {comp.mail.pine; pine-info; pine-alpha} -> pine
    ++ Any message in the folders "comp.mail.pine", "pine-info" or "pine-alpha" ++ will be saved to the folder "pine". ++ ++

    _FROM_ << {Pine Master} -> pine
    ++ Any message whose From field contains ++ "Pine Master" will be saved in the folder pine. ++ ++

    _FOLDER_ << {Lynx; pine-info; comp.mail.pine} => ++ _INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)}
    Use a ++ different index-format for the folders "Lynx", "pine-info" and ++ "comp.mail.pine", where the size is not present. ++ ++

    _FOLDER_ == {Lynx;pine-info} => _REPLY_{*** _FROM_ (_ADDRESS_) ++ wrote in the _FOLDER_ list _SMARTDATE_("Today" "today" "on ++ _LONGDATE_"):}
    If a message is in one of the incoming folders "Lynx" ++ or "pine-info", create a reply-leadin-string that acknowledges that. Note ++ the absence of "," in the function _SMARTDATE_. For example answering to a ++ message in the pine-info list would look like: ++ ++

    ++ *** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today: ++ ++

    ++ However replying for a message in the Lynx list would look: ++ ++

    ++ *** mattack@area.com (mattack@area.com) wrote in the Lynx list today: ++ ++

    ++ If you write in more than one language you can use this feature to create ++ Reply-leadin-strings in different languages. ++ ++

    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. ++ ++

    _FOLDER_ << {Lynx; comp.mail.pine; pine_info; pine-alpha} => ++ _SORT_{OrderedSubj}
    This means upon opening, sort the folders "Lynx", ++ "comp.mail.pine", 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). ++ ++

    The last examples use the function _TRIM_ which has a special form. ++ This function can only be used in the index list. ++ ++

    _FOLDER_ << {Lynx} => _SUBJECT_ := _TRIM_{lynx-dev }
    In ++ the folder "Lynx" eliminate from the subject the string "lynx-dev " (with ++ the space at the end). For example a message whose subject is "Re: ++ lynx-dev unvisited Visited Links", would be shown in the index with ++ subject: "Re: unvisited Visited Links", making the subject shorter and ++ giving the same information. ++ ++

    _FROM_ >> {Name (Comment)} => _FROM_ := ++ _TRIM_{ (Comment)}
    Remove the part " (Comment)" ++ from the _FROM_, so when displaying in the index the real From "Name" ++ will appear. ++ ++

    _SUBJECT_ == {} => _RESUB_{Re: your mail without subject} ++ If there is no subject in the message, use the subject "Re: your mail ++ wiyhout subject" as a subject for the reply message. ++ ++

    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 ++ ++

    _FOLDER_ == {INBOX} && _FROM_ >> {Rubye} => _SAVE_{Personal} ++ ++

    We could also have a rule that is triggered by an "or" ++ condition by, sat for messages from Andres or messages in the index ++ to trigger a specific reply leadin string. ++ ++

    _FOLDER_ == {INBOX} || _FROM_ >> {Andres} => _REPLY_{You wrote:} ++ ++

    Observe that the construction ++ ++

    _TOKEN_ == {value1} || _TOKEN_ == {value2} ++ ++

    can be shortened to ++ ++

    _TOKEN_ == {value1;value2} ++ ++

    Round parentheses can be used to group some conditions, for example ++ ++

    (_FROM_ >> {Andres} && _FOLDER_ == {INBOX}) || _FROM_ >> {Rubye} ++ ++ ++

    You can also list your index by nick, in the following way:
    ++ _NICK_ != {} => _FROM_ := _REPLACE_{_NICK_} ++ ++

    ++ If you want to open the folder "pine-info" in the first non-read message ++ use the rule:
    ++ _FOLDER_ == {pine-info} => _STARTUP_{first-unseen} ++ ++

    ++ If you want to move your deleted messages to a folder, called "Trash", use ++ the following rule:
    ++ _FLAG_ >> {D} -> Trash ++ ++

    ++ The reason why the above test is not "_FLAG_ == {D}" 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. ++ ++

    If you want to use a specific signature when you are in a specific collection ++ use the following rule:
    ++ _COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature} ++ ++

    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 "saving" rules do not compete with "sorting" rules. So the first ++ "saving" rule that matches will be executed in the case of saving and so ++ on. ++ ++

    ++

    ++ <End of help on this topic> ++ ++ + ====== h_config_char_set ===== + + +*************** the From field is used to show the relat +*** 28312,28317 **** +--- 29367,29442 ---- + <End of help on this topic> + + ++ ====== h_config_thread_display_style_rule ===== ++ ++ ++ OPTION: Threading-Display-Style-Rule ++ ++ ++

    OPTION: Threading-Display-Style-Rule

    ++ ++ This option is very similar to ++ , but it is a rule which specifies the ++ display styles for a thread that you want displayed in a specific ++ folder or collection. ++

    ++ The token to be used in this function is _THREADSTYLE_. Here there is ++ an example of its use ++

    ++ _FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like} ++

    ++ 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 threading-display-style ++ configuration option. ++ ++

    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 link. ++ ++

    ++

    ++ <End of help on this topic> ++ ++ ++ ====== h_config_thread_index_style_rule ===== ++ ++ ++ OPTION: Threading-Index-Style-Rule ++ ++ ++

    OPTION: Threading-Index-Style-Rule

    ++ ++ This option is very similar to ++ , but it is a rule which specifies the ++ index styles for a thread that you want displayed in a specific ++ folder or collection. ++

    ++ The token to be used in this function is _THREADINDEX_. Here there is ++ an example of its use ++

    ++ _FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads} ++

    ++ 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 ++ configuration option. ++ ++

    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 link. ++ ++

    ++

    ++ <End of help on this topic> ++ ++ + ====== h_config_pruning_rule ===== + + +*************** automatically transfer all read messages +*** 32003,32008 **** +--- 33128,33156 ---- + them as deleted in the INBOX. Messages in the INBOX marked with an + "N" (meaning New, or unseen) are not affected. +

    ++

    ++ <End of help on this topic> ++ ++ ++ ====== h_config_auto_read_msgs_rules ===== ++ ++ ++ FEATURE: auto-move-read-msgs-using-rules ++ ++ ++

    FEATURE: auto-move-read-msgs-using-rules

    ++ This feature controls an aspect of Alpine's behavior upon quitting. If set, ++ and the ++ "" ++ 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 ++ "" and mark ++ them as deleted in the INBOX. Messages in the INBOX marked with an ++ "N" (meaning New, or unseen) are not affected. ++

    +

    +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); -+ if(error){ -+ q_status_message(SM_ORDER | SM_DING, 0, 2, tmp_20k_buf); -+ return (NO_OP_COMMAND); -+ } -+ process_init_cmds(ps_global, list); -+ if(ps_global->init_errs){ -+ queue_init_errors(ps_global); -+ return (NO_OP_COMMAND); -+ } -+ ucs = read_char(tm); -+ ps_global->in_init_seq = 1; /* no output please */ -+ for(k = 0; k < commas; k++) -+ if(list[k]) fs_give((void **)&list[k]); -+ if (list) fs_give((void **)list); -+ } -+ } - if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE) - zero_new_mail_count(); - -@@ -1158,6 +1196,7 @@ process_config_input(UCS *ch) - if(ps_global->initial_cmds && !*ps_global->initial_cmds && ps_global->free_initial_cmds){ - fs_give((void **) &ps_global->free_initial_cmds); - ps_global->initial_cmds = NULL; -+ firsttime = (char) 1; - } - - return(ret); -Index: alpine-2.23/alpine/reply.c -=================================================================== ---- alpine-2.23.orig/alpine/reply.c -+++ alpine-2.23/alpine/reply.c -@@ -62,7 +62,8 @@ The evolution continues... - #include "../pith/tempfile.h" - #include "../pith/busy.h" - #include "../pith/ablookup.h" -- -+#include "../pith/copyaddr.h" -+#include "../pith/rules.h" - - /* - * Internal Prototypes -@@ -109,11 +110,12 @@ reply(struct pine *pine_state, ACTION_S - long msgno, j, totalm, rflags, *seq = NULL; - int i, include_text = 0, times = -1, warned = 0, rv = 0, - flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; -- int rolemsg = 0, copytomsg = 0; -+ int rolemsg = 0, copytomsg = 0, do_role_early = 0; - gf_io_t pc; - PAT_STATE dummy; - REDRAFT_POS_S *redraft_pos = NULL; - ACTION_S *role = NULL, *nrole; -+ RULE_RESULT *rule; - #if defined(DOS) && !defined(_WINDOWS) - char *reserve; - #endif -@@ -139,6 +141,69 @@ reply(struct pine *pine_state, ACTION_S - && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) - reply_raw_body = 1; - -+ /* Setup possible role */ -+ if(role_arg) -+ role = copy_action(role_arg); -+ -+ if(!role && F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)){ -+ for(msgno = mn_first_cur(pine_state->msgmap); -+ msgno > 0L; msgno = mn_next_cur(pine_state->msgmap)){ -+ -+ env = pine_mail_fetchstructure(pine_state->mail_stream, -+ mn_m2raw(pine_state->msgmap, msgno), -+ NULL); -+ if(!env) { -+ q_status_message1(SM_ORDER,3,4, -+ _("Error fetching message %s. Can't reply to it."), -+ long2string(msgno)); -+ goto done_early; -+ } -+ -+ if(rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env)){ -+ RULELIST *list = get_rulelist_from_code(V_REPLY_INDENT_RULES, -+ ps_global->rule_list); -+ RULE_S *prule = get_rule(list, rule->number); -+ if(condition_contains_token(prule->condition, ROLE_TOKEN)) -+ do_role_early++; -+ if(rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ } -+ } -+ -+ if(do_role_early){ -+ rflags = ROLE_REPLY; -+ if(nonempty_patterns(rflags, &dummy)){ -+ /* setup default role */ -+ nrole = NULL; -+ j = mn_first_cur(pine_state->msgmap); -+ do { -+ role = nrole; -+ nrole = set_role_from_msg(pine_state, rflags, -+ mn_m2raw(pine_state->msgmap, j), -+ NULL); -+ } while(nrole && (!role || nrole == role) -+ && (j=mn_next_cur(pine_state->msgmap)) > 0L); -+ -+ if(!role || nrole == role) -+ role = nrole; -+ else -+ role = NULL; -+ -+ if(confirm_role(rflags, &role)) -+ role = combine_inherited_role(role); -+ else{ /* cancel reply */ -+ role = NULL; -+ cmd_cancelled("Reply"); -+ goto done_early; -+ } -+ } -+ } -+ -+ if (role) -+ ps_global->role = cpystr(role->nick); /* remember the role */ -+ - /* - * We may have to loop through first to figure out what default - * reply-indent-string to offer... -@@ -287,8 +352,18 @@ reply(struct pine *pine_state, ACTION_S - outgoing->subject = cpystr("Re: several messages"); - } - } -- else -- outgoing->subject = reply_subject(env->subject, NULL, 0); -+ else{ -+ RULE_RESULT *rule; -+ rule = get_result_rule(V_RESUB_RULES,FOR_RESUB|FOR_TRIM , env); -+ if (rule){ -+ outgoing->subject = reply_subject(rule->result, NULL, 0); -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ else -+ outgoing->subject = reply_subject(env->subject, NULL, 0); -+ } - } - - /* fill reply header */ -@@ -307,13 +382,7 @@ reply(struct pine *pine_state, ACTION_S - if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ - goto done_early; - -- /* Setup possible role */ -- if (ps_global->reply.role_chosen) -- role = ps_global->reply.role_chosen; -- else if(role_arg) -- role = copy_action(role_arg); -- -- if(!role){ -+ if(!do_role_early){ - rflags = ROLE_REPLY; - if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){ - /* setup default role */ -@@ -724,6 +793,9 @@ reply(struct pine *pine_state, ACTION_S - if(prefix) - fs_give((void **)&prefix); - -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ - if(fcc) - fs_give((void **) &fcc); - -@@ -1598,9 +1670,14 @@ forward(struct pine *ps, ACTION_S *role_ - } - } - -- if(role) -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ -+ if(role){ - q_status_message1(SM_ORDER, 3, 4, - _("Forwarding using role \"%s\""), role->nick); -+ ps_global->role = cpystr(role->nick); -+ } - - if(role && role->template){ - char *filtered; -@@ -1832,6 +1909,7 @@ forward(struct pine *ps, ACTION_S *role_ - #if defined(DOS) && !defined(_WINDOWS) - free((void *)reserve); - #endif -+ outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL; - pine_send(outgoing, &body, "FORWARD MESSAGE", - role, NULL, &reply, redraft_pos, - NULL, NULL, 0); -Index: alpine-2.23/alpine/roleconf.c -=================================================================== ---- alpine-2.23.orig/alpine/roleconf.c -+++ alpine-2.23/alpine/roleconf.c -@@ -7706,6 +7706,11 @@ role_text_tool_inick(struct pine *ps, in - if(apval) - *apval = (role && role->nick) ? cpystr(role->nick) : NULL; - -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ if (role && role->nick) -+ ps_global->role = cpystr(role->nick); -+ - if((*cl)->value) - fs_give((void **)&((*cl)->value)); - -Index: alpine-2.23/alpine/send.c -=================================================================== ---- alpine-2.23.orig/alpine/send.c -+++ alpine-2.23/alpine/send.c -@@ -63,7 +63,7 @@ static char rcsid[] = "$Id: send.c 1142 - #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; -@@ -236,6 +236,11 @@ alt_compose_screen(struct pine *pine_sta - role->nick = cpystr("Default Role"); - } - -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ -+ ps_global->role = cpystr(role->nick); -+ - pine_state->redrawer = NULL; - compose_mail(NULL, NULL, role, NULL, NULL); - free_action(&role); -@@ -445,8 +450,12 @@ compose_mail(char *given_to, char *fcc_a - - ps_global->next_screen = prev_screen; - ps_global->redrawer = redraw; -- if(role) -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ if(role){ - role = combine_inherited_role(role); -+ ps_global->role = cpystr(role->nick); -+ } - } - break; - -@@ -641,9 +650,14 @@ compose_mail(char *given_to, char *fcc_a - } - } - -- if(role) -+ if (ps_global->role) -+ fs_give((void **)&ps_global->role); -+ -+ if(role){ - q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), - role->nick); -+ ps_global->role = cpystr(role->nick); -+ } - - /* - * The type of storage object allocated below is vitally -@@ -2482,6 +2496,26 @@ pine_send(ENVELOPE *outgoing, struct mai - removing_trailing_white_space(pf->textbuf); - (void)removing_double_quotes(pf->textbuf); - build_address(pf->textbuf, &addr, NULL, NULL, NULL); -+ if (!strncmp(pf->name,"Lcc",3) && addr && *addr){ -+ RULE_RESULT *rule; -+ -+ outgoing->date = (unsigned char *) cpystr(addr); -+ ps_global->procid = cpystr("fwd-lcc"); -+ rule = get_result_rule(V_FORWARD_RULES, -+ FOR_COMPOSE|FOR_TRIM, outgoing); -+ if (rule){ -+ addr = cpystr(rule->result); -+ removing_trailing_white_space(addr); -+ (void)removing_extra_stuff(addr); -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ fs_give((void **)&ps_global->procid); -+ if (outgoing->date) -+ fs_give((void **)&outgoing->date); -+ } -+ - rfc822_parse_adrlist(pf->addr, addr, - ps_global->maildomain); - fs_give((void **)&addr); -Index: alpine-2.23/pith/Makefile.am -=================================================================== ---- alpine-2.23.orig/pith/Makefile.am -+++ alpine-2.23/pith/Makefile.am -@@ -26,7 +26,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c a - filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \ - keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ - margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ -- readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ -+ readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ - state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ - thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c - -Index: alpine-2.23/pith/Makefile.in -=================================================================== ---- alpine-2.23.orig/pith/Makefile.in -+++ alpine-2.23/pith/Makefile.in -@@ -142,7 +142,7 @@ am_libpith_a_OBJECTS = ablookup.$(OBJEXT - mimedesc.$(OBJEXT) mimetype.$(OBJEXT) msgno.$(OBJEXT) \ - newmail.$(OBJEXT) news.$(OBJEXT) pattern.$(OBJEXT) \ - pipe.$(OBJEXT) readfile.$(OBJEXT) remote.$(OBJEXT) \ -- reply.$(OBJEXT) rfc2231.$(OBJEXT) save.$(OBJEXT) \ -+ reply.$(OBJEXT) rfc2231.$(OBJEXT) rules.$(OBJEXT) save.$(OBJEXT) \ - search.$(OBJEXT) sequence.$(OBJEXT) send.$(OBJEXT) \ - sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ - store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ -@@ -441,7 +441,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c a - filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \ - keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ - margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ -- readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ -+ readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ - state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ - thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c - -@@ -575,6 +575,7 @@ distclean-compile: - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@ - - .c.o: - @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -Index: alpine-2.23/pith/adrbklib.c -=================================================================== ---- alpine-2.23.orig/pith/adrbklib.c -+++ alpine-2.23/pith/adrbklib.c -@@ -5138,8 +5138,14 @@ init_addrbooks(OpenStatus want_status, i - if(as.cur >= as.how_many_personals) - pab->type |= GLOBAL; - -- pab->access = adrbk_access(pab); -- -+ if(ps_global->mail_stream && -+ ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){ -+ as.initialized = 0; -+ pab->access = NoAccess; -+ } -+ else{ -+ pab->access = adrbk_access(pab); -+ } - /* global address books are forced readonly */ - if(pab->type & GLOBAL && pab->access != NoAccess) - pab->access = ReadOnly; -Index: alpine-2.23/pith/conf.c -=================================================================== ---- alpine-2.23.orig/pith/conf.c -+++ alpine-2.23/pith/conf.c -@@ -29,6 +29,7 @@ static char rcsid[] = "$Id: conf.c 1266 - #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" -@@ -225,6 +226,36 @@ CONF_TXT_T cf_text_unk_character_set[] = - - CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature."; - -+CONF_TXT_T cf_text_compose_rules[] = "Allows a user to set rules when composing messages."; -+ -+CONF_TXT_T cf_text_forward_rules[] = "Allows a user to set rules when forwarding messages."; -+ -+CONF_TXT_T cf_text_reply_rules[] = "Allows a user to set rules when replying messages."; -+ -+CONF_TXT_T cf_text_index_rules[] = "Allows a user to supersede global index format variable in designated folders."; -+ -+CONF_TXT_T cf_text_key_def_rules[] = "Allows a user to override keystrokes in certain screens."; -+ -+CONF_TXT_T cf_text_replace_rules[] = "Allows a user to change the form a specify field in the index-format is \n# displayed."; -+ -+CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules."; -+ -+CONF_TXT_T cf_text_reply_leadin_rules[] = "Allows a user to replace the reply-leadin message based on different parameters."; -+ -+CONF_TXT_T cf_text_reply_subject_rules[] = "Allows a user to replace the subject of a message in a customs based way"; -+ -+CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders"; -+ -+CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders"; -+ -+CONF_TXT_T cf_text_save_rules[] = "Allows a user to specify a save folder message for specific senders or folders."; -+ -+CONF_TXT_T cf_text_smtp_rules[] = "Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here."; -+ -+CONF_TXT_T cf_text_sort_rules[] = "Allows a user to specify the sort default order of a specific folder."; -+ -+CONF_TXT_T cf_text_startup_rules[] = "Allows a user to specify the position of a highlighted message when opening a \n# folder."; -+ - CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer."; - - #ifdef _WINDOWS -@@ -570,6 +601,34 @@ static struct variable variables[] = { - NULL, cf_text_thread_exp_char}, - {"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - "Threading Last Reply Character", cf_text_thread_lastreply_char}, -+{"threading-display-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Threading Display Style Rule", cf_text_thread_displaystyle_rule}, -+{"threading-index-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Threading Index Style Rule", cf_text_thread_indexstyle_rule}, -+{"compose-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Compose Rules", cf_text_compose_rules}, -+{"forward-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Forward Rules", cf_text_forward_rules}, -+{"index-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, -+ "Index Rules", cf_text_index_rules}, -+{"key-definition-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, -+ "Key Definition Rules", cf_text_key_def_rules}, -+{"replace-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, -+ "Replace Rules", cf_text_replace_rules}, -+{"reply-indent-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Reply Indent Rules", cf_text_reply_indent_rules}, -+{"reply-leadin-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, -+ "Reply Leadin Rules", cf_text_reply_leadin_rules}, -+{"reply-subject-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, -+ "Reply Subject Rules", cf_text_reply_subject_rules}, -+{"save-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Save Rules", cf_text_save_rules}, -+{"smtp-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Smtp Rules", cf_text_smtp_rules}, -+{"sort-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Sort Rules", cf_text_sort_rules}, -+{"startup-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ "Startup Rules", cf_text_startup_rules}, - #ifndef _WINDOWS - {"display-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - NULL, cf_text_disp_char_set}, -@@ -2718,6 +2777,7 @@ init_vars(struct pine *ps, void (*cmds_f - if(cmds_f) - (*cmds_f)(ps, VAR_INIT_CMD_LIST); - -+ (void)create_rule_list(ps_global->vars); - #ifdef _WINDOWS - mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global)); - #endif /* _WINDOWS */ -@@ -3174,6 +3234,8 @@ feature_list(int index) - F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0}, - {"auto-move-read-msgs", "Auto Move Read Messages", - F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0}, -+ {"auto-move-read-msgs-using-rules", "Auto Move Read Messages Using Rules", -+ F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC, 0}, - {"auto-unselect-after-apply", NULL, - F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0}, - {"auto-unzoom-after-apply", NULL, -@@ -7842,6 +7904,34 @@ config_help(int var, int feature) - return(h_config_ab_sort_rule); - case V_FLD_SORT_RULE : - return(h_config_fld_sort_rule); -+ case V_THREAD_DISP_STYLE_RULES: -+ return(h_config_thread_display_style_rule); -+ case V_THREAD_INDEX_STYLE_RULES: -+ return(h_config_thread_index_style_rule); -+ case V_COMPOSE_RULES: -+ return(h_config_compose_rules); -+ case V_FORWARD_RULES: -+ return(h_config_forward_rules); -+ case V_INDEX_RULES: -+ return(h_config_index_rules); -+ case V_KEY_RULES: -+ return(h_config_key_macro_rules); -+ case V_REPLACE_RULES: -+ return(h_config_replace_rules); -+ case V_REPLY_INDENT_RULES: -+ return(h_config_reply_indent_rules); -+ case V_REPLY_LEADIN_RULES: -+ return(h_config_reply_leadin_rules); -+ case V_RESUB_RULES: -+ return(h_config_resub_rules); -+ case V_SAVE_RULES: -+ return(h_config_save_rules); -+ case V_SMTP_RULES: -+ return(h_config_smtp_rules); -+ case V_SORT_RULES: -+ return(h_config_sort_rules); -+ case V_STARTUP_RULES: -+ return(h_config_startup_rules); - case V_POST_CHAR_SET : - return(h_config_post_char_set); - case V_UNK_CHAR_SET : -Index: alpine-2.23/pith/conf.h -=================================================================== ---- alpine-2.23.orig/pith/conf.h -+++ alpine-2.23/pith/conf.h -@@ -157,6 +157,46 @@ - #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p - #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p - #define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p -+#define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l -+#define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l -+#define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l -+#define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l -+#define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l -+#define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l -+#define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l -+#define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l -+#define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l -+#define VAR_KEY_RULES vars[V_KEY_RULES].current_val.l -+#define GLO_KEY_RULES vars[V_KEY_RULES].global_val.l -+#define USR_KEY_RULES vars[V_KEY_RULES].user_val.l -+#define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l -+#define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l -+#define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l -+#define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l -+#define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l -+#define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l -+#define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l -+#define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l -+#define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l -+#define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l -+#define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l -+#define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l -+#define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l -+#define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l -+#define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l -+#define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l -+#define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l -+#define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l -+#define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l -+#define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l -+#define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l -+#define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l -+#define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l -+#define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l -+#define USR_SORT_RULES vars[V_SORT_RULES].user_val.l -+#define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l -+#define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l -+#define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l - #ifndef _WINDOWS - #define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p - #define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p -Index: alpine-2.23/pith/conftype.h -=================================================================== ---- alpine-2.23.orig/pith/conftype.h -+++ alpine-2.23/pith/conftype.h -@@ -71,6 +71,20 @@ typedef enum { V_PERSONAL_NAME = 0 - , V_THREAD_MORE_CHAR - , V_THREAD_EXP_CHAR - , V_THREAD_LASTREPLY_CHAR -+ , V_THREAD_DISP_STYLE_RULES -+ , V_THREAD_INDEX_STYLE_RULES -+ , V_COMPOSE_RULES -+ , V_FORWARD_RULES -+ , V_INDEX_RULES -+ , V_KEY_RULES -+ , V_REPLACE_RULES -+ , V_REPLY_INDENT_RULES -+ , V_REPLY_LEADIN_RULES -+ , V_RESUB_RULES -+ , V_SAVE_RULES -+ , V_SMTP_RULES -+ , V_SORT_RULES -+ , V_STARTUP_RULES - #ifndef _WINDOWS - , V_CHAR_SET - , V_OLD_CHAR_SET -@@ -348,6 +362,7 @@ typedef enum { - F_FULL_AUTO_EXPUNGE, - F_EXPUNGE_MANUALLY, - F_AUTO_READ_MSGS, -+ F_AUTO_READ_MSGS_RULES, - F_AUTO_FCC_ONLY, - F_READ_IN_NEWSRC_ORDER, - F_SELECT_WO_CONFIRM, -Index: alpine-2.23/pith/detoken.c -=================================================================== ---- alpine-2.23.orig/pith/detoken.c -+++ alpine-2.23/pith/detoken.c -@@ -25,7 +25,7 @@ static char rcsid[] = "$Id: detoken.c 76 - #include "../pith/reply.h" - #include "../pith/mailindx.h" - #include "../pith/options.h" -- -+#include "../pith/rules.h" - - /* - * Hook to read signature from local file -@@ -91,6 +91,8 @@ detoken(ACTION_S *role, ENVELOPE *env, i - - if(is_sig){ - /* -+ * First we check if there is a rule about signatures, if there is -+ * use it, otherwise keep going and do the following: - * If role->litsig is set, we use it; - * Else, if VAR_LITERAL_SIG is set, we use that; - * Else, if role->sig is set, we use that; -@@ -104,14 +106,25 @@ detoken(ACTION_S *role, ENVELOPE *env, i - * there is no reason to mix them, so we don't provide support to - * do so. - */ -- if(role && role->litsig) -- literal_sig = role->litsig; -- else if(ps_global->VAR_LITERAL_SIG) -- literal_sig = ps_global->VAR_LITERAL_SIG; -- else if(role && role->sig) -- sigfile = role->sig; -- else -- sigfile = ps_global->VAR_SIGNATURE_FILE; -+ { RULE_RESULT *rule; -+ rule = get_result_rule(V_COMPOSE_RULES, FOR_COMPOSE, env); -+ if (rule){ -+ sigfile = cpystr(rule->result); -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ } -+ if (!sigfile){ -+ if(role && role->litsig) -+ literal_sig = role->litsig; -+ else if(ps_global->VAR_LITERAL_SIG) -+ literal_sig = ps_global->VAR_LITERAL_SIG; -+ else if(role && role->sig) -+ sigfile = role->sig; -+ else -+ sigfile = ps_global->VAR_SIGNATURE_FILE; -+ } - } - else if(role && role->template) - sigfile = role->template; -@@ -302,7 +315,7 @@ top: - } - } - } -- else if(pt->what_for & FOR_REPLY_INTRO) -+ else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE)) - repl = get_reply_data(env, role, pt->ctype, - subbuf, sizeof(subbuf)-1); - -Index: alpine-2.23/pith/indxtype.h -=================================================================== ---- alpine-2.23.orig/pith/indxtype.h -+++ alpine-2.23/pith/indxtype.h -@@ -84,6 +84,11 @@ typedef enum {iNothing, iStatus, iFStatu - iCurNews, iArrow, - iMailbox, iAddress, iInit, iCursorPos, - iDay2Digit, iMon2Digit, iYear2Digit, -+ iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey, -+ iNick, iFccFrom, iFccSender, iAltAddress, -+ iAddressTo, iAddressCc, iAddressRecip, iAddressSender, -+ iBcc, iLcc, -+ iFfrom, iFadd, - iSTime, iSTime24, iKSize, - iRoleNick, iNewLine, - iHeader, iText, -@@ -105,15 +110,26 @@ typedef struct index_parse_tokens { - - - /* these are flags for the what_for field in INDEX_PARSE_T */ --#define FOR_NOTHING 0x00 --#define FOR_INDEX 0x01 --#define FOR_REPLY_INTRO 0x02 --#define FOR_TEMPLATE 0x04 /* or for signature */ --#define FOR_FILT 0x08 --#define DELIM_USCORE 0x10 --#define DELIM_PAREN 0x20 --#define DELIM_COLON 0x40 -- -+#define FOR_NOTHING 0x00000 -+#define FOR_INDEX 0x00001 -+#define FOR_REPLY_INTRO 0x00002 -+#define FOR_TEMPLATE 0x00004 /* or for signature */ -+#define FOR_FILT 0x00008 -+#define DELIM_USCORE 0x00010 -+#define DELIM_PAREN 0x00020 -+#define DELIM_COLON 0x00040 -+#define FOR_FOLDER 0x00080 /* for rules */ -+#define FOR_RULE 0x00100 /* for rules */ -+#define FOR_TRIM 0x00200 /* for rules */ -+#define FOR_RESUB 0x00400 /* for rules */ -+#define FOR_REPLACE 0x00800 /* for rules */ -+#define FOR_SORT 0x01000 /* for rules */ -+#define FOR_FLAG 0x02000 /* for rules */ -+#define FOR_COMPOSE 0x04000 /* for rules */ -+#define FOR_THREAD 0x08000 /* for rules */ -+#define FOR_STARTUP 0x10000 /* for rules */ -+#define FOR_KEY 0x20000 /* for rules */ -+#define FOR_SAVE 0x40000 /* for rules */ - - #define DEFAULT_REPLY_INTRO "default" - -Index: alpine-2.23/pith/mailcmd.c -=================================================================== ---- alpine-2.23.orig/pith/mailcmd.c -+++ alpine-2.23/pith/mailcmd.c -@@ -39,6 +39,7 @@ static char rcsid[] = "$Id: mailcmd.c 11 - #include "../pith/ablookup.h" - #include "../pith/search.h" - #include "../pith/charconv/utf8.h" -+#include "../pith/rules.h" - - #ifdef _WINDOWS - #include "../pico/osdep/mswin.h" -@@ -665,6 +666,7 @@ do_broach_folder(char *newfolder, CONTEX - strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1); - ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; - ps_global->context_current = ps_global->context_list; -+ setup_threading_index_style(); - reset_index_format(); - clear_index_cache(ps_global->mail_stream, 0); - /* MUST sort before restoring msgno! */ -@@ -991,6 +993,7 @@ do_broach_folder(char *newfolder, CONTEX - - clear_index_cache(ps_global->mail_stream, 0); - reset_index_format(); -+ setup_threading_index_style(); - - /* - * Start news reading with messages the user's marked deleted -@@ -1114,7 +1117,10 @@ do_broach_folder(char *newfolder, CONTEX - - if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ - -- perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream); -+ perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream, -+ V_STARTUP_RULES, newfolder); -+ -+ reset_startup_rule(ps_global->mail_stream); - - if(ps_global->start_entry > 0){ - mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) -@@ -1136,124 +1142,7 @@ do_broach_folder(char *newfolder, CONTEX - else - use_this_startup_rule = ps_global->inc_startup_rule; - -- switch(use_this_startup_rule){ -- /* -- * For news in incoming collection we're doing the same thing -- * for first-unseen and first-recent. In both those cases you -- * get first-unseen if FAKE_NEW is off and first-recent if -- * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the -- * same as first recent because all recent msgs are unseen -- * and all unrecent msgs are seen (see pine_mail_open). -- */ -- case IS_FIRST_UNSEEN: --first_unseen: -- mn_set_cur(ps_global->msgmap, -- (sp_first_unseen(m) -- && mn_get_sort(ps_global->msgmap) == SortArrival -- && !mn_get_revsort(ps_global->msgmap) -- && !get_lflag(ps_global->mail_stream, NULL, -- sp_first_unseen(m), MN_EXLD) -- && (n = mn_raw2m(ps_global->msgmap, -- sp_first_unseen(m)))) -- ? n -- : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID)); -- break; -- -- case IS_FIRST_RECENT: --first_recent: -- /* -- * We could really use recent for news but this is the way -- * it has always worked, so we'll leave it. That is, if -- * the FAKE_NEW feature is on, recent and unseen are -- * equivalent, so it doesn't matter. If the feature isn't -- * on, all the undeleted messages are unseen and we start -- * at the first one. User controls with the FAKE_NEW feature. -- */ -- if(IS_NEWS(ps_global->mail_stream)){ -- mn_set_cur(ps_global->msgmap, -- first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID)); -- } -- else{ -- mn_set_cur(ps_global->msgmap, -- first_sorted_flagged(F_RECENT | F_UNSEEN -- | F_UNDEL, -- m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID)); -- } -- break; -- -- case IS_FIRST_IMPORTANT: -- mn_set_cur(ps_global->msgmap, -- first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID)); -- break; -- -- case IS_FIRST_IMPORTANT_OR_UNSEEN: -- -- if(IS_NEWS(ps_global->mail_stream)) -- goto first_unseen; -- -- { -- MsgNo flagged, first_unseen; -- -- flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID); -- first_unseen = (sp_first_unseen(m) -- && mn_get_sort(ps_global->msgmap) == SortArrival -- && !mn_get_revsort(ps_global->msgmap) -- && !get_lflag(ps_global->mail_stream, NULL, -- sp_first_unseen(m), MN_EXLD) -- && (n = mn_raw2m(ps_global->msgmap, -- sp_first_unseen(m)))) -- ? n -- : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID); -- mn_set_cur(ps_global->msgmap, -- (MsgNo) MIN((int) flagged, (int) first_unseen)); -- -- } -- -- break; -- -- case IS_FIRST_IMPORTANT_OR_RECENT: -- -- if(IS_NEWS(ps_global->mail_stream)) -- goto first_recent; -- -- { -- MsgNo flagged, first_recent; -- -- flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID); -- first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN -- | F_UNDEL, -- m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID); -- mn_set_cur(ps_global->msgmap, -- (MsgNo) MIN((int) flagged, (int) first_recent)); -- } -- -- break; -- -- case IS_FIRST: -- mn_set_cur(ps_global->msgmap, -- first_sorted_flagged(F_UNDEL, m, pc, -- THREADING() ? 0 : FSF_SKIP_CHID)); -- break; -- -- case IS_LAST: -- mn_set_cur(ps_global->msgmap, -- first_sorted_flagged(F_UNDEL, m, pc, -- FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); -- break; -- -- default: -- alpine_panic("Unexpected incoming startup case"); -- break; -- -- } -+ find_startup_position(use_this_startup_rule, m, pc); - } - else if(IS_NEWS(ps_global->mail_stream)){ - /* -@@ -1431,9 +1320,11 @@ expunge_and_close(MAILSTREAM *stream, ch - /* Save read messages? */ - if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] - && sp_flagged(stream, SP_INBOX) -- && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){ -+ && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || -+ (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){ - - if(F_ON(F_AUTO_READ_MSGS,ps_global) -+ || F_ON(F_AUTO_READ_MSGS_RULES, ps_global) - || (pith_opt_read_msg_prompt - && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER))) - /* move inbox's read messages */ -@@ -1716,6 +1607,9 @@ move_read_msgs(MAILSTREAM *stream, char - char *bufp = NULL; - MESSAGECACHE *mc; - -+ if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global)) -+ return move_read_msgs_using_rules(stream, dstfldr, buf); -+ - if(!is_absolute_path(dstfldr) - && !(save_context = default_save_context(ps_global->context_list))) - save_context = ps_global->context_list; -@@ -1755,8 +1649,9 @@ move_read_msgs(MAILSTREAM *stream, char - snprintf(buf, buflen, "Moving %s read message%s to \"%s\"", - comatose(searched), plural(searched), dstfldr); - we_cancel = busy_cue(buf, NULL, 0); -- if(save(ps_global, stream, save_context, dstfldr, msgmap, -- SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched) -+ ps_global->exiting = 1; -+ if((save(ps_global, stream, save_context, dstfldr, msgmap, -+ SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)) - strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */ - - buf[buflen-1] = '\0'; -@@ -1794,7 +1689,9 @@ move_read_incoming(MAILSTREAM *stream, C - && ((context_isambig(folder) - && folder_is_nick(folder, FOLDERS(context), 0)) - || folder_index(folder, context, FI_FOLDER) > 0) -- && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){ -+ && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL)) -+ || (F_ON(F_AUTO_READ_MSGS,ps_global) && -+ F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){ - - for(; f && *archive; archive++){ - char *p; -@@ -2761,3 +2658,295 @@ get_uname(char *mailbox, char *target, i - - return(*target ? target : NULL); - } -+ -+char * -+move_read_msgs_using_rules(MAILSTREAM *stream, char *dstfldr, char *buf) -+{ -+ CONTEXT_S *save_context = NULL; -+ char **folder_to_save = NULL; -+ int num, we_cancel; -+ long i, j, success; -+ MSGNO_S *msgmap = NULL; -+ unsigned long nmsgs = 0L, stream_nmsgs; -+ -+ if(!is_absolute_path(dstfldr) -+ && !(save_context = default_save_context(ps_global->context_list))) -+ save_context = ps_global->context_list; -+ -+ folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *)); -+ folder_to_save[0] = NULL; -+ mn_init(&msgmap, stream->nmsgs); -+ stream_nmsgs = stream->nmsgs; -+ for (i = 1L; i <= stream_nmsgs ; i++){ -+ set_lflag(stream, msgmap, i, MN_SLCT, 0); -+ folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD) -+ ? NULL : get_folder_to_save(stream, i, dstfldr); -+ } -+ for (i = 1L; i <= stream_nmsgs; i++){ -+ num = 0; -+ if (folder_to_save[i]){ -+ mn_init(&msgmap, stream_nmsgs); -+ for (j = i; j <= stream_nmsgs ; j++){ -+ if (folder_to_save[j]){ -+ if (!strcmp(folder_to_save[i], folder_to_save[j])){ -+ set_lflag(stream, msgmap, j, MN_SLCT, 1); -+ num++; -+ if (j != i) -+ fs_give((void **)&folder_to_save[j]); -+ } -+ } -+ } -+ pseudo_selected(stream, msgmap); -+ sprintf(buf, "Moving %s read message%s to \"%.45s\"", -+ comatose(num), plural(num), folder_to_save[i]); -+ we_cancel = busy_cue(buf, NULL, 1); -+ ps_global->exiting = 1; -+ if(success = save(ps_global, stream,save_context, folder_to_save[i], -+ msgmap, SV_DELETE | SV_FIX_DELS)) -+ nmsgs += success; -+ if(we_cancel) -+ cancel_busy_cue(success ? 0 : -1); -+ for (j = i; j <= stream_nmsgs ; j++) -+ set_lflag(stream, msgmap, j, MN_SLCT, 0); -+ fs_give((void **)&folder_to_save[i]); -+ mn_give(&msgmap); -+ } -+ } -+ ps_global->exiting = 0; /* useful if we call from aggregate operations */ -+ sprintf(buf, "Moved automatically %s message%s", -+ comatose(nmsgs), plural(nmsgs)); -+ if (folder_to_save) -+ fs_give((void **)folder_to_save); -+ rule_curpos = 0L; -+ return buf; -+} -+ -+char * -+get_folder_to_save(MAILSTREAM *stream, long i, char *dstfldr) -+{ -+ MESSAGECACHE *mc = NULL; -+ RULE_RESULT *rule; -+ MSGNO_S *msgmap = NULL; -+ char *folder_to_save = NULL, *save_folder = NULL; -+ int n; -+ long msgno; -+ -+ /* The plan is as follows: Select each message of the folder. We -+ * need to set the cursor correctly so that iFlag gets the value -+ * correctly too, otherwise iFlag will get the value of the position -+ * of the cursor. After that we need to look for a rule that applies -+ * to the message and get the saving folder. If we get a saving folder, -+ * and we used the _FLAG_ token, use that folder, if no -+ * _FLAG_ token was used, move only if seen and not deleted, to the -+ * folder specified in the saving rule. If we did not get a saving -+ * folder from the rule, just save in the default folder. -+ */ -+ mn_init(&msgmap, stream->nmsgs); -+ rule_curpos = i; -+ msgno = mn_m2raw(msgmap, i); -+ if (msgno > 0L){ -+ mc = mail_elt(stream, msgno); -+ rule = (RULE_RESULT *) -+ get_result_rule(V_SAVE_RULES, FOR_SAVE, mc->private.msg.env); -+ if (rule){ -+ folder_to_save = cpystr(rule->result); -+ n = rule->number; -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ } -+ -+ if (folder_to_save && *folder_to_save){ -+ RULELIST *list = get_rulelist_from_code(V_SAVE_RULES, -+ ps_global->rule_list); -+ RULE_S *prule = get_rule(list, n); -+ if (condition_contains_token(prule->condition, "_FLAG_") -+ || (mc->valid && mc->seen && !mc->deleted) -+ || (!mc->valid && mc->searched)) -+ save_folder = cpystr(folder_to_save); -+ else -+ save_folder = NULL; -+ } -+ else -+ if (!mc || (mc->seen && !mc->deleted)) -+ save_folder = cpystr(dstfldr); -+ mn_give(&msgmap); -+ rule_curpos = 0L; -+ return save_folder; -+} -+ -+unsigned long -+rules_cursor_pos(MAILSTREAM *stream) -+{ -+ MSGNO_S *msgmap = sp_msgmap(stream); -+ return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap)); -+} -+ -+void -+setup_threading_index_style(void) -+{ -+ RULE_RESULT *rule; -+ NAMEVAL_S *v; -+ int i; -+ -+ rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD, NULL); -+ if (rule || ps_global->VAR_THREAD_INDEX_STYLE){ -+ for(i = 0; v = thread_index_styles(i); 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/pith/mailcmd.h -=================================================================== ---- alpine-2.23.orig/pith/mailcmd.h -+++ alpine-2.23/pith/mailcmd.h -@@ -42,6 +42,8 @@ - #define DB_FROMTAB 0x02 /* opening because of TAB command */ - #define DB_INBOXWOCNTXT 0x04 /* interpret inbox as one true inbox */ - -+static MAILSTREAM *saved_stream; -+static unsigned long rule_curpos = 0L; - - /* - * generic "is aggregate message command?" test -@@ -63,7 +65,13 @@ int do_broach_folder(char *, CONTEXT_ - void expunge_and_close(MAILSTREAM *, char **, unsigned long); - void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int); - char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long); -+char *move_read_msgs_using_rules (MAILSTREAM *, char *, char *); -+unsigned get_perfolder_startup_rule (MAILSTREAM *, int, char *); -+void setup_threading_index_style (void); -+void find_startup_position (int, MAILSTREAM *, long); -+char *get_folder_to_save (MAILSTREAM *, long, char *); - char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t); -+unsigned long rules_cursor_pos (MAILSTREAM *); - void cross_delete_crossposts(MAILSTREAM *); - long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int); - int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *); -Index: alpine-2.23/pith/mailindx.c -=================================================================== ---- alpine-2.23.orig/pith/mailindx.c -+++ alpine-2.23/pith/mailindx.c -@@ -41,6 +41,7 @@ static char rcsid[] = "$Id: mailindx.c 1 - #include "../pith/send.h" - #include "../pith/options.h" - #include "../pith/ablookup.h" -+#include "../pith/rules.h" - #ifdef _WINDOWS - #include "../pico/osdep/mswin.h" - #endif -@@ -379,6 +380,13 @@ reset_index_format(void) - PAT_STATE pstate; - PAT_S *pat; - int we_set_it = 0; -+ char *rule; -+ -+ if(rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)){ -+ 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)){ -@@ -452,7 +460,7 @@ free_hdrtok(HEADER_TOK_S **hdrtok) - static INDEX_PARSE_T itokens[] = { - {"STATUS", iStatus, FOR_INDEX}, - {"MSGNO", iMessNo, FOR_INDEX}, -- {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -+ {"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}, -@@ -460,7 +468,7 @@ static INDEX_PARSE_T itokens[] = { - {"SIZETHREAD", iSizeThread, FOR_INDEX}, - {"SIZENARROW", iSizeNarrow, FOR_INDEX}, - {"KSIZE", iKSize, FOR_INDEX}, -- {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -+ {"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}, -@@ -472,56 +480,60 @@ static INDEX_PARSE_T itokens[] = { - {"SUBJECTTEXT", iSubjectText, FOR_INDEX}, - {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX}, - {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX}, -- {"OPENINGTEXT", iOpeningText, FOR_INDEX}, -- {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX}, -- {"KEY", iKey, FOR_INDEX}, -- {"KEYINIT", iKeyInit, FOR_INDEX}, -+ {"OPENINGTEXT", iOpeningText, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, -+ {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, -+ {"KEY", iKey, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, -+ {"KEYINIT", iKeyInit, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, - {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, - {"ATT", iAtt, FOR_INDEX}, - {"SCORE", iScore, FOR_INDEX}, - {"PRIORITY", iPrio, FOR_INDEX}, - {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX}, -- {"PRIORITY!", iPrioBang, FOR_INDEX}, -- {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -+ {"PRIORITY!", iPrioBang, FOR_INDEX}, -+ {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, - {"SMARTTIME24", iSTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -+ {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, -+ {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, -+ {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_SAVE}, -+ {"ADDRESSTO", iAddressTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"ADDRESSCC", iAddressCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"ADDRESSRECIPS", iAddressRecip, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"ADDRESSSENDER", iAddressSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, - {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -@@ -530,56 +542,71 @@ static INDEX_PARSE_T itokens[] = { - {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -+ {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, -+ {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE}, - {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -- {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -+ {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, - {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, -- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, - {"CURPREFDATETIME", iCurPrefDateTime, -- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, - {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, -- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -- {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, -+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, -+ {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, - {"HEADER", iHeader, FOR_INDEX}, - {"TEXT", iText, FOR_INDEX}, - {"ARROW", iArrow, FOR_INDEX}, - {"NEWLINE", iNewLine, FOR_REPLY_INTRO}, - {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, -+ {"NICK", iNick, FOR_RULE|FOR_SAVE}, -+ {"FCCFROM", iFccFrom, FOR_RULE|FOR_SAVE}, -+ {"FCCSENDER", iFccSender, FOR_RULE|FOR_SAVE}, -+ {"ALTADDRESS", iAltAddress, FOR_RULE|FOR_SAVE}, -+ {"FOLDER", iFolder, FOR_RULE|FOR_SAVE|FOR_FOLDER}, -+ {"ROLE", iRole, FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE}, -+ {"PROCID", iProcid, FOR_RULE|FOR_RESUB|FOR_FLAG|FOR_COMPOSE|FOR_TRIM|FOR_TEMPLATE}, -+ {"PKEY", iPkey, FOR_RULE|FOR_KEY}, -+ {"SCREEN", iScreen, FOR_RULE|FOR_KEY}, -+ {"FLAG", iFlag, FOR_RULE|FOR_SAVE|FOR_FLAG}, -+ {"COLLECTION", iCollection, FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER}, -+ {"BCC", iBcc, FOR_COMPOSE|FOR_RULE}, -+ {"LCC", iLcc, FOR_COMPOSE|FOR_RULE}, -+ {"FORWARDFROM", iFfrom, FOR_COMPOSE|FOR_RULE}, -+ {"FORWARDADDRESS", iFadd, FOR_COMPOSE|FOR_RULE}, - {NULL, iNothing, FOR_NOTHING} - }; - -@@ -2486,6 +2513,24 @@ format_index_index_line(INDEXDATA_S *ida - from_str(cdesc->ctype, idata, str, sizeof(str), ice); - break; - -+ case iAddressTo: -+ case iAddressCc: -+ case iAddressRecip: -+ {ENVELOPE *env; -+ int we_clear; -+ env = rules_fetchenvelope(idata, &we_clear); -+ sprintf(str, "%-*.*s", ifield->width, ifield->width, -+ detoken_src((cdesc->ctype == iAddressTo -+ ? "_ADDRESSTO_" -+ : (cdesc->ctype == iAddressCc -+ ? "_ADRESSCC_" -+ : "_ADRESSRECIPS_")), FOR_INDEX, -+ env, NULL, NULL, NULL)); -+ if(we_clear) -+ mail_free_envelope(&env); -+ } -+ break; -+ - case iTo: - if(((field = ((addr = fetch_to(idata)) - ? "To" -@@ -3880,7 +3925,17 @@ try_again: - - if(p > buf){ - size_t l; -+ ENVELOPE *env; -+ char *rule_result; - -+ if(rule_result = find_value((delete_quotes -+ ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"), -+ buf, PROCESS_SP, idata, 4)){ -+ 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)); -@@ -5459,10 +5514,10 @@ subj_str(INDEXDATA_S *idata, char *str, - { - char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL; - char *p, *border, *q = NULL, *free_subj = NULL; -- char *sp; -+ char *sp, *rule_result; - size_t len; - int width = -1; -- int depth = 0, mult = 2; -+ int depth = 0, mult = 2, collapsed, i, we_clear = 0; - int save; - int do_subj = 0, truncated_tree = 0; - PINETHRD_S *thd, *thdorig; -@@ -5518,6 +5573,14 @@ subj_str(INDEXDATA_S *idata, char *str, - * to free it at the end of this routine. - */ - -+ if (rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)){ -+ if(origsubj) -+ fs_give((void **)&origsubj); -+ we_clear++; -+ origsubj = cpystr(rule_result); -+ fs_give((void **)&rule_result); -+ } -+ - if(shorten) - shorten_subject(origsubj); - -@@ -5956,6 +6019,9 @@ subj_str(INDEXDATA_S *idata, char *str, - - if(free_subj) - fs_give((void **) &free_subj); -+ -+ if (we_clear && origsubj) -+ fs_give((void **)&origsubj); - } - - -@@ -6321,16 +6387,33 @@ from_str(IndexColType ctype, INDEXDATA_S - ? "To" - : (addr = fetch_cc(idata)) - ? "Cc" -- : NULL)) -- && set_index_addr(idata, field, addr, "To: ", -- strsize-1, fptr)) -- break; -+ : NULL))){ -+ char *rule_result; -+ rule_result = find_value("_FROM_", NULL, 0, idata, 1); -+ if (!rule_result) -+ set_index_addr(idata, field, addr, "To: ", -+ strsize-1, fptr); -+ else{ -+ sprintf(str, "%-*.*s", strsize-1, strsize-1, -+ rule_result); -+ fs_give((void **)&rule_result); -+ } - -+ break; -+ } - if(ctype == iFromTo && - (newsgroups = fetch_newsgroups(idata)) && - *newsgroups){ -- snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4), -- newsgroups); -+ char *rule_result; -+ rule_result = find_value("_FROM_", NULL, 0, idata, 1); -+ if (!rule_result) -+ sprintf(str, "To: %-*.*s", strsize-1-4, -+ strsize-1-4, newsgroups); -+ else{ -+ sprintf(str, "%-*.*s", strsize-1, strsize-1, -+ rule_result); -+ fs_give((void **)&rule_result); -+ } - break; - } - -@@ -6343,7 +6426,15 @@ from_str(IndexColType ctype, INDEXDATA_S - break; - - case iFrom: -- set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); -+ { char *rule_result; -+ rule_result = find_value("_FROM_", NULL, 0, idata, 4); -+ if (!rule_result) -+ set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); -+ else{ -+ sprintf(str, "%-*.*s", strsize-1, strsize-1, rule_result); -+ fs_give((void **)&rule_result); -+ } -+ } - break; - - case iAddress: -@@ -6641,3 +6732,64 @@ set_print_format(IELEM_S *ielem, int wid - } - } - } -+ -+void -+setup_threading_display_style(void) -+{ -+ RULE_RESULT *rule; -+ NAMEVAL_S *v; -+ int i; -+ -+ rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL); -+ if (rule || ps_global->VAR_THREAD_DISP_STYLE){ -+ for(i = 0; v = thread_disp_styles(i); 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; + } -+ return rule_result; -+} -Index: alpine-2.23/pith/mailindx.h -=================================================================== ---- alpine-2.23.orig/pith/mailindx.h -+++ alpine-2.23/pith/mailindx.h -@@ -30,6 +30,9 @@ extern void (*setup_header_widths)(MAIL - - - /* exported prototypes */ -+SortOrder translate (char *, int); -+char *find_value (char *, char *, int, INDEXDATA_S *, int); -+void setup_threading_display_style (void); - int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int); - void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *); - unsigned long line_hash(char *); -Index: alpine-2.23/pith/makefile.wnt -=================================================================== ---- alpine-2.23.orig/pith/makefile.wnt -+++ alpine-2.23/pith/makefile.wnt -@@ -46,7 +46,8 @@ HFILES= ../include/system.h ../include/g - init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ - mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ - options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ -- rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h smime.h smkeys.h sort.h sorttype.h \ -+ rfc2231.h rules.h rulestype.h save.h savetype.h search.h send.h sequence.h signal.h \ -+ smime.h smkeys.h sort.h sorttype.h \ - state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ - thread.h url.h user.h util.h - -@@ -56,7 +57,7 @@ OFILES= ablookup.obj abdlc.obj addrbook. - ical.obj imap.obj init.obj \ - keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ - margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ -- readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj \ -+ readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj \ - smime.obj smkeys.obj sort.obj state.obj status.obj store.obj stream.obj string.obj strlst.obj \ - takeaddr.obj tempfile.obj text.obj thread.obj adjtime.obj url.obj util.obj - -Index: alpine-2.23/pith/pine.hlp -=================================================================== ---- alpine-2.23.orig/pith/pine.hlp -+++ alpine-2.23/pith/pine.hlp -@@ -4310,6 +4310,7 @@ There are also additional details on -

  • FEATURE: -
  • FEATURE: -
  • FEATURE: -+
  • FEATURE: -
  • FEATURE: -
  • FEATURE: -
  • FEATURE: -@@ -19760,6 +19761,7 @@ This set of special tokens may be used i - "" option, - in the "" option, - in signature files, -+in the "new-rules" option, - in template files used in - "roles", and in the folder name - that is the target of a Filter Rule. -@@ -19772,7 +19774,7 @@ and in the target of Filter Rules. -

    -

    - --

    Tokens Available for all Cases (except Filter Rules)

    -+

    Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)

    - -
    -
    SUBJECT
    -@@ -19806,6 +19808,22 @@ email address, never the personal name. - For example, "mailbox@domain". -
  • - -+
    ADDRESSTO
    -+
    -+This is similar to the "TO" 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, "mailbox@domain" when -+the e-mail message contains only one person in the To: field, or -+"peter@flintstones.com president@world.com". -+
    -+ -+
    ADDRESSSENDER
    -+
    -+This is similar to the "sender" token, only it is always the -+email address of all person listed in the Sender: field of the message. -+Example: "mailbox@domain". -+
    -+ -
    MAILBOX
    -
    - This is the same as the "ADDRESS" except that the -@@ -19853,6 +19871,15 @@ are unavailable) of the persons specifie - message's "Cc:" header field. -
    - -+
    ADDRESSCC
    -+
    -+This is similar to the "CC" 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: "mailbox@domain" when -+the e-mail message contains only one person in the Cc: field, or -+"peter@flintstones.com president@world.com". -+
    -+ -
    RECIPS
    -
    - This token represents the personal names (or email addresses if the names -@@ -19861,6 +19888,14 @@ message's "To:" header field a - the message's "Cc:" header field. -
    - -+
    ADDRESSRECIPS
    -+
    -+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. -+
    -+ -+ -
    NEWSANDRECIPS
    -
    - This token represents the newsgroups from the -@@ -20993,6 +21028,110 @@ This is an end of line marker. - - -

    -+

    Tokens Available Only for New-Rules

    -+ -+
    -+
    FCCFROM
    -+
    -+The Fcc: folder assigned to the email address in the From: field in the -+addressbook. -+
    -+
    -+ -+
    -+
    FCCSENDER
    -+
    -+The Fcc: folder assigned to the email address in the Sender: field in the -+addressbook. -+
    -+
    -+ -+
    -+
    ALTADDRESS
    -+
    -+The value of your -+ -+variable. At this time, no expansion of regular expressions is supported. -+
    -+
    -+ -+
    -+
    NICK
    -+
    -+Nickname of the person in the From field in your addressbook. -+
    -+
    -+ -+
    -+
    FOLDER
    -+
    -+Name of the folder where the rule will be applied. -+
    -+
    -+ -+
    -+
    COLLECTION
    -+
    -+Name of the collection list where the rule will be applied. -+
    -+
    -+ -+
    -+
    ROLE
    -+
    -+Name of the Role used to reply a message. -+
    -+
    -+ -+
    -+
    BCC
    -+
    -+Not implemented yet, but it will be implemented in future versions. It will -+be used for compose -+reply -+forward -+rules. -+
    -+
    -+ -+
    -+
    LCC
    -+
    -+This is the value of the Lcc: field at the moment that you start the composition. -+
    -+
    -+ -+
    -+
    FORWARDFROM
    -+
    -+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. -+
    -+
    -+ -+
    -+
    FORWARDADDRESS
    -+
    -+This is the address of the person that sent the message that you -+are forwarding. -+
    -+
    -+ -+ -+ -+ -+
    -+
    FLAG
    -+
    -+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 -+new rules explanation and example help. -+
    -+
    -+ -+

    -

    Token Available Only for Templates and Signatures

    - -
    -@@ -24390,6 +24529,922 @@ character sets Alpine knows about by usi - <End of help on this topic> - - -+====== h_config_procid ===== -+ -+ -+Token: PROCID -+ -+ -+

    TOKEN: PROCID explained

    -+ -+

    -+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. -+ -+

    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. -+ -+

    -+Consider the following examples for forward-rules: -+ -+

    -+_ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} -+ -+

    -+and -+ -+

    -+_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} -+ -+

    -+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 -+ -+

    -+_PROCID_ == {fwd-subject} && _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} -+ -+

    -+In this case, this rule will be tested fully only when we are determining -+the subject line of a forwarded message, not otherwise. -+ -+

    -+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. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_compose_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_compose-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    At this time, this option is used to generate values for signature -+files that is not possible to do with the use of -+roles. -+ -+

    For example, you can have a rule like:
    -+_TO_ >> {Peter Flintstones} => _SIGNATURE_{~/.petersignature} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_forward_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_forward-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    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 this link. -+ -+

    If you want to edit the subject of a forwarded message, use the -+PROCID fwd-subject. For example you could have a rule like -+ -+

    -+_ROLE_ == {admin} && _SUBJECT_ !> {[tag] } => _COPY_{[tag] _SUBJECT_} -+ -+

    Another way in which this option can be used, is to trim the values of -+some fields. For this application the PROCID is fwd-lcc. For -+example it can be used in the following way: -+ -+

    -+_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} -+ -+

    Other functions that can be used in this option are _EXEC_, _REXTRIM_ and -+_REXSUB_. -+ -+

    You can also use the _EXEC_ function. The documentation for this function -+is in the -+ -+help text. -+ -+

    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 "{" and "}". For example, to -+delete only one ocurrence of the string "Re: " in a subject we -+would write a rule such as -+ -+

    -+_FOLDER_ >> {} => _SUBJECT_ := _REXSUB_{Re: }{}{1} -+ -+

    The last parameter of the rexsub function is optional. In the sense -+that its omission is understood as if the third parameter was -+"{1}". In order to make unlimited substitutions, use -+"{g}" as the last parameter. -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_index_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_index-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used to supersede the value of the option for specific folders. In -+this form you can have different index-formats for different folders. For -+example an entry here may be: -+ -+

    -+_FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_pretty_command ===== -+ -+ -+Pretty-Command Explained -+ -+ -+

    Pretty Command Explained

    -+ -+

    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. -+ -+

    [Command "RETURN" not defined for this screen. Use ? for help] -+ -+

    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. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_key_macro_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_key-definition-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    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. -+ -+

    Always use the _SCREEN_ token as defined below.. You have been -+warned! -+ -+

    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. -+ -+

    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. -+ -+

    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 (MESSAGE INDEX), the -+screen where your message is displayed (MESSAGE -+TEXT) the screen where the list of folders is displayed (FOLDER LIST) and the attachment index screen (ATTACHMENT INDEX). The internal names of -+these screens for this patch are "index", "text", -+"folder", and "attachment", respectively. Please note -+that the internal names are all in lowercase and are case sensitive. -+ -+

    In order to define the screen, you use the _SCREEN_ token, so for -+example, you can write _SCREEN_ == {index}. -+ -+

    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 "pressed key"). For -+example you could use _PKEY_ == {~}, to designate the "~" -+key to activate the command. Some keystrokes (like control, or -+function keys) are encoded in special ways. You should read the -+full explanation on how to find -+out the encoding for each keystroke. -+ -+

    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 -+"$" and "h". You use the _COMMAND_ function to specify -+this. The syntax in this case is _COMMAND_{$,h}. -+ -+

    Observe that in the above example the different inputs are separated -+by commas. This is the standard way in which the -+ 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. -+ -+

    -+"_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}" -+ -+

    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 -+ -+

    -+_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{"$h"} -+ -+

    For more information on how to define the argument of the _COMMAND_ -+token see the help of -+. -+ -+

    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. -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_replace_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_replace-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used to have Alpine print different values for specific -+tokens in the . For example you -+can replace strings like "To: newsgroup" by your name. -+ -+

    Here are examples of possible rules: -+ -+

    _FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} -+ -+

    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 -+ -+

    _FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} -+ -+

    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.
    -+ -+

    _FOLDER_ == {some-folder} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: } -+ -+

    You can also use the _EXEC_ function. The documentation for this function -+is in the -+ -+help text. -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_reply_leadin_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_reply-leadin-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used to have Alpine generate a different -+ string dependent either on -+the person you are replying to, or the folder where the message is being -+replied is in, or both. -+ -+

    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: -+

    -+_FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} -+ -+

    Here there is an example that one can use to change the reply indent string -+to reply people that speak spanish. -+

    -+_FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_resub_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_reply-subject-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used to have Alpine generate a different subject when -+replying rather than the one Alpine would generate automatically. -+ -+

    Here there are a couple of examples about how to use this -+configuration option: -+ -+

    In order to have messages with empty subject to be replied with the message -+"your message" use the rule
    -+

    _SUBJECT_ == {} => _RESUB_{Re: your message}
    -+ -+

    If you want to trim some parts of the subject when you reply use the -+rule
    -+

    _SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}
    -+ -+

    this rule removes the brackets "[" and "]" whenever the string "[one]" -+appears in it, it also removes the word "two" from it. -+ -+

    Another example where you may want to use this rule is when you -+correspond with people that change the reply string from "Re:" -+to "AW:" or "Sv:". In this case a rule like
    -+

    _SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }
    -+

    -+would eliminate undesired strings in replies. -+ -+

    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 "a" of a file. -+ -+

    -+#!/bin/sh
    -+sed 's/a//g' $1 > /tmp/mytest
    -+mv /tmp/mytest $1
    -+
    -+ -+

    -+As you can see this script took "$1" 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 "$1". This is the kind of behavior -+that your program is expected to have. -+ -+

    -+The content of the input file ("$1" above) is the value of a token -+like _SUBJECT_. In order to indicate this, we use the notation -+ -+

    -+_SUBJECT_ := _EXEC_{/path/to/script} -+ -+

    for the action. So for example -+ -+

    -+_FOLDER_ := {sent-mail} => _SUBJECT_ := _EXEC_{/path/to/script} -+ -+

    is a valid rule. -+ -+

    You can also use this configuration option to customize reply subjects -+according to the sender of the message. -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+====== h_config_sort_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_sort-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used to have Alpine sort different folders in different orders -+and thus override the value already set in the -+ configuration option. -+ -+

    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. -+ -+

    -+_COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} -+ -+

    Another example could be
    -+_FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+ -+====== h_config_save_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_save-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    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). -+ -+

    If this option is set and the -+ configuration -+option is also enabled then these definitions will be used to move messages -+from your INBOX when exiting Alpine. -+ -+

    Here there are some examples
    -+_FLAG_ >> {D} -> Trash
    -+_FROM_ == {U2} -> Bono
    -+_FOLDER_ == {comp.mail.pine} -> pine-stuff
    -+_NICK_ != {} -> _NICK_/_NICK_
    -+_DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+ -+====== h_config_reply_indent_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_reply-indent-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    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 -+is used. -+ -+

    The associated function to this configuration option is called "RESTR" (for -+REply STRing). Some examples of its use are:
    -+_FROM_ == {Your Boss} => _RESTR_{"> "}
    -+_FROM_ == {My Wife} => _RESTR_{":* "}
    -+_FROM_ == {Perter Flintstone;Wilma Flintstone} => _RESTR_{"_INIT_ > "}
    -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+ -+====== h_config_smtp_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_smtp-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    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 -+"" configuration -+option according to the rules used in that variable. -+ -+

    The function associated to this configuration option is _SMTP_, an example -+of the use of this function is
    -+_ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+ -+====== h_config_startup_rules ===== -+ -+ -+OPTION: <!--#echo var="VAR_startup-rules"--> -+ -+ -+

    OPTION:

    -+ -+

    This option is used when a folder is being opened. You can use it to specify its and override -+Alpine's global value set for all folders. -+ -+

    An example of the usage of this option is:
    -+_FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen} -+ -+

    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 link. -+ -+

    <End of help on this topic> -+ -+ -+ -+====== h_config_new_rules ===== -+ -+ -+OPTION: New Rules Explained -+ -+ -+

    OPTION: New Rules Explained

    -+ -+This is a quite powerful option. Here you can define rules that override -+the values of any other option you have set in Alpine. -+ -+

    -+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 "rules" 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. -+ -+

    -+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. -+ -+

    -+ Here is an example: -+ -+

    -+ _FROM_ == {Fred Flintstone} => _SAVE_{Fred} -+ -+

    -+ Here the separator is "=>". 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 "Fred -+Flintstone", and the action will be that you will be offered to save -+it in the folder "Fred", whenever you press the letter -+"S" to save a message. -+ -+

    -+ The separator is always "=>", with one exception to be seen -+later. But for the most part this will be the only one you will ever need. -+ -+

    -+ 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 -+"{" and "}", so for example you can specify -+_SAVE_{saved-messages} as a valid sentence. -+ -+

    -+ Later in the document you will find examples. Here is a short -+description of what each function does: -+ -+

    -+

      -+
    • _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 -+, -+ and -+. -+See the help of those options for examples of how to use this function -+and configure these rules. -+
       
      -+
    • _INDEX_ : This function takes as an argument an index-format, and -+makes that the index-format for the specified folder. -+
       
      -+
    • _REPLACE_ : This function replaces the subject/from of the given e-mail by -+another subject/from only when displaying the index. -+
       
      -+
    • _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. -+
       
      -+
    • _RESTR_ : This function takes as an argument the value of the -+reply-indent-string to be used to answer the message being replied to. -+
       
      -+
    • _RESUB_ : This function replaces the subject of the given e-mail by -+another subject only when replying to a message. -+
       
      -+
    • _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. -+
       
      -+
    • _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. -+
       
      -+
    • _SMTP_ : This function takes as an argument the definition of a -+SMTP server. -+
       
      -+
    • _SORT_ : This function takes as an argument a Sort Style, and sorts a -+specified folder in that sort order. -+
       
      -+
    • _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_. -+
       
      -+
    • _REXTRIM_ : Same as _TRIM_ but its argument is one and -+only one extended regular expression. -+
       
      -+
    • _REXSUB_ : This function takes three arguments. The structure -+is _REXSUB_{pattern}{text}{number of times}. The "pattern" -+to match is an extended regular expression. If the pattern is matched, -+it will be replaced by "text" as many times as the "number -+of times" argument specifies. If the last argument is -+omitted (that is, there are only two arguments) then the last argument -+is considered as "{1}". -+
       
      -+
    • _STARTUP_ : This function takes as an argument an -+incoming-startup-rule, and open an specified folder using that rule. -+
       
      -+
    • _THREADSTYLE_ : This function takes as an argument a -+threading-display-style and uses it to display threads in a folder. -+
       
      -+
    • _THREADINDEX_ : This function takes as an argument a -+threading-index-style and uses it to display threads in a folder. -+
    -+ -+

    -+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: -+ -+

    -+ _FROM_ == {Fred Flintstone} => _SAVE_{Fred} -+ -+

    it will only be applied if the from is "Fred Flintstone". If -+the From is "Wilma Flintstone" the rule will be skipped. -+ -+

    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 "{" and "}" in the condition, this part -+of the condition is called the "condition set". The definition -+of each token can be found here. -+ -+

    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 -+this link. -+ -+

    There are two more tokens related to the option -+key-definition-rules. Those tokens -+are only specific to that option, and hence are not explained here. -+ -+

    You can also test in different ways, you can use the following -+"test operands": <<, !<, >>, !>, == and !=. -+All of them are two characters long. Here is the meaning of them: -+ -+

    -+

      -+
    • << : It tests if the value of the token is contained in -+the condition set. Here for example if the condition set were equal to -+"Freddy", then the condition: _NICK_ << {Freddy}, would be true if -+the value of _NICK_ were "Fred", "red" or "Freddy". You are just looking -+for substrings here. -+
    • >> : 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 -+"Fred", then the condition: _FROM_ >> {Fred}, would be true if -+the value of _FROM_ were "Fred Flintstone" or "Fred P. Flintstone" or "Freddy". -+
    • == : 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 "Freddy" or "red". -+
    • !< : This is true only when << is false and vice versa. -+
    • !> : This is true only when >> is false and vice versa. -+
    • != : This is true only when == is false and vice versa. -+
    -+ -+

    -+ Now let us say that you want the same action to be applied to more than -+one person or folder, say you want "folder1" and "folder2" to be sorted by -+Ordered Subject upon entering. Then you can list them all of them in the -+condition part separting them by a ";". Here is the way to do it. -+ -+

    -+ _FOLDER_ << {folder1; folder2} => _SORT_{OrderedSubj} -+ -+

    -+ Here is the first subtlety about these definitions. Notice that the -+following rule: -+ -+

    -+ _FOLDER_ == {folder1; folder2} => _SORT_{Reverse OrderedSubj} -+ -+

    works only for "folder1" but not for "folder2". This is because the -+comparison of the name of the folder is done with whatever is in between -+"{", ";" or "}", so in the above rule you would be testing
    -+"folder2" == " folder2". The extra space makes the difference. -+The reason why the first rule does not fail is because -+"folder2" << " folder2" is actually -+true. If something ever fails this may be something to look into. -+ -+

    -+ Here are a few examples of what we have talked about before. -+ -+

    -+_NICK_ == {lisa;kika} => _SAVE_{_NICK_/_NICK_}
    -+This means that if the nick is lisa, it will -+save the message in the folder "lisa/lisa", and if the nick -+is "kika", it will save the message in the folder "kika/kika" -+ -+

    -+_FOLDER_ == {Lynx} -> lynx
    -+This, is an abbreviation of the following rule:
    -+_FOLDER_ == {Lynx} => _SAVE_{lynx}
    -+(note the change in separator from "=>" to "->"). In the future -+I will use that abbreviation. -+ -+

    _FOLDER_ << {comp.mail.pine; pine-info; pine-alpha} -> pine
    -+Any message in the folders "comp.mail.pine", "pine-info" or "pine-alpha" -+will be saved to the folder "pine". -+ -+

    _FROM_ << {Pine Master} -> pine
    -+Any message whose From field contains -+"Pine Master" will be saved in the folder pine. -+ -+

    _FOLDER_ << {Lynx; pine-info; comp.mail.pine} => -+_INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)}
    Use a -+different index-format for the folders "Lynx", "pine-info" and -+"comp.mail.pine", where the size is not present. -+ -+

    _FOLDER_ == {Lynx;pine-info} => _REPLY_{*** _FROM_ (_ADDRESS_) -+wrote in the _FOLDER_ list _SMARTDATE_("Today" "today" "on -+_LONGDATE_"):}
    If a message is in one of the incoming folders "Lynx" -+or "pine-info", create a reply-leadin-string that acknowledges that. Note -+the absence of "," in the function _SMARTDATE_. For example answering to a -+message in the pine-info list would look like: -+ -+

    -+*** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today: -+ -+

    -+However replying for a message in the Lynx list would look: -+ -+

    -+*** mattack@area.com (mattack@area.com) wrote in the Lynx list today: -+ -+

    -+If you write in more than one language you can use this feature to create -+Reply-leadin-strings in different languages. -+ -+

    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. -+ -+

    _FOLDER_ << {Lynx; comp.mail.pine; pine_info; pine-alpha} => -+_SORT_{OrderedSubj}
    This means upon opening, sort the folders "Lynx", -+"comp.mail.pine", 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). -+ -+

    The last examples use the function _TRIM_ which has a special form. -+This function can only be used in the index list. -+ -+

    _FOLDER_ << {Lynx} => _SUBJECT_ := _TRIM_{lynx-dev }
    In -+the folder "Lynx" eliminate from the subject the string "lynx-dev " (with -+the space at the end). For example a message whose subject is "Re: -+lynx-dev unvisited Visited Links", would be shown in the index with -+subject: "Re: unvisited Visited Links", making the subject shorter and -+giving the same information. -+ -+

    _FROM_ >> {Name (Comment)} => _FROM_ := -+_TRIM_{ (Comment)}
    Remove the part " (Comment)" -+from the _FROM_, so when displaying in the index the real From "Name" -+will appear. -+ -+

    _SUBJECT_ == {} => _RESUB_{Re: your mail without subject} -+If there is no subject in the message, use the subject "Re: your mail -+wiyhout subject" as a subject for the reply message. -+ -+

    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 -+ -+

    _FOLDER_ == {INBOX} && _FROM_ >> {Rubye} => _SAVE_{Personal} -+ -+

    We could also have a rule that is triggered by an "or" -+condition by, sat for messages from Andres or messages in the index -+to trigger a specific reply leadin string. -+ -+

    _FOLDER_ == {INBOX} || _FROM_ >> {Andres} => _REPLY_{You wrote:} -+ -+

    Observe that the construction -+ -+

    _TOKEN_ == {value1} || _TOKEN_ == {value2} -+ -+

    can be shortened to -+ -+

    _TOKEN_ == {value1;value2} -+ -+

    Round parentheses can be used to group some conditions, for example -+ -+

    (_FROM_ >> {Andres} && _FOLDER_ == {INBOX}) || _FROM_ >> {Rubye} -+ -+ -+

    You can also list your index by nick, in the following way:
    -+_NICK_ != {} => _FROM_ := _REPLACE_{_NICK_} -+ -+

    -+ If you want to open the folder "pine-info" in the first non-read message -+use the rule:
    -+_FOLDER_ == {pine-info} => _STARTUP_{first-unseen} -+ -+

    -+ If you want to move your deleted messages to a folder, called "Trash", use -+the following rule:
    -+_FLAG_ >> {D} -> Trash -+ -+

    -+The reason why the above test is not "_FLAG_ == {D}" 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. -+ -+

    If you want to use a specific signature when you are in a specific collection -+use the following rule:
    -+_COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature} -+ -+

    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 "saving" rules do not compete with "sorting" rules. So the first -+"saving" rule that matches will be executed in the case of saving and so -+on. -+ -+

    -+

    -+<End of help on this topic> -+ -+ - ====== h_config_char_set ===== - - -@@ -28142,6 +29197,76 @@ the From field is used to show the relat - <End of help on this topic> - - -+====== h_config_thread_display_style_rule ===== -+ -+ -+OPTION: Threading-Display-Style-Rule -+ -+ -+

    OPTION: Threading-Display-Style-Rule

    -+ -+This option is very similar to -+, but it is a rule which specifies the -+display styles for a thread that you want displayed in a specific -+folder or collection. -+

    -+The token to be used in this function is _THREADSTYLE_. Here there is -+an example of its use -+

    -+_FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like} -+

    -+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 threading-display-style -+configuration option. -+ -+

    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 link. -+ -+

    -+

    -+<End of help on this topic> -+ -+ -+====== h_config_thread_index_style_rule ===== -+ -+ -+OPTION: Threading-Index-Style-Rule -+ -+ -+

    OPTION: Threading-Index-Style-Rule

    -+ -+This option is very similar to -+, but it is a rule which specifies the -+index styles for a thread that you want displayed in a specific -+folder or collection. -+

    -+The token to be used in this function is _THREADINDEX_. Here there is -+an example of its use -+

    -+_FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads} -+

    -+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 -+configuration option. -+ -+

    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 link. -+ -+

    -+

    -+<End of help on this topic> -+ -+ - ====== h_config_pruning_rule ===== - - -@@ -31833,6 +32958,29 @@ automatically transfer all read messages - them as deleted in the INBOX. Messages in the INBOX marked with an - "N" (meaning New, or unseen) are not affected. -

    -+

    -+<End of help on this topic> -+ -+ -+====== h_config_auto_read_msgs_rules ===== -+ -+ -+FEATURE: auto-move-read-msgs-using-rules -+ -+ -+

    FEATURE: auto-move-read-msgs-using-rules

    -+This feature controls an aspect of Alpine's behavior upon quitting. If set, -+and the -+"" -+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 -+"" and mark -+them as deleted in the INBOX. Messages in the INBOX marked with an -+"N" (meaning New, or unseen) are not affected. -+

    -

    -Index: alpine-2.23/pith/reply.c -=================================================================== ---- alpine-2.23.orig/pith/reply.c -+++ alpine-2.23/pith/reply.c -@@ -47,6 +47,8 @@ static char rcsid[] = "$Id: reply.c 1074 - #include "../pith/mailcmd.h" - #include "../pith/margin.h" - #include "../pith/smime.h" -+#include "../pith/copyaddr.h" -+#include "../pith/rules.h" - - - /* -@@ -864,8 +866,27 @@ char * - reply_quote_str(ENVELOPE *env) - { - char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; -+ char reply_string[MAX_PREFIX+1]; - -- strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1); -+ { RULE_RESULT *rule; -+ rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env); -+ if (rule){ -+ strncpy(reply_string,rule->result,sizeof(reply_string)); -+ reply_string[sizeof(reply_string)-1] = '\0'; -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ else -+ if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ -+ strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); -+ reply_string[sizeof(reply_string)-1] = '\0'; -+ } -+ else -+ strncpy(reply_string,"> ",sizeof("> ")); -+ } -+ -+ strncpy(buf, reply_string, sizeof(buf)-1); - buf[sizeof(buf)-1] = '\0'; - - /* set up the prefix to quote included text */ -@@ -917,10 +938,29 @@ reply_quote_str(ENVELOPE *env) - int - reply_quote_str_contains_tokens(void) - { -- return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] && -- (strstr(ps_global->VAR_REPLY_STRING, from_token) || -- strstr(ps_global->VAR_REPLY_STRING, nick_token) || -- strstr(ps_global->VAR_REPLY_STRING, init_token))); -+ char *reply_string; -+ -+ reply_string = (char *) malloc( 80*sizeof(char)); -+ { RULE_RESULT *rule; -+ rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE, NULL); -+ if (rule){ -+ reply_string = cpystr(rule->result); -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ else -+ if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ -+ strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); -+ reply_string[sizeof(reply_string)-1] = '\0'; -+ } -+ else -+ reply_string = cpystr("> "); -+ } -+ return(reply_string && reply_string[0] && -+ (strstr(reply_string, from_token) || -+ strstr(reply_string, nick_token) || -+ strstr(reply_string, init_token))); - } - - -@@ -1486,6 +1526,10 @@ get_addr_data(ENVELOPE *env, IndexColTyp - buf[0] = '\0'; - - switch(type){ -+ case iFfrom: -+ addr = env && env->sparep ? env->sparep : NULL; -+ break; -+ - case iFrom: - addr = env ? env->from : NULL; - break; -@@ -1898,22 +1942,194 @@ get_reply_data(ENVELOPE *env, ACTION_S * - - break; - -+ case iProcid: -+ if(ps_global->procid){ -+ strncpy(buf, ps_global->procid, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iRole: -+ if (ps_global->role){ -+ strncpy(buf, ps_global->role, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iRoleNick: -+ if(role && role->nick){ -+ strncpy(buf, role->nick, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iPkey: -+ if(ps_global->pressed_key){ -+ strcpy(buf, ps_global->pressed_key); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iScreen: -+ if(ps_global->screen_name){ -+ strncpy(buf, ps_global->screen_name, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iFfrom: - case iFrom: - case iTo: - case iCc: - case iSender: - case iRecips: - case iInit: -+ if (env) - get_addr_data(env, type, buf, maxlen); - break; - -- case iRoleNick: -- if(role && role->nick){ -- strncpy(buf, role->nick, maxlen); -- buf[maxlen] = '\0'; -+ case iFolder: -+ if(ps_global->cur_folder){ -+ strncpy(buf,ps_global->cur_folder, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iCollection: -+ if(ps_global->context_current->nickname){ -+ strncpy(buf,ps_global->context_current->nickname, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ -+ case iFlag: -+ {MAILSTREAM *stream = ps_global->mail_stream; -+ MSGNO_S *msgmap = NULL; -+ long msgno; -+ MESSAGECACHE *mc; -+ 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); -@@ -1941,6 +2157,11 @@ get_reply_data(ENVELOPE *env, ACTION_S * - - break; - -+ case iLcc: /* fake it, there are not enough spare pointers */ -+ if (env && env->date) -+ sprintf(buf,"%s",env->date); -+ break; -+ - case iNews: - case iCurNews: - get_news_data(env, type, buf, maxlen); -@@ -1990,6 +2211,14 @@ get_reply_data(ENVELOPE *env, ACTION_S * - - break; - -+ case iOpeningText: -+ case iOpeningTextNQ: -+ if(env && env->sparep){ -+ strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen); -+ buf[maxlen] = '\0'; -+ } -+ break; -+ - case iSubject: - case iShortSubject: - if(env && env->subject){ -@@ -2052,7 +2281,18 @@ reply_delimiter(ENVELOPE *env, ACTION_S - if(!env) - return; - -- strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); -+ { RULE_RESULT *rule; -+ rule = get_result_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO, env); -+ if(rule){ -+ strncpy(buf, rule->result, MAX_DELIM); -+ if (rule->result) -+ fs_give((void **)&rule->result); -+ fs_give((void **)&rule); -+ } -+ else -+ strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); -+ } -+ - buf[MAX_DELIM] = '\0'; - /* preserve exact default behavior from before */ - if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ -@@ -2311,6 +2551,7 @@ forward_subject(ENVELOPE *env, int flags - { - size_t l; - char *p, buftmp[MAILTMPLEN]; -+ RULE_RESULT *rule; - - if(!env) - return(NULL); -@@ -2318,9 +2559,19 @@ forward_subject(ENVELOPE *env, int flags - dprint((9, "checking subject: \"%s\"\n", - env->subject ? env->subject : "NULL")); - -- if(env->subject && env->subject[0]){ /* add (fwd)? */ -- snprintf(buftmp, sizeof(buftmp), "%s", env->subject); -- buftmp[sizeof(buftmp)-1] = '\0'; -+ buftmp[0] = '\0'; -+ ps_global->procid = cpystr("fwd-subject"); -+ if (rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)){ -+ 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/pith/rules.c -=================================================================== ---- /dev/null -+++ alpine-2.23/pith/rules.c -@@ -0,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); ++ ++ /* 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; -+ } -+ -+ 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; ++ 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 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 do_rextrim(test, tval->next); + } -+ 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; ++ ++ 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_give((void **)&test); ++ fs_resize((void **) &ret_string, strlen(ret_string) + 1); ++ regfree(&preg); ++ ++ return ret_string; + } -+ 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; ++ ++ 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; + } -+ fs_give((void **)&test); ++ 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; + } -+ 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; ++ ++ /* Regular Expressions Support */ ++ char * ++ expand (char *string, char *pattern) ++ { ++ char c, *ret_string = NULL; + 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); ++ ++ 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; + } -+ 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) ++ } ++ ++ 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(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/pith/rules.h -=================================================================== ---- /dev/null -+++ alpine-2.23/pith/rules.h -@@ -0,0 +1,154 @@ -+/* Included file rules.h */ -+ -+#ifndef PITH_RULES_INCLUDED -+#define PITH_RULES_INCLUDED -+ -+#include "../pith/conftype.h" -+#include "../pith/detoken.h" -+#include "../pith/indxtype.h" -+#include "../pith/rulestype.h" -+ -+/* Exported prototypes */ -+ -+void create_rule_list (struct variable *); -+SPAREP_S *get_sparep_for_rule(char *, int); -+void free_sparep_for_rule(void **); -+void free_parsed_rule_list (PRULELIST_S **); -+RULE_RESULT *get_result_rule (int, int, ENVELOPE *); -+char *get_rule_result (int , char *, int); -+char *process_rule (RULE_S *, int, ENVELOPE *); -+char **functions_for_token (char *); -+RULELIST *get_rulelist_from_code (int, PRULELIST_S *); -+RULE_S *get_rule (RULELIST *, int); -+int condition_contains_token (CONDITION_S *, char *); -+int context_for_function (char *); -+ENVELOPE *rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear); -+ -+/* Separators: -+ * -+ * A separator is a string that separates the rule condition with the rule -+ * action. Below is the list of separators -+ * -+ */ -+ -+#define SAVE_TO_SEP "->" -+#define APPLY_SEP "=>" -+ -+/*------- Definitions of tokens -------*/ -+/*------ Keep the list alphabetically sorted, thanks -------*/ -+ -+#define ADDR_TOKEN "_ADDRESS_" -+#define ADDCC_TOKEN "_ADDRESSCC_" -+#define ADDRECIP_TOKEN "_ADDRESSRECIPS_" -+#define ADDTO_TOKEN "_ADDRESSTO_" -+#define BCC_TOKEN "_BCC_" -+#define CC_TOKEN "_CC_" -+#define COLLECT_TOKEN "_COLLECTION_" -+#define FCCF_TOKEN "_FCCFROM_" -+#define FCCS_TOKEN "_FCCSENDER_" -+#define FLAG_TOKEN "_FLAG_" -+#define FOLDER_TOKEN "_FOLDER_" -+#define FADDRESS_TOKEN "_FORWARDADDRESS_" -+#define FFROM_TOKEN "_FORWARDFROM_" -+#define FROM_TOKEN "_FROM_" -+#define KEY_TOKEN "_PKEY_" -+#define LCC_TOKEN "_LCC_" -+#define NICK_TOKEN "_NICK_" -+#define OTEXT_TOKEN "_OPENINGTEXT_" -+#define OTEXTNQ_TOKEN "_OPENINGTEXTNQ_" -+#define PROCID_TOKEN "_PROCID_" -+#define ROLE_TOKEN "_ROLE_" -+#define SCREEN_TOKEN "_SCREEN_" -+#define SEND_TOKEN "_SENDER_" -+#define SUBJ_TOKEN "_SUBJECT_" -+#define THDDSPSTY_TOKEN "_THREADSTYLE_" -+#define THDNDXSTY_TOKEN "_THREADINDEX_" -+#define TO_TOKEN "_TO_" -+ -+/*------ Definitions of relational operands -------------*/ -+ -+typedef struct { -+ char *value; -+ TestType ttype; -+ int (*execute)(); -+} REL_TOKEN; -+ -+/* Relational Operands */ -+#define AND_REL "&&" /* For putting more than one condition */ -+#define IN_REL "<<" /* For belonging relation */ -+#define NI_REL ">>" /* For contain relation */ -+#define NOT_IN_REL "!<" /* Negation of IN_REL */ -+#define NOT_NI_REL "!>" /* Negation of NI_REL */ -+#define EQ_REL "==" /* Test of equality */ -+#define NOT_EQ_REL "!=" /* Test of inequality */ -+#define OPEN_SET "{" /* Braces to open a set */ -+#define CLOSE_SET "}" /* Braces to close a set*/ -+ -+/*--- Context in which these variables can be used ---*/ -+ -+typedef struct use_context { -+ char *name; -+ int what_for; -+} USE_IN_CONTEXT; -+ -+static USE_IN_CONTEXT tokens_use[] = { -+ {NICK_TOKEN, FOR_SAVE}, -+ {FROM_TOKEN, FOR_SAVE}, -+ {OTEXT_TOKEN, FOR_SAVE|FOR_FOLDER}, -+ {OTEXTNQ_TOKEN, FOR_SAVE|FOR_FOLDER}, -+ {ROLE_TOKEN, FOR_COMPOSE}, -+ {FOLDER_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_THREAD|FOR_COMPOSE}, -+ {SUBJ_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_COMPOSE}, -+ {FLAG_TOKEN, FOR_SAVE|FOR_FLAG}, -+ {COLLECT_TOKEN, FOR_SAVE|FOR_COMPOSE|FOR_FOLDER|FOR_THREAD}, -+ {THDDSPSTY_TOKEN, FOR_THREAD}, -+ {THDNDXSTY_TOKEN, FOR_THREAD}, -+ {ADDR_TOKEN, FOR_SAVE|FOR_FOLDER}, -+ {TO_TOKEN, FOR_SAVE}, -+ {ADDTO_TOKEN, FOR_SAVE|FOR_COMPOSE}, -+ {ADDCC_TOKEN, FOR_SAVE|FOR_COMPOSE}, -+ {ADDRECIP_TOKEN, FOR_SAVE|FOR_COMPOSE}, -+ {SCREEN_TOKEN, FOR_KEY}, -+ {KEY_TOKEN, FOR_KEY}, -+ {SEND_TOKEN, FOR_SAVE}, -+ {CC_TOKEN, FOR_SAVE}, -+ {BCC_TOKEN, FOR_COMPOSE}, -+ {LCC_TOKEN, FOR_COMPOSE}, -+ {FFROM_TOKEN, FOR_COMPOSE}, -+ {FADDRESS_TOKEN, FOR_COMPOSE}, -+ {NULL, FOR_NOTHING} -+}; -+ -+typedef struct { -+ char *name; -+ size_t len; -+ unsigned is_assignment:1; /* has structure _TOKEN_ := _FUNCTION_{} */ -+ int return_type; /* the type of data returned by the parser, when it fills voidtxt */ -+ TOKEN_VALUE *(*parse_action_value)(char *); -+ char *(*execute)(); -+ int what_for; -+} RULE_FCN; -+ -+#define COMMAND_FCN "_COMMAND_" -+#define COPY_FCN "_COPY_" -+#define EXEC_FCN "_EXEC_" -+#define INDEX_FCN "_INDEX_" -+#define REPLACE_FCN "_REPLACE_" -+#define REPLYSTR_FCN "_RESTR_" -+#define REPLY_FCN "_REPLY_" -+#define RESUB_FCN "_RESUB_" -+#define REXSUB_FCN "_REXSUB_" -+#define REXTRIM_FCN "_REXTRIM_" -+#define SAVE_FCN "_SAVE_" -+#define SIGNATURE_FCN "_SIGNATURE_" -+#define SMTP_FCN "_SMTP_" -+#define SORT_FCN "_SORT_" -+#define STARTUP_FCN "_STARTUP_" -+#define THRDSTYLE_FCN "_THREADSTYLE_" -+#define THRDINDEX_FCN "_THREADINDEX_" -+#define TRIM_FCN "_TRIM_" -+ -+#define STRICTLY 0x1 -+#define RELAXED 0x2 -+ -+#endif /* PITH_RULES_INCLUDED */ -Index: alpine-2.23/pith/rulestype.h -=================================================================== ---- /dev/null -+++ alpine-2.23/pith/rulestype.h -@@ -0,0 +1,94 @@ -+#ifndef PITH_RULESTYPE_INCLUDED -+#define PITH_RULESTYPE_INCLUDED -+ -+typedef struct rule { -+ char *result; /* The result of the rule */ -+ int number; /* The number of the rule that succeded, -1 if not */ -+} RULE_RESULT; -+ -+typedef struct { -+ char *value; -+ int type; -+} RULE_ACTION; -+ -+ -+#define TOKEN_VALUE struct tokenvalue_s -+#define CONDITION_S struct condition_s -+#define REXSUB_S struct rexsub_s -+#define RULEACTION_S struct ruleaction_s -+#define RULE_S struct rule_s -+#define RULELIST struct rulelist_s -+#define PRULELIST_S struct parsedrulelist_s -+ -+#define FREEREGEX 1 -+ -+#define UNDEFINED_TYPE 0 -+#define CHAR_TYPE 1 -+#define REXSUB_TYPE 2 -+ -+TOKEN_VALUE { -+ char *testxt; -+ void *voidtxt; -+ int voidtype; -+ int codefcn; -+ TOKEN_VALUE *next; -+}; -+ -+typedef enum {Equal, Subset, Includes, NotEqual, NotSubset, NotIncludes, EndTypes} TestType; -+ -+typedef enum {And, Or, ParOpen, ParClose, Condition} CondType; -+ -+typedef struct condvalue_s { -+ char *tname; /* tname ttype {value} */ -+ TestType ttype; /* type of rule */ -+ TOKEN_VALUE *value; /* value to check against */ -+} CONDVALUE_S; -+ -+CONDITION_S { -+ void *cndrule; /* text in condition */ -+ CondType cndtype; /* type of object */ -+ CONDITION_S *next; /* next condition to test */ -+}; -+ -+#define COND(C) ((CONDVALUE_S *)((C)->cndrule)) -+ -+RULEACTION_S { -+ char *token; /* token := function{value} or token = null */ -+ char *function; /* token := function{value} or simply function{value}*/ -+ TOKEN_VALUE *value; /* token := function{value} or simply function{value}*/ -+ int context; /* context in which this rule can be used */ -+ char* (*exec)(); -+}; -+ -+REXSUB_S { -+ char *pattern; /* pattern to use in the replacement */ -+ char *text; /* text to replace */ -+ int times; /* how many times to repeat the substitution */ -+}; -+ -+RULE_S { -+ CONDITION_S *condition; -+ RULEACTION_S *action; -+}; -+ -+RULELIST { -+ RULE_S *prule; -+ RULELIST *next; -+}; -+ -+PRULELIST_S { -+ int varnum; /* number associated to the variable */ -+ RULELIST *rlist; -+ PRULELIST_S *next; -+}; -+ -+#define USE_RAW_SP 0x001 -+#define PROCESS_SP 0x010 -+ -+typedef struct sparep { -+ int flag; -+ char *value; -+} SPAREP_S; -+ -+ -+#endif /* PITH_RULESTYPE_INCLUDED */ -Index: alpine-2.23/pith/save.c -=================================================================== ---- alpine-2.23.orig/pith/save.c -+++ alpine-2.23/pith/save.c -@@ -954,7 +954,7 @@ save(struct pine *state, MAILSTREAM *str - *date = '\0'; - - rv = save_fetch_append(stream, mn_m2raw(msgmap, i), -- NULL, save_stream, save_folder, context, -+ NULL, save_stream, folder, context, - mc ? mc->rfc822_size : 0L, flags, date, so); - - if(flags) -Index: alpine-2.23/pith/send.c -=================================================================== ---- alpine-2.23.orig/pith/send.c -+++ alpine-2.23/pith/send.c -@@ -44,6 +44,7 @@ static char rcsid[] = "$Id: send.c 1204 - #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" -@@ -1695,9 +1696,9 @@ call_mailer(METAENV *header, struct mail - char error_buf[200], *error_mess = NULL, *postcmd; - ADDRESS *a; - ENVELOPE *fake_env = NULL; -- int addr_error_count, we_cancel = 0; -+ int addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1; - long smtp_opts = 0L; -- char *verbose_file = NULL; -+ char *verbose_file = NULL, **smtp_list; - BODY *bp = NULL; - PINEFIELD *pf; - BODY *origBody = body; -@@ -1852,20 +1853,49 @@ call_mailer(METAENV *header, struct mail - * OK, who posts what? We tried an mta_handoff above, but there - * was either none specified or we decided not to use it. So, - * if there's an smtp-server defined anywhere, -+ * First we check for rules and make a list using the rules. - */ -- if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){ -- /*---------- SMTP ----------*/ -- dprint((4, "call_mailer: via TCP (%s)\n", -- alt_smtp_servers[0])); -- TIME_STAMP("smtp-open start (tcp)", 1); -- sending_stream = smtp_open(alt_smtp_servers, smtp_opts); -+ if(ps_global->VAR_SMTP_RULES && ps_global->VAR_SMTP_RULES[0] -+ && ps_global->VAR_SMTP_RULES[0][0]) -+ while (ps_global->VAR_SMTP_RULES[num_rules]) num_rules++; -+ -+ if(num_rules){ -+ int i, j; -+ -+ added_rules = 0; -+ smtp_list = (char **) fs_get ((num_rules + 1)*sizeof(char*)); -+ for (i = 0, j = 0; i < num_rules; i++){ -+ RULELIST *rule = get_rulelist_from_code(V_SMTP_RULES, -+ ps_global->rule_list); -+ RULE_S *prule = get_rule(rule, i); -+ if(prule){ -+ char *rule_result = process_rule(prule, FOR_COMPOSE, header->env); -+ if(rule_result && *rule_result){ -+ smtp_list[j++] = cpystr(rule_result); -+ added_rules++; -+ } -+ } -+ } -+ } -+ -+ if (added_rules < 0){ -+ smtp_list = (char **) fs_get (sizeof(char*)); -+ added_rules = 0; - } -- else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] -- && ps_global->VAR_SMTP_SERVER[0][0]){ -- /*---------- SMTP ----------*/ -- dprint((4, "call_mailer: via TCP\n")); -- TIME_STAMP("smtp-open start (tcp)", 1); -- sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts); -+ smtp_list[added_rules] = NULL; -+ -+ choice = smtp_list && smtp_list[0] && smtp_list[0][0] ? 3 : -+ (alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0] ? 2 : -+ (ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] -+ && ps_global->VAR_SMTP_SERVER[0][0] ? 1 : -1)); -+ -+ if(choice > 0){ -+ /*---------- SMTP ----------*/ -+ dprint((4, "call_mailer: via TCP (%s)\n",smtp_list[0])); -+ TIME_STAMP("smtp-open start (tcp)", 1); -+ sending_stream = smtp_open(choice == 3 ? smtp_list -+ : (choice == 2 ? alt_smtp_servers -+ : ps_global->VAR_SMTP_SERVER), smtp_opts); - } - else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){ - char *cmdlist[2]; -Index: alpine-2.23/pith/sort.c -=================================================================== ---- alpine-2.23.orig/pith/sort.c -+++ alpine-2.23/pith/sort.c -@@ -30,7 +30,7 @@ static char rcsid[] = "$Id: sort.c 1142 - #include "../pith/signal.h" - #include "../pith/busy.h" - #include "../pith/icache.h" -- -+#include "../pith/rules.h" - - /* - * global place to store mail_sort and mail_thread results -@@ -686,6 +686,41 @@ pine_compare_scores(const qsort_t *a, co - : ((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) -@@ -695,7 +730,21 @@ reset_sort_order(unsigned int flags) - PAT_S *pat; - SortOrder the_sort_order; - int sort_is_rev; -- -+ char *rule_result; -+ SortOrder new_sort = EndofList; -+ int is_rev; -+ -+ rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES); -+ if (rule_result && *rule_result){ -+ new_sort = (SortOrder) translate(rule_result, 1); -+ is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1; -+ fs_give((void **)&rule_result); ++ ++ if((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 (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 -@@ -721,6 +770,7 @@ reset_sort_order(unsigned int flags) - - 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/pith/sort.h ++ if (we_clear) *we_clear = 0; ++ env = pine_mail_fetchenvelope(idata->stream, idata->rawno); ++ return env; ++ } +Index: alpine-2.23.2/pith/rules.h =================================================================== ---- alpine-2.23.orig/pith/sort.h -+++ alpine-2.23/pith/sort.h -@@ -45,6 +45,6 @@ char *sort_name(SortOrder); - void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); - int decode_sort(char *, SortOrder *, int *, int); - void reset_sort_order(unsigned); -- -+SortOrder translate(char *, int); - - #endif /* PITH_SORT_INCLUDED */ -Index: alpine-2.23/pith/state.c +*** /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 =================================================================== ---- alpine-2.23.orig/pith/state.c -+++ alpine-2.23/pith/state.c -@@ -35,6 +35,7 @@ static char rcsid[] = "$Id: state.c 1074 - #include "../pith/smime.h" - #include "../pith/ical.h" - #include "../pith/bldaddr.h" -+#include "../pith/rules.h" - - /* - * Globals referenced throughout pine... -@@ -255,6 +256,9 @@ free_pine_struct(struct pine **pps) - 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/pith/state.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.orig/pith/state.h -+++ alpine-2.23/pith/state.h -@@ -33,7 +33,7 @@ - #include "../pith/stream.h" - #include "../pith/color.h" - #include "../pith/user.h" -- -+#include "../pith/rulestype.h" - - /* - * Printing control structure -@@ -112,6 +112,11 @@ struct pine { - MAILSTREAM *mail_stream; /* ptr to current folder stream */ - MSGNO_S *msgmap; /* ptr to current message map */ - -+ char screen_name[16]; /* name of current screen */ -+ char *role; /* role used when composing */ -+ char *procid; /* procedure id when needed */ -+ int exiting; -+ - unsigned read_predicted:1; - - char cur_folder[MAXPATH+1]; -@@ -367,6 +372,7 @@ struct pine { - struct { - char *(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *); - char *(*display_filter_trigger)(BODY *, char *, size_t); -+ char *(*exec_rule)(char *, gf_io_t, gf_io_t); - } tools; - - KEYWORD_S *keywords; -@@ -377,6 +383,9 @@ struct pine { - char last_error[500]; - INIT_ERR_S *init_errs; - -+ PRULELIST_S *rule_list; -+ char *pressed_key; -+ - PRINT_S *print; - - #ifdef SMIME -Index: alpine-2.23/pith/string.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.orig/pith/string.c -+++ alpine-2.23/pith/string.c -@@ -20,6 +20,7 @@ static char rcsid[] = "$Id: string.c 910 - string.c - Misc extra and useful string functions - - rplstr replace a substring with another string -+ - collspaces consecutive spaces are reduced to one space. - - sqzspaces Squeeze out the extra blanks in a string - - sqznewlines Squeeze out \n and \r. - - removing_trailing_white_space -@@ -131,6 +132,31 @@ rplstr(char *os, size_t oslen, int dl, c - return(x3); - } - -+/*---------------------------------------------------------------------- -+ collapse blank space -+ ----------------------------------------------------------------------*/ -+void -+collspaces(char *string) -+{ -+ char *p = string; -+ int only_one_space = 0; -+ -+ if(!string) -+ return; -+ -+ for(;isspace(*p); p++); -+ -+ while(*string = *p++) -+ if(!isspace((unsigned char)*string)){ -+ only_one_space = 0; -+ string++; -+ } -+ else if(!only_one_space){ -+ string++; -+ only_one_space++; -+ } -+ *string = '\0'; -+} - - - /*---------------------------------------------------------------------- -@@ -2997,3 +3023,35 @@ convert_decimal_to_alpha (char *rn, size - 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/pith/string.h +*** 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.orig/pith/string.h -+++ alpine-2.23/pith/string.h -@@ -87,6 +87,7 @@ struct date { - - /* exported prototypes */ - char *rplstr(char *, size_t, int, char *); -+void collspaces(char *); - void sqzspaces(char *); - void sqznewlines(char *); - void removing_leading_white_space(char *); -@@ -94,6 +95,7 @@ void removing_trailing_white_space(c - void replace_tabs_by_space(char *); - void removing_leading_and_trailing_white_space(char *); - int removing_double_quotes(char *); -+void removing_extra_stuff (char *); - char *skip_white_space(char *); - char *skip_to_white_space(char *); - char *removing_quotes(char *); +*** 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 *);