diff options
Diffstat (limited to 'pith')
49 files changed, 6447 insertions, 489 deletions
diff --git a/pith/Makefile.am b/pith/Makefile.am index ce6c78a3..7def45ef 100644 --- a/pith/Makefile.am +++ b/pith/Makefile.am @@ -25,7 +25,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldadd filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ - readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ + readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c diff --git a/pith/Makefile.in b/pith/Makefile.in index 322291b0..708abccf 100644 --- a/pith/Makefile.in +++ b/pith/Makefile.in @@ -83,7 +83,7 @@ am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \ margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ - remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(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) \ @@ -319,7 +319,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldadd filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ - readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ + 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 @@ -451,6 +451,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@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/pith/adrbklib.c b/pith/adrbklib.c index 01d00353..904928b8 100644 --- a/pith/adrbklib.c +++ b/pith/adrbklib.c @@ -5136,8 +5136,14 @@ init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, 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; diff --git a/pith/charconv/utf8.c b/pith/charconv/utf8.c index 411e1ddd..bca0d26b 100644 --- a/pith/charconv/utf8.c +++ b/pith/charconv/utf8.c @@ -1049,6 +1049,56 @@ utf8_width(char *str) /* + * Returns the screen cells width of the UTF-8 string argument, treating tabs + * in a special way. + */ +unsigned +utf8_widthis(char *str) +{ + unsigned width = 0; + int this_width; + UCS ucs; + unsigned long remaining_octets; + char *readptr; + + if(!(str && *str)) + return(width); + + readptr = str; + remaining_octets = readptr ? strlen(readptr) : 0; + + while(remaining_octets > 0 && *readptr){ + + ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets); + + if(ucs & U8G_ERROR){ + /* + * This should not happen, but do something to handle it anyway. + * Treat each character as a single width character, which is what should + * probably happen when we actually go to write it out. + */ + remaining_octets--; + readptr++; + this_width = 1; + } + else{ + this_width = (ucs == TAB) ? ((~width & 0x07) + 1) : wcellwidth(ucs); + + /* + * If this_width is -1 that means we can't print this character + * with our current locale. Writechar will print a '?'. + */ + if(this_width < 0) + this_width = 1; + } + + width += (unsigned) this_width; + } + + return(width); +} + +/* * Copy UTF-8 characters from src into dst. * This is intended to be used if you want to truncate a string at * the start instead of the end. For example, you have a long string diff --git a/pith/charconv/utf8.h b/pith/charconv/utf8.h index d22a8a7c..09a2a95c 100644 --- a/pith/charconv/utf8.h +++ b/pith/charconv/utf8.h @@ -81,6 +81,7 @@ UCS *ucs4_strncat(UCS *ucs4dst, UCS *ucs4src, size_t n); UCS *ucs4_strchr(UCS *s, UCS c); UCS *ucs4_strrchr(UCS *s, UCS c); unsigned utf8_width(char *); +unsigned utf8_widthis(char *); size_t utf8_to_width_rhs(char *, char *, size_t, unsigned); int utf8_snprintf(char *, size_t, char *, ...); size_t utf8_to_width(char *, char *, size_t, unsigned, unsigned *); diff --git a/pith/color.c b/pith/color.c index 9794294b..b5dc320c 100644 --- a/pith/color.c +++ b/pith/color.c @@ -21,7 +21,8 @@ static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" - +#include "../pith/mailview.h" +#include "../pico/estruct.h" char * color_embed(char *fg, char *bg) @@ -70,23 +71,110 @@ struct quote_colors { struct quote_colors *next; }; +int +is_word (buf, i, j) + char buf[NSTRING]; + int i, j; +{ + return i <= j && is_letter(buf[i]) ? + (i < j ? is_word(buf,i+1,j) : 1) : 0; +} + +int +is_mailbox(buf,i,j) +char buf[NSTRING]; + int i, j; +{ + return i <= j && (is_letter(buf[i]) || is_digit(buf[i]) || buf[i] == '.') + ? (i < j ? is_mailbox(buf,i+1,j) : 1) : 0; +} + +int +next_level_quote(buf, line, i, is_flowed) + char *buf; + char **line; + int i; + int is_flowed; +{ + int j; + + if (!single_level(buf[i])){ + if(is_mailbox(buf,i,i)){ + for (j = i; buf[j] && !isspace(buf[j]); j++); + if (is_word(buf,i,j-1) || is_mailbox(buf,i,j-1)) + j += isspace(buf[j]) ? 2 : 1; + } + else{ + switch(buf[i]){ + case ':' : + if (next(buf,i) != RPAREN) + j = i + 1; + else + j = i + 2; + break; + + case '-' : + if (next(buf,i) != '-') + j = i + 2; + else + j = i + 3; + break; + + case '+' : + case '*' : + if (next(buf,i) != ' ') + j = i + 2; + else + j = i + 3; + break; + + default : + for (j = i; buf[j] && !isspace(buf[j]) + && (!single_level(buf[i]) && !is_letter(buf[j])); j++); + + j += isspace(buf[j]) ? 1 : 0; + break; + } + } + if (line && *line) + (*line) += j - i; + } + else{ + j = i+1; + if (line && *line) + (*line)++; + } + if(!is_flowed){ + if(line && *line) + for(; isspace((unsigned char)*(*line)); (*line)++); + for (i = j; isspace((unsigned char) buf[i]); i++); + } + else i = j; + if (is_flowed && i != j) + buf[i] = '\0'; + return i; +} int color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) { - int countem = 0; + int countem = 0, i, j = 0; struct variable *vars = ps_global->vars; - char *p; + char *p, buf[NSTRING] = {'\0'}; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; + int code; + + code = (is_flowed ? IS_FLOWED : NO_FLOWED) | COLORAQUO; + select_quote(linenum, line, ins, (void *) &code); + strncpy(buf, tmp_20k_buf, NSTRING < SIZEOF_20KBUF ? NSTRING : SIZEOF_20KBUF); + buf[sizeof(buf)-1] = '\0'; p = line; - if(!is_flowed) - while(isspace((unsigned char)*p)) - p++; + for(i = 0; isspace((unsigned char)buf[i]); i++, p++); - if(p[0] == '>'){ + if(buf[i]){ struct quote_colors *c; /* @@ -135,7 +223,7 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) free_color_pair(&col); cp = NULL; - while(*p == '>'){ + while(buf[i]){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) @@ -145,10 +233,9 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) countem = (countem == 1) ? 0 : countem; - p++; - if(!is_flowed) - for(; isspace((unsigned char)*p); p++) - ; + i = next_level_quote(buf, &p, i, is_flowed); + for (; isspace((unsigned char)*p); p++); + for (; isspace((unsigned char)buf[i]); i++); } if(colors){ @@ -211,7 +298,7 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) } } - return(0); + return(1); } diff --git a/pith/color.h b/pith/color.h index b90d82cf..01bbbb58 100644 --- a/pith/color.h +++ b/pith/color.h @@ -21,6 +21,24 @@ #include "../pith/pattern.h" #include "../pith/osdep/color.h" +#define NO_FLOWED 0x0000 +#define IS_FLOWED 0x0001 +#define DELETEQUO 0x0010 +#define COLORAQUO 0x0100 +#define RAWSTRING 0x1000 + +/* This is needed for justification, I will move it to a better place later + * or maybe not + */ +#define is_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + +#define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \ + ((c) >= 'A' && (c) <= 'Z')) + +#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + +#define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \ + ((c) == ']')) typedef struct spec_color_s { int inherit; /* this isn't a color, it is INHERIT */ @@ -80,6 +98,7 @@ typedef struct spec_color_s { /* exported protoypes */ char *color_embed(char *, char *); int colorcmp(char *, char *); +int next_level_quote(char *, char **, int, int); int color_a_quote(long, char *, LT_INS_S **, void *); void free_spec_colors(SPEC_COLOR_S **); diff --git a/pith/conf.c b/pith/conf.c index 18eaf8f0..876667e5 100644 --- a/pith/conf.c +++ b/pith/conf.c @@ -29,6 +29,7 @@ static char rcsid[] = "$Id: conf.c 1266 2009-07-14 18:39:12Z hubert@u.washington #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" @@ -206,6 +207,8 @@ CONF_TXT_T cf_text_fcc_name_rule[] = "Determines default name for Fcc...\n# Choi CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\"."; +CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread."; + 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\"."; @@ -222,12 +225,44 @@ CONF_TXT_T cf_text_unk_character_set[] = "Defaults to nothing, which is equivale 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."; CONF_TXT_T cf_text_deadlets[] = "Specifies the number of dead letter files to keep when canceling."; CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap."; +CONF_TXT_T cf_special_text_color[] = "Specifies a comma separated list of text and regular expresions that Pine\n# will highlight"; + CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message."; CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message."; @@ -340,6 +375,8 @@ CONF_TXT_T cf_text_stat_msg_delay[] = "The number of seconds to sleep after writ CONF_TXT_T cf_text_busy_cue_rate[] = "Number of times per-second to update busy cue messages"; +CONF_TXT_T cf_text_sleep[] = "The number of seconds between a viewer finishing opening a file and removing\n#it. See more details in configuration screen. Default: 0"; + CONF_TXT_T cf_text_mailcheck[] = "The approximate number of seconds between checks for new mail"; CONF_TXT_T cf_text_mailchecknoncurr[] = "The approximate number of seconds between checks for new mail in folders\n# other than the current folder and inbox.\n# Default is same as mail-check-interval"; @@ -430,6 +467,9 @@ CONF_TXT_T cf_text_window_position[] = "Window position in the format: CxR+X+Y\n CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; +#ifndef _WINDOWS +CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; +#endif /*---------------------------------------------------------------------- These are the variables that control a number of pine functions. They @@ -520,6 +560,8 @@ static struct variable variables[] = { NULL, cf_text_fcc_name_rule}, {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_sort_key}, +{"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_thread_sort_key}, {"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, @@ -542,6 +584,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}, @@ -560,6 +630,8 @@ static struct variable variables[] = { NULL, cf_text_speller}, {"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_fillcol}, +{"special-text-color", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + NULL, cf_special_text_color}, {"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, NULL, cf_text_replystr}, {"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, @@ -616,6 +688,8 @@ static struct variable variables[] = { NULL, cf_text_stat_msg_delay}, {"busy-cue-rate", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_busy_cue_rate}, +{"sleep-interval-length", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_sleep}, {"mail-check-interval", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_mailcheck}, {"mail-check-interval-noncurrent", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, @@ -630,6 +704,10 @@ static struct variable variables[] = { NULL, cf_text_news_active}, {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_news_spooldir}, +#ifndef _WINDOWS +{"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + "Maildir Location", cf_text_maildir_location}, +#endif {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_upload_cmd}, {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, @@ -817,6 +895,8 @@ static struct variable variables[] = { {"incoming-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, +{"special-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, +{"special-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"header-general-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, @@ -1566,7 +1646,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) 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 */ - obs_save_by_sender, i, def_sort_rev; + obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev; long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; @@ -1591,6 +1671,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) GLO_FEATURE_LEVEL = cpystr("sappling"); GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); GLO_SORT_KEY = cpystr(DF_SORT_KEY); + GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY); 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); @@ -1615,6 +1696,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) GLO_LOCAL_FULLNAME = cpystr(DF_LOCAL_FULLNAME); GLO_LOCAL_ADDRESS = cpystr(DF_LOCAL_ADDRESS); GLO_OVERLAP = cpystr(DF_OVERLAP); + GLO_SLEEP = cpystr("0"); GLO_MAXREMSTREAM = cpystr(DF_MAXREMSTREAM); GLO_MARGIN = cpystr(DF_MARGIN); GLO_FILLCOL = cpystr(DF_FILLCOL); @@ -1985,6 +2067,8 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE); set_current_val(&vars[V_EDITOR], TRUE, TRUE); set_current_val(&vars[V_SPELLER], TRUE, TRUE); + set_current_val(&vars[V_SPECIAL_TEXT], TRUE, TRUE); + regex_pattern(VAR_SPECIAL_TEXT); set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE); set_current_val(&vars[V_BROWSER], TRUE, TRUE); set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE); @@ -2069,6 +2153,13 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) } } + set_current_val(&vars[V_SLEEP], TRUE, TRUE); + ps->sleep = i = 0; + if(SVAR_SLEEP(ps, i, tmp_20k_buf, SIZEOF_20KBUF)) + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + else + ps->sleep = i; + set_current_val(&vars[V_OVERLAP], TRUE, TRUE); ps->viewer_overlap = i = atoi(DF_OVERLAP); if(SVAR_OVERLAP(ps, i, tmp_20k_buf, SIZEOF_20KBUF)) @@ -2258,6 +2349,12 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) mail_parameters(NULL, SET_NEWSSPOOL, (void *)VAR_NEWS_SPOOL_DIR); +#ifndef _WINDOWS + set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); + if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) + mail_parameters(NULL, SET_MDINBOXPATH, (void *)VAR_MAILDIR_LOCATION); +#endif + /* guarantee a save default */ set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) @@ -2497,7 +2594,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) 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); - if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){ + if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){ 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; @@ -2506,6 +2603,17 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) else ps->def_sort_rev = def_sort_rev; + set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE); + if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, + &thread_def_sort_rev, 1) == -1){ + sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY); + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + ps->thread_def_sort = SortThread; + ps->thread_def_sort_rev = 0; + } + else + ps->thread_def_sort_rev = thread_def_sort_rev; + 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++) @@ -2592,6 +2700,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) 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 */ @@ -2795,6 +2904,8 @@ feature_list(int index) F_ALWAYS_SPELL_CHECK, h_config_always_spell_check, PREF_COMP, 0}, /* Reply Prefs */ + {"alternate-reply-menu", NULL, + F_ALT_REPLY_MENU, h_config_alt_reply_menu, PREF_RPLY, 0}, {"copy-to-address-to-from-if-it-is-us", "Copy To Address to From if it is Us", F_COPY_TO_TO_FROM, h_config_copy_to_to_from, PREF_RPLY, 0}, {"enable-reply-indent-string-editing", NULL, @@ -2839,6 +2950,8 @@ feature_list(int index) F_NO_FCC_ATTACH, h_config_no_fcc_attach, PREF_SEND, 0}, {"fcc-on-bounce", "Include Fcc When Bouncing Messages", F_FCC_ON_BOUNCE, h_config_fcc_on_bounce, PREF_SEND, 0}, + {"return-path-uses-domain-name", NULL, + F_USE_DOMAIN_NAME, h_config_use_domain, PREF_SEND, 0}, {"mark-fcc-seen", NULL, F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND, 0}, {"fcc-only-without-confirm", "Send to Fcc Only Without Confirming", @@ -2885,6 +2998,10 @@ feature_list(int index) F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0}, {"vertical-folder-list", "Use Vertical Folder List", F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0}, +#ifndef _WINDOWS + {"use-courier-folder-list", "Courier Style Folder List", + F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR, 0}, +#endif /* Addr book */ {"combined-addrbook-display", "Combined Address Book Display", @@ -2901,6 +3018,8 @@ feature_list(int index) /* Index prefs */ {"auto-open-next-unread", NULL, F_AUTO_OPEN_NEXT_UNREAD, h_config_auto_open_unread, PREF_INDX, 0}, + {"enable-circular-tab", NULL, + F_AUTO_CIRCULAR_TAB, h_config_circular_tab, PREF_INDX, 0}, {"continue-tab-without-confirm", "Continue NextNew Without Confirming", F_TAB_NO_CONFIRM, h_config_tab_no_prompt, PREF_INDX, 0}, {"convert-dates-to-localtime", NULL, @@ -2913,6 +3032,8 @@ feature_list(int index) F_ENABLE_SPACE_AS_TAB, h_config_cruise_mode, PREF_INDX, 0}, {"enable-cruise-mode-delete", "Enable Cruise Mode With Deleting", F_ENABLE_TAB_DELETES, h_config_cruise_mode_delete, PREF_INDX, 0}, + {"mark-for-me-in-group", "Mark for Group Message to Me", + F_MARK_FOR_GROUP, h_config_mark_for_group, PREF_INDX, 1}, {"mark-for-cc", "Mark for CC", F_MARK_FOR_CC, h_config_mark_for_cc, PREF_INDX, 1}, {"next-thread-without-confirm", "Read Next Thread Without Confirming", @@ -2929,6 +3050,8 @@ feature_list(int index) 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}, + {"enhanced-fancy-thread-support", "Enhanced Fancy Thread Support", + F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX, 0}, /* Viewer prefs */ {"enable-msg-view-addresses", "Enable Message View Address Links", @@ -2939,6 +3062,8 @@ feature_list(int index) F_VIEW_SEL_URL, h_config_enable_view_url, PREF_VIEW, 1}, {"enable-msg-view-web-hostnames", "Enable Message View Web Hostname Links", F_VIEW_SEL_URL_HOST, h_config_enable_view_web_host, PREF_VIEW, 1}, + {"enable-msg-view-long-url", "Enable Recognition of Long URLS without Delimiter", + F_VIEW_LONG_URL, h_config_enable_long_url, PREF_VIEW, 0}, {"enable-msg-view-forced-arrows", "Enable Message View Forced Arrows", F_FORCE_ARROWS, h_config_enable_view_arrows, PREF_VIEW, 0}, /* set to TRUE for windows */ @@ -3036,6 +3161,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, @@ -3097,6 +3224,8 @@ feature_list(int index) F_FULL_AUTO_EXPUNGE, h_config_full_auto_expunge, PREF_MISC, 0}, {"force-arrow-cursor", NULL, F_FORCE_ARROW, h_config_force_arrow, PREF_MISC, 0}, + {"ignore-size-changes", NULL, + F_IGNORE_SIZE, h_config_ignore_size, PREF_MISC, 0}, {"maildrops-preserve-state", NULL, F_MAILDROPS_PRESERVE_STATE, h_config_maildrops_preserve_state, PREF_MISC, 0}, @@ -6442,6 +6571,7 @@ set_current_color_vals(struct pine *ps) set_color_val(&vars[V_IND_OP_FORE_COLOR], 0); set_color_val(&vars[V_INCUNSEEN_FORE_COLOR], 0); set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0); + set_color_val(&vars[V_SPECIAL_TEXT_FORE_COLOR], 0); set_current_val(&ps->vars[V_INDEX_TOKEN_COLORS], TRUE, TRUE); set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE); @@ -6964,6 +7094,12 @@ toggle_feature(struct pine *ps, struct variable *var, FEATURE_S *f, break; +#ifndef _WINDOWS + case F_COURIER_FOLDER_LIST: + mail_parameters(NULL,SET_COURIERSTYLE,(void *)(F_ON(f->id ,ps)? 1 : 0)); + break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ +#endif + case F_COLOR_LINE_IMPORTANT : case F_DATES_TO_LOCAL : clear_index_cache(ps->mail_stream, 0); @@ -6975,6 +7111,7 @@ toggle_feature(struct pine *ps, struct variable *var, FEATURE_S *f, break; case F_MARK_FOR_CC : + case F_MARK_FOR_GROUP : clear_index_cache(ps->mail_stream, 0); if(THREADING() && sp_viewing_a_thread(ps->mail_stream)) unview_thread(ps, ps->mail_stream, ps->msgmap); @@ -7569,10 +7706,40 @@ config_help(int var, int feature) return(h_config_fcc_rule); case V_SORT_KEY : return(h_config_sort_key); + case V_THREAD_SORT_KEY : + return(h_config_thread_sort_key); case V_AB_SORT_RULE : 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 : @@ -7613,6 +7780,8 @@ config_help(int var, int feature) return(h_config_incoming_second_interv); case V_INCCHECKLIST : return(h_config_incoming_list); + case V_SLEEP : + return(h_config_sleep); case V_OVERLAP : return(h_config_viewer_overlap); case V_MAXREMSTREAM : @@ -7623,6 +7792,8 @@ config_help(int var, int feature) return(h_config_scroll_margin); case V_DEADLETS : return(h_config_deadlets); + case V_SPECIAL_TEXT : + return(h_config_special_text_to_color); case V_FILLCOL : return(h_config_composer_wrap_column); case V_TCPOPENTIMEO : @@ -7745,6 +7916,10 @@ config_help(int var, int feature) return(h_config_newmailwidth); case V_NEWSRC_PATH : return(h_config_newsrc_path); +#ifndef _WINDOWS + case V_MAILDIR_LOCATION : + return(h_config_maildir_location); +#endif case V_BROWSER : return(h_config_browser); #if defined(DOS) || defined(OS2) @@ -7788,6 +7963,9 @@ config_help(int var, int feature) case V_SIGNATURE_FORE_COLOR : case V_SIGNATURE_BACK_COLOR : return(h_config_signature_color); + case V_SPECIAL_TEXT_FORE_COLOR : + case V_SPECIAL_TEXT_BACK_COLOR : + return(h_config_special_text_color); case V_PROMPT_FORE_COLOR : case V_PROMPT_BACK_COLOR : return(h_config_prompt_color); @@ -8271,3 +8449,4 @@ pcpine_general_help(titlebuf) } #endif /* _WINDOWS */ + diff --git a/pith/conf.h b/pith/conf.h index 54c23ff5..2761d65b 100644 --- a/pith/conf.h +++ b/pith/conf.h @@ -144,10 +144,53 @@ #define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p #define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p #define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p +#define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p +#define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p +#define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p #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 #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 @@ -161,6 +204,8 @@ #define GLO_EDITOR vars[V_EDITOR].global_val.l #define VAR_SPELLER vars[V_SPELLER].current_val.p #define GLO_SPELLER vars[V_SPELLER].global_val.p +#define VAR_SPECIAL_TEXT vars[V_SPECIAL_TEXT].current_val.l +#define GLO_SPECIAL_TEXT vars[V_SPECIAL_TEXT].global_val.l #define VAR_FILLCOL vars[V_FILLCOL].current_val.p #define GLO_FILLCOL vars[V_FILLCOL].global_val.p #define VAR_DEADLETS vars[V_DEADLETS].current_val.p @@ -229,6 +274,8 @@ #define GLO_OPENING_SEP vars[V_OPENING_SEP].global_val.p #define VAR_ABOOK_FORMATS vars[V_ABOOK_FORMATS].current_val.l #define VAR_INDEX_FORMAT vars[V_INDEX_FORMAT].current_val.p +#define VAR_SLEEP vars[V_SLEEP].current_val.p +#define GLO_SLEEP vars[V_SLEEP].global_val.p #define VAR_OVERLAP vars[V_OVERLAP].current_val.p #define GLO_OVERLAP vars[V_OVERLAP].global_val.p #define VAR_MAXREMSTREAM vars[V_MAXREMSTREAM].current_val.p @@ -250,6 +297,10 @@ #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p +#ifndef _WINDOWS +#define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p +#define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p +#endif #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p @@ -456,6 +507,8 @@ #define GLO_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].global_val.p #define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p #define GLO_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].global_val.p +#define VAR_SPECIAL_TEXT_FORE_COLOR vars[V_SPECIAL_TEXT_FORE_COLOR].current_val.p +#define VAR_SPECIAL_TEXT_BACK_COLOR vars[V_SPECIAL_TEXT_BACK_COLOR].current_val.p #define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l @@ -671,6 +724,10 @@ */ #define Q_SUPP_LIMIT (4) #define Q_DEL_ALL (-10) +#define SVAR_SLEEP(ps,n,e,el) strtoval((ps)->VAR_SLEEP, \ + &(n), 0, 120, 0, (e), \ + (el), \ + "Sleep-Interval-Length") #define SVAR_OVERLAP(ps,n,e,el) strtoval((ps)->VAR_OVERLAP, \ &(n), 0, 20, 0, (e), \ (el), \ diff --git a/pith/conftype.h b/pith/conftype.h index c654f6c5..496a616f 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -59,6 +59,7 @@ typedef enum { V_PERSONAL_NAME = 0 , V_SAVED_MSG_NAME_RULE , V_FCC_RULE , V_SORT_KEY + , V_THREAD_SORT_KEY , V_AB_SORT_RULE , V_FLD_SORT_RULE , V_GOTO_DEFAULT_RULE @@ -70,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 @@ -80,6 +95,7 @@ typedef enum { V_PERSONAL_NAME = 0 , V_EDITOR , V_SPELLER , V_FILLCOL + , V_SPECIAL_TEXT , V_REPLY_STRING , V_REPLY_INTRO , V_QUOTE_REPLACE_STRING @@ -108,6 +124,7 @@ typedef enum { V_PERSONAL_NAME = 0 , V_MARGIN , V_STATUS_MSG_DELAY , V_ACTIVE_MSG_INTERVAL + , V_SLEEP , V_MAILCHECK , V_MAILCHECKNONCURR , V_MAILDROPCHECK @@ -115,6 +132,9 @@ typedef enum { V_PERSONAL_NAME = 0 , V_NEWSRC_PATH , V_NEWS_ACTIVE_PATH , V_NEWS_SPOOL_DIR +#ifndef _WINDOWS + , V_MAILDIR_LOCATION +#endif , V_UPLOAD_CMD , V_UPLOAD_CMD_PREFIX , V_DOWNLOAD_CMD @@ -230,6 +250,8 @@ typedef enum { V_PERSONAL_NAME = 0 , V_INCUNSEEN_BACK_COLOR , V_SIGNATURE_FORE_COLOR , V_SIGNATURE_BACK_COLOR + , V_SPECIAL_TEXT_FORE_COLOR + , V_SPECIAL_TEXT_BACK_COLOR , V_PROMPT_FORE_COLOR , V_PROMPT_BACK_COLOR , V_HEADER_GENERAL_FORE_COLOR @@ -327,6 +349,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, @@ -342,9 +365,11 @@ typedef enum { F_FORCE_ARROW, F_PRUNE_USES_ISO, F_ALT_ED_NOW, + F_IGNORE_SIZE, F_SHOW_DELAY_CUE, F_CANCEL_CONFIRM, F_AUTO_OPEN_NEXT_UNREAD, + F_AUTO_CIRCULAR_TAB, F_DISABLE_INDEX_LOCALE_DATES, F_SELECTED_SHOWN_BOLD, F_QUOTE_ALL_FROMS, @@ -388,10 +413,14 @@ typedef enum { F_PASS_C1_CONTROL_CHARS, F_SINGLE_FOLDER_LIST, F_VERTICAL_FOLDER_LIST, +#ifndef _WINDOWS + F_COURIER_FOLDER_LIST, +#endif F_TAB_CHK_RECENT, F_AUTO_REPLY_TO, F_VERBOSE_POST, F_FCC_ON_BOUNCE, + F_USE_DOMAIN_NAME, F_SEND_WO_CONFIRM, F_USE_SENDER_NOT_X, F_BLANK_KEYMENU, @@ -408,6 +437,7 @@ typedef enum { F_FIRST_SEND_FILTER_DFLT, F_ALWAYS_LAST_FLDR_DFLT, F_TAB_TO_NEW, + F_MARK_FOR_GROUP, F_MARK_FOR_CC, F_WARN_ABOUT_NO_SUBJECT, F_WARN_ABOUT_NO_FCC, @@ -443,6 +473,7 @@ typedef enum { F_VIEW_SEL_ATTACH, F_VIEW_SEL_URL, F_VIEW_SEL_URL_HOST, + F_VIEW_LONG_URL, F_SCAN_ADDR, F_FORCE_ARROWS, F_PREFER_PLAIN_TEXT, @@ -501,11 +532,13 @@ typedef enum { F_MAILDROPS_PRESERVE_STATE, F_EXPOSE_HIDDEN_CONFIG, F_ALT_COMPOSE_MENU, + F_ALT_REPLY_MENU, F_ALT_ROLE_MENU, F_ALWAYS_SPELL_CHECK, F_QUELL_TIMEZONE, F_QUELL_USERAGENT, F_COLOR_LINE_IMPORTANT, + F_ENHANCED_THREAD, F_SLASH_COLL_ENTIRE, F_ENABLE_FULL_HDR_AND_TEXT, F_QUELL_FULL_HDR_RESET, @@ -713,5 +746,6 @@ typedef struct smime_stuff { /* exported protoypes */ +#define DF_THREAD_SORT_KEY "thread" #endif /* PITH_CONFTYPE_INCLUDED */ diff --git a/pith/detoken.c b/pith/detoken.c index 6f0584ab..0546cf04 100644 --- a/pith/detoken.c +++ b/pith/detoken.c @@ -24,7 +24,7 @@ static char rcsid[] = "$Id: detoken.c 769 2007-10-24 00:15:40Z hubert@u.washingt #include "../pith/reply.h" #include "../pith/mailindx.h" #include "../pith/options.h" - +#include "../pith/rules.h" /* * Hook to read signature from local file @@ -90,6 +90,8 @@ detoken(ACTION_S *role, ENVELOPE *env, int prenewlines, int postnewlines, 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; @@ -103,14 +105,25 @@ detoken(ACTION_S *role, ENVELOPE *env, int prenewlines, int postnewlines, * 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; @@ -301,7 +314,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); diff --git a/pith/filter.c b/pith/filter.c index 692d6319..32309882 100644 --- a/pith/filter.c +++ b/pith/filter.c @@ -46,6 +46,7 @@ static char rcsid[] = "$Id: filter.c 1266 2009-07-14 18:39:12Z hubert@u.washingt #include "../pith/conf.h" #include "../pith/store.h" #include "../pith/color.h" +#include "../pith/osdep/color.h" #include "../pith/escapes.h" #include "../pith/pipe.h" #include "../pith/status.h" @@ -1375,14 +1376,24 @@ gf_b64_binary(FILTER_S *f, int flg) register unsigned char t = f->t; register int n = (int) f->n; register int state = f->f1; + register unsigned char lastc; while(GF_GETC(f, c)){ + lastc = c; + if(f->f2){ + GF_PUTC(f->next, c); + continue; + } + if(state){ state = 0; if (c != '=') { - gf_error("Illegal '=' in base64 text"); - /* NO RETURN */ + f->f2++; + GF_PUTC(f->next, c); + q_status_message(SM_ORDER,3,3, + _("Warning: Illegal '=' in base64 text")); + continue; } } @@ -1399,8 +1410,11 @@ gf_b64_binary(FILTER_S *f, int flg) break; default: /* impossible quantum position */ - gf_error("Internal base64 decoder error"); - /* NO RETURN */ + f->f2++; + GF_PUTC(f->next, lastc); + q_status_message(SM_ORDER,3,3, + _("Warning: Internal base64 decode error")); + break; } } } @@ -1441,6 +1455,7 @@ gf_b64_binary(FILTER_S *f, int flg) dprint((9, "-- gf_reset b64_binary\n")); f->n = 0L; /* quantum position */ f->f1 = 0; /* state holder: equal seen? */ + f->f2 = 0; /* No errors when we start */ } } @@ -7599,6 +7614,7 @@ html_element_comment(FILTER_S *f, char *s) char *p, buf[MAILTMPLEN]; ADDRESS *adr; extern char datestamp[]; + extern char plevstamp[]; if(!strcmp(s = removing_quotes(s + 4), "ALPINE_VERSION")){ p = ALPINE_VERSION; @@ -7612,6 +7628,9 @@ html_element_comment(FILTER_S *f, char *s) else if(!strcmp(s, "ALPINE_COMPILE_DATE")){ p = datestamp; } + else if(!strcmp(s, "ALPINE_PATCHLEVEL")){ + p = plevstamp; + } else if(!strcmp(s, "ALPINE_TODAYS_DATE")){ rfc822_date(p = buf); } @@ -9167,6 +9186,11 @@ typedef struct wrap_col_s { margin_r, indent; char special[256]; + long curlinenum; /* current line number */ + int curqstrpos; /* current position in quote string */ + long linenum; /* line number */ + long qstrlen; /* multiples of 100 */ + char **qstrln; /* qstrln[i] = quote string line i - 1 */ } WRAP_S; #define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l) @@ -9208,6 +9232,12 @@ typedef struct wrap_col_s { #define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color) #define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0])) #define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces) +#define WRAP_CURLINE(F) (((WRAP_S *)(F)->opt)->curlinenum) +#define WRAP_CURPOS(F) (((WRAP_S *)(F)->opt)->curqstrpos) +#define WRAP_LINENUM(F) (((WRAP_S *)(F)->opt)->linenum) +#define WRAP_QSTRLEN(F) (((WRAP_S *)(F)->opt)->qstrlen) +#define WRAP_QSTRN(F) (((WRAP_S *)(F)->opt)->qstrln) +#define WRAP_QSTR(F, N) (((WRAP_S *)(F)->opt)->qstrln[(N)]) #define WRAP_PUTC(F,C,W) { \ if((F)->linep == WRAP_LASTC(F)){ \ size_t offset = (F)->linep - (F)->line; \ @@ -9285,6 +9315,8 @@ gf_wrap(FILTER_S *f, int flg) case CCR : /* CRLF or CR in text ? */ state = BOL; /* either way, handle start */ + WRAP_CURLINE(f)++; + WRAP_CURPOS(f) = 0; if(WRAP_FLOW(f)){ /* wrapped line? */ if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){ @@ -9378,7 +9410,11 @@ gf_wrap(FILTER_S *f, int flg) case BOL : if(WRAP_FLOW(f)){ - if(c == '>'){ + if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && WRAP_QSTR(f, WRAP_CURLINE(f)) + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ + WRAP_CURPOS(f)++; WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } @@ -9392,7 +9428,16 @@ gf_wrap(FILTER_S *f, int flg) } /* quote level change implies new paragraph */ - if(WRAP_FL_QD(f)){ + if (WRAP_CURLINE(f) > 0 + && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && (WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL + || WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) + && ((WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1) == NULL) + || (WRAP_QSTR(f, WRAP_CURLINE(f)) == NULL && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) + || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; @@ -9444,8 +9489,12 @@ gf_wrap(FILTER_S *f, int flg) break; case FL_QLEV : - if(c == '>'){ /* another level */ - WRAP_FL_QC(f)++; + if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && WRAP_QSTR(f, WRAP_CURLINE(f)) + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ + WRAP_CURPOS(f)++; + WRAP_FL_QC(f)++; /* another level */ } else { /* if EMBEDed, process it and return here */ @@ -9457,7 +9506,16 @@ gf_wrap(FILTER_S *f, int flg) } /* quote level change signals new paragraph */ - if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){ + if (WRAP_CURLINE(f) > 0 + && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && (WRAP_QSTR(f, WRAP_CURLINE(f)) + || WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + && ((WRAP_QSTR(f, WRAP_CURLINE(f)) && + !WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + || (!WRAP_QSTR(f, WRAP_CURLINE(f)) && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ @@ -9514,6 +9572,13 @@ gf_wrap(FILTER_S *f, int flg) state = FL_SIG; break; + case ' ' : /* what? */ + if (WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && WRAP_QSTR(f, WRAP_CURLINE(f))){ + WRAP_SPC_LEN(f)++; + so_writec(' ', WRAP_SPACES(f)); + } + default : /* something else */ state = DFL; goto case_dfl; /* handle c like DFL */ @@ -9530,7 +9595,7 @@ gf_wrap(FILTER_S *f, int flg) &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ - wrap_bol(f, 1, 1, &ip, &eib, + wrap_bol(f, 1, WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } @@ -10027,7 +10092,7 @@ gf_wrap(FILTER_S *f, int flg) wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ - wrap_bol(f,1,1, &ip, &eib, &op, + wrap_bol(f,1,WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } @@ -10100,6 +10165,13 @@ gf_wrap(FILTER_S *f, int flg) if(WRAP_COLOR(f)) free_color_pair(&WRAP_COLOR(f)); + { long i; + for (i = 0L; i < WRAP_QSTRLEN(f); i++) + if (WRAP_QSTR(f,i)) + fs_give((void **) &(WRAP_QSTR(f,i))); + fs_give((void **)&WRAP_QSTRN(f)); + } + fs_give((void **) &f->line); /* free temp line buffer */ so_give(&WRAP_SPACES(f)); fs_give((void **) &f->opt); /* free wrap widths struct */ @@ -10450,7 +10522,8 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, { int j, i; COLOR_PAIR *col = NULL; - char *prefix = NULL, *last_prefix = NULL; + char *prefix = NULL, *last_prefix = NULL, *wrap_qstr = NULL; + int level = 0, oldj, len; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); @@ -10459,10 +10532,22 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, last_prefix = NULL; } } + + if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) && WRAP_QSTR(f, WRAP_CURLINE(f))) + wrap_qstr = cpystr(WRAP_QSTR(f, WRAP_CURLINE(f))); + len = wrap_qstr ? strlen(wrap_qstr) : 0; - for(j = 0; j < WRAP_FL_QD(f); j++){ + for (j = wrap_qstr && *wrap_qstr == ' ' ? 1 : 0; + j < len && isspace((unsigned char)wrap_qstr[j]); j++){ + GF_PUTC_GLO(f->next, wrap_qstr[j]); + f->n += ((wrap_qstr[j] == TAB) ? (~f->n & 0x07) + 1 : 1); + } + + for(; j < len && level < len; level++){ + oldj = j; + j = next_level_quote(wrap_qstr, (char **)NULL, j, WRAP_FLOW(f)); if(WRAP_USE_CLR(f)){ - if((j % 3) == 0 + if((level % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, @@ -10470,7 +10555,7 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } - else if((j % 3) == 1 + else if((level % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, @@ -10478,7 +10563,7 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } - else if((j % 3) == 2 + else if((level % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, @@ -10492,43 +10577,60 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, } } + if (j > 1 && wrap_qstr[j-1] == ' ') + j -= 1; + if(!WRAP_LV_FLD(f)){ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ for(i = 0; prefix[i]; i++) GF_PUTC_GLO(f->next, prefix[i]); - f->n += utf8_width(prefix); - } - else if(ps_global->VAR_REPLY_STRING - && (!strcmp(ps_global->VAR_REPLY_STRING, ">") - || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ - GF_PUTC_GLO(f->next, '>'); - f->n += 1; + f->n += utf8_widthis(prefix); } else{ - GF_PUTC_GLO(f->next, '>'); - GF_PUTC_GLO(f->next, ' '); - f->n += 2; + for (i = oldj; i < j; i++) + GF_PUTC_GLO(f->next, wrap_qstr[i]); + f->n += j - oldj; } } else{ - GF_PUTC_GLO(f->next, '>'); - f->n += 1; - } + for (i = oldj; i < j; i++) + GF_PUTC_GLO(f->next, wrap_qstr[i]); + f->n += j - oldj; + } + for (i = j; isspace((unsigned char)wrap_qstr[i]); i++); + if(!wrap_qstr[i]){ + f->n += i - j; + for (; j < i; j++) + GF_PUTC_GLO(f->next, ' '); + } + else{ + if((WRAP_LV_FLD(f) + || !ps_global->VAR_QUOTE_REPLACE_STRING || !prefix) + || !ps_global->VAR_REPLY_STRING + || (strcmp(ps_global->VAR_REPLY_STRING, ">") + && strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ + GF_PUTC_GLO(f->next, ' '); + f->n += 1; + } + } + for (; isspace((unsigned char)wrap_qstr[j]); j++); } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } - else if(j && last_prefix){ + else if(j && !value_is_space(wrap_qstr) && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); - f->n += utf8_width(last_prefix); + f->n += utf8_widthis(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); + if (wrap_qstr) + fs_give((void **)&wrap_qstr); return 0; } @@ -10560,6 +10662,12 @@ gf_wrap_filter_opt(int width, int width_max, int *margin, int indent, int flags) wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR; wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE; wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN; + wrap->curlinenum = 0L; + wrap->curqstrpos = 0; + wrap->linenum = 0L; + wrap->qstrlen = 100L; + wrap->qstrln = (char **) fs_get(100*sizeof(char *)); + memset(wrap->qstrln, 0, 100*sizeof(char *)); return((void *) wrap); } @@ -11003,7 +11111,215 @@ typedef struct _linetest_s { } \ } +#define ADD_QUOTE_STRING(F) { \ + int len = tmp_20k_buf[0] ? strlen(tmp_20k_buf) + 1 : 0; \ + FILTER_S *fltr; \ + \ + for(fltr = (F); fltr && fltr->f != gf_wrap; fltr = fltr->next); \ + if (fltr){ \ + if (WRAP_LINENUM(fltr) >= WRAP_QSTRLEN(fltr)){ \ + fs_resize((void **)&WRAP_QSTRN(fltr), \ + (WRAP_QSTRLEN(fltr) + 100) * sizeof(char *)); \ + memset(WRAP_QSTRN(fltr)+WRAP_QSTRLEN(fltr), 0, \ + 100*sizeof(char*)); \ + WRAP_QSTRLEN(fltr) += 100L; \ + } \ + if (len){ \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = \ + (char *) fs_get(len*sizeof(char)); \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = cpystr(tmp_20k_buf);\ + } \ + WRAP_LINENUM(fltr)++; \ + } \ +} + +int end_of_line(char *line) +{ + int i; + + for(i= 0; line && line[i]; i++){ + if((line[i] == '\015' && line[i+1] == '\012') || line[i] == '\012') + break; + } + return i; +} + +/* This macro is used in gf_quote_test. It receives a return code + from a filter. All filters that will print something must send + return code 0, except color_a_quote which must send return code + 1 + */ + +#define GF_ADD_QUOTED_LINE(F, line) \ +{ \ + LT_INS_S *ins = NULL, *insp; \ + int done; \ + char *gline, *cline;\ + unsigned char ch;\ + register char *cp;\ + register int l;\ + \ + for (gline = cline = line; gline && cline; ){\ + if(cline = strchr(gline,'\012'))\ + *cline = '\0';\ + done = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, gline, &ins,\ + ((LINETEST_S *) (F)->opt)->local);\ + if (done < 2){ \ + if(done == 1)\ + ADD_QUOTE_STRING((F));\ + for(insp = ins, cp = gline; *cp ; ){\ + if(insp && cp == insp->where){\ + if(insp->len > 0){ \ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + continue; \ + } else if(insp->len < 0){ \ + cp -= insp->len; \ + insp = insp->next; \ + continue; \ + } \ + }\ + GF_PUTC((F)->next, *cp);\ + cp++;\ + }\ + while(insp){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + }\ + gf_line_test_free_ins(&ins);\ + if(cline){ \ + *cline = '\012';\ + gline += cline - gline + 1;\ + }\ + GF_PUTC((F)->next, '\015');\ + GF_PUTC((F)->next, '\012');\ + }\ + }\ +} +/* test second line of old line first */ +#define SECOND_LINE_QUOTE_TEST(line, F) \ +{\ + *p = '\0';\ + i = end_of_line((F)->oldline); \ + if (((F)->oldline)[i]){\ + i += (((F)->oldline)[i] == '\015') ? 2 : 1;\ + line = (F)->oldline + i;\ + i = end_of_line(line); \ + if(line[i])\ + line[i] = '\0'; \ + }\ + for (i = 0; ((F)->line) \ + && (i < LINE_TEST_BLOCK) \ + && (i < SIZEOF_20KBUF)\ + && ((F)->line)[i] \ + && (((F)->line)[i] != '\015')\ + && (((F)->line)[i] != '\012')\ + && (tmp_20k_buf[i] = ((F)->line)[i]); i++);\ + tmp_20k_buf[i] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ +} + +#define FIRST_LINE_QUOTE_TEST(line, F)\ +{\ + *p = '\0';\ + line = (F)->line;\ + if ((F)->oldline)\ + fs_give((void **)&(F)->oldline);\ + (F)->oldline = cpystr(line);\ + i = end_of_line(line); \ + if (line[i]){ \ + j = (line[i] == '\015') ? 2 : 1;\ + line[i] = '\0'; \ + i += j; \ + }\ + for (j = 0; ((F)->line) \ + && ((i + j) < LINE_TEST_BLOCK) \ + && (j < SIZEOF_20KBUF) \ + && ((F)->line)[i + j] \ + && (((F)->line)[i + j] != '\015')\ + && (((F)->line)[i + j] != '\012')\ + && (tmp_20k_buf[j] = ((F)->line)[i + j]); j++);\ + tmp_20k_buf[j] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ +} + + +void +gf_quote_test(f, flg) + FILTER_S *f; + int flg; +{ + register char *p = f->linep; + register char *eobuf = GF_LINE_TEST_EOB(f); + char *line = NULL; + int i, j; + GF_INIT(f, f->next); + + if(flg == GF_DATA){ + register unsigned char c; + register int state = f->f1; + + while(GF_GETC(f, c)){ + + GF_LINE_TEST_ADD(f, c); + if(c == '\012') + state++; + if(state == 2){ /* two full lines read */ + state = 0; + + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + p = f->line; + } + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(flg == GF_EOD){ + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + /* We are out of data. In this case we have processed the second + * line of an oldline, then the first line of a line, but we need + * to process the second line of the given line. We do this by + * processing it now!. + */ + if (line[i]){ + tmp_20k_buf[0] = '\0'; /* No next line */ + GF_ADD_QUOTED_LINE(f, line+i); + } + + fs_give((void **) &f->oldline); /* free old line buffer */ + fs_give((void **) &f->line); /* free line buffer */ + fs_give((void **) &f->opt); /* free test struct */ + GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } + else if(flg == GF_RESET){ + f->f1 = 0; /* state */ + f->n = 0L; /* line number */ + f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */ + f->line = p = (char *) fs_get(f->f2 * sizeof(char)); + } + + f->linep = p; +} /* * this simple filter accumulates characters until a newline, offers it diff --git a/pith/filter.h b/pith/filter.h index 9916803d..e4021f23 100644 --- a/pith/filter.h +++ b/pith/filter.h @@ -216,6 +216,7 @@ void gf_prepend_editorial(FILTER_S *, int); void *gf_prepend_editorial_opt(prepedtest_t, char *); void gf_nvtnl_local(FILTER_S *, int); void gf_local_nvtnl(FILTER_S *, int); +void gf_quote_test(FILTER_S *, int); void *gf_url_hilite_opt(URL_HILITE_S *, HANDLE_S **, int); diff --git a/pith/filttype.h b/pith/filttype.h index 21a1bec5..684299ca 100644 --- a/pith/filttype.h +++ b/pith/filttype.h @@ -35,6 +35,8 @@ typedef struct filter_s { /* type to hold data for filter function */ unsigned char t; /* temporary char */ char *line; /* place for temporary storage */ char *linep; /* pointer into storage space */ + char *oldline; /* the previous line to "line" */ + char *oldlinep; /* the previous line to "line" */ void *opt; /* optional per instance data */ void *data; /* misc internal data pointer */ unsigned char queue[1 + GF_MAXBUF]; diff --git a/pith/flag.c b/pith/flag.c index b1bbf9c4..cf0ea39a 100644 --- a/pith/flag.c +++ b/pith/flag.c @@ -594,14 +594,16 @@ set_lflag(MAILSTREAM *stream, MSGNO_S *msgs, long int n, int f, int v) was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; + thrd = fetch_thread(stream, rawno); + if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ thrd = fetch_thread(stream, rawno); if(thrd && thrd->top){ - if(thrd->top == thrd->rawno) + if(top_thread(stream, thrd->top) == thrd->rawno) topthrd = thrd; else - topthrd = fetch_thread(stream, thrd->top); + topthrd = fetch_thread(stream, top_thread(stream, thrd->top)); } if(topthrd){ diff --git a/pith/imap.c b/pith/imap.c index ea4c5b1f..f0b93c9a 100644 --- a/pith/imap.c +++ b/pith/imap.c @@ -967,8 +967,18 @@ imap_get_passwd(MMLOGIN_S *m_list, char *passwd, char *user, STRLIST_S *hostlist && !strcmp(user, l->user) && l->altflag == altflag){ if(passwd){ + if(l->invalidpwd == 0){ strncpy(passwd, l->passwd, NETMAXPASSWD); passwd[NETMAXPASSWD-1] = '\0'; + } + else{ + q_status_message(SM_ORDER | SM_DING, 3, 4, + "Failed to login!. Re-enter password."); + dprint((9, "imap_get_passwd: reseting password due to login failure.\n")); + dprint((10, "imap_get_passwd: Old passwd=\"%s\"\n", + passwd ? passwd : "?")); + return FALSE; + } } dprint((9, "imap_get_passwd: match\n")); dprint((10, "imap_get_passwd: trying passwd=\"%s\"\n", @@ -1016,6 +1026,7 @@ imap_set_passwd(MMLOGIN_S **l, char *passwd, char *user, STRLIST_S *hostlist, (*l)->altflag = altflag; (*l)->ok_novalidate = ok_novalidate; (*l)->warned = warned; + (*l)->invalidpwd = 0; /* assume correct password for now */ if(!(*l)->user) (*l)->user = cpystr(user); diff --git a/pith/imap.h b/pith/imap.h index 86a0b533..a57400b9 100644 --- a/pith/imap.h +++ b/pith/imap.h @@ -35,6 +35,7 @@ typedef struct _mmlogin_s { unsigned altflag:1; unsigned ok_novalidate:1; unsigned warned:1; + unsigned invalidpwd:1; /* password is invalid, assume valid */ STRLIST_S *hosts; struct _mmlogin_s *next; } MMLOGIN_S; diff --git a/pith/indxtype.h b/pith/indxtype.h index ee01a9bb..031e5ff5 100644 --- a/pith/indxtype.h +++ b/pith/indxtype.h @@ -76,12 +76,15 @@ typedef enum {iNothing, iStatus, iFStatus, iIStatus, iSIStatus, iKey, iKeyInit, iPrefDate, iPrefTime, iPrefDateTime, iCurPrefDate, iCurPrefTime, iCurPrefDateTime, - iSize, iSizeComma, iSizeNarrow, iDescripSize, + iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread, iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, iCurNews, iArrow, iMailbox, iAddress, iInit, iCursorPos, iDay2Digit, iMon2Digit, iYear2Digit, + iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey, + iNick, iAddressTo, iAddressCc, iAddressRecip, iBcc, iLcc, + iFfrom, iFadd, iSTime, iKSize, iRoleNick, iNewLine, iHeader, iText, @@ -103,15 +106,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" diff --git a/pith/init.c b/pith/init.c index d7942dcb..69379bf8 100644 --- a/pith/init.c +++ b/pith/init.c @@ -408,6 +408,9 @@ get_mail_list(CONTEXT_S *list_cntxt, char *folder_base) && stricmp(filename, folder_base)){ #else if(strncmp(filename, folder_base, folder_base_len) == 0 +#ifndef _WINDOWS + && filename[folder_base_len] != list_cntxt->dir->delim +#endif && strcmp(filename, folder_base)){ #endif #endif diff --git a/pith/mailcap.c b/pith/mailcap.c index 34dce329..44285a8a 100644 --- a/pith/mailcap.c +++ b/pith/mailcap.c @@ -53,6 +53,7 @@ typedef struct mcap_entry { int needsterminal; char *contenttype; char *command; + char *nametemplate; char *testcommand; char *label; /* unused */ char *printcommand; /* unused */ @@ -213,6 +214,9 @@ mc_init(void) if(mc->printcommand) dprint((11, " printcommand: %s", mc->printcommand ? mc->printcommand : "?")); + if(mc->nametemplate) + dprint((11, " nametemplate: %s", + mc->nametemplate ? mc->nametemplate : "?")); dprint((11, " needsterminal %d\n", mc->needsterminal)); } } @@ -488,6 +492,11 @@ mc_build_entry(char **tokens) dprint((9, "mailcap: printcommand=%s\n", mc->printcommand ? mc->printcommand : "?")); } + else if(arg && !strucmp(*tokens, "nametemplate")){ + mc->nametemplate = arg; + dprint((9, "mailcap: nametemplate=%s\n", + arg ? arg : "?")); + } else if(arg && !strucmp(*tokens, "compose")){ /* not used */ dprint((9, "mailcap: not using compose=%s\n", @@ -974,3 +983,60 @@ mailcap_free(void) mail_free_stringlist(&MailcapData.raw); mc_free_entry(&MailcapData.head); } + +char * +mc_template(char *tmp_file, BODY *body, int chk_extension) +{ + MailcapEntry *mc; + int quoted = 0; + char *s, *to, *namefile = NULL; + + mc = mc_get_command(body->type, body->subtype, body, chk_extension, NULL); + if(!mc || !mc->nametemplate || !tmp_file) + return tmp_file; + + /* remove extension if requested for a specific extension */ + if(mc->nametemplate && tmp_file && (s = strrchr(tmp_file, '.')) != NULL + && strchr(s, C_FILESEP) == NULL) + *s = '\0'; + + to = tmp_20k_buf; + if((s = strrchr(tmp_file, C_FILESEP)) != NULL){ + *s++ = '\0'; + sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf)); + if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = C_FILESEP; + namefile = s; + } + + for(s = mc->nametemplate; *s; s++) + if(quoted){ + quoted = 0; + switch(*s){ + case '%': + if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = '%'; + break; + + case 's': + sstrncpy(&to, namefile ? namefile : tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf)); + break; + + default: + dprint((9, + "Ignoring unercognized format code in nametemplate: %%%c\n", *s )); + break; + } + } + else if(*s == '%') + quoted = 1; + else if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = *s; + + *to++ = '\0'; + + fs_give((void **)&tmp_file); + tmp_file = cpystr(tmp_20k_buf); + return tmp_file; +} + diff --git a/pith/mailcap.h b/pith/mailcap.h index 25ccd115..c9f7a256 100644 --- a/pith/mailcap.h +++ b/pith/mailcap.h @@ -29,6 +29,7 @@ char *mc_conf_path(char *, char *, char *, int, char *); int mailcap_can_display(int, char *, BODY *, int); MCAP_CMD_S *mailcap_build_command(int, char *, BODY *, char *, int *, int); void mailcap_free(void); +char *mc_template(char *, BODY *, int); /* currently mandatory to implement stubs */ diff --git a/pith/mailcmd.c b/pith/mailcmd.c index 78ba98ad..3838c65d 100644 --- a/pith/mailcmd.c +++ b/pith/mailcmd.c @@ -39,6 +39,7 @@ static char rcsid[] = "$Id: mailcmd.c 1142 2008-08-13 17:22:21Z hubert@u.washing #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, CONTEXT_S *new_context, MAILSTREAM **streamp, 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! */ @@ -990,6 +992,7 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp, 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 @@ -1113,7 +1116,10 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp, 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) @@ -1135,124 +1141,7 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp, 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: - panic("Unexpected incoming startup case"); - break; - - } + find_startup_position(use_this_startup_rule, m, pc); } else if(IS_NEWS(ps_global->mail_stream)){ /* @@ -1430,9 +1319,11 @@ expunge_and_close(MAILSTREAM *stream, char **final_msg, long unsigned int flags) /* 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 */ @@ -1703,6 +1594,9 @@ move_read_msgs(MAILSTREAM *stream, char *dstfldr, char *buf, size_t buflen, long 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; @@ -1742,8 +1636,9 @@ move_read_msgs(MAILSTREAM *stream, char *dstfldr, char *buf, size_t buflen, long 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'; @@ -1781,7 +1676,9 @@ move_read_incoming(MAILSTREAM *stream, CONTEXT_S *context, char *folder, && ((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; @@ -2739,3 +2636,295 @@ get_uname(char *mailbox, char *target, int len) 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: + panic("Unexpected incoming startup case"); + break; + + } +} diff --git a/pith/mailcmd.h b/pith/mailcmd.h index 9e99c6f3..d590b7c1 100644 --- a/pith/mailcmd.h +++ b/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_S *, MAILSTREAM **, unsigned long); void expunge_and_close(MAILSTREAM *, char **, unsigned long); void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int); char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long); +char *move_read_msgs_using_rules (MAILSTREAM *, char *, char *); +unsigned get_perfolder_startup_rule (MAILSTREAM *, int, char *); +void setup_threading_index_style (void); +void find_startup_position (int, MAILSTREAM *, long); +char *get_folder_to_save (MAILSTREAM *, long, char *); char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t); +unsigned long rules_cursor_pos (MAILSTREAM *); void cross_delete_crossposts(MAILSTREAM *); long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int); int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *); diff --git a/pith/mailindx.c b/pith/mailindx.c index 09cdc2c8..12fbb5d2 100644 --- a/pith/mailindx.c +++ b/pith/mailindx.c @@ -17,6 +17,7 @@ static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washin #include "../pith/headers.h" #include "../pith/mailindx.h" +#include "../pith/pineelt.h" #include "../pith/mailview.h" #include "../pith/flag.h" #include "../pith/icache.h" @@ -40,6 +41,7 @@ static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washin #include "../pith/send.h" #include "../pith/options.h" #include "../pith/ablookup.h" +#include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif @@ -104,7 +106,6 @@ char *copy_format_str(int, int, char *, int); void set_print_format(IELEM_S *, int, int); void set_ielem_widths_in_field(IFIELD_S *); - #define BIGWIDTH 2047 @@ -228,6 +229,7 @@ init_index_format(char *format, INDEX_COL_S **answer) case iSTime: case iKSize: case iSize: + case iSizeThread: case iPrioAlpha: (*answer)[column].req_width = 7; break; @@ -374,6 +376,13 @@ reset_index_format(void) PAT_STATE pstate; PAT_S *pat; int we_set_it = 0; + char *rule; + + if(rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)){ + 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)){ @@ -447,14 +456,15 @@ 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}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, + {"SIZETHREAD", iSizeThread, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, - {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, + {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, {"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX}, @@ -463,55 +473,58 @@ 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}, - {"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}, + {"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}, + {"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}, {"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}, @@ -520,56 +533,68 @@ 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}, + {"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} }; @@ -943,7 +968,7 @@ static IndexColType fixed_ctypes[] = { iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, iSDateTimeIso24, iSDateTimeIsoS24, iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, - iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, + iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iSizeThread, iPrio, iPrioBang, iPrioAlpha, iInit, iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek @@ -1136,6 +1161,7 @@ setup_index_header_widths(MAILSTREAM *stream) case iTime12: case iSize: case iKSize: + case iSizeThread: cdesc->actual_length = 7; cdesc->adjustment = Right; break; @@ -1229,7 +1255,7 @@ setup_index_header_widths(MAILSTREAM *stream) cdesc->ctype != iNothing; cdesc++) if(cdesc->ctype == iSize || cdesc->ctype == iKSize || - cdesc->ctype == iSizeNarrow || + cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread || cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ if(cdesc->actual_length == 0){ if((fix=cdesc->width) > 0){ /* had this reserved */ @@ -1612,10 +1638,12 @@ build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, /* find next thread which is visible */ do{ + unsigned long branch; if(mn_get_revsort(msgmap) && thrd->prevthd) thrd = fetch_thread(stream, thrd->prevthd); - else if(!mn_get_revsort(msgmap) && thrd->nextthd) - thrd = fetch_thread(stream, thrd->nextthd); + /*branch = get_branch(stream,thrd)*/ + else if(!mn_get_revsort(msgmap) && thrd->branch) + thrd = fetch_thread(stream, thrd->branch); else thrd = NULL; } while(thrd @@ -2027,13 +2055,10 @@ format_index_index_line(INDEXDATA_S *idata) */ ice = copy_ice(ice); + thrd = fetch_thread(idata->stream, idata->rawno); /* is this a collapsed thread index line? */ - if(!idata->bogus && THREADING()){ - thrd = fetch_thread(idata->stream, idata->rawno); - collapsed = thrd && thrd->next - && get_lflag(idata->stream, NULL, - idata->rawno, MN_COLL); - } + if(!idata->bogus && THREADING()) + collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); /* calculate contents of the required fields */ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) @@ -2075,10 +2100,15 @@ format_index_index_line(INDEXDATA_S *idata) if(to_us == ' ') to_us = '+'; + if(to_us == '+' + && F_ON(F_MARK_FOR_GROUP,ps_global) && + (addr->next || addr != fetch_to(idata))) + to_us = '.'; + break; } - if(to_us != '+' && resent_to_us(idata)){ + if(to_us == ' ' && resent_to_us(idata)){ ice->to_us = 1; if(to_us == ' ') to_us = '+'; @@ -2132,7 +2162,7 @@ format_index_index_line(INDEXDATA_S *idata) ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR); } } - else if(str[0] == '+' || str[0] == '-'){ + else if(str[0] == '+' || str[0] == '-' || str[0] == '.'){ if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){ ielem = ifield->ielem; ielem->freecolor = 1; @@ -2188,6 +2218,12 @@ format_index_index_line(INDEXDATA_S *idata) for(addr = fetch_to(idata); addr; addr = addr->next) if(address_is_us(addr, ps_global)){ to_us = '+'; + + if(to_us == '+' + && F_ON(F_MARK_FOR_GROUP,ps_global) && + (addr->next || addr != fetch_to(idata))) + to_us = '.'; + break; } @@ -2283,7 +2319,7 @@ format_index_index_line(INDEXDATA_S *idata) if(pico_usingcolor()){ - if(str[0] == '+' || str[0] == '-'){ + if(str[0] == '+' || str[0] == '-' || str[0] == '.'){ if(start == 0 && VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){ @@ -2465,6 +2501,24 @@ format_index_index_line(INDEXDATA_S *idata) 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" @@ -2531,7 +2585,30 @@ format_index_index_line(INDEXDATA_S *idata) break; + case iSizeThread: + if (!THREADING()){ + goto getsize; + } else if (collapsed){ + l = count_flags_in_thread(idata->stream, thrd, F_NONE); + snprintf(str, sizeof(str), "(%lu)", l); + } + else{ + thrd = fetch_thread(idata->stream, idata->rawno); + if(!thrd) + snprintf(str, sizeof(str), "%s", "Error"); + else{ + long lengthb; + lengthb = get_length_branch(idata->stream, idata->rawno); + if (lengthb > 0L) + snprintf(str, sizeof(str), "(%lu)", lengthb); + else + snprintf(str,sizeof(str), "%s", " "); + } + } + break; + case iSize: +getsize: /* 0 ... 9999 */ if((l = fetch_size(idata)) < 10*1000L) snprintf(str, sizeof(str), "(%lu)", l); @@ -2785,7 +2862,6 @@ format_index_index_line(INDEXDATA_S *idata) if(first_text){ strncpy(str, first_text, BIGWIDTH); str[BIGWIDTH] = '\0'; - fs_give((void **) &first_text); } } @@ -3107,7 +3183,7 @@ format_thread_index_line(INDEXDATA_S *idata) tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR); } - else if((to_us == '+' || to_us == '-') + else if((to_us == '+' || to_us == '-' || to_us == '.') && VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){ ielem = ifield->ielem; ielem->freecolor = 1; @@ -3710,6 +3786,26 @@ fetch_firsttext(INDEXDATA_S *idata, int delete_quotes) gf_io_t pc; long partial_fetch_len = 0L; SEARCHSET *ss, **sset; + MESSAGECACHE *mc; + PINELT_S *pelt; + + /* we cache the result we get from this function, so that we do not have to + * refetch the text in case there is a change. We could cache in the envelope + * but c-client does not have a special field for that, nor we want to use the + * sparep pointer, since there could be other uses for sparep later, and even + * if we add a pointer to the ENVELOPE structure, we would be caching the same + * text twice (one in a private pointer, and the new pointer) and that would + * not make sense. Instead we will use an elt for this + */ + + if((mc = mail_elt(idata->stream, idata->rawno)) + && ((pelt = (PINELT_S *) mc->sparep) == NULL)){ + pelt = (PINELT_S *) fs_get(sizeof(PINELT_S)); + memset(pelt, 0, sizeof(PINELT_S)); + } + + if(pelt && pelt->firsttext != NULL) + return(pelt->firsttext); try_again: @@ -3803,7 +3899,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)); @@ -3827,6 +3933,8 @@ try_again: goto try_again; } } + if(mc && pelt) + pelt->firsttext = firsttext; } } } @@ -5267,10 +5375,10 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi { 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; @@ -5324,7 +5432,13 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi * origsubj is the original subject but it has been decoded. We need * 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); + } /* * prepend_keyword will put the keyword stuff before the subject @@ -5412,10 +5526,8 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, - thd && thd->next - && get_lflag(idata->stream, - NULL,idata->rawno, - MN_COLL)); + this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && + (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); /* * width is < available strsize and @@ -5765,6 +5877,9 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi if(free_subj) fs_give((void **) &free_subj); + + if (we_clear && origsubj) + fs_give((void **)&origsubj); } @@ -6043,11 +6158,8 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_ border = str + width; if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, - thd && thd->next - && get_lflag(idata->stream, - NULL,idata->rawno, - MN_COLL)); - + this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && + (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); fptr = str; if(thd) @@ -6133,16 +6245,33 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_ ? "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", strsize-1-4, 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; } @@ -6155,7 +6284,15 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_ 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: @@ -6452,3 +6589,64 @@ set_print_format(IELEM_S *ielem, int width, int leftadj) } } } + +void +setup_threading_display_style(void) +{ + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL); + if (rule || ps_global->VAR_THREAD_DISP_STYLE){ + for(i = 0; v = thread_disp_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_disp_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } +} + +char * +find_value(char *token, char *use_this, int flag, INDEXDATA_S *idata, int nfcn) +{ + int n = 0, i, rule_context, we_clear; + char *rule_result = NULL, **list; + ENVELOPE *env; + RULELIST *rule; + RULE_S *prule; + + env = rules_fetchenvelope(idata, &we_clear); + if(env && env->sparep) + fs_give((void **)&env->sparep); + if(we_clear) + mail_free_envelope(&env); + if(rule = get_rulelist_from_code(V_REPLACE_RULES, ps_global->rule_list)){ + list = functions_for_token(token); + while(rule_result == NULL && (prule = get_rule(rule,n++))){ + rule_context = 0; + if (prule->action->token && !strcmp(prule->action->token, token)){ + for (i = 0; i < nfcn; i++) + if(list[i+1] && !strcmp(prule->action->function, list[i+1])) + rule_context |= context_for_function(list[i+1]); + if (rule_context){ + env = rules_fetchenvelope(idata, &we_clear); + if(use_this) + env->sparep = get_sparep_for_rule(use_this, flag); + rule_result = process_rule(prule, rule_context, env); + if(env->sparep) + free_sparep_for_rule(&env->sparep); + if(we_clear) + mail_free_envelope(&env); + } + } + } + } + return rule_result; +} diff --git a/pith/mailindx.h b/pith/mailindx.h index 8eb23033..fdea552c 100644 --- a/pith/mailindx.h +++ b/pith/mailindx.h @@ -30,6 +30,9 @@ extern void (*setup_header_widths)(MAILSTREAM *); /* exported prototypes */ +SortOrder translate (char *, int); +char *find_value (char *, char *, int, INDEXDATA_S *, int); +void setup_threading_display_style (void); int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int); void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *); unsigned long line_hash(char *); diff --git a/pith/mailview.c b/pith/mailview.c index 40728aab..5df0dbdb 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -52,7 +52,11 @@ static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washin #include "../pith/escapes.h" #include "../pith/keyword.h" #include "../pith/smime.h" - +#include "../pith/osdep/color.h" +#include "../pico/osdep/color.h" +#include "../pico/estruct.h" +#include "../pico/pico.h" +#include "../pico/efunc.h" #define FBUF_LEN (50) @@ -282,9 +286,17 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f if((flgs & FM_DISPLAY) && !(flgs & FM_NOCOLOR) && pico_usingcolor() + && ps_global->VAR_SPECIAL_TEXT_FORE_COLOR + && ps_global->VAR_SPECIAL_TEXT_BACK_COLOR){ + gf_link_filter(gf_line_test, gf_line_test_opt(color_this_text, NULL)); + } + + if((flgs & FM_DISPLAY) + && !(flgs & FM_NOCOLOR) + && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ - gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); + gf_link_filter(gf_quote_test, gf_line_test_opt(color_signature, &is_in_sig)); } if((flgs & FM_DISPLAY) @@ -292,8 +304,10 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ - gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL)); + gf_link_filter(gf_quote_test, gf_line_test_opt(color_a_quote, NULL)); } + else + gf_link_filter(gf_quote_test,gf_line_test_opt(select_quote, NULL)); if(!(flgs & FM_NOWRAP)){ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE; @@ -1098,27 +1112,88 @@ int color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig) { struct variable *vars = ps_global->vars; - int *in_sig_block; + int *in_sig_block, i, j,same_qstr = 0, plb; COLOR_PAIR *col = NULL; + static char GLine[NSTRING] = {'\0'}; + static char PLine[NSTRING] = {'\0'}; + static char PPLine[NSTRING] = {'\0'}; + char NLine[NSTRING] = {'\0'}; + char rqstr[NSTRING] = {'\0'}; + char *p, *q; + static char *buf, buf2[NSTRING] = {'\0'}; + QSTRING_S *qs; + static int qstrlen = 0; if(is_in_sig == NULL) return 0; + if (linenum > 0){ + strncpy(PLine, GLine, sizeof(PLine)); + PLine[sizeof(PLine)-1] = '\0'; + } + + if(p = strchr(tmp_20k_buf, '\015')) *p = '\0'; + strncpy(NLine, tmp_20k_buf, sizeof(NLine)); + NLine[sizeof(NLine) - 1] = '\0'; + if (p) *p = '\015'; + + strncpy(GLine, line, sizeof(GLine)); + GLine[sizeof(GLine) - 1] = '\0'; + + ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix + ? (void *) ps_global->prefix : (void *) ">", 0); + plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING); + qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb); + if(linenum > 0) + strncpy(PPLine, PLine, NSTRING); + strncpy(buf2, rqstr, NSTRING); + i = buf2 && buf2[0] ? strlen(buf2) : 0; + free_qs(&qs); + + /* determine if buf and buf2 are the same quote string */ + if (!struncmp(buf, buf2, qstrlen)){ + for (j = qstrlen; buf2[j] && isspace((unsigned char)buf2[j]); j++); + if (!buf2[j] || buf2[j] == '|' || (buf2[j] == '*' && buf2[j+1] != '>')) + same_qstr++; + } + in_sig_block = (int *) is_in_sig; - if(!strcmp(line, SIGDASHES)) - *in_sig_block = START_SIG_BLOCK; - else if(*line == '\0') + if (*in_sig_block != OUT_SIG_BLOCK){ + if (line && *line && (strlen(line) >= qstrlen) && same_qstr) + line += qstrlen; + else if (strlen(line) < qstrlen) + line += i; + else if (!same_qstr) + *in_sig_block = OUT_SIG_BLOCK; + } + else + line += i; + + if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){ + *in_sig_block = START_SIG_BLOCK; + buf = (char *) fs_get((i + 1)*sizeof(char)); + buf = cpystr(buf2); + qstrlen = i; + } + else if(*line == '\0'){ /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + } else *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + if (*in_sig_block == OUT_SIG_BLOCK){ + qstrlen = 0; /* reset back in case there's another paragraph */ + if (buf) + fs_give((void **)&buf); + } + if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, @@ -1482,18 +1557,78 @@ color_headers(long int linenum, char *line, LT_INS_S **ins, void *local) return(0); } +int +incomplete_url(char *up, int n, int delim) +{ + char *line, *line2; + int rv = 0, len; + + if(*(up + n) != '\0') + return 0; + + if(delim > 0) + return 1; + + if(F_ON(F_VIEW_LONG_URL, ps_global)){ + line = up; + if(!strncmp(line, "http://", 7)) + line += 7; + else if(!strncmp(line, "https://", 8)) + line += 8; + if(strchr(line, '/') != NULL && (line = strrchr(line, '/')) != NULL){ + line++; + line2 = strrchr(line, '.'); + rv = (strpbrk(line,"+#?=&") != NULL) + || (!line2 || line-line2 > 4); + } + } + return rv; +} + int url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) { register char *lp, *up = NULL, *urlp = NULL, *weburlp = NULL, *mailurlp = NULL; - int n, n1, n2, n3, l; + char *use_this_line, c, *begin_line, *end_line; + static int scannextline, delim = -1; + int n, n1, n2, n3, l, len; + int we_clear = 0, newhandle = 1, tie_off = 0; char buf[256], color[256]; HANDLE_S *h; URL_HILITE_S *uh; - for(lp = line; ; lp = up + n){ + uh = (URL_HILITE_S *) local; + if((uh && uh->handlesp && ((h = *(uh->handlesp)) == NULL) || h->key == 0) || + (!line || !*line) || linenum == 0) + scannextline = 0; /* initialize scannextline */ + + if(scannextline != 0){ + up = rfc1738_scan(line, &n1); + + /* if we found a url in the current line, but it is not at the beginning of + * the next line, or if there is no url in this line, we check if the url + * in the previous line continues in this line. + */ + + if(line != up){ + if(*uh->handlesp == NULL) + h = new_handle(uh->handlesp); + for(h = *uh->handlesp; h->next; h = h->next); /* get last handle */ + len = h->h.url.path ? strlen(h->h.url.path) : 0; + use_this_line = (char *) fs_get((len + strlen(line) + 1)*sizeof(char)); + sprintf(use_this_line,"%s%s", (h->h.url.path ? h->h.url.path : ""), line); + we_clear++; + newhandle = 0; + } + else + use_this_line = line; + } + else + use_this_line = line; + + for(lp = use_this_line; ; lp = up + n){ /* scan for all of them so we can choose the first */ if(F_ON(F_VIEW_SEL_URL,ps_global)) urlp = rfc1738_scan(lp, &n1); @@ -1503,6 +1638,10 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) mailurlp = mail_addr_scan(lp, &n3); if(urlp || weburlp || mailurlp){ + if(scannextline == 0){ + newhandle++; + delim = -1; + } up = urlp ? urlp : weburlp ? weburlp : mailurlp; if(up == urlp && weburlp && weburlp < up) @@ -1511,7 +1650,16 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) up = mailurlp; if(up == urlp){ + if(delim < 0) + delim = up > use_this_line && *(up - 1) == '<'; n = n1; + if(incomplete_url(up,n, delim)) + scannextline++; + else{ + if(scannextline) + tie_off++; + scannextline = 0; + } weburlp = mailurlp = NULL; } else if(up == weburlp){ @@ -1528,36 +1676,58 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) uh = (URL_HILITE_S *) local; - h = new_handle(uh->handlesp); - h->type = URL; - h->h.url.path = (char *) fs_get((n + 10) * sizeof(char)); - snprintf(h->h.url.path, n+10, "%s%.*s", + if(tie_off){ + tie_off = 0; /* do only once */ + begin_line = line; + end_line = line + n - strlen(h->h.url.path); + fs_give((void **)&h->h.url.path); + c = *(use_this_line + n); + *(use_this_line+n) = '\0'; + h->h.url.path = cpystr(use_this_line); + *(use_this_line+n) = c; + } + else{ + if(newhandle){ + h = new_handle(uh->handlesp); + h->type = URL; + } + begin_line = newhandle ? (we_clear ? line + strlen(line) - strlen(up) + : up) : line; + end_line = newhandle ? begin_line + n : line + strlen(line); + if(scannextline && h->h.url.path) + fs_give((void **)&h->h.url.path); + h->h.url.path = (char *) fs_get((n + 10) * sizeof(char)); + snprintf(h->h.url.path, n+10, "%s%.*s", weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up); - h->h.url.path[n+10-1] = '\0'; + h->h.url.path[n+10-1] = '\0'; + } if(handle_start_color(color, sizeof(color), &l, uh->hdr_color)) - ins = gf_line_test_new_ins(ins, up, color, l); + ins = gf_line_test_new_ins(ins, begin_line, color, l); else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)) - ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2); + ins = gf_line_test_new_ins(ins, begin_line, url_embed(TAG_BOLDON), 2); buf[0] = TAG_EMBED; buf[1] = TAG_HANDLE; snprintf(&buf[3], sizeof(buf)-3, "%d", h->key); buf[sizeof(buf)-1] = '\0'; buf[2] = strlen(&buf[3]); - ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3); + ins = gf_line_test_new_ins(ins, begin_line, buf, (int) buf[2] + 3); /* in case it was the current selection */ - ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2); + ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_INVOFF), 2); if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color)) - ins = gf_line_test_new_ins(ins, up + n, color, l); + ins = gf_line_test_new_ins(ins, end_line, color, l); else - ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2); + ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_BOLDOFF), 2); urlp = weburlp = mailurlp = NULL; } + if(we_clear) + fs_give((void **)&use_this_line); + return(0); } @@ -1678,6 +1848,77 @@ pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local) } +/* This filter gives a quote string of a line. It sends its reply back to the + calling filter in the tmp_20k_buf variable. This filter replies with + the full quote string including tailing spaces if any. It is the + responsibility of the calling filter to figure out if thos spaces are + useful for that filter or if they should be removed before doing any + useful work. For example, color_a_quote does not require the trailing + spaces, but gf_wrap does. + */ +int +select_quote(long linenum, char *line, LT_INS_S **ins, void *local) +{ + int i, plb, *code; + char rqstr[NSTRING] = {'\0'}, buf[NSTRING] = {'\0'}; + char GLine[NSTRING] = {'\0'}, PLine[NSTRING] = {'\0'}; + char PPLine[NSTRING] = {'\0'}, NLine[NSTRING] = {'\0'}; + static char GLine1[NSTRING] = {'\0'}; + static char PLine1[NSTRING] = {'\0'}; + static char PPLine1[NSTRING] = {'\0'}; + static char GLine2[NSTRING] = {'\0'}; + static char PLine2[NSTRING] = {'\0'}; + static char PPLine2[NSTRING] = {'\0'}; + QSTRING_S *qs; + int buflen = NSTRING < SIZEOF_20KBUF ? NSTRING - 1: SIZEOF_20KBUF - 1; + int who, raw; + + code = (int *)local; + who = code ? (*code & COLORAQUO) : 0; /* may I ask who is calling? */ + raw = code ? (*code & RAWSTRING) : 0; /* return raw string */ + strncpy(GLine, (who ? GLine1 : GLine2), buflen); + strncpy(PLine, (who ? PLine1 : PLine2), buflen); + strncpy(PPLine, (who ? PPLine1 : PPLine2), buflen); + + if (linenum > 0) + strncpy(PLine, GLine, buflen); + + strncpy(NLine, tmp_20k_buf, buflen); + + if (line) + strncpy(GLine, line, buflen); + else + GLine[0] = '\0'; + + ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix + ? (void *) ps_global->prefix : (void *) ">", 0); + plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING); + + qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb); + if (raw) + strncpy(buf, rqstr, NSTRING); + else + flatten_qstring(qs, buf, NSTRING); + if(qs) + record_quote_string(qs); + free_qs(&qs); + + /* do not paint an extra level for a line with a >From string at the + * begining of it + */ + if (buf[0]){ + i = strlen(buf); + if (strlen(line) >= i + 6 && !strncmp(line+i-1,">From ", 6)) + buf[i - 1] = '\0'; + } + strncpy(tmp_20k_buf, buf, buflen); + if (linenum > 0) + strncpy((who ? PPLine1 : PPLine2), PLine, buflen); + strncpy((who ? GLine1 : GLine2), GLine, buflen); + strncpy((who ? PLine1 : PLine2), PLine, buflen); + return 1; +} + #define UES_LEN 12 #define UES_MAX 32 @@ -2503,6 +2744,190 @@ hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor) return(color_pair); } +void +interval_free(IVAL_S **ival) +{ + if (!(*ival)) + return; + + if ((*ival)->next) + interval_free(&((*ival)->next)); + + fs_give((void **)(ival)); +} + +IVAL_S * +compute_interval (char *string, int endm) +{ + IVAL_S *ival = NULL; + regmatch_t pmatch; + + if(ps_global->paterror == 0 && + regexec(&ps_global->colorpat, string + endm, 1, &pmatch, 0) == 0){ + ival = (IVAL_S *) fs_get(sizeof(IVAL_S)); + ival->start = endm + pmatch.rm_so; + ival->end = endm + pmatch.rm_eo; + ival->next = compute_interval(string, ival->end); + } + return ival; +} + +void +regex_pattern(char **plist) +{ + int i = 0, j = 0, len = 0; + char *pattern = NULL; + regex_t preg; + + if(ps_global->paterror == 0) + regfree(&ps_global->colorpat); + + if(plist && *plist && *plist){ + for (i = 0; plist[i] && plist[i][0]; i++) + len += strlen(plist[i]) + 1; + pattern = (char *) fs_get(len * sizeof(char)); + *pattern = '\0'; + for (j = 0; j < i; j++){ + strcat(pattern, plist[j]); + strcat(pattern, (j < i - 1) ? "|" : ""); + } + if ((ps_global->paterror = regcomp(&preg, pattern, REG_EXTENDED)) != 0) + regfree(&preg); + else + ps_global->colorpat = preg; + } + if(pattern) + fs_give((void **)&pattern); +} + +LT_INS_S ** +insert_color_special_text(LT_INS_S **ins, char **p, IVAL_S *ival, int last_end, + COLOR_PAIR *col) +{ + struct variable *vars = ps_global->vars; + + if (ival){ + *p += ival->start - last_end; + ins = gf_line_test_new_ins(ins, *p, color_embed(col->fg, col->bg), + (2 * RGBLEN) + 4); + *p += ival->end - ival->start; + ins = gf_line_test_new_ins(ins, *p, color_embed(VAR_NORM_FORE_COLOR, + VAR_NORM_BACK_COLOR), (2 * RGBLEN) + 4); + ins = insert_color_special_text(ins, p, ival->next, ival->end, col); + } + return ins; +} + +int +length_color(char *p, int begin_color) +{ + int len = 0, done = begin_color ? 0 : -1; + char *orig = p; + + while (*p && done <= 0){ + switch(*p++){ + case TAG_HANDLE : + p += *p + 1; + done++; + break; + + case TAG_FGCOLOR : + case TAG_BGCOLOR : + p += RGBLEN; + if (!begin_color) + done++; + break; + + default : + break; + } + } + len = p - orig; + return len; +} + +int +any_color_in_string(char *p) +{ + int rv = 0; + char *orig = p; + while (*p && !rv) + if (*p++ == TAG_EMBED) + rv = p - orig; + return rv; +} + +void +remove_spaces_ival(IVAL_S **ivalp, char *p) +{ + IVAL_S *ival; + int i; + if (!ivalp || !*ivalp) + return; + ival = *ivalp; + for (i = 0; isspace((unsigned char) p[ival->start + i]); i++); + if (ival->start + i < ival->end) /* do not do this if match only spaces */ + ival->start += i; + else + return; + for (i = 0; isspace((unsigned char) p[ival->end - i - 1]); i++); + ival->end -= i; + if (ival->next) + remove_spaces_ival(&(ival->next), p); +} + +int +color_this_text(long linenum, char *line, LT_INS_S **ins, void *local) +{ + struct variable *vars = ps_global->vars; + COLOR_PAIR *col = NULL; + char *p; + int i = 0; + static char *pattern = NULL; + +/* select_quote(linenum, line, ins, (void *) &i); + for (i = 0; tmp_20k_buf[i] != '\0'; i++); */ + p = line + i; + + if(VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR + && (col = new_color_pair(VAR_SPECIAL_TEXT_FORE_COLOR, + VAR_SPECIAL_TEXT_BACK_COLOR)) + && !pico_is_good_colorpair(col)) + free_color_pair(&col); + + if(ps_global->VAR_SPECIAL_TEXT && *ps_global->VAR_SPECIAL_TEXT + && **ps_global->VAR_SPECIAL_TEXT && col){ + IVAL_S *ival; + int done = 0, begin_color = 0; + + while (!done){ + if (i = any_color_in_string(p)){ + begin_color = (begin_color + 1) % 2; + if (begin_color){ + p[i - 1] = '\0'; + ival = compute_interval(p, 0); + remove_spaces_ival(&ival, p); + p[i - 1] = TAG_EMBED; + ins = insert_color_special_text(ins, &p, ival, 0, col); + } + for (;*p++ != TAG_EMBED; ); + p += length_color(p, begin_color); + } + else{ + ival = compute_interval(p, 0); + remove_spaces_ival(&ival, p); + ins = insert_color_special_text(ins, &p, ival, 0, col); + done++; + } + interval_free(&ival); + if (!*p) + done++; + } + free_color_pair(&col); + } + + return 0; +} /* * The argument fieldname is something like "Subject:..." or "Subject". diff --git a/pith/mailview.h b/pith/mailview.h index a89f3f95..5d2fe171 100644 --- a/pith/mailview.h +++ b/pith/mailview.h @@ -30,6 +30,12 @@ #include "../pith/color.h" +typedef struct IVAL { + int start; + int end; + struct IVAL *next; +} IVAL_S; + /* format_message flags */ #define FM_DISPLAY 0x0001 /* result is headed for display */ #define FM_NEW_MESS 0x0002 /* a new message so zero out attachment descrip */ @@ -126,6 +132,15 @@ char *format_body(long int, BODY *, HANDLE_S **, HEADER_S *, int, int, gf_io_t); int url_hilite(long, char *, LT_INS_S **, void *); int handle_start_color(char *, size_t, int *, int); int handle_end_color(char *, size_t, int *); +IVAL_S *compute_interval(char *, int); +void remove_spaces_ival(IVAL_S **, char *); +void interval_free(IVAL_S **); +void regex_pattern(char **); +LT_INS_S **insert_color_special_text(LT_INS_S **, char **, IVAL_S *, + int, COLOR_PAIR *); +int any_color_in_string(char *); +int length_color(char *, int); +int color_this_text(long, char *, LT_INS_S **, void *); /* * BUG: BELOW IS UNIX/PC ONLY since config'd browser means nothing to webpine @@ -142,6 +157,7 @@ COLOR_PAIR *hdr_color(char *, char *, SPEC_COLOR_S *); char *display_parameters(PARAMETER *); char *pine_fetch_header(MAILSTREAM *, long, char *, char **, long); int color_signature(long, char *, LT_INS_S **, void *); +int select_quote(long, char *, LT_INS_S **, void *); int scroll_handle_start_color(char *, size_t, int *); int scroll_handle_end_color(char *, size_t, int *, int); int width_at_this_position(unsigned char *, unsigned long); diff --git a/pith/makefile.wnt b/pith/makefile.wnt index d9316dba..b9464cd4 100644 --- a/pith/makefile.wnt +++ b/pith/makefile.wnt @@ -44,7 +44,7 @@ HFILES= ../include/system.h ../include/general.h \ init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ - rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ + rfc2231.h rules.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ thread.h url.h user.h util.h @@ -53,7 +53,7 @@ OFILES= ablookup.obj abdlc.obj addrbook.obj addrstring.obj adrbklib.obj bldaddr. filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ - readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ + readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \ thread.obj adjtime.obj url.obj util.obj diff --git a/pith/msgno.c b/pith/msgno.c index 465a42e0..e72ee0d3 100644 --- a/pith/msgno.c +++ b/pith/msgno.c @@ -933,6 +933,9 @@ free_pine_elt(void **sparep) if((*peltp)->pthrd) fs_give((void **) &(*peltp)->pthrd); + if((*peltp)->firsttext) + fs_give((void **) &(*peltp)->firsttext); + if((*peltp)->ice) free_ice(&(*peltp)->ice); diff --git a/pith/osdep/color.c b/pith/osdep/color.c index faf3c675..cad5502e 100644 --- a/pith/osdep/color.c +++ b/pith/osdep/color.c @@ -31,7 +31,7 @@ static char rcsid[] = "$Id: color.c 761 2007-10-23 22:35:18Z hubert@u.washington #include <system.h> #include "./color.h" - +#include "./collate.h" /* @@ -91,3 +91,1257 @@ pico_set_colorp(COLOR_PAIR *col, int flags) { return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags)); } + + + /* + * Extended Justification support also does not belong here + * but otherwise webpine will not build, so we move everything + * here. Hopefully this will be the permanent place for these + * routines. These routines used to be in pico/word.c + */ +#define NSTRING 256 +#include "../../include/general.h" + +/* Support of indentation of paragraphs */ +#define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \ + (c) == '*' || (c) == '+' || is_a_digit(c) || \ + ISspace(c) || (c) == '-' || \ + (c) == ']') ? 1 : 0) +#define allowed_after_digit(c,word,k) ((((c) == '.' && \ + allowed_after_period(next((word),(k)))) ||\ + (c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || is_a_digit(c) || \ + ((c) == '-' ) && \ + allowed_after_dash(next((word),(k)))) \ + ? 1 : 0) +#define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || (c) == '-' || \ + is_a_digit(c)) ? 1 : 0) +#define allowed_after_parenth(c) (ISspace(c) ? 1 : 0) +#define allowed_after_space(c) (ISspace(c) ? 1 : 0) +#define allowed_after_braces(c) (ISspace(c) ? 1 : 0) +#define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\ + (c) == ']' || (c) == '}') ? 1 : 0) +#define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0) +#define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\ + (c) == '!') ? 1 : 0) + + +/* Extended justification support */ +#define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':') +#define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || ((c) == '?') || \ + ((c) == '@') || ((c) == '.') || \ + ((c) == '!') || ((c) == '\'') || \ + ((c) == ',') || ((c) == '\"') ? 1 : 0) +#define isaquote(c) ((c) == '\"' || (c) == '\'') +#define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0) +#define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0) +#define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\ + ((c) == ',') || ((c) == '.') || ((c) == '-') ||\ + ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\ + ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\ + (((c) >= '0') && ((c) <= '9')) || ((c) == '?')) +#define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\ + ((((c) >= 'A') && ((c) <= 'Z'))||\ + is8bit(c)) +#define is_cnumber(c) ((c) >= '0' && (c) <= '9') +#define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c)) +#define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN)) +#define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0) +#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) +#define now(w,i) ((w)[(i)]) +#define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0) +#define is_colon(c) (((c) == ':') ? 1 : 0) +#define is_rarrow(c) (((c) == '>') ? 1 : 0) +#define is_tilde(c) (((c) == '~') ? 1 : 0) +#define is_dash(c) (((c) == '-') ? 1 : 0) +#define is_pound(c) (((c) == '#') ? 1 : 0) +#define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) +#define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \ + is_pound(c)) +#define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg)) + +/* Internal justification functions */ +QSTRING_S *is_quote(char **, char *, int); +QSTRING_S *qs_normal_part(QSTRING_S *); +QSTRING_S *qs_remove_trailing_spaces(QSTRING_S *); +QSTRING_S *trim_qs_from_cl(QSTRING_S *, QSTRING_S *, QSTRING_S *); +QSTRING_S *fix_qstring(QSTRING_S *, QSTRING_S *, QSTRING_S *); +QSTRING_S *fix_qstring_allowed(QSTRING_S *, QSTRING_S *, QSTRING_S *); +QSTRING_S *qs_add(char **, char *, QStrType, int, int, int, int); +QSTRING_S *remove_qsword(QSTRING_S *); +QSTRING_S *do_raw_quote_match(char **, char *, char *, char *, QSTRING_S **, QSTRING_S **); +void free_qs(QSTRING_S **); +int word_is_prog(char *); +int qstring_is_normal(QSTRING_S *); +int exists_good_part(QSTRING_S *); +int strcmp_qs(char *, char *); +int count_levels_qstring(QSTRING_S *); +int same_qstring(QSTRING_S *, QSTRING_S *); +int isaword(char *,int ,int); +int isamailbox(char *,int ,int); +int double_check_qstr(char *); + +int +word_is_prog(char *word) +{ + static char *list1[] = {"#include", + "#define", + "#ifdef", + "#ifndef", + "#elif", + "#if", + NULL}; + static char *list2[] = {"#else", + "#endif", + NULL}; + int i, j = strlen(word), k, rv = 0; + + for(i = 0; rv == 0 && list1[i] && (k = strlen(list1[i])) && k < j; i++) + if(!strncmp(list1[i], word, k) && ISspace(word[k])) + rv++; + + if(rv) + return rv; + + for(i = 0; rv == 0 && list2[i] && (k = strlen(list2[i])) && k <= j; i++) + if(!strncmp(list2[i], word, k) && (!word[k] || ISspace(word[k]))) + rv++; + + return rv; +} + +/* + * This function creates a qstring pointer with the information that + * is_quote handles to it. + * Parameters: + * qs - User supplied quote string + * word - The line of text that the user is trying to read/justify + * beginw - Where we need to start copying from + * endw - Where we end copying + * offset - Any offset in endw that we need to account for + * typeqs - type of the string to be created + * neednext - boolean, indicating if we need to compute the next field + * of leave it NULL + * + * It is a mistake to call this function if beginw >= endw + offset. + * Please note the equality sign in the above inequality (this is because + * we always assume that qstring->value != ""). + */ +QSTRING_S * +qs_add(char **qs, char word[NSTRING], QStrType typeqs, int beginw, int endw, + int offset, int neednext) +{ + QSTRING_S *qstring, *nextqs; + int i; + + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + qstring->qstype = qsNormal; + + if (beginw == 0){ + beginw = endw + offset; + qstring->qstype = typeqs; + } + + nextqs = neednext ? is_quote(qs, word+beginw, 1) : NULL; + + qstring->value = (char *) malloc((beginw+1)*sizeof(char)); + strncpy(qstring->value, word, beginw); + qstring->value[beginw] = '\0'; + + qstring->next = nextqs; + + return qstring; +} + +int +qstring_is_normal(QSTRING_S *cl) +{ + for (;cl && (cl->qstype == qsNormal); cl = cl->next); + return cl ? 0 : 1; +} + +/* + * Given a quote string, this function returns the part that is the leading + * normal part of it. (the normal part is the part that is tagged qsNormal, + * that is to say, the one that is not controversial at all (like qsString + * for example). + */ +QSTRING_S * +qs_normal_part(QSTRING_S *cl) +{ + + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->qstype != qsNormal) + free_qs(&cl); + + if (cl) + cl->next = qs_normal_part(cl->next); + + return cl; +} + +/* + * this function removes trailing spaces from a quote string, but leaves the + * last one if there are trailing spaces + */ +QSTRING_S * +qs_remove_trailing_spaces(QSTRING_S *cl) +{ + QSTRING_S *rl = cl; + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->next) + cl->next = qs_remove_trailing_spaces(cl->next); + else{ + if (value_is_space(cl->value)) + free_qs(&cl); + else{ + int i, l; + i = l = strlen(cl->value) - 1; + while (cl->value && cl->value[i] + && ISspace(cl->value[i])) + i--; + i += (i < l) ? 2 : 1; + cl->value[i] = '\0'; + } + } + return cl; +} + +/* + * This function returns if two strings are the same quote string. + * The call is not symmetric. cl must preceed the line nl. This function + * should be called for comparing the last part of cl and nl. + */ +int +strcmp_qs(char *valuecl, char *valuenl) +{ + int j; + + for (j = 0; valuecl[j] && (valuecl[j] == valuenl[j]); j++); + return !strcmp(valuecl, valuenl) + || (valuenl[j] && value_is_space(valuenl+j) + && value_is_space(valuecl+j) + && strlenis(valuecl+j) >= strlenis(valuenl+j)) + || (!valuenl[j] && value_is_space(valuecl+j)); +} + +int +count_levels_qstring(QSTRING_S *cl) +{ + int count; + for (count = 0; cl ; count++, cl = cl->next); + + return count; +} + +int +value_is_space(char *value) +{ + for (; value && *value && ISspace(*value); value++); + + return value && *value ? 0 : 1; +} + +void +free_qs(QSTRING_S **cl) +{ + if (!(*cl)) + return; + + if ((*cl)->next) + free_qs(&((*cl)->next)); + + (*cl)->next = (QSTRING_S *) NULL; + + if ((*cl)->value) + free((void *)(*cl)->value); + (*cl)->value = (char *) NULL; + free((void *)(*cl)); + *cl = (QSTRING_S *) NULL; +} + +/* + * This function returns the number of agreements between + * cl and nl. The call is not symmetric. cl must be the line + * preceding nl. + */ +int +same_qstring(QSTRING_S *cl, QSTRING_S *nl) +{ + int same = 0, done = 0; + + for (;cl && nl && !done; cl = cl->next, nl = nl->next) + if (cl->qstype == nl->qstype + && (!strcmp(cl->value, nl->value) + || (!cl->next && strcmp_qs(cl->value, nl->value)))) + same++; + else + done++; + return same; +} + +QSTRING_S * +trim_qs_from_cl(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) +{ + QSTRING_S *cqstring = pl ? pl : nl; + QSTRING_S *tl = pl ? pl : nl; + int p, c; + + if (qstring_is_normal(tl)) + return tl; + + p = same_qstring(pl ? pl : cl, pl ? cl : nl); + + for (c = 1; c < p; c++, cl = cl->next, tl = tl->next); + + /* + * cl->next and tl->next differ, it may be because cl->next does not + * exist or tl->next does not exist or simply both exist but are + * different. In this last case, it may be that cl->next->value is made + * of spaces. If this is the case, tl advances once more. + */ + + if (tl->next){ + if (cl && cl->next && value_is_space(cl->next->value)) + tl = tl->next; + if (tl->next) + free_qs(&(tl->next)); + } + + if (!p) + free_qs(&cqstring); + + return cqstring; +} + +/* This function trims cl so that it returns a real quote string based + * on information gathered from the previous and next lines. pl and cl are + * also trimmed, but that is done in another function, not here. + */ +QSTRING_S * +fix_qstring(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) +{ + QSTRING_S *cqstring = cl, *nqstring = nl, *pqstring = pl; + int c, n; + + if (qstring_is_normal(cl)) + return cl; + + c = count_levels_qstring(cl); + n = same_qstring(cl,nl); + + if (!n){ /* no next line or no agreement with next line */ + int p = same_qstring(pl, cl); /* number of agreements between pl and cl */ + QSTRING_S *tl; /* test line */ + + /* + * Here p <= c, so either p < c or p == c. If p == c, we are done, + * and return cl. If not, there are two cases, either p == 0 or + * 0 < p < c. In the first case, we do not have enough evidence + * to return anything other than the normal part of cl, in the second + * case we can only return p levels of cl. + */ + + if (p == c) + tl = cqstring; + else{ + if (p){ + for (c = 1; c < p; c++) + cl = cl->next; + free_qs(&(cl->next)); + tl = cqstring; + } + else{ + int done = 0; + QSTRING_S *al = cl; /* another line */ + /* + * Ok, we really don't have enough evidence to return anything, + * different from the normal part of cl, but it could be possible + * that we may want to accept the not-normal part, so we better + * make an extra test to determine what needs to be freed + */ + while (pl && cl && cl->qstype == pl->qstype + && !strucmp(cl->value, pl->value)){ + cl = cl->next; + pl = pl->next; + } + if (pl && cl && cl->qstype == pl->qstype + && strcmp_qs(pl->value, cl->value)) + cl = cl->next; /* next level differs only in spaces */ + while (!done){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + if (cl){ + if ((cl->qstype == qsString) + && (cl->value[strlen(cl->value) - 1] == '>')) + cl = cl->next; + else done++; + } + else done++; + } + if (al == cl){ + free_qs(&(cl)); + tl = cl; + } + else { + while (al && (al->next != cl)) + al = al->next; + cl = al; + if (cl && cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + } + } + return tl; + } + if (n + 1 < c){ /* if there are not enough agreements */ + int p = same_qstring(pl, cl); /* number of agreement between pl and cl */ + QSTRING_S *tl; /* test line */ + /* + * There's no way we can use cl in this case, but we can use + * part of cl, this is if pl does not have more agreements + * with cl. + */ + if (p == c) + tl = cqstring; + else{ + int m = p < n ? n : p; + for (c = 1; c < m; c++){ + pl = pl ? pl->next : (QSTRING_S *) NULL; + nl = nl ? nl->next : (QSTRING_S *) NULL; + cl = cl->next; + } + if (p == n && pl && pl->next && nl && nl->next + && ((cl->next->qstype == pl->next->qstype) + || (cl->next->qstype == nl->next->qstype)) + && (strcmp_qs(cl->next->value, pl->next->value) + || strcmp_qs(pl->next->value, cl->next->value) + || strcmp_qs(cl->next->value, nl->next->value) + || strcmp_qs(nl->next->value, cl->next->value))) + cl = cl->next; /* next level differs only in spaces */ + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n + 1 == c){ + int p = same_qstring(pl, cl); + QSTRING_S *tl; /* test line */ + + /* + * p <= c, so p <= n+1, which means p < n + 1 or p == n + 1. + * If p < n + 1, then p <= n. + * so we have three possibilities: + * p == n + 1 or p == n or p < n. + * In the first case we copy p == n + 1 == c levels, in the second + * and third case we copy n levels, and check if we can copy the + * n + 1 == c level. + */ + if (p == n + 1) /* p == c, in the above sense of c */ + tl = cl; /* use cl, this is enough evidence */ + else{ + for (c = 1; c < n; c++) + cl = cl->next; + /* + * Here c == n, we only have one more level of cl, and at least one + * more level of nl + */ + if (cl->next->qstype == qsNormal) + cl = cl->next; + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n == c) /* Yeah!!! */ + return cqstring; +} + +QSTRING_S * +fix_qstring_allowed(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) +{ + if(!cl) + return (QSTRING_S *) NULL; + + if (qs_allowed(cl)) + cl->next = fix_qstring_allowed(cl->next, (nl ? nl->next : NULL), + (pl ? pl->next : NULL)); + else + if((nl && cl->qstype == nl->qstype) || (pl && cl->qstype == pl->qstype) + || (!nl && !pl)) + free_qs(&cl); + return cl; +} + +/* + * This function flattens the quote string returned to us by is_quote. A + * crash in this function implies a bug elsewhere. + */ +void +flatten_qstring(QSTRING_S *qs, char *buff, int bufflen) +{ + int i, j; + if(!buff || bufflen <= 0) + return; + + for (i = 0; qs; qs = qs->next) + for (j = 0; i < bufflen - 1 + && (qs->value[j]) && (buff[i++] = qs->value[j]); j++); + buff[i] = '\0'; +} + +extern int list_len; + + +int +double_check_qstr(char *q) +{ + if(!q || !*q) + return 0; + + return (*q == '#') ? 1 : 0; +} + +/* + * Given a string, we return the position where the function thinks that + * the quote string is over, if you are ever thinking of fixing something, + * you got to the right place. Memory freed by caller. Experience shows + * that it only makes sense to initialize memory when we need it, not at + * the start of this function. + */ +QSTRING_S * +is_quote (char **qs,char *word, int been_here) +{ + int i = 0, j, nxt, prev, finished = 0, offset; + unsigned char c; + QSTRING_S *qstring = (QSTRING_S *) NULL; + + if (word == NULL || word[0] == '\0') + return (QSTRING_S *) NULL; + + while (!finished){ + /* + * Before we apply our rules, let's advance past the quote string + * given by the user, this will avoid not recognition of the + * user's indent string and application of the arbitrary rules + * below. Notice that this step may bring bugs into this + * procedure, but these bugs will only appear if the indent string + * is really really strange and the text to be justified + * cooperates a lot too, so in general this will not be a problem. + * If you are concerned about this bug, simply remove the + * following lines after this comment and before the "switch" + * command below and use a more normal quote string!. + */ + for(j = 0; j < list_len; j++){ + if(!double_check_qstr(qs[j])){ + i += advance_quote_string(qs[j], word, i); + if (!word[i]) /* went too far? */ + return qs_add(qs, word, qsNormal, 0, i, 0, 0); + } + else + break; + } + + switch (c = (unsigned char) now(word,i)){ + case NBSP: + case TAB : + case ' ' : { QSTRING_S *nextqs, *d; + + for (; ISspace(word[i]); i++); /* FIX ME */ + nextqs = is_quote(qs,word+i, 1); + /* + * Merge qstring and nextqs, since this is an artificial + * separation, unless nextqs is of different type. + * What this means in practice is that if + * qs->qstype == qsNormal and qs->next != NULL, then + * qs->next->qstype != qsNormal. + * + * Can't use qs_add to merge because it could lead + * to an infinite loop (e.g a line "^ ^"). + */ + i += nextqs && nextqs->qstype == qsNormal + ? strlen(nextqs->value) : 0; + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + qstring->value = (char *) malloc((i+1)*sizeof(char)); + strncpy(qstring->value, word, i); + qstring->value[i] = '\0'; + qstring->qstype = qsNormal; + if(nextqs && nextqs->qstype == qsNormal){ + d = nextqs->next; + nextqs->next = NULL; + qstring->next = d; + free_qs(&nextqs); + } + else + qstring->next = nextqs; + + return qstring; + } + break; + case RPAREN: /* parenthesis ')' */ + if ((i != 0) || ((i == 0) && been_here)) + i++; + else + if (i == 0) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case ':': /* colon */ + case '~': nxt = next(word,i); + if ((is_tilde(c) && (nxt == '/')) + || (is_colon(c) && !is_cquote(nxt) + && !is_cword(nxt) && nxt != RPAREN)) + finished++; + else if (is_cquote(c) + || is_cquote(nxt) + || (c != '~' && nxt == RPAREN) + || (i != 0 && ISspace(nxt)) + || is_cquote(prev = before(word,i)) + || (ISspace(prev) && !is_tilde(c)) + || (is_tilde(c) && nxt != '/')) + i++; + else if (i == 0 && been_here) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case '<' : + case '=' : + case '-' : offset = is_cquote(nxt = next(word,i)) ? 2 + : (nxt == c && is_cquote(next(word,i+1))) ? 3 : -1; + + if (offset > 0) + return qs_add(qs, word, qsString, i, i, offset, 1); + else + finished++; + break; + + case '[' : + case '+' : /* accept +>, *> */ + case '*' : if (is_rarrow(nxt = next(word, i)) || /* stars */ + (ISspace(nxt) && is_rarrow(next(word,i+1)))) + i++; + else + finished++; + break; + + case '^' : + case '!' : + case '%' : if (next(word,i) != c) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + case '_' : if(ISspace(next(word, i))) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + case '#' : { QStrType qstype = qsChar; + if((nxt = next(word, i)) != c){ + if(isdigit((int) nxt)) + qstype = qsGdb; + else + if(word_is_prog(word)) + qstype = qsProg; + return qs_add(qs, word, qstype, i, i+1, 0, 1); + } + else + finished++; + break; + } + + default: + if (is_cquote(c)) + i++; + else if (is_cletter(c)){ + for (j = i; (is_cletter(nxt = next(word,j)) || is_cnumber(nxt)) + && !(ISspace(nxt));j++); + /* + * The whole reason why we are splitting the quote + * string is so that we will be able to accept quote + * strings that are strange in some way. Here we got to + * a point in which a quote string might exist, but it + * could be strange, so we need to create a "next" field + * for the quote string to warn us that something + * strange is coming. We need to confirm if this is a + * good choice later. For now we will let it pass. + */ + if (isaword(word,i,j) || isamailbox(word,i,j)){ + int offset; + QStrType qstype; + + offset = (is_cquote(c = next(word,j)) + || (c == RPAREN)) ? 2 + : ((ISspace(c) + && is_cquote(next(word,j+1))) ? 3 : -1); + + qstype = (is_cquote(c) || (c == RPAREN)) + ? (is_qsword(c) ? qsWord : qsString) + : ((ISspace(c) && is_cquote(next(word,j+1))) + ? (is_qsword(next(word,j+1)) + ? qsWord : qsString) + : qsString); + + /* + * qsWords are valid quote strings only when + * they are followed by text. + */ + if (offset > 0 && qstype == qsWord && + !allwd_after_qsword(now(word,j + offset))) + offset = -1; + + if (offset > 0) + return qs_add(qs, word, qstype, i, j, offset, 1); + } + finished++; + } + else{ + if(i > 0) + return qs_add(qs, word, qsNormal, 0, i, 0, 1); + else if(!forbidden(c)) + return qs_add(qs, word, qsChar, 0, 1, 0, 1); + else /* chao pescao */ + finished++; + } + break; + } /* End Switch */ + } /* End while */ + if (i > 0) + qstring = qs_add(qs, word, qsNormal, 0, i, 0, 0); + return qstring; +} + +int +isaword(char word[NSTRING], int i, int j) +{ + return i <= j && is_cletter(word[i]) ? + (i < j ? isaword(word,i+1,j) : 1) : 0; +} + +int +isamailbox(char word[NSTRING], int i, int j) +{ + return i <= j && (is_cletter(word[i]) || is_a_digit(word[i]) + || word[i] == '.') + ? (i < j ? isamailbox(word,i+1,j) : 1) : 0; +} + +/* + This routine removes the last part that is qsword or qschar that is not + followed by a normal part. This means that if a qsword or qschar is + followed by a qsnormal (or qsstring), we accept the qsword (or qschar) + as part of a quote string. + */ +QSTRING_S * +remove_qsword(QSTRING_S *cl) +{ + QSTRING_S *np = cl; + QSTRING_S *cp = np; /* this variable trails cl */ + + while(1){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + + if (cl){ + if (((cl->qstype == qsWord) || (cl->qstype == qsChar)) + && !exists_good_part(cl)){ + if (np == cl) /* qsword or qschar at the beginning */ + free_qs(&cp); + else{ + while (np->next != cl) + np = np->next; + free_qs(&(np->next)); + } + break; + } + else + cl = cl->next; + } + else + break; + } + return cp; +} + +int +exists_good_part (QSTRING_S *cl) +{ + return (cl ? (((cl->qstype != qsWord) && (cl->qstype != qsChar) + && qs_allowed(cl) && !value_is_space(cl->value)) + ? 1 : exists_good_part(cl->next)) + : 0); +} + +int +line_isblank(char **q, char *GLine, char *NLine, char *PLine, int buflen) +{ + int n = 0; + QSTRING_S *cl; + char qstr[NSTRING]; + + cl = do_raw_quote_match(q, GLine, NLine, PLine, NULL, NULL); + + flatten_qstring(cl, qstr, NSTRING); + + free_qs(&cl); + + for(n = strlen(qstr); n < buflen && GLine[n]; n++) + if(!ISspace((unsigned char) GLine[n])) + return(FALSE); + + return(TRUE); +} + +QSTRING_S * +do_raw_quote_match(char **q, char *GLine, char *NLine, char *PLine, QSTRING_S **nlp, QSTRING_S **plp) +{ + QSTRING_S *cl, *nl = NULL, *pl = NULL; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + int emptypl = 0, emptynl = 0; + + if (!(cl = is_quote(q, GLine, 0))) /* if nothing in, nothing out */ + return cl; + + nl = is_quote(q, NLine, 0); /* Next Line */ + if (nlp) *nlp = nl; + pl = is_quote(q, PLine, 0); /* Previous Line */ + if (plp) *plp = pl; + /* + * If there's nothing in the preceeding or following line + * there is not enough information to accept it or discard it. In this + * case it's likely to be an isolated line, so we better accept it + * if it does not look like a word. + */ + flatten_qstring(pl, pbuf, NSTRING); + emptypl = (!PLine || !PLine[0] || + (pl && value_is_space(pbuf)) && !PLine[strlen(pbuf)]) ? 1 : 0; + if (emptypl){ + flatten_qstring(nl, nbuf, NSTRING); + emptynl = (!NLine || !NLine[0] || + (nl && value_is_space(nbuf) && !NLine[strlen(nbuf)])) ? 1 : 0; + if (emptynl){ + cl = remove_qsword(cl); + if((cl = fix_qstring_allowed(cl, NULL, NULL)) != NULL) + cl = qs_remove_trailing_spaces(cl); + free_qs(&nl); + free_qs(&pl); + if(nlp) *nlp = NULL; + if(plp) *plp = NULL; + + return cl; + } + } + + /* + * If either cl, nl or pl contain suspicious characters that may make + * them (or not) be quote strings, we need to fix them, so that the + * next pass will be done correctly. + */ + + cl = fix_qstring(cl, nl, pl); + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + if((cl = fix_qstring_allowed(cl, nl, pl)) != NULL){ + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + } + else{ + free_qs(&nl); + free_qs(&pl); + } + if(nlp) + *nlp = nl; + else + free_qs(&nl); + if(plp) + *plp = pl; + else + free_qs(&pl); + return cl; +} + +QSTRING_S * +do_quote_match(char **q, char *GLine, char *NLine, char *PLine, char *rqstr, +int rqstrlen, int plb) +{ + QSTRING_S *cl, *nl = NULL, *pl = NULL; + int c, n, p,i, j, NewP, NewC, NewN, clength, same = 0; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + + if(rqstr) + *rqstr = '\0'; + + /* if nothing in, nothing out */ + cl = do_raw_quote_match(q, GLine, NLine, PLine, &nl, &pl); + if(cl == NULL){ + free_qs(&nl); + free_qs(&pl); + return cl; + } + + flatten_qstring(cl, rqstr, rqstrlen); + flatten_qstring(cl, buf, NSTRING); + flatten_qstring(nl, nbuf, NSTRING); + flatten_qstring(pl, pbuf, NSTRING); + + /* + * Once upon a time, is_quote used to return the length of the quote + * string that it had found. One day, not long ago, black hand came + * and changed all that, and made is_quote return a quote string + * divided in several fields, making the algorithm much more + * complicated. Fortunately black hand left a few comments in the + * source code to make it more understandable. Because of this change + * we need to compute the lengths of the quote strings separately + */ + c = buf && buf[0] ? strlen(buf) : 0; + n = nbuf && nbuf[0] ? strlen(nbuf) : 0; + p = pbuf && pbuf[0] ? strlen(pbuf) : 0; + /* + * When quote strings contain only blank spaces (ascii code 32) the + * above count is equal to the length of the quote string, but if + * there are TABS, the length of the quote string as seen by the user + * is different than the number that was just computed. Because of + * this we demand a recount (hmm.. unless you are in Florida, where + * recounts are forbidden) + */ + NewP = strlenis(pbuf); + NewC = strlenis(buf); + NewN = strlenis(nbuf); + + /* + * For paragraphs with spaces in the first line, but no space in the + * quote string of the second line, we make sure we choose the quote + * string without a space at the end of it. + */ + if ((NLine && !NLine[0]) + && ((PLine && !PLine[0]) + || (((same = same_qstring(pl, cl)) != 0) + && (same != count_levels_qstring(cl))))) + cl = qs_remove_trailing_spaces(cl); + else + if (NewC > NewN){ + int agree = 0; + for (j = 0; (j < n) && (GLine[j] == NLine[j]); j++); + clength = j; + /* clength is the common length in which Gline and Nline agree */ + /* j < n means that they do not agree fully */ + /* GLine = " \tText" + NLine = " Text" */ + if(j == n) + agree++; + if (clength < n){ /* see if buf and nbuf are padded with spaces and tabs */ + for (i = clength; i < n && ISspace(NLine[i]); i++); + if (i == n){/* padded NLine until the end of spaces? */ + for (i = clength; i < c && ISspace(GLine[i]); i++); + if (i == c) /* Padded CLine until the end of spaces? */ + agree++; + } + } + if (agree){ + for (j = clength; j < c && ISspace(GLine[j]); j++); + if (j == c){ + /* + * If we get here, it means that the current line has the same + * quote string (visually) than the next line, but both of them + * are padded with different amount of TABS or spaces at the end. + * The current line (GLine) has more spaces/TABs than the next + * line. This is the typical situation that is found at the + * begining of a paragraph. We need to check this, however, by + * checking the previous line. This avoids that we confuse + * ourselves with being in the last line of a paragraph. + * Example when it should not free_qs(cl) + * " Text in Paragraph 1" (PLine) + * " Text in Paragraph 1" (GLine) + * " Other Paragraph Number 2" (NLine) + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) Text" (GLine) c = 5 + * ":) More text" (NLine) n = 3 + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) :) " (PLine) p = 6, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) > > > " (PLine) p = 13, j = 11 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * The following example is very interesting. The "Other Text" + * line below should free the quote string an make it equal to the + * quote string of the line below it, but any algorithm trying + * to advance past that line should make it stop there, so + * we need one more check, to check the raw quote string and the + * processed quote string at the same time. + * FREE qs in this example. + * " Some Text" (PLine) p = 3, j = 0 + * "\tOther Text" (GLine) c = 1 + * " More Text" (NLine) n = 3 + * + */ + for (j = 0; (j < p) && (GLine[j] == PLine[j]); j++); + if ((p != c || j != p) && NLine[n]) + if(!get_indent_raw_line(q, PLine, nbuf, NSTRING, p, plb) + || NewP + strlenis(nbuf) != NewC){ + free_qs(&cl); + free_qs(&pl); + return nl; + } + } + } + } + + free_qs(&nl); + free_qs(&pl); + + return cl; +} + +/* + * Given a line, an initial position, and a quote string, we advance the + * current line past the quote string, including arbitraty spaces + * contained in the line, except that it removes trailing spaces. We do + * not handle TABs, if any, contained in the quote string. At least not + * yet. + * + * Arguments: q - quote string + * l - a line to process + * i - position in the line to start processing. i = 0 is the + * begining of that line. + */ +int +advance_quote_string(char *q, char l[NSTRING], int i) +{ + int n = 0, j = 0, is = 0, es = 0; + int k, m, p, adv; + char qs[NSTRING] = {'\0'}; + if(!q || !*q) + return(0); + for (p = strlen(q); (p > 0) && (q[p - 1] == ' '); p--, es++); + if (!p){ /* string contains only spaces */ + for (k = 0; ISspace(l[i + k]); k++); + k -= k % es; + return k; + } + for (is = 0; ISspace(q[is]); is++); /* count initial spaces */ + for (m = 0 ; is + m < p ; m++) + qs[m] = q[is + m]; /* qs = quote string without any space at the end */ + /* advance as many spaces as there are at the begining */ + for (k = 0; ISspace(l[i + j]); k++, j++); + /* now find the visible string in the line */ + for (m = 0; qs[m] && l[i + j] == qs[m]; m++, j++); + if (!qs[m]){ /* no match */ + /* + * So far we have advanced at least "is" spaces, plus the visible + * string "qs". Now we need to advance the trailing number of + * spaces "es". If we can do that, we have found the quote string. + */ + for (p = 0; ISspace(l[i + j + p]); p++); + adv = advance_quote_string(q, l, i + j + ((p < es) ? p : es)); + n = ((p < es) ? 0 : es) + k + m + adv; + } + return n; +} + +/* + * This function returns the effective length in screen of the quote + * string. If the string contains a TAB character, it is added here, if + * not, the length returned is the length of the string + */ +int strlenis(char *qstr) +{ + int i, rv = 0; + for (i = 0; qstr && qstr[i]; i++) + rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1); + return rv; +} + +int +is_indent (char word[NSTRING], int plb) +{ + int i = 0, finished = 0, c, nxt, j, k, digit = 0, bdigits = -1, alpha = 0; + + if (!word || !word[0]) + return i; + + for (i = 0, j = 0; ISspace(word[i]); i++, j++); + while ((i < NSTRING - 2) && !finished){ + switch (c = now(word,i)){ + case NBSP: + case TAB : + case ' ' : for (; ISspace(word[i]); i++); + if (!is_indent_char(now(word,i))) + finished++; + break; + + case '+' : + case '.' : + case ']' : + case '*' : + case '}' : + case '-' : + case RPAREN: + nxt = next(word,i); + if ((c == '.' && allowed_after_period(nxt) && alpha) + || (c == '*' && allowed_after_star(nxt)) + || (c == '}' && allowed_after_braces(nxt)) + || (c == '-' && allowed_after_dash(nxt)) + || (c == '+' && allowed_after_dash(nxt)) + || (c == RPAREN && allowed_after_parenth(nxt)) + || (c == ']' && allowed_after_parenth(nxt))) + i++; + else + finished++; + break; + + default : if (is_a_digit(c) && plb){ + if (bdigits < 0) + bdigits = i; /* first digit */ + for (k = i; is_a_digit(now(word,k)); k++); + if (k - bdigits > 2){ /* more than 2 digits? */ + i = bdigits; /* too many! */ + finished++; + } + else{ + if(allowed_after_digit(now(word,k),word,k)){ + alpha++; + i = k; + } + else{ + i = bdigits; + finished++; + } + } + } + else + finished++; + break; + + } + } + if (i == j) + i = 0; /* there must be something more than spaces in an indent string */ + return i; +} + +int +get_indent_raw_line(char **q, char *GLine, char *buf, int buflen, int k, int plb) +{ + int i, j; + char testline[1024]; + + if(k > 0){ + for(j = 0; GLine[j] != '\0'; j++){ + testline[j] = GLine[j]; + testline[j+1] = '\0'; + if(strlenis(testline) >= strlenis(buf)) + break; + } + k = ++j; /* reset k */ + } + i = is_indent(GLine+k, plb); + + for (j = 0; j < i && j < buflen && (buf[j] = GLine[j + k]); j++); + buf[j] = '\0'; + + return i; +} + +/* support for remembering quote strings across messages */ +char **allowed_qstr = NULL; +int list_len = 0; + +void +free_allowed_qstr(void) +{ + int i; + char **q = allowed_qstr; + + if(q == NULL) + return; + + for(i = 0; i < list_len; i++) + fs_give((void **)&q[i]); + + fs_give((void **)q); + list_len = 0; +} + +void +add_allowed_qstr(void *q, int type) +{ + int i; + + if(allowed_qstr == NULL){ + allowed_qstr = malloc(sizeof(char *)); + list_len = 0; + } + + if(type == 0){ + allowed_qstr[list_len] = malloc((1+strlen((char *)q))*sizeof(char)); + strcpy(allowed_qstr[list_len], (char *)q); + } + else + allowed_qstr[list_len] = (char *) ucs4_to_utf8_cpystr((UCS *)q); + + fs_resize((void **)&allowed_qstr, (++list_len + 1)*sizeof(char *)); + allowed_qstr[list_len] = NULL; +} + +void +record_quote_string (QSTRING_S *qs) +{ + int i, j, k; + + for(; qs && qs->value; qs = qs->next){ + j = 0; + for (; ;){ + k = j; + for(i = 0; i < list_len; i++){ + j += advance_quote_string(allowed_qstr[i], qs->value, j); + for(; ISspace(qs->value[j]); j++); + } + if(k == j) + break; + } + if(qs->value[j] != '\0') + add_allowed_qstr((void *)(qs->value + j), 0); + } +} + +/* type utf8: code 0; ucs4: code 1. */ +char ** +default_qstr(void *q, int type) +{ + if(allowed_qstr == NULL) + add_allowed_qstr(q, type); + + return allowed_qstr; +} + diff --git a/pith/osdep/color.h b/pith/osdep/color.h index 32c86242..929389a2 100644 --- a/pith/osdep/color.h +++ b/pith/osdep/color.h @@ -17,6 +17,24 @@ #ifndef PITH_OSDEP_COLOR_INCLUDED #define PITH_OSDEP_COLOR_INCLUDED +/* + * struct that will help us determine what the quote string of a line + * is. The "next" field indicates the presence of a possible continuation. + * The idea is that if a continuation fails, we free it and check for the + * remaining structure left + */ + +typedef enum {qsNormal, qsString, qsWord, qsChar, qsGdb, qsProg, qsText} QStrType; + +typedef struct QSTRING { + char *value; /* possible quote string */ + QStrType qstype; /* type of quote string */ + struct QSTRING *next; /* possible continuation */ +} QSTRING_S; + +#define UCH(c) ((unsigned char) (c)) +#define NBSP UCH('\240') +#define ISspace(c) (UCH(c) == ' ' || UCH(c) == TAB || UCH(c) == NBSP) #define RGBLEN 11 #define MAXCOLORLEN 11 /* longest string a color can be */ @@ -93,6 +111,11 @@ char *pico_get_last_fg_color(void); char *pico_get_last_bg_color(void); char *color_to_canonical_name(char *); int pico_count_in_color_table(void); - +int is_indent(char *, int); +int get_indent_raw_line (char **, char *, char *, int, int, int); +int line_isblank(char **, char *, char *, char *, int); +int strlenis(char *); +int value_is_space(char *); +int advance_quote_string(char *, char *, int); #endif /* PITH_OSDEP_COLOR_INCLUDED */ diff --git a/pith/pattern.c b/pith/pattern.c index 84a32c41..9d09462a 100644 --- a/pith/pattern.c +++ b/pith/pattern.c @@ -1756,7 +1756,7 @@ parse_action_slash(char *str, ACTION_S *action) SortOrder def_sort; int def_sort_rev; - if(decode_sort(p, &def_sort, &def_sort_rev) != -1){ + if(decode_sort(p, &def_sort, &def_sort_rev, 0) != -1){ action->sort_is_set = 1; action->sortorder = def_sort; action->revsort = (def_sort_rev ? 1 : 0); @@ -5483,6 +5483,15 @@ match_pattern_folder_specific(PATTERN_S *folders, MAILSTREAM *stream, int flags) break; case '#': +#ifndef _WINDOWS + if(!struncmp(patfolder, "#md/", 4) + || !struncmp(patfolder, "#mc/", 4)){ + maildir_file_path(patfolder, tmp1, sizeof(tmp1)); + if(!strcmp(patfolder, stream->mailbox)) + match++; + break; + } +#endif if(!strcmp(patfolder, stream->mailbox)) match++; @@ -7903,7 +7912,7 @@ move_filtered_msgs(MAILSTREAM *stream, MSGNO_S *msgmap, char *dstfldr, int we_cancel = 0, width; CONTEXT_S *save_context = NULL; char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; - char *save_ref = NULL; + char *save_ref = NULL, *save_dstfldr = NULL, *save_dstfldr2 = NULL; #define FILTMSG_MAX 30 if(!stream) @@ -7937,6 +7946,16 @@ move_filtered_msgs(MAILSTREAM *stream, MSGNO_S *msgmap, char *dstfldr, if(F_OFF(F_QUELL_FILTER_MSGS, ps_global)) we_cancel = busy_cue(buf, NULL, 0); +#ifndef _WINDOWS + if(!struncmp(dstfldr, "#md/", 4) || !struncmp(dstfldr, "#mc/", 4)){ + char tmp1[MAILTMPLEN]; + maildir_file_path(dstfldr, tmp1, sizeof(tmp1)); + save_dstfldr2 = dstfldr; + save_dstfldr = cpystr(tmp1); + dstfldr = save_dstfldr; + } +#endif + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; @@ -8000,6 +8019,11 @@ move_filtered_msgs(MAILSTREAM *stream, MSGNO_S *msgmap, char *dstfldr, if(we_cancel) cancel_busy_cue(buf[0] ? 0 : -1); + if(save_dstfldr){ + fs_give((void **)&save_dstfldr); + dstfldr = save_dstfldr2; + } + return(buf[0] != '\0'); } diff --git a/pith/pine.hlp b/pith/pine.hlp index 82872f19..6fbe15ac 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -89,6 +89,7 @@ Where "variable" is one of either: ALPINE_VERSION ALPINE_REVISION ALPINE_COMPILE_DATE + ALPINE_PATCHLEVEL ALPINE_TODAYS_DATE C_CLIENT_VERSION _LOCAL_FULLNAME_ @@ -159,6 +160,14 @@ Version <!--#echo var="ALPINE_VERSION"-->(<!--#echo var="ALPINE_REVISION"-->) </DIV> <P> +This version was modified from its original source code. More information +about some of the patches applied to this version can be found <A HREF="h_patches">here</A>. +<!--chtml if pinemode="running"--> +The patch level of this version, including creation date of the patch is: +<!--#echo var=ALPINE_PATCHLEVEL-->. +<!--chtml endif--> + +<P> Alpine is an "Alternatively Licensed Program for Internet News and Email" produced until 2008 by the University of Washington. It is intended to be an easy-to-use program for @@ -198,7 +207,7 @@ message, as specified by original sender. Bugs that have been addressed include: <P> <UL> - <LI> Proper quote of shell characters in urls. + <LI> Do not use a shell to open a browser. <LI> Configure script did not test for crypto or pam libraries. <LI> Fix Cygwin separator to "/". <LI> Crash when a non-compliant SMTP server closes a connection without a QUIT command. @@ -3183,9 +3192,11 @@ There are also additional details on <li><a href="h_config_alt_role_menu">FEATURE: <!--#echo var="FEAT_alternate-role-menu"--></a> <li><a href="h_config_force_low_speed">FEATURE: <!--#echo var="FEAT_assume-slow-link"--></a> <li><a href="h_config_auto_read_msgs">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></a> +<li><a href="h_config_auto_read_msgs_rules">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs-using-rules"--></a> <li><a href="h_config_auto_open_unread">FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></a> <li><a href="h_config_auto_unselect">FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></a> <li><a href="h_config_auto_unzoom">FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></a> +<li><a href="h_config_circular_tab">FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></a> <li><a href="h_config_auto_zoom">FEATURE: <!--#echo var="FEAT_auto-zoom-after-select"--></a> <li><a href="h_config_use_boring_spinner">FEATURE: <!--#echo var="FEAT_busy-cue-spinner-only"--></a> <li><a href="h_config_check_mail_onquit">FEATURE: <!--#echo var="FEAT_check-newmail-when-quitting"--></a> @@ -3285,6 +3296,7 @@ There are also additional details on <li><a href="h_config_full_auto_expunge">FEATURE: <!--#echo var="FEAT_expunge-without-confirm-everywhere"--></a> <li><a href="h_config_no_fcc_attach">FEATURE: <!--#echo var="FEAT_fcc-without-attachments"--></a> <li><a href="h_config_force_arrow">FEATURE: <!--#echo var="FEAT_force-arrow-cursor"--></a> +<li><a href="h_config_ignore_size">FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></a> <li><a href="h_config_forward_as_attachment">FEATURE: <!--#echo var="FEAT_forward-as-attachment"--></a> <li><a href="h_config_preserve_field">FEATURE: <!--#echo var="FEAT_preserve-original-fields"--></a> <li><a href="h_config_quell_empty_dirs">FEATURE: <!--#echo var="FEAT_quell-empty-directories"--></a> @@ -3298,6 +3310,7 @@ There are also additional details on <li><a href="h_config_add_ldap">FEATURE: <!--#echo var="FEAT_ldap-result-to-addrbook-add"--></a> <li><a href="h_config_maildrops_preserve_state">FEATURE: <!--#echo var="FEAT_maildrops-preserve-state"--></a> <li><a href="h_config_mark_fcc_seen">FEATURE: <!--#echo var="FEAT_mark-fcc-seen"--></a> +<li><a href="h_config_mark_for_group">FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></a> <li><a href="h_config_mark_for_cc">FEATURE: <!--#echo var="FEAT_mark-for-cc"--></a> <li><a href="h_config_mulnews_as_typed">FEATURE: <!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></a> <li><a href="h_config_news_uses_recent">FEATURE: <!--#echo var="FEAT_news-approximates-new-status"--></a> @@ -3500,6 +3513,7 @@ There are also additional details on <li><a href="h_config_image_viewer">OPTION: <!--#echo var="VAR_image-viewer"--></a> <li><a href="h_config_inbox_path">OPTION: <!--#echo var="VAR_inbox-path"--></a> <li><a href="h_config_archived_folders">OPTION: <!--#echo var="VAR_incoming-archive-folders"--></a> +<li><a href="h_config_sleep">OPTION: <!--#echo var="VAR_sleep-interval-length"--></a> <li><a href="h_config_incoming_interv">OPTION: <!--#echo var="VAR_incoming-check-interval"--></a> <li><a href="h_config_incoming_second_interv">OPTION: <!--#echo var="VAR_incoming-check-interval-secondary"--></a> <li><a href="h_config_incoming_list">OPTION: <!--#echo var="VAR_incoming-check-list"--></a> @@ -3552,6 +3566,7 @@ There are also additional details on <li><a href="h_config_print_cat">OPTION: <!--#echo var="VAR_personal-print-category"--></a> <li><a href="h_config_print_command">OPTION: <!--#echo var="VAR_personal-print-command"--></a> <li><a href="h_config_post_char_set">OPTION: <!--#echo var="VAR_posting-character-set"--></a> +<li><a href="h_config_special_text_to_color">OPTION: <!--#echo var="VAR_h_config_special_text_to_color"--></a> <li><a href="h_config_postponed_folder">OPTION: <!--#echo var="VAR_postponed-folder"--></a> <li><a href="h_config_print_font_char_set">OPTION: Print-Font-Char-Set</a> <li><a href="h_config_print_font_name">OPTION: Print-Font-Name</a> @@ -3580,9 +3595,11 @@ There are also additional details on <li><a href="h_config_sending_filter">OPTION: <!--#echo var="VAR_sending-filters"--></a> <li><a href="h_config_sendmail_path">OPTION: <!--#echo var="VAR_sendmail-path"--></a> <li><a href="h_config_signature_color">OPTION: Signature Color</a> +<li><a href="h_config_special_text_color">OPTION: Special Text Color</a> <li><a href="h_config_signature_file">OPTION: <!--#echo var="VAR_signature-file"--></a> <li><a href="h_config_smtp_server">OPTION: <!--#echo var="VAR_smtp-server"--></a> <li><a href="h_config_sort_key">OPTION: <!--#echo var="VAR_sort-key"--></a> +<li><a href="h_config_thread_sort_key">OPTION: <!--#echo var="VAR_thread-sort-key"--></a> <li><a href="h_config_speller">OPTION: <!--#echo var="VAR_speller"--></a> <li><a href="h_config_sshcmd">OPTION: <!--#echo var="VAR_ssh-command"--></a> <li><a href="h_config_ssh_open_timeo">OPTION: <!--#echo var="VAR_ssh-open-timeout"--></a> @@ -4442,6 +4459,10 @@ thread: message in the thread was sent to you as a cc:. This symbol will only show up if the feature "<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>" is turned on (which is the default). + <LI> "." for messages that were sent to you as part of a group message, regardless + of if all addresses in the To: field are yours or not. This symbol will only show up if + the feature + "<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>" is turned on (which is the default). <LI> "X" for selected. You have selected at least one message in the thread by using the "select" command. (Some systems may optionally allow selected messages to be denoted by the index line being displayed in bold @@ -4601,6 +4622,10 @@ message: message was sent to you as a cc:. This symbol will only show up if the feature "<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>" is turned on (which is the default). + <LI> "." for messages that were sent to you as part of a group message, regardless + of if all addresses in the To: field are yours or not. This symbol will only show up if + the feature + "<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>" is turned on (which is the default). <LI> "X" for selected. You have selected the message by using the "select" command. (Some systems may optionally allow selected messages to be denoted by the index line being displayed in bold @@ -5515,6 +5540,163 @@ the names of the carbon copy addresses of the message. <End of help on this topic> </BODY> </HTML> +======= h_thread_index_sort_arrival ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Arrival</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Arrival</H1> + +The <EM>Arrival</EM> sort option arranges threads according to the last +time that a message was added to it. In this order the last thread +contains the most recent message in the folder. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_date ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Date</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Date</H1> + +The <EM>Date</EM> sort option in the THREAD INDEX screen sorts +threads by the date in which messages were sent. The thread containing the +last message in this order is displayed last. +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_subj ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Subject</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Subject</H1> + +The <EM>Subject</EM> sort option has not been defined yet. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_ordsubj ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: OrderedSubject</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: OrderedSubject</H1> + +The <EM>OrderedSubject</EM> sort option in the THREAD INDEX screen is +the same as sorting by <A HREF="h_thread_index_sort_subj">Subject</A>. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_thread ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Thread</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Thread</H1> + +The <EM>Thread</EM> sort option in the THREAD INDEX screen sorts all +messages by the proposed algorithm by Crispin and Murchison. In this +method of sorting once threads have been isolated they are sorted by the +date of their parents, or if that is missing, the first message in that +thread. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_from ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: From</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: From</H1> + +The <EM>From</EM> sort option has not been defined yet. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_size ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Size</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Size</H1> + +The <EM>Size</EM> sort option sorts threads by their size (the number +of messages in the thread). This could be used to find conversations +where no reply has been sent by any of the participants in the thread +(e.g. those whose length is equal to one). Longer threads appear +below shorter ones. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_score ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Score</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Score</H1> + +The <EM>Score</EM> sort option means that threads are sorted according to +the maximum score of a message in that thread. A thread all of whose +messages contain a smaller score than a message in some other thread is +placed in an earlier place in the list of messages for that folder; that +is, threads with the highest scores appear at the bottom of the index +list. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_to ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: To</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: To</H1> + +The <EM>To</EM> sort option has not been defined yet. + +<P> +<End of help on this topic> +</BODY> +</HTML> +======= h_thread_index_sort_cc ======= +<HTML> +<HEAD> +<TITLE>SORT OPTION: Cc</TITLE> +</HEAD> +<BODY> +<H1>SORT OPTION: Cc</H1> + +The <EM>Cc</EM> sort option has not been defined yet. + +<P> +<End of help on this topic> +</BODY> +</HTML> ======= h_index_cmd_whereis ======= <HTML> <HEAD> @@ -6738,6 +6920,46 @@ hold down the "Control" key on your keyboard. The second "^" "type the character ^". <P> +This version of Alpine contains an enhanced algorithm for justification, +which allows you to justify text that contains more complicated quote +strings. This algorithm is based on pragmatics, rather than on a theory, +and seems to work well with most messages. Below you will find technical +information on how this algorithm works. + +<P> +When justifying, Alpine goes through each line of the text and tries to +determine for each line what the quote string of that line is. The quote +string you provided is always recognized. Among other characters +recognized is ">". + +<P> +Some other constructions of quote strings are recognized only if they +appear enough in the text. For example "Peter :" is only +recognized if it appears in two consecutive lines. + +<P> +Additionaly, Alpine recognizes indent-strings and justifies text in a +paragraph to the right of indent-string, padding with spaces if necessary. +An indent string is one which you use to delimit elements of a list. For +example, if you were to write a list of groceries, one may write: + +<UL> +<LI> Fruit +<LI> Bread +<LI> Eggs +</UL> + +<P> +In this case the character "*" is the indent-string. Aline +recognizes numbers (0, 1, 2.5, etc) also as indent-strings, and certain +combinations of spaces, periods, and parenthesis. In any case, numbers are +recognized <B>ONLY</B> if the line preceeding the given line is empty or +ends in one of the characters "." or ":". +In addition to the explanation of what constitutes a paragraph above, a +new paragraph is recognized when an indent-string is found in it (and +validated according to the above stated rules). + +<P> <End of help on this topic> </BODY> </HTML> @@ -18300,6 +18522,7 @@ This set of special tokens may be used in the <A HREF="h_config_index_format">"<!--#echo var="VAR_index-format"-->"</A> option, in the <A HREF="h_config_reply_intro">"<!--#echo var="VAR_reply-leadin"-->"</A> option, in signature files, +in the <A HREF="h_config_reply_leadin_rules">"new-rules" option</A>, in template files used in <A HREF="h_rules_roles">"roles"</A>, and in the folder name that is the target of a Filter Rule. @@ -18312,7 +18535,7 @@ and in the target of Filter Rules. <P> <P> -<H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1> +<H1><EM>Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)</EM></H1> <DL> <DT>SUBJECT</DT> @@ -18345,6 +18568,15 @@ email address, never the personal name. For example, "mailbox@domain". </DD> +<DT>ADDRESSTO</DT> +<DD> +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". +</DD> + <DT>MAILBOX</DT> <DD> This is the same as the "ADDRESS" except that the @@ -18392,6 +18624,15 @@ are unavailable) of the persons specified in the message's "Cc:" header field. </DD> +<DT>ADDRESSCC</DT> +<DD> +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". +</DD> + <DT>RECIPS</DT> <DD> This token represents the personal names (or email addresses if the names @@ -18400,6 +18641,14 @@ message's "To:" header field and the message's "Cc:" header field. </DD> +<DT>ADDRESSRECIPS</DT> +<DD> +This token represent the e-mail addresses of the people in the To: and +Cc: fields, exactly in that order separated by a space. It is almost obtained +by concatenating the ADDRESSTO and ADDRESSCC tokens. +</DD> + + <DT>NEWSANDRECIPS</DT> <DD> This token represents the newsgroups from the @@ -18745,7 +18994,11 @@ aspects of the message's state. The first character is either blank, a '*' for message marked Important, or a '+' indicating a message addressed directly to you (as opposed to your having received it via a -mailing list, for example). +mailing list, for example). The symbol '.' will be used +for messages that were sent to you as part of a group message, regardless +of if all addresses in the To: field are yours or not. This symbol will only show up if +the feature +"<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>" is turned on (which is the default). When the feature "<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>" is set, if the first character would have been @@ -18881,6 +19134,14 @@ The progression of sizes used looks like: <P> </DD> +<DT>SIZETHREAD</DT> +<DD> +This token represents the total size of the thread for a collapsed thread +or the size of the branch for an expanded thread. The field is omitted for +messages that are not top of threads nor branches and it defaults to +the SIZE token when your folders is not sorted by thread. +</DD> + <DT>SIZENARROW</DT> <DD> This token represents the total size, in bytes, of the message. @@ -19496,6 +19757,78 @@ This is an end of line marker. </DL> <P> +<H1><EM>Tokens Available Only for New-Rules</EM></H1> + +<DL> +<DT>FOLDER</DT> +<DD> +Name of the folder where the rule will be applied +</DD> +</DL> + +<DL> +<DT>COLLECTION</DT> +<DD> +Name of the collection list where the rule will be applied. +</DD> +</DL> + +<DL> +<DT>ROLE</DT> +<DD> +Name of the Role used to reply a message. +</DD> +</DL> + +<DL> +<DT>BCC</DT> +<DD> +Not implemented yet, but it will be implemented in future versions. It will +be used for <A HREF="h_config_compose_rules">compose</A> +<A HREF="h_config_reply_rules">reply</A> +<A HREF="h_config_forward_rules">forward</A> +rules. +</DD> +</DL> + +<DL> +<DT>LCC</DT> +<DD> +This is the value of the Lcc: field at the moment that you start the composition. +</DD> +</DL> + +<DL> +<DT>FORWARDFROM</DT> +<DD> +This corresponds to the personal name (or address if there's no personal +name) of the person who sent the message that you are forwarding. +</DD> +</DL> + +<DL> +<DT>FORWARDADDRESS</DT> +<DD> +This is the address of the person that sent the message that you +are forwarding. +</DD> +</DL> + + + + +<DL> +<DT>FLAG</DT> +<DD> +A string containing the value of all the flags associated to a specific +message. The possible values of allowed flags are "*" for Important, "N" +for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for +answered and "D" for deleted. See an example of its use in the +<A HREF="h_config_new_rules">new rules</A> explanation and example help. +</DD> +</DL> + +<P> <H1><EM>Token Available Only for Templates and Signatures</EM></H1> <DL> @@ -20420,6 +20753,53 @@ give up and consider it a failed connection. <End of help on this topic> </BODY> </HTML> +====== h_config_sleep ====== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_sleep-interval-length"--> (UNIX Alpine only)</TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_sleep-interval-length"--> (UNIX Alpine only)</H1> + +<P> +When you open an attachment, Alpine goes through a list of viewers either +in your .mailcap file, or some other mailcap file in your system. The +normal behavior is that Alpine opens a local copy of the attachment, which +is removed from the system <B>after</B> the viewer has completed +displaying it. For example, if you open an attachment on a viewer and +later close the viewer, then control will return to the system and the +copy of the attachment will be removed from the system. This is the normal +behavior and has been accepted for years as the correct behavior. + +<P> +However, if an instance of the viewer is already open, the viewer may +return control to the system <B>before</B> it reads the copy of the +attachment. Given that Alpine removes the copy of the attachment after +control is returned to the system, this may cause Alpine to remove the +copy of the attachment <B>before</B> it is actually opened by the viewer. + +<P> +Since Alpine has no control over when a viewer returns from opening a file, +and viewers should not return before they read the file, Alpine has no control +over when the viewer will read the file and avoid the problem described above. + +<P> +The value of this option is the number of seconds that Alpine will wait +between the time that the viewer returns control to the system and the +when it will remove it from the system. You will not notice this +delay, since this will happen in the background. The default value is +0 which means that no delay will occur, and it is a value which may trigger +the problem described above with some viewers. By modifying the value of +this option you can set Alpine to wait longer. A reasonable small value is 5, +which works with all viewers tested to date. The maximum value is 120 +(2 minutes). +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_incoming_interv ====== <HTML> <HEAD> @@ -21484,6 +21864,102 @@ your account's home directory). <End of help on this topic> </BODY> </HTML> +====== h_config_maildir_location ====== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_maildir-location"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_maildir-location"--></H1> + +<P> +This option should be used only if you have a Maildir folder which you +want to use as your INBOX. If this is not your case (or don't know what +this is), you can safely ignore this option. + +<P> +This option overrides the default directory Pine uses to find the location of +your INBOX, in case this is in Maildir format. The default value of this +option is "Maildir", but in some systems, this directory could have been +renamed (e.g. to ".maildir"). If this is your case use this option to change +the default. + +<P> +The value of this option is prefixed with the "~/" string to determine the +full path to your INBOX. + +<P> +You should probably <A HREF="h_config_maildir">read</A> a few tips that +teach you how to configure your maildir for optimal performance. This +version also has <A HREF="h_config_courier_list">support</A> for the +Courier style file system when a maildir collection is accessed locally. + +<P><UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL> +<P> +<End of help on this topic> +</BODY> +</HTML> +====== h_config_maildir ===== +<HTML> +<HEAD> +<TITLE>Maildir Support</TITLE> +</HEAD> +<BODY> +<H1>Maildir Support</H1> + +This version of Alpine has been enhanced with Maildir support. This text is +intended to be a reference on its support. +<P> + +A Maildir folder is a directory that contains three directories called +cur, tmp and new. A program that delivers mail (e.g. postfix) will put new +mail in the new directory. A program that reads mail will look for for old +messages in the cur directory, while it will look for new mail in the new +directory. +<P> + +In order to use maildir support it is better to set your inbox-path to the +value "#md/inbox" (without quotes). This assumes that your mail +delivery agent is delivering new mail to ~/Maildir/new. If the directory +where new mail is being delivered is not called "Maildir", you can set the +name of the subdirectory of home where it is being delivered in the <A +HREF="h_config_maildir_location"><!--#echo var="VAR_maildir-location"--></A> configuration +variable. Most of the time you will not have to worry about the +<!--#echo var="VAR_maildirlocation"--> variable, because it will probably be set by your +administrator in the pine.conf configuration file. +<P> + +One of the advantages of the Maildir support of this version of Alpine is +that you do not have to stop using folders in another styles (mbox, mbx, +etc.). This is desirable since the usage of a specific mail storage system +is a personal decision. Folders in the maildir format that are part of the +Mail collection will be recognized without any extra configuration of your +part. If your mail/ collection is located under the mail/ directory, then +creating a new maildir folder in this collection is done by pressing "A" +and entering the string "#driver.md/mail/newfolder". Observe that adding a +new folder as "newfolder" may not create such folder in maildir format. + +<P> +If you would like to have all folders created in the maildir format by +default, you do so by adding a Maildir Collection. In order to convert +your current mail/ collection into a maildir collection, edit the +collection and change the path variable from "mail/" to +"#md/mail". In a maildir collection folders of any other format +are ignored. + +<P> Finally, This version also has +<A HREF="h_config_courier_list">support</A> for the Courier style file system +when a maildir collection is accessed locally. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_literal_sig ===== <HTML> <HEAD> @@ -22246,6 +22722,45 @@ command, then it will not be re-sorted until the next time it is opened. <End of help on this topic> </BODY> </HTML> +====== h_config_thread_sort_key ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE></H1> + +This option determines the order in which threads will be displayed. You +can choose from the options listed below. Each folder is sorted in one of +the sort orders displayed below first, then the thread containing the last +message of that sorted list is put at the end of the index. All messages +of that thread are "removed" from the sorted list and the +process is repeated with the remaining messages in that list. + +<P> +<UL> + <LI> <A HREF="h_thread_index_sort_arrival">Arrival</A> + <LI> <A HREF="h_thread_index_sort_date">Date</A> +<!-- <LI> <A HREF="h_thread_index_sort_subj">Subject</A> + <LI> <A HREF="h_thread_index_sort_ordsubj">OrderedSubj</A>--> + <LI> <A HREF="h_thread_index_sort_thread">Thread</A> +<!-- <LI> <A HREF="h_thread_index_sort_from">From</A> --> + <LI> <A HREF="h_thread_index_sort_size">Size</A> + <LI> <A HREF="h_thread_index_sort_score">Score</A> +<!-- <LI> <A HREF="h_thread_index_sort_to">To</A> + <LI> <A HREF="h_thread_index_sort_cc">Cc</A>--> +</UL> + +<P> Each type of sort may also be reversed. Normal default is by +"Thread". + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_other_startup ===== <HTML> <HEAD> @@ -22516,6 +23031,898 @@ character sets Alpine knows about by using the "T" ToCharsets command. <End of help on this topic> </BODY> </HTML> +====== h_config_procid ===== +<HTML> +<HEAD> +<TITLE>Token: PROCID</TITLE> +</HEAD> +<BODY> +<H1>TOKEN: PROCID explained</H1> + +<P> +The PROCID token is a way in which the user and the program can differentiate +between different parts of a program. It allows the user to tell the +program when to use a specific rule, and only use it at that specific +moment. + +<P> The normal way in which this is done is by adding a new configuration +variable. The idea behind the PROCID token is that instead of adding a new +configuration variable (which means the user has to go through more +configuration variables just to tune the program to his liking), we reuse +an old variable and let the user look inside that variable for the desired +behavior, which is actually set by setting the PROCID token. + +<P> +Consider the following examples for forward-rules: + +<P> +_ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +<P> +and + +<P> +_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +<P> +both are triggered by the same condition. Since both are configured in the +same variable, only one of them will be executed all the time (whichever +is first). Therefore in order to differentiate, we add a _PROCID_ token. +So, for example, the first example above will be executed only when we are +determining the subject. In this case, the following rule will accomplish +this task + +<P> +_PROCID_ == {fwd-subject} && _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +<P> +In this case, this rule will be tested fully only when we are determining +the subject line of a forwarded message, not otherwise. + +<P> +It is wise to add the _PROCID_ token as the first condition in a rule, so +that other conditions will not be tested in a long list of rules. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_compose_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_compose-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_compose-rules"--></H1> + +<P> At this time, this option is used to generate values for signature +files that is not possible to do with the use of +<A HREF="h_rules_roles">roles</A>. + +<P> For example, you can have a rule like:<BR> +_TO_ >> {Peter Flinstones} => _SIGNATURE_{~/.petersignature} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_forward_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_forward-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_forward-rules"--></H1> + +<P> This option has several uses. This feature uses the PROCID function +to identify different features of forwarding. You can read more about PROCID +by following <A HREF="h_config_procid">this link</A>. + +<P> If you want to edit the subject of a forwarded message, use the +PROCID <I>fwd-subject</I>. For example you could have a rule like + +<P> +_ROLE_ == {admin} && _SUBJECT_ !> {[tag] } => _COPY_{[tag] _SUBJECT_} + +<P> Another way in which this option can be used, is to trim the values of +some fields. For this application the PROCID is <I>fwd-lcc</I>. For +example it can be used in the following way: + +<P> +_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +<P> Other functions that can be used in this option are _EXEC_ and _REXTRIM_. + +<P> You can also use the _EXEC_ function. The documentation for this function +is in the +<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A> +help text. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_index_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_index-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_index-rules"--></H1> + +<P> This option is used to supersede the value of the option <A +HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> for specific folders. In +this form you can have different index-formats for different folders. For +example an entry here may be: + +<P> +_FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_pretty_command ===== +<HTML> +<HEAD> +<TITLE>Pretty-Command Explained</TITLE> +</HEAD> +<BODY> +<H1>Pretty Command Explained</H1> + +<P> This text explains how to encode keys so that they will be recognized +by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the +same way. For example, the key ~ is recognized by the same character. The +issue is how control, or functions keys are recognized. The internal code +is most times easy to find out. If the key you want to use is not already +recognized by Alpine simply press it. Alpine will print its code. For example, +the return key is not recognized in this screen, so if you press it, you +will see the following message. + +<P> [Command "RETURN" not defined for this screen. Use ? for help] + +<P> from here you can guess that the code for the return command is +RETURN. You can try other commands, like Control-C, the TAB key, F4, etc. +to see their codes. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_key_macro_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_key-definition-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_key-definition-rules"--></H1> + +<P> This option can be used to define macros, that is, to define a key that +when pressed executes a group of predetermined keystrokes. Since Alpine is +a menu driven program, sometimes the same key may have different meanings +in different screens, so a global redefinition of a key although possible +is not advisable. + +<P> <B>Always use the _SCREEN_ token as defined below.</B>. You have been +warned! + +<P> In each screen, every time you press a recognized key a command is +activated. In order to understand this feature, think of commands instead +of keystrokes. For example, you can think of the sort by thread command. +This command is associated to the keystrokes $ and h. You may want to +associate this command to a specific keystroke, like ~, so every time you +press the ~ key, Alpine understand the $ and h keystrokes, which activates +the sort by thread command. + +<P> Therefore, in order to use this option you must think of three +components. The screen where you will use the macro, the keystroke you +want to use and the set of keystrokes used by Alpine to accomplish the task +you want to accomplish. We will talk about these three components in what +follows. + +<P> First you must decide in which screen the macro will be used. This +feature is currently only available for the screen where your messages +are listed in index form (<A HREF="h_mail_index">MESSAGE INDEX</A>), +the screen where your message is displayed +(<A HREF="h_mail_view">MESSAGE TEXT</A>) and the screen where the list of +folders is displayed (<A HREF="h_folder_maint">FOLDER LIST</A>). The +internal names of these screens for this patch are "index", +"text" and +"folder" respectively. Please note that the internal names are +all in lowercase are are case sensitive. + +<P> In order to define the screen, you use the _SCREEN_ token, so for +example, you can write _SCREEN_ == {index}. + +<P> Second you must think of which key you will use to activate the macro. +Here you can use any key of your choice. The token you use to designate a +key is the _PKEY_ token (PKEY stands for "pressed key"). For +example you could use _PKEY_ == {~}, to designate the "~" +character to activate the command. Some keystrokes (like control, or +function keys) are encoded in special ways. You should read the +<A HREF="h_config_pretty_command">full explanation</A> on how to find +out the encoding for each keystroke. + +<P> Last, you must think of the list of keys you will use to accomplish +the task you want Alpine to perform. Say for example you want to have the +folder sorted by thread. That means you want Aline to execute the keys +"$" and "h". You use the _COMMAND_ function to specify +this. The syntax in this case is _COMMAND_{$,h}. + +<P> Observe that in the above example the different inputs are separated +by commas. This is the standard way in which the +<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A> command works from +the command line. Due to restrictions in the way Alpine works, a comma is a +special character, which when added to a configuration option like this +will cause the configuration to split into several lines in the +configuration screen. This has the effect of producing several +configuration options, all of which are incorrect. This is undesirable +because what you want is to have it all in one line. In order to force the +configuration into one line you must quote the comma. The best way to +accomplish this is by quoting the full definition of the rule. For +example. + +<P> +"_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}" + +<P> Another way to accomplish the same effect is by quoting the command and +not using quotes for the full command, nor commas to separate the +keystrokes in the command, for example + +<P> +_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{"$h"} + +<P> For more information on how to define the argument of the _COMMAND_ +token see the help of +<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A>. + +<P> Because the $ command can also be used as the first character in the +definition of an environemnt variable, no expansion of environment variables +is done when parsing this variable. The $ character does not need quoting +and quoting it will make Alpine fail to produce the correct result. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_replace_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_replace-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_replace-rules"--></H1> + +<P> This option is used to have Alpine print different values for specific +tokens in the <A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>. For example you +can replace strings like "To: newsgroup" by your name. + +<P> Here are examples of possible rules:<BR> +_FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} + +<P> or if you receive messages with tags that contain arbitrary numbers, and +you want them removed from the index (but not from the subject), use a rule +like the following<BR> +_FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} + +<P> You can also use this configuration option to remove specific strings of +the index display screen, so that you can trim unnecessary information in +your index, like the reply leadin string in the OPENINGTEXTNQ token of the index. +<BR> +_FOLDER_ == {mailing-list} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: } + +<P> or if you receive messages with tags that contain arbitrary numbers, and +you want them removed from the index (but not from the subject), use a rule +like the following<BR> + +<P> You can also use the _EXEC_ function. The documentation for this function +is in the +<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A> +help text. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_reply_leadin_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></H1> + +<P> This option is used to have Alpine generate a different +<A HREF="h_config_reply_intro"><!--#echo var="VAR_reply-leadin"--></A> string dependent either on +the person you are replying to, or the folder where the message is being +replied is in, or both. + +<P> Here there are examples of how this can be used. One can use the definition +below to post to newsgroups and the pine-info mailing list, say: +<P> +_FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} + +<P> Here there is an example that one can use to change the reply indent string +to reply people that speak spanish. +<P> +_FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_resub_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_reply-subject-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_reply-subject-rules"--></H1> + +<P> This option is used to have Alpine generate a different subject when +replying rather than the one Alpine would generate automatically. + +<P> Here there are a couple of examples about how to use this +configuration option: + +<P> In order to have messages with empty subject to be replied with the message +"your message" use the rule<BR> +<center>_SUBJECT_ == {} => _RESUB_{Re: your message}</center> + +<P> If you want to trim some parts of the subject when you reply use the +rule<BR> +<center>_SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}</center> + +<P>this rule removes the brackets "[" and "]" whenever the string "[one]" +appears in it, it also removes the word "two" from it. + +<P>Another example where you may want to use this rule is when you +correspond with people that change the reply string from "Re:" +to "AW:" or "Sv:". In this case a rule like<BR> +<center>_SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }</center> +<P> +would eliminate undesired strings in replies. + +<P> Another interesting use of this option is the use of the _EXEC_ function. +This function takes as an argument a program or a script. This program +must take as the input a file, and write its output to that file. For example, +below is a sample of a script that removes the letter "a" of a file. + +<PRE> +#!/bin/sh +sed 's/a//g' $1 > /tmp/mytest +mv /tmp/mytest $1 +</PRE> + +<P> +As you can see this script took "$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. + +<P> +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 + +<P> +_SUBJECT_ := _EXEC_{/path/to/script} + +<P> for the action. So for example + +<P> +_FOLDER_ := {sent-mail} => _SUBJECT_ := _EXEC_{/path/to/script} + +<P> is a valid rule. + +<P> You can also use this configuration option to customize reply subjects +according to the sender of the message. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> +</BODY> +</HTML> +====== h_config_sort_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_sort-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_sort-rules"--></H1> + +<P> This option is used to have Alpine sort different folders in different orders +and thus override the value already set in the +<A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A> configuration option. + +<P> Here's an example of the way it can be used. In this case all incoming +folders are mailing lists, except for INBOX, so we sort INBOX by arrival +(which is the default type of sort), but we want all the rest of mailing +lists and newsgroups to be sorted by thread. + +<P> +_COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} + +<P> Another example could be<BR> +_FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> + +</BODY> +</HTML> +====== h_config_save_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_save-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_save-rules"--></H1> + +<P> This option is used to specify which folder should be used to save a +message depending either on the folder the message is in, who the message +is from, or text that the message contains in specific headers (Cc:, +Subject:, etc). + +<P> If this option is set and the +<A HREF="h_config_auto_read_msgs"><!--#echo var="FEAT_auto-move-read-msgs"--></A> configuration +option is also enabled then these definitions will be used to move messages +from your INBOX when exiting Alpine. + +<P>Here there are some examples<BR> +_FLAG_ >> {D} -> Trash<BR> +_FROM_ == {U2} -> Bono<BR> +_FOLDER_ == {comp.mail.pine} -> pine-stuff<BR> +_NICK_ != {} -> _NICK_/_NICK_<BR> +_DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> + +</BODY> +</HTML> +====== h_config_reply_indent_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_reply-indent-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_reply-indent-rules"--></H1> + +<P> This option is used to specify which reply-indent-string is to be used +when replying to an e-mail. If none of the rules are successful, the result in +the variable <a href="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></a> +is used. + +<P> The associated function to this configuration option is called "RESTR" (for +REply STRing). Some examples of its use are:<BR> +_FROM_ == {Your Boss} => _RESTR_{"> "}<BR> +_FROM_ == {My Wife} => _RESTR_{":* "}<BR> +_FROM_ == {Perter Flinstone;Wilma Flinstone} => _RESTR_{"_INIT_ > "}<BR> + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> + +</BODY> +</HTML> +====== h_config_smtp_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_smtp-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_smtp-rules"--></H1> + +<P> This option is used to specify which SMTP server should be used when +sending a message, if this rule is not defined, or the execution of the rule +results in no server selected, then Alpine will look for +the value from the role that is being used to compose the message. If no smtp +server is defined in that role or you are not using a role, then Alpine will get +the name of the server from the +<A HREF="h_config_smtp_server">"<!--#echo var="VAR_smtp-server"-->"</A> configuration +option according to the rules used in that variable. + +<P> The function associated to this configuration option is _SMTP_, an example +of the use of this function is<BR> +_ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> + +</BODY> +</HTML> +====== h_config_startup_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_startup-rules"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_startup-rules"--></H1> + +<P> This option is used when a folder is being opened. You can use it to specify its <A +HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A> and override +Alpine's global value set for all folders. + +<P> An example of the usage of this option is:<BR> +_FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen} + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P><End of help on this topic> + +</BODY> +</HTML> +====== h_config_new_rules ===== +<HTML> +<HEAD> +<TITLE>OPTION: New Rules Explained</TITLE> +</HEAD> +<BODY> +<H1>OPTION: New Rules Explained</H1> + +This is a quite powerful option. Here you can define rules that override +the values of any other option you have set in Alpine. + +<P> + For example, you can set your folders to be sorted in a certain way when +you open them (say by Arrival). You may want, however, your newsgroups to +be sorted by thread. The set of "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. + +<P> + Every rule has three parts: a condition, a separator and an action. The +action is what will happen if the condition of the rule is satisified. + +<P> + Here is an example: + +<P> + _FROM_ == {Fred Flinstone} => _SAVE_{Fred} + +<P> + Here the separator is "=>". Whatever is to the left of the separator +is the condition (that is _FROM_ == {Fred Flinstone}) 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 +Flinstone", 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. + +<P> + 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. + +<P> + Now let us see how to do it. There are 13 functions already defined for +you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_, +_SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _THREADSTYLE and +_THREADINDEX_. The parameter of a function has to be enclosed between +"{" and "}", so for example you can specify +_SAVE_{saved-messages} as a valid sentence. + +<P> + Later in the document you will find examples. Here is a short +description of what each function does: + +<P> +<UL> +<LI> _EXEC_ : This function takes as an argument a program. This program +gets as the input a file and must rewrite its output to that file, which +is then taken as the value to replace from the contents of that file. You +can use this function with +<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>, +<A HREF="h_config_replace_rules"><!--#echo var="VAR_replace-rules"--></A> and +<A HREF="h_config_forward_rules"><!--#echo var="VAR_forward-rules"--></A>. +See the help of those options for examples of how to use this function +and configure these rules. +<BR> <BR> +<LI> _INDEX_ : This function takes as an argument an index-format, and +makes that the index-format for the specified folder. +<BR> <BR> +<LI> _REPLACE_ : This function replaces the subject/from of the given e-mail by +another subject/from only when displaying the index. +<BR> <BR> +<LI> _REPLY_ : This function takes as an argument a definition of a +reply-leadin-string and makes this the reply-leading-string of the +specified folder or person. +<BR> <BR> +<LI> _RESTR_ : This function takes as an argument the value of the +reply-indent-string to be used to answer the message being replied to. +<BR> <BR> +<LI> _RESUB_ : This function replaces the subject of the given e-mail by +another subject only when replying to a message. +<BR> <BR> +<LI> _SAVE_ : The save function takes as an argument the name of a +possibly non existing folder, whenever you want to save a message, that +folder will be offered for you to save. +<BR> <BR> +<LI> _SIGNATURE_ : This function takes as an argument a signature file and +uses that file as the signature for the message you are about to +compose/reply/forward. +<BR> <BR> +<LI> _SMTP_ : This function takes as an argument the definition of a +SMTP server. +<BR> <BR> +<LI> _SORT_ : This function takes as an argument a Sort Style, and sorts a +specified folder in that sort order. +<BR> <BR> +<LI> _TRIM_ : This function takes as an argument a list of strings that +you want removed from another string. At this time this only works for +_FROM_ and _SUBJECT_. +<BR> <BR> +<LI> _REXTRIM_ : Same as _TRIM_ but its argument is one and +only one extended regular expression. +<BR> <BR> +<LI> _STARTUP_ : This function takes as an argument an +incoming-startup-rule, and open an specified folder using that rule. +<BR> <BR> +<LI> _THREADSTYLE_ : This function takes as an argument a +threading-display-style and uses it to display threads in a folder. +<BR> <BR> +<LI> _THREADINDEX_ : This function takes as an argument a +threading-index-style and uses it to display threads in a folder. +</UL> + +<P> +You must me wondering how to define the person/folder over who to apply +the action. This is done in the condition. When you specify a rule, the +rule is only executed if the condition is satisfied. In another words for +the rule: + +<P> + _FROM_ == {Fred Flinstone} => _SAVE_{Fred} + +<P> it will only be applied if the from is "Fred Flinstone". If +the From is "Wilma Flinstone" the rule will be skipped. + +<P> In order to test a condition you can use the following tokens (in +alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE, +_SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what +it is between "{" and "}" in the condition, this part +of the condition is called the "condition set". The definition +of each token can be found <A HREF="h_index_tokens">here</A>. + +<P> A special testing token called _PROCID_ can be used to differentiate +inside a rule, between two rules that are triggered by the same condition. +A full explanation of the _PROCID_ token can be found in +<A HREF="h_config_procid">this link</A>. + +<P> There are two more tokens related to the option +<A HREF="h_config_key_macro_rules">key-definition-rules</A>. Those tokens +are only specific to that option, and hence are not explained here. + +<P> You can also test in different ways, you can use the following +"test operands": <<, !<, >>, !>, == and !=. +All of them are two strings long. Here is the meaning of them: + +<P> +<UL> +<LI> << : 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. +<LI> >> : 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 Flinstone" or "Fred P. Flinstone" or "Freddy". +<LI> == : It tests if the value of the token is exactly equal to the value +of the set condition. For example _NICK_ == {Fred} will be false if the value +of _NICK_ is "Freddy" or "red". +<LI> !< : This is true only when << is false and viceversa. +<LI> !> : This is true only when >> is false and viceversa. +<LI> != : This is true only when == is false and viceversa. +</UL> + +<P> + Now let us say that you want the same action to be applied to more than +one person or folder, say you want "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. + +<P> + _FOLDER_ << {folder1; folder2} => _SORT_{OrderedSubj} + +<P> + Here is the first subtelty about these definitions. Notice that the +following rule: + +<P> + _FOLDER_ == {folder1; folder2} => _SORT_{Reverse OrderedSubj} + +<P> 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 <BR> +"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. + +<P> + Here are a few examples of what we have talked about before. + +<P> +_NICK_ == {lisa;kika} => _SAVE_{_NICK_/_NICK_} <BR> +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" + +<P> +_FOLDER_ == {Lynx} -> lynx <BR> +This, is an abreviation of the following rule:<BR> +_FOLDER_ == {Lynx} => _SAVE_{lynx} <BR> +(note the change in separator from "=>" to "->"). In the future +I will use that abreviation. + +<P> _FOLDER_ << {comp.mail.pine; pine-info; pine-alpha} -> pine <BR> +Any message in the folders "comp.mail.pine", "pine-info" or "pine-alpha" +will be saved to the folder "pine". + +<P> _FROM_ << {Pine Master} -> pine <BR> +Any message whose From field contains +"Pine Master" will be saved in the folder pine. + +<P> _FOLDER_ << {Lynx; pine-info; comp.mail.pine} => +_INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)} <BR> Use a +different index-format for the folders "Lynx", "pine-info" and +"comp.mail.pine", where the size is not present. + +<P> _FOLDER_ == {Lynx;pine-info} => _REPLY_{*** _FROM_ (_ADDRESS_) +wrote in the _FOLDER_ list _SMARTDATE_("Today" "today" "on +_LONGDATE_"):}<BR> 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: + +<P> +*** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today: + +<P> +However replying for a message in the Lynx list would look: + +<P> +*** mattack@area.com (mattack@area.com) wrote in the Lynx list today: + +<P> +If you write in more than one language you can use this feature to create +Reply-leadin-strings in different languages. + +<P> Note that at least for people you can create particular +reply-leadin-string using the role features, but it does not work as this +one does. This seems to be the right way to do it. + +<P> _FOLDER_ << {Lynx; comp.mail.pine; pine_info; pine-alpha} => +_SORT_{OrderedSubj}<BR> 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). + +<P> The last examples use the function _TRIM_ which has a special form. +This function can only be used in the index list. + +<P> _FOLDER_ << {Lynx} => _SUBJECT_ := _TRIM_{lynx-dev }<BR> 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. + +<P> _FROM_ >> {Name (Comment)} => _FROM_ := +_TRIM_{ (Comment)}<BR> Remove the part " (Comment)" +from the _FROM_, so when displaying in the index the real From "Name" +will appear. + +<P> _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. + +<P> You can add more complexity to your rules by checking more than one +conditions before a rule is executed. For example: Assume that you want to +answer every email that contains the string "bug report", with the subject +"Re: About your bug report", you could make + +<P> +_SUBJECT_ == {bug report} => _RESUB_{Re: About your _SUBJECT_} + +<P> The problem with this construction is that if the person emails you +back, then the next time you answer the message the subject will be: "Re: +About your Re: About your bug report", so it grew. You may want to avoid +this growth by using the following rule: + +<P> +_SUBJECT_ >> {bug report} && _SUBJECT_ !> {Re: } => _RESUB_{Re: About your _SUBJECT_}<BR> + +<P> +which will only add the string "Re: About your" only the first time the +message is replied. + +<P> + Say your personal name is "Fred Flinstones", and assume that you don't +like to see "To: comp.mail.pine" in every post you make to this newsgroup, +but instead would like to see it as everyone else sees it. <BR> +_FOLDER_ == {comp.mail.pine} && _FROM_ == {Fred Flinstones} => _FROM_ := _REPLACE_{_FROM_} + +<P> + You can also list your index by nick, in the following way:<BR> +_NICK_ != {} => _FROM_ := _REPLACE_{_NICK_} + +<P> + If you want to open the folder "pine-info" in the first non-read message +use the rule:<BR> +_FOLDER_ == {pine-info} => _STARTUP_{first-unseen} + +<P> + If you want to move your deleted messages to a folder, called "Trash", use +the following rule:<BR> +_FLAG_ >> {D} -> Trash + +<P> +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. + +<P> If you want to use a specific signature when you are in a specific collection +use the following rule:<BR> +_COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature} + +<P> Finally about the question of which rule will be executed. Only the +first rule that matches will be executed. It is important to notice though +that "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. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_char_set ===== <HTML> <HEAD> @@ -22827,6 +24234,43 @@ That won't work because spell works in a different way. <End of help on this topic> </BODY> </HTML> +====== h_config_special_text_to_color ===== +<HTML> +<HEAD> +<TITLE>OPTION: <!--#echo var="VAR_special-text-color"--></TITLE> +</HEAD> +<BODY> +<H1>OPTION: <!--#echo var="VAR_special-text-color"--></H1> + +Use this option to enter patterns (text or regular expressions) that +Alpine will highlight in the body of the text that is not part of a handle +(an internal or external link that Alpine paints in a different color). + +<P> +Enter each pattern in a different line. Pine will internally merge these +patterns (by adding a "|" character), or you can add them all in one line +by separating them by a "|" character. There is only a <A +HREF="h_regex_text">set</A> of regular expressions that are matched. + +<P> +Pine will use the colors defined in the +<A HREF="h_config_special_text_color">Special Text Color</A> variable. +to paint any match. + +<P> +If the Special Text Color is not set, setting this variable will not +cause that special text to be indicated in any special way. It will look +like any normal text. You must set those colors in order to make Pine +paint the screen differently when it finds the patterns specified in this +variable. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_display_filters ===== <HTML> <HEAD> @@ -22991,6 +24435,12 @@ specified. Command Modifying Tokens: <DL> +<DT>_ADDRESS_</DT> +<DD>When the command is executed, this token is replaced +with the address of the person sending the message in the format +mailbox@host. +</DD> + <DT>_RECIPIENTS_</DT> <DD>When the command is executed, this token is replaced with the space delimited list of recipients of the @@ -26103,6 +27553,76 @@ the From field is used to show the relationships instead of the Subject field. <End of help on this topic> </BODY> </HTML> +====== h_config_thread_display_style_rule ===== +<HTML> +<HEAD> +<TITLE>OPTION: Threading-Display-Style-Rule</TITLE> +</HEAD> +<BODY> +<H1>OPTION: Threading-Display-Style-Rule</H1> + +This option is very similar to <A HREF="h_config_thread_disp_style"> +<!--#echo var="VAR_threading-display-style"--></A>, but it is a rule which specifies the +display styles for a thread that you want displayed in a specific +folder or collection. +<P> +The token to be used in this function is _THREADSTYLE_. Here there is +an example of its use +<P> +_FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like} +<P> +The values that can be given for the _THREADSTYLE_ function are the +values of the threading-display-style function, which can be found +listed in the <A HREF="h_config_thread_disp_style">threading-display-style</A> +configuration option. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> +====== h_config_thread_index_style_rule ===== +<HTML> +<HEAD> +<TITLE>OPTION: Threading-Index-Style-Rule</TITLE> +</HEAD> +<BODY> +<H1>OPTION: Threading-Index-Style-Rule</H1> + +This option is very similar to <A HREF="h_config_thread_index_style"> +<!--#echo var="VAR_threading-index-style"--></A>, but it is a rule which specifies the +index styles for a thread that you want displayed in a specific +folder or collection. +<P> +The token to be used in this function is _THREADINDEX_. Here there is +an example of its use +<P> +_FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads} +<P> +The values that can be given for the _THREADINDEX_ function are the +values of the threading-index-display function, which can be found +listed in the <A HREF="h_config_thread_index_style"><!--#echo var="VAR_threading-index-style"--></A> +configuration option. + +<P> This configuration option is just one of many that allow you to +override the value of some global configurations within Alpine. There is a +help text explaining how to define all of them, which you can read by +following this <A HREF="h_config_new_rules">link</A>. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_pruning_rule ===== <HTML> <HEAD> @@ -28009,6 +29529,22 @@ See also <A HREF="h_config_allow_chg_from">"<!--#echo var="FEAT_allow-chang <End of help on this topic> </BODY> </HTML> +====== h_config_use_domain ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_return-path-uses-domain-name"--> </TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_return-path-uses-domain-name"--></H1> + +If you enable this configuration option Pine will use your domain name and your +username in that domain name to construct your Return-Path header, if not Pine +will use the address that you have set in the From: field to construct it. + +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_use_sender_not_x ===== <HTML> <HEAD> @@ -28605,6 +30141,71 @@ of flowed text. <End of help on this topic> </BODY> </HTML> +====== h_config_alt_reply_menu ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--></H1> + +This feature controls the menu that is displayed when Reply is selected. +If set, a list of options will be presented, with each option representing +the type of composition that could be used. This feature is most useful +for users who want to avoid being prompted with each option separately, or +would like to override some defaults set in your configuration for the +message that you are replying (e.g. you may have set the option to strip +signatures, but for the message you are answering you would like not to do +that) + +<P> +The way this feature works is as follows. Initially you get the question +if you want to include the message, and in the menu you will see several +options, each option is accompanied by some text explaining what will +happen if you press the associated command. For example, if you read the +text "S Strip Sig", it means that if you press the letter +"S" the signature will be stripped off the message you are +replying. Observer that the menu will change to +"S No Strip", which means that if you press "S", the +signature will not be stripped off from the message. Your choices are +activated when you press RETURN. + +<P> +Another way to remember what Pine will do, is that what will be done is +exactly the opposite of what you read in the menu. + +<P> +The possible options are: + +<OL> +<LI> A: This determines if Pine will include or not the attachments sent to +you in the message that you are replying. By default Pine will use the value +of the configuration option +<A HREF="h_config_attach_in_reply"><!--#echo var="FEAT_include-attachments-in-reply"--></A>, but +you can use this option to override such behavior in a per message basis. + +<LI> F: To decide if you want to send flowed text or not. This option appears +unless you have quelled sending flowed text. + +<LI> H: This option determines if the headers of a message are to be +included in the body of the message that is being replied. By default Pine +will use the value of the configuration option +<A HREF="h_config_include_header"><!--#echo var="FEAT_include-header-in-reply"--></A>, but +you can use this option to override such behavior in a per message basis. + +<LI> R: To set a role, if you do not want Pine to set one automatically for you +or would like to set one when you can not select any. + +<LI> S: To strip the signature from a message, only available is the feature + <a href="h_config_sigdashes"><!--#echo var="FEAT_enable-sigdashes"--></a> or the +<a href="h_config_strip_sigdashes"><!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></a> option are +enabled. + +</OL> +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_del_from_dot ===== <HTML> <HEAD> @@ -29128,6 +30729,38 @@ Ctrl-B key can be used to select the previous web hostnames in the same way. <End of help on this topic> </BODY> </HTML> +====== h_config_enable_long_url ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-long-url"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-long-url"--></H1> + +This feature modifies the behavior of Alpine's MESSAGE TEXT screen. When this feature +is set alpine will attempt to recognize long urls (those that spread over several +lines in the text) for the HTTP protocol, even when they have not been enclosed between +delimiters "<" and ">". + +<P>The normal behavior in Alpine is that if a URL is preceeded by the "<" +character and this URL was not finished before the end of the line, then a +continuation of the URL is searched in the following line(s). Normally, this type of +URLs will be ended by the ">" character, and if it is not, there is a +possibility of including erroneous text into the URL. + +<P>Enabling this feature will make Alpine search for a continuation of certain URLs in +lines following its location. This will be of great help most times, but in some cases +the algorithm will catch some text into the URL that is not part of the URL. + +<P>If you find that Alpine failed to recognize correctly a URL simply edit the URL before +passing it to your browser. + +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_enable_view_addresses ===== <HTML> <HEAD> @@ -29162,6 +30795,27 @@ Ctrl-B key can be used to select the previous web hostnames in the same way. <End of help on this topic> </BODY> </HTML> +====== h_config_circular_tab ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></H1> + +<P> +This Feature is like +<A HREF="h_config_auto_open_unread">"<!--#echo var="FEAT_auto-open-next-unread"-->"</A>, +in the sense that you can use TAB to browse through all of your Incoming +Folders checking for new mail. Once it gets to the last folder of the +collection it goes back to check again until it returns to the original +folder where it started. +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_enable_view_arrows ===== <HTML> <HEAD> @@ -29435,6 +31089,49 @@ than across the columns as is the default. <End of help on this topic> </BODY> </HTML> +====== h_config_courier_list ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_courier-folder-list"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_courier-folder-list"--></H1> + +In a maildir collection, a folder could be used as a directory to store +folders. In the Courier server if you create a folder, then a directory +with the same name is created. If you use this patch to access a +collection created by the Courier server, then the display of such +collection will look confusing. The best way to access a maildir +collection created by the Courier server is by using the "#mc/" +prefix instead of the "#md/" prefix. If you use this alternate +prefix, then this feature applies to you, otherwise you can safely ignore +the text that follows. +<P> +Depending on if you have enabled the option +<a href="h_config_separate_fold_dir_view"><!--#echo var="FEAT_separate-folder-and-directory-entries"--></a> +a folder may be listed as "folder[.]", or as two entries in the +list by "folder" and "folder.". +<P> +If this option is disabled, Pine will list local folders that are in Courier +style format, as "folder", and those that are also directories as +"folder[.]". This makes the default display cleaner. +<P> +If this feature is enabled then creating folders in a maildir collection +will create a directory with the same name. If this feature is disabled, then +a folder is considered a directory only if it contains subfolders, so you can +not create a directory with the same name as an exisiting folder unless +you create a subfolder of that folder first (e.g. if you have a folder +called "foo" simply add "foo.bar" directly. This will +create the directory "foo" and the subfolder "bar" of it). +<P> +Observe that this feature works only for maildir collections that are accessed +locally. If a collection is accessed remotely then this feature has no value, +as the report is created in a server, and Pine only reports what received +from the server in this case. +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_verbose_post ===== <HTML> <HEAD> @@ -29589,6 +31286,29 @@ them as deleted in the INBOX. Messages in the INBOX marked with an <End of help on this topic> </BODY> </HTML> +====== h_config_auto_read_msgs_rules ===== +<HTML> +<HEAD> +<TITLE>FEATURE: auto-move-read-msgs-using-rules</TITLE> +</HEAD> +<BODY> +<H1>FEATURE: auto-move-read-msgs-using-rules</H1> +This feature controls an aspect of Alpine's behavior upon quitting. If set, +and the +<A HREF="h_config_read_message_folder">"<!--#echo var="VAR_read-message-folder"-->"</A> +option is also set, then Alpine will automatically transfer all read +messages to the designated folder using the rules that you have defined in +your +<A HREF="h_config_save_rules">"<!--#echo var="VAR_save-rules"-->"</A> and mark +them as deleted in the INBOX. Messages in the INBOX marked with an +"N" (meaning New, or unseen) are not affected. +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_auto_fcc_only ===== <HTML> <HEAD> @@ -30039,6 +31759,23 @@ Reply Use, Forward Use, and Compose Use. <End of help on this topic> </BODY> </HTML> +====== h_config_enhanced_thread ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></H1> + +If this option is set certain commands in Pine will operate in loose +threads too. For example, the command ^R marks a thread deleted, but if +this feature is set, it will remove all threads that share the same missing +parent with this thread. + +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_news_cross_deletes ===== <HTML> <HEAD> @@ -30517,6 +32254,40 @@ but that is not implemented. <End of help on this topic> </BODY> </HTML> +====== h_config_ignore_size ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></H1> + +When you have an account residing in an IMAP server, Alpine gets the size of +each message from the server. However, when Alpine saves a message residing +in an IMAP server, Alpine computes the size of the message independently. If +these two numbers do not match for a message, Alpine asks you if you still +want to take the risk of saving the message, since data corruption or loss +of data could result of this save. + +<P> +Sometimes the root of this problem is that the server is defective, and +there will not be loss of information when saving such message. Enabling +this feature will make Aline ignore such error and continue saving the +message. If you can determine that this is the case, enable this feature +so that the saving operation will succeed. An example of a defective server +is the Gmail IMAP server. Another example is some versions of the Exchange +server. + +<P> +It is recommended that this feature be disabled most of the time and only +enabled when you find a server which you can determine that has the above +mentioned defect, but be disabled again after making this operation +succeed. + +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_force_low_speed ===== <HTML> <HEAD> @@ -31204,6 +32975,30 @@ to see the available Editing and Navigation commands. <End of help on this topic> </BODY> </HTML> +====== h_config_special_text_color ===== +<HTML> +<HEAD> +<TITLE>OPTION: Special Text Color</TITLE> +</HEAD> +<BODY> +<H1>OPTION: Special Text Color</H1> + +Sets the color Pine uses for coloring any text in the body of the message +that is not part of a handle (and internal or external link that Pine +paints in a different color). By default, this variable is not defined, +which means that text that matches the pattern is not painted in any +particular way. This variable must be set in a special form if you +want text to be painted. + +<P> +<A HREF="h_color_setup">Descriptions of the available commands</A> +<P> +Look <A HREF="h_edit_nav_cmds">here</A> +to see the available Editing and Navigation commands. +<P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_index_arrow_color ===== <HTML> <HEAD> @@ -33013,6 +34808,28 @@ messages. <End of help on this topic> </BODY> </HTML> +====== h_config_mark_for_group ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></H1> + +This feature affects Alpine's MESSAGE INDEX display. +By default, a '+' is displayed in the first column if the +message is addressed directly to you. +When this feature is set and the message is addressed to you as part of a group message +(that is, your address appears in the To: field, but there is more than one recipient), then a +'.' character is displayed instead. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ====== h_config_mark_for_cc ===== <HTML> <HEAD> @@ -33022,7 +34839,7 @@ messages. <H1>FEATURE: <!--#echo var="FEAT_mark-for-cc"--></H1> This feature affects Alpine's MESSAGE INDEX display. -By default, a '+' is displayed in the first column if the +By default, a '+' or a '.' is displayed in the first column if the message is addressed directly to you. When this feature is set and the message is not addressed to you, then a '-' character is displayed if the message is instead Cc'd directly diff --git a/pith/pineelt.h b/pith/pineelt.h index e44ae37a..d88f5fb9 100644 --- a/pith/pineelt.h +++ b/pith/pineelt.h @@ -40,6 +40,7 @@ typedef struct pine_elt { PINETHRD_S *pthrd; PARTEX_S *exceptions; ICE_S *ice; + char *firsttext; /* per-message pine state bits */ unsigned int hidden:1; unsigned int excluded:1; diff --git a/pith/reply.c b/pith/reply.c index 3445097f..348ebf26 100644 --- a/pith/reply.c +++ b/pith/reply.c @@ -46,6 +46,8 @@ static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washingto #include "../pith/ablookup.h" #include "../pith/mailcmd.h" #include "../pith/margin.h" +#include "../pith/copyaddr.h" +#include "../pith/rules.h" /* @@ -814,8 +816,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]; + + { 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, ps_global->VAR_REPLY_STRING, sizeof(buf)-1); + strncpy(buf, reply_string, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* set up the prefix to quote included text */ @@ -867,10 +888,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))); } @@ -972,7 +1012,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, if(!orig_body || orig_body->type == TYPETEXT || reply_raw_body - || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){ + || !ps_global->reply.attach){ char *charset = NULL; /*------ Simple text-only message ----*/ @@ -980,7 +1020,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, body->type = TYPETEXT; body->contents.text.data = msgtext; reply_delimiter(env, role, pc); - if(F_ON(F_INCLUDE_HEADER, ps_global)) + if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); @@ -1038,7 +1078,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, if(reply_body_text(orig_body, &tmp_body)){ reply_delimiter(env, role, pc); - if(F_ON(F_INCLUDE_HEADER, ps_global)) + if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); @@ -1076,7 +1116,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, body->nested.part->body.subtype = cpystr("Plain"); } reply_delimiter(env, role, pc); - if(F_ON(F_INCLUDE_HEADER, ps_global)) + if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); @@ -1099,7 +1139,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, int partnum; reply_delimiter(env, role, pc); - if(F_ON(F_INCLUDE_HEADER, ps_global)) + if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); @@ -1334,6 +1374,10 @@ get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen) buf[0] = '\0'; switch(type){ + case iFfrom: + addr = env && env->sparep ? env->sparep : NULL; + break; + case iFrom: addr = env ? env->from : NULL; break; @@ -1719,21 +1763,150 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size 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'; - } - 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 iNick: + { + ADDRESS *tmp_adr = NULL; + if (env){ + tmp_adr = env->from ? copyaddr(env->from) + : env->sender ? copyaddr(env->sender) : NULL; + get_nickname_from_addr(tmp_adr,buf,maxlen); + mail_free_address(&tmp_adr); + } + } + break; + + 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) + : ((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)){ @@ -1762,6 +1935,11 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size 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); @@ -1811,6 +1989,14 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size break; + case iOpeningText: + case iOpeningTextNQ: + if(env && env->sparep){ + strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen); + buf[maxlen] = '\0'; + } + break; + case iSubject: if(env && env->subject){ size_t n, len; @@ -1869,7 +2055,18 @@ reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc) 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)){ @@ -2128,6 +2325,7 @@ forward_subject(ENVELOPE *env, int flags) { size_t l; char *p, buftmp[MAILTMPLEN]; + RULE_RESULT *rule; if(!env) return(NULL); @@ -2135,9 +2333,20 @@ 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)){ + sprintf(buftmp, "%.200s", rule->result); + if(rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + else if(env->subject) + sprintf(buftmp, "%.200s", 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) @@ -2638,9 +2847,12 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, * tied our hands, alter the prefix to continue flowed * formatting... */ - if(flow_res) + if(flow_res && !ps_global->reply.no_send_flowed) wrapflags |= GFW_FLOW_RESULT; + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, NULL); + filters[filtcnt].filter = gf_wrap; /* * The 80 will cause longer lines than what is likely @@ -2674,9 +2886,9 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, * We also want to fold "> " quotes so we get the * attributions correct. */ - if(flow_res && prefix && !strucmp(prefix, "> ")) + if(flow_res && !ps_global->reply.no_send_flowed && prefix && !strucmp(prefix, "> ")) *(prefix_p = prefix + 1) = '\0'; - + ps_global->reply.no_send_flowed = 0; /* reset for next call */ if(!(wrapflags & GFW_FLOWED) && flow_res){ filters[filtcnt].filter = gf_line_test; @@ -2709,9 +2921,7 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, } if(prefix){ - if(ps_global->full_header != 2 - && (F_ON(F_ENABLE_SIGDASHES, ps_global) - || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){ + if(ps_global->reply.strip){ dashdata = 0; filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata); @@ -2736,7 +2946,7 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, dq.do_color = 0; dq.delete_all = 1; - filters[filtcnt].filter = gf_line_test; + filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } diff --git a/pith/save.c b/pith/save.c index 957e163b..22cfa4a6 100644 --- a/pith/save.c +++ b/pith/save.c @@ -954,7 +954,7 @@ save(struct pine *state, MAILSTREAM *stream, CONTEXT_S *context, char *folder, *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) @@ -1157,6 +1157,7 @@ long save_fetch_append_cb(MAILSTREAM *stream, void *data, char **flags, snprintf(buf, sizeof(buf), "Message to save shrank: source msg # %ld may be saved incorrectly", mn_raw2m(pkg->msgmap, raw)); + if(F_OFF(F_IGNORE_SIZE, ps_global)) q_status_message(SM_ORDER, 0, 3, buf); } else{ diff --git a/pith/send.c b/pith/send.c index a0c60439..3cca3365 100644 --- a/pith/send.c +++ b/pith/send.c @@ -44,6 +44,7 @@ static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington #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" @@ -53,7 +54,7 @@ static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington /* name::type::canedit::writehdr::localcopy::rcptto */ PINEFIELD pf_template[] = { {"X-Auth-Received", FreeText, 0, 1, 1, 0}, /* N_AUTHRCVD */ - {"From", Address, 0, 1, 1, 0}, + {"From", Address, 1, 1, 1, 0}, {"Reply-To", Address, 0, 1, 1, 0}, {TONAME, Address, 1, 1, 1, 1}, {CCNAME, Address, 1, 1, 1, 1}, @@ -257,6 +258,13 @@ postponed_stream(MAILSTREAM **streamp, char *mbox, char *type, int checknmsgs) if(exists & FEX_ISFILE){ context_apply(tmp, p_cntxt, mbox, sizeof(tmp)); +#ifndef _WINDOWS + if (!struncmp(tmp, "#md/",4) || !struncmp(tmp, "#mc/", 4)){ + char tmp2[MAILTMPLEN]; + maildir_file_path(tmp, tmp2, sizeof(tmp2)); + strcpy(tmp, tmp2); + } +#endif if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){ /* * The mbox is relative to the home directory. @@ -1229,7 +1237,7 @@ pine_new_env(ENVELOPE *outgoing, char **fccp, char ***tobufpp, PINEFIELD *custom *p = *(p+4); pf->type = pf_template[i].type; - pf->canedit = pf_template[i].canedit; + pf->canedit = (i == N_FROM) ? CAN_EDIT(ps_global) : pf_template[i].canedit; pf->rcptto = pf_template[i].rcptto; pf->writehdr = pf_template[i].writehdr; pf->localcopy = pf_template[i].localcopy; @@ -1738,9 +1746,9 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve 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; @@ -1893,20 +1901,49 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve * 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]; @@ -2142,6 +2179,8 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve if(error_mess){ q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess); dprint((1, "call_mailer ERROR: %s\n", error_mess)); + if (ps_global->send_immediately) + printf("%s\n",error_mess); } return(-1); diff --git a/pith/send.h b/pith/send.h index 69d763fc..ee3245e5 100644 --- a/pith/send.h +++ b/pith/send.h @@ -159,6 +159,8 @@ struct local_message_copy { unsigned text_written:1; }; +#define CAN_EDIT(x) (!((x)->never_allow_changing_from) && \ + F_ON(F_ALLOW_CHANGING_FROM, (x))) #define TONAME "To" #define CCNAME "cc" diff --git a/pith/sort.c b/pith/sort.c index 68a9c10c..4d75012f 100644 --- a/pith/sort.c +++ b/pith/sort.c @@ -30,7 +30,7 @@ static char rcsid[] = "$Id: sort.c 1142 2008-08-13 17:22:21Z hubert@u.washington #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 @@ -91,7 +91,7 @@ Args: msgmap -- ----*/ void sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, - int new_rev, unsigned int flags) + int new_rev, unsigned int flags, int first) { long raw_current, i, j; unsigned long *sort = NULL; @@ -101,6 +101,15 @@ sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, int current_rev; MESSAGECACHE *mc; + if (first){ + if (new_sort == SortThread) + find_msgmap(stream, msgmap, flags, + ps_global->thread_cur_sort, new_rev); + else + sort_folder(stream, msgmap, new_sort, new_rev, flags, 0); + return; + } + dprint((2, "Sorting by %s%s\n", sort_name(new_sort), new_rev ? "/reverse" : "")); @@ -530,20 +539,20 @@ percent_sorted(void) * argument also means arrival/reverse. */ int -decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev) +decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev, int thread) { char *sep; char *fix_this = NULL; - int x, reverse; + int x = 0, reverse; if(!sort_spec || !*sort_spec){ - *def_sort = SortArrival; + *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 0; return(0); } if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ - *def_sort = SortArrival; + *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 1; return(0); } @@ -572,7 +581,7 @@ decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev) if(ps_global->sort_types[x] == EndofList) return(-1); - *def_sort = ps_global->sort_types[x]; + *def_sort = ps_global->sort_types[x]; *def_sort_rev = reverse; return(0); } @@ -686,10 +695,26 @@ reset_sort_order(unsigned int flags) PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; - + char *rule_result; + SortOrder new_sort = EndofList; + int is_rev; + + rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES); + if (rule_result && *rule_result){ + new_sort = (SortOrder) translate(rule_result, 1); + is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1; + fs_give((void **)&rule_result); + } + if (new_sort != EndofList){ + the_sort_order = new_sort; + sort_is_rev = is_rev; + } + else{ /* set default order */ the_sort_order = ps_global->def_sort; - sort_is_rev = ps_global->def_sort_rev; + sort_is_rev = the_sort_order == SortThread + ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2 + : ps_global->def_sort_rev; if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ @@ -702,9 +727,52 @@ reset_sort_order(unsigned int flags) && pat->action->sort_is_set){ the_sort_order = pat->action->sortorder; sort_is_rev = pat->action->revsort; + sort_is_rev = the_sort_order == SortThread + ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2 + : pat->action->revsort; } } + } + if(the_sort_order == SortThread && !(flags & SRT_MAN)) + ps_global->thread_cur_sort = ps_global->thread_def_sort; sort_folder(ps_global->mail_stream, ps_global->msgmap, - the_sort_order, sort_is_rev, flags); + the_sort_order, sort_is_rev, flags, 1); } + +SortOrder translate(char *order, int is_rev) +{ + int rev = 0; + if (!strncmp(order,"tHread", 6) + || (rev = !strncmp(order,"Reverse tHread", 14))) + return is_rev || rev ? SortThread : EndofList; + if (!strncmp(order,"OrderedSubj", 11) + || (rev = !strncmp(order,"Reverse OrderedSubj", 19))) + return is_rev || rev ? SortSubject2 : EndofList; + if (!strncmp(order,"Subject", 7) + || (rev = !strncmp(order,"Reverse SortSubject", 15))) + return is_rev || rev ? SortSubject : EndofList; + if (!strncmp(order,"Arrival", 7) + || (rev = !strncmp(order,"Reverse Arrival", 15))) + return is_rev || rev ? SortArrival : EndofList; + if (!strncmp(order,"From", 4) + || (rev = !strncmp(order,"Reverse From", 12))) + return is_rev || rev ? SortFrom : EndofList; + if (!strncmp(order,"To", 2) + || (rev = !strncmp(order,"Reverse To", 10))) + return is_rev || rev ? SortTo : EndofList; + if (!strncmp(order,"Cc", 2) + || (rev = !strncmp(order,"Reverse Cc", 10))) + return is_rev || rev ? SortCc : EndofList; + if (!strncmp(order,"Date", 4) + || (rev = !strncmp(order,"Reverse Date", 12))) + return is_rev || rev ? SortDate : EndofList; + if (!strncmp(order,"siZe", 4) + || (rev = !strncmp(order,"Reverse siZe", 12))) + return is_rev || rev ? SortSize : EndofList; + if (!strncmp(order,"scorE", 5) + || (rev = !strncmp(order,"Reverse scorE", 13))) + return is_rev || rev ? SortScore : EndofList; + return EndofList; +} + diff --git a/pith/sort.h b/pith/sort.h index ce383a04..2dfcde71 100644 --- a/pith/sort.h +++ b/pith/sort.h @@ -22,7 +22,7 @@ #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ - mn_get_revsort(M), (F)) + mn_get_revsort(M), (F), 1) struct global_sort_data { MSGNO_S *msgmap; @@ -41,9 +41,9 @@ extern struct global_sort_data g_sort; /* exported protoypes */ char *sort_name(SortOrder); -void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned); -int decode_sort(char *, SortOrder *, int *); +void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); +int decode_sort(char *, SortOrder *, int *, int); void reset_sort_order(unsigned); - +SortOrder translate(char *, int); #endif /* PITH_SORT_INCLUDED */ diff --git a/pith/state.c b/pith/state.c index fd2b4f71..6d1d8455 100644 --- a/pith/state.c +++ b/pith/state.c @@ -33,7 +33,7 @@ static char rcsid[] = "$Id: state.c 1074 2008-06-04 00:08:43Z hubert@u.washingto #include "../pith/remote.h" #include "../pith/list.h" #include "../pith/smime.h" - +#include "../pith/rules.h" /* * Globals referenced throughout pine... @@ -74,6 +74,7 @@ new_pine_struct(void) p = (struct pine *)fs_get(sizeof (struct pine)); memset((void *) p, 0, sizeof(struct pine)); + p->thread_def_sort = SortDate; p->def_sort = SortArrival; p->sort_types[0] = SortSubject; p->sort_types[1] = SortArrival; @@ -116,6 +117,9 @@ free_pine_struct(struct pine **pps) if(!(pps && (*pps))) return; + if((*pps)->subject != NULL) + fs_give((void **)&(*pps)->subject); + if((*pps)->hostname != NULL) fs_give((void **)&(*pps)->hostname); @@ -131,6 +135,9 @@ free_pine_struct(struct pine **pps) if((*pps)->folders_dir != NULL) fs_give((void **)&(*pps)->folders_dir); + if((*pps)->paterror == 0) + regfree(&(*pps)->colorpat); + if((*pps)->ui.homedir) fs_give((void **)&(*pps)->ui.homedir); @@ -192,6 +199,8 @@ free_pine_struct(struct pine **pps) if((*pps)->kw_colors) free_spec_colors(&(*pps)->kw_colors); + free_allowed_qstr(); + if((*pps)->atmts){ int i; @@ -206,6 +215,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); diff --git a/pith/state.h b/pith/state.h index 47e97ce4..6dbdd025 100644 --- a/pith/state.h +++ b/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 @@ -105,6 +105,11 @@ struct pine { MAILSTREAM *mail_stream; /* ptr to current folder stream */ MSGNO_S *msgmap; /* ptr to current message map */ + char screen_name[10]; /* name of current screen */ + char *role; /* role used when composing */ + char *procid; /* procedure id when needed */ + int exiting; + unsigned read_predicted:1; char cur_folder[MAXPATH+1]; @@ -137,6 +142,8 @@ struct pine { unsigned unseen_in_view:1; unsigned start_in_context:1; /* start fldr_scrn in current cntxt */ unsigned def_sort_rev:1; /* true if reverse sort is default */ + unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */ + unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */ unsigned restricted:1; unsigned save_msg_rule:5; @@ -244,10 +251,23 @@ struct pine { SPEC_COLOR_S *hdr_colors; /* list of configed colors for view */ SPEC_COLOR_S *index_token_colors; /* list of configed colors for index */ + char *prefix; /* prefix for fillpara */ + char **list_qstr; /* list of known quote strings */ short init_context; + struct { + ACTION_S *role_chosen; + int attach; + int strip; + int no_send_flowed; + int inchdr; + } reply; + int *initial_cmds; /* cmds to execute on startup */ int *free_initial_cmds; /* used to free when done */ + int *initial_cmds_backup; /* keep a copy in case they are freed */ + int *free_initial_cmds_backup; /* free the copy */ + int initial_cmds_offset; /* how many commands we have executed */ char c_client_error[300]; /* when nowhow_error is set and PARSE */ @@ -285,6 +305,9 @@ struct pine { EditWhich ew_for_srch_take; SortOrder def_sort, /* Default sort type */ + thread_def_sort, /* Default Sort Type in Thread Screen */ + thread_cur_sort, /* current sort style for threads */ + msgmap_thread_sort, sort_types[22]; int preserve; @@ -301,10 +324,16 @@ struct pine { int nmw_width; + char *subject; + int send_immediately; + int failed_read; + int hours_to_timeout; int tcp_query_timeout; + int sleep; /* time in seconds to sleep before removing temp file */ + int inc_check_timeout; int inc_check_interval; /* for local and IMAP */ int inc_second_check_interval; /* for other */ @@ -323,6 +352,8 @@ struct pine { char *display_charmap; /* needs to be freed */ char *keyboard_charmap; /* needs to be freed */ void *input_cs; + regex_t colorpat; + int paterror; char *posting_charmap; /* needs to be freed */ @@ -334,6 +365,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; @@ -344,6 +376,9 @@ struct pine { char last_error[500]; INIT_ERR_S *init_errs; + PRULELIST_S *rule_list; + char *pressed_key; + PRINT_S *print; #ifdef SMIME diff --git a/pith/store.c b/pith/store.c index e8508257..58154c53 100644 --- a/pith/store.c +++ b/pith/store.c @@ -171,6 +171,14 @@ so_get(SourceType source, char *name, int rtype) if(source == TmpFileStar) our_unlink(so->name); + if (ps_global->send_immediately){ + ps_global->failed_read++; + if(ps_global->failed_read == 5){ + printf("No configurationf file found. Where is your .pinerc file?\n"); + exit(1); + } + } + fs_give((void **)&so->name); fs_give((void **)&so); /* so freed & set to NULL */ } diff --git a/pith/string.c b/pith/string.c index 84717c3e..2cd43d7d 100644 --- a/pith/string.c +++ b/pith/string.c @@ -20,6 +20,7 @@ static char rcsid[] = "$Id: string.c 910 2008-01-14 22:28:38Z hubert@u.washingto 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 @@ -132,6 +133,31 @@ rplstr(char *os, size_t oslen, int dl, char *is) 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'; +} /*---------------------------------------------------------------------- @@ -2860,3 +2886,35 @@ free_strlist(STRLIST_S **strp) fs_give((void **) strp); } } + + +void +removing_extra_stuff(string) + char *string; +{ + char *p = NULL; + int change = 0, length = 0; + + + if(!string) + return; + + for(; *string; string++, length++) + p = ((unsigned char)*string != ',') ? NULL : (!p) ? string : p; + + if(p) + *p = '\0'; + + string -= length; + for (; *string; string++){ + if (change){ + *string = ' '; + change = 0; + } + if ((((unsigned char)*string == ' ') || + ((unsigned char)*string == ',')) && + ((unsigned char)*(string + 1) == ',')) + change++; + } +} + diff --git a/pith/string.h b/pith/string.h index 11c4d45f..e6ee6497 100644 --- a/pith/string.h +++ b/pith/string.h @@ -86,12 +86,14 @@ struct date { /* exported protoypes */ 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(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 *); diff --git a/pith/text.c b/pith/text.c index 5de53e51..2857ddad 100644 --- a/pith/text.c +++ b/pith/text.c @@ -92,7 +92,7 @@ decode_text(ATTACH_S *att, char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; - int is_flowed_msg = 0; + int is_flowed_msg = 0, add_me = 1, doraw = RAWSTRING; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; @@ -171,6 +171,15 @@ decode_text(ATTACH_S *att, gf_url_hilite_opt(&uh,handlesp,0)); } + if((flags & FM_DISPLAY) + && !(flags & FM_NOCOLOR) + && pico_usingcolor() + && VAR_SPECIAL_TEXT_FORE_COLOR + && VAR_SPECIAL_TEXT_BACK_COLOR){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); + } + /* * First, paint the signature. * Disclaimers noted below for coloring quotes apply here as well. @@ -180,7 +189,7 @@ decode_text(ATTACH_S *att, && pico_usingcolor() && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ - filters[filtcnt].filter = gf_line_test; + filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(color_signature, &is_in_sig); } @@ -198,9 +207,9 @@ decode_text(ATTACH_S *att, && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ - filters[filtcnt].filter = gf_line_test; - filters[filtcnt++].data = gf_line_test_opt(color_a_quote, - &is_flowed_msg); + add_me = 0; + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(color_a_quote, &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ @@ -281,6 +290,11 @@ decode_text(ATTACH_S *att, } } + if (add_me){ + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, &doraw); + } + /* * If the message is not flowed, we do the quote suppression before * the wrapping, because the wrapping does not preserve the quote @@ -305,7 +319,7 @@ decode_text(ATTACH_S *att, dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); - filters[filtcnt].filter = gf_line_test; + filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } if(ps_global->VAR_QUOTE_REPLACE_STRING @@ -364,7 +378,7 @@ decode_text(ATTACH_S *att, dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); - filters[filtcnt].filter = gf_line_test; + filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } @@ -569,7 +583,7 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local) { DELQ_S *dq; char *lp; - int i, lines, not_a_quote = 0; + int i, lines, not_a_quote = 0, code; size_t len; dq = (DELQ_S *) local; @@ -589,6 +603,8 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local) for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--) if(*lp++ != SPACE) not_a_quote++; + while(isspace((unsigned char) *lp)) + lp++; /* skip over leading tags */ while(!not_a_quote @@ -628,13 +644,12 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local) } } - /* skip over whitespace */ - if(!dq->is_flowed) - while(isspace((unsigned char) *lp)) - lp++; - - /* check first character to see if it is a quote */ - if(!not_a_quote && *lp != '>') + len = lp - line; + if(strlen(tmp_20k_buf) > len) + strcpy(tmp_20k_buf, tmp_20k_buf+len); + code = (dq->is_flowed ? IS_FLOWED : NO_FLOWED) | DELETEQUO; + select_quote(linenum, lp, ins, &code); + if (!not_a_quote && !tmp_20k_buf[0]) not_a_quote++; if(not_a_quote){ diff --git a/pith/thread.c b/pith/thread.c index ff9bff54..fc0e32b3 100644 --- a/pith/thread.c +++ b/pith/thread.c @@ -30,12 +30,18 @@ static char rcsid[] = "$Id: thread.c 942 2008-03-04 18:21:33Z hubert@u.washingto #include "../pith/mailcmd.h" #include "../pith/ablookup.h" +static int erase_thread_info = 1; + +typedef struct sizethread_t { + int count; + long pos; +} SIZETHREAD_T; /* * Internal prototypes */ long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, - char *, long, PINETHRD_S *, unsigned); + char *, long, PINETHRD_S *, unsigned, int, long, long); void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); THREADNODE *collapse_threadnode_tree(THREADNODE *); THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); @@ -43,6 +49,7 @@ THREADNODE *sort_threads_and_collapse(THREADNODE *); THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *); unsigned long branch_greatest_num(THREADNODE *, int); long calculate_visible_threads(MAILSTREAM *); +int pine_compare_size_thread(const qsort_t *, const qsort_t *); PINETHRD_S * @@ -95,20 +102,22 @@ void set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) { PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; if(!(stream && thrd && msgmap)) return; set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream,thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_flags_for_thread(stream, msgmap, f, nthrd, v); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_flags_for_thread(stream, msgmap, f, bthrd, v); } @@ -122,7 +131,7 @@ erase_threading_info(MAILSTREAM *stream, MSGNO_S *msgmap) MESSAGECACHE *mc; PINELT_S *peltp; - if(!(stream && stream->spare)) + if(!(stream && stream->spare) || !erase_thread_info) return; ps_global->view_skipped_index = 0; @@ -155,7 +164,7 @@ sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree) PINETHRD_S *thrd = NULL; unsigned long msgno, rawno; int un_view_thread = 0; - long raw_current; + long raw_current, branch; char *dup_chk = NULL; @@ -168,10 +177,11 @@ sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree) * way. If the dummy node is at the top-level, then its children are * promoted to the top-level as separate threads. */ - if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)) - collapsed_tree = collapse_threadnode_tree_sorted(tree); - else - collapsed_tree = collapse_threadnode_tree(tree); + collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global) + ? copy_tree(tree) + : (F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) + ? collapse_threadnode_tree_sorted(tree) + : collapse_threadnode_tree(tree)); /* dup_chk is like sort with an origin of 1 */ dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); @@ -182,7 +192,7 @@ sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree) (void) sort_thread_flatten(collapsed_tree, stream, &g_sort.msgmap->sort[1], dup_chk, mn_get_nmsgs(g_sort.msgmap), - NULL, THD_TOP); + NULL, THD_TOP, 0, 1L, 0L); /* reset the inverse array */ msgno_reset_isort(g_sort.msgmap); @@ -340,12 +350,14 @@ sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree) else{ thrd = fetch_head_thread(stream); while(thrd){ + unsigned long raw = thrd->rawno; + unsigned long top = top_thread(stream, raw); /* * The top-level threads aren't hidden by collapse. */ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); - if(msgno) - set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); + if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL)) + set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); if(thrd->next){ PINETHRD_S *nthrd; @@ -359,9 +371,10 @@ sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree) MN_COLL)); } - if(thrd->nextthd) - thrd = fetch_thread(stream, thrd->nextthd); - else + while (thrd && top_thread(stream, thrd->rawno) == top + && thrd->nextthd) + thrd = fetch_thread(stream, thrd->nextthd); + if (!(thrd && thrd->nextthd)) thrd = NULL; } } @@ -412,7 +425,7 @@ make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd, int a_parent_is_collapsed) { PINETHRD_S *nthrd, *bthrd; - unsigned long msgno; + unsigned long msgno, next, branch; if(!thrd) return; @@ -430,8 +443,8 @@ make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd, set_lflag(stream, msgmap, msgno, MN_CHID, 0); } - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) make_thrdflags_consistent(stream, msgmap, nthrd, a_parent_is_collapsed @@ -440,8 +453,8 @@ make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd, MN_COLL)); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) make_thrdflags_consistent(stream, msgmap, bthrd, a_parent_is_collapsed); @@ -488,9 +501,10 @@ calculate_visible_threads(MAILSTREAM *stream) long * sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, long *entry, char *dup_chk, long maxno, - PINETHRD_S *thrd, unsigned int flags) + PINETHRD_S *thrd, unsigned int flags, + int adopted, long top, long threadno) { - PINETHRD_S *newthrd = NULL; + PINETHRD_S *newthrd = NULL, *save_thread = NULL; if(node){ if(node->num > 0L && node->num <= maxno){ /* holes happen */ @@ -498,6 +512,9 @@ sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, *entry = node->num; dup_chk[node->num] = 1; + if(adopted == 2) + top = node->num; + /* * Build a richer threading structure that will help us paint * and operate on threads and subthreads. @@ -506,20 +523,51 @@ sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, if(newthrd){ entry++; + if(adopted == 2) + threadno = newthrd->thrdno; + if(adopted){ + newthrd->toploose = top; + newthrd->thrdno = threadno; + } + adopted = adopted ? 1 : 0; if(node->next) entry = sort_thread_flatten(node->next, stream, entry, dup_chk, maxno, - newthrd, THD_NEXT); + newthrd, THD_NEXT, adopted, top, threadno); if(node->branch) entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, maxno, newthrd, - (flags == THD_TOP) ? THD_TOP - : THD_BRANCH); + ((flags == THD_TOP) ? THD_TOP + : THD_BRANCH), + adopted, top, threadno); } } } + else{ + adopted = 2; + if(node->next) + entry = sort_thread_flatten(node->next, stream, entry, dup_chk, + maxno, thrd, THD_TOP, adopted, top, threadno); + adopted = 0; + if(node->branch){ + if(entry){ + long *last_entry = entry; + + do{ + last_entry--; + save_thread = ((PINELT_S *)mail_elt(stream, *last_entry)->sparep)->pthrd; + } while (save_thread->parent != 0L); + entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, + maxno, save_thread, (flags == THD_TOP ? THD_TOP : THD_BRANCH), + adopted, top, threadno); + } + else + entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, + maxno, NULL, THD_TOP, adopted, top, threadno); + } + } } return(entry); @@ -788,7 +836,7 @@ msgno_thread_info(MAILSTREAM *stream, long unsigned int rawno, */ void collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, - long unsigned int msgno) + long unsigned int msgno, int display) { int collapsed, adjust_current = 0; PINETHRD_S *thrd = NULL, *nthrd; @@ -841,7 +889,7 @@ collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, if(!thrd) return; - collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next; + collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno); if(collapsed){ msgno = mn_raw2m(msgmap, thrd->rawno); @@ -859,13 +907,13 @@ collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, msgno = mn_raw2m(msgmap, thrd->rawno); if(msgno > 0L && msgno <= mn_get_total(msgmap)){ set_lflag(stream, msgmap, msgno, MN_COLL, 1); - if((nthrd = fetch_thread(stream, thrd->next)) != NULL) + if((thrd->next) && ((nthrd = fetch_thread(stream, thrd->next)) != NULL)) set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); clear_index_cache_ent(stream, msgno, 0); } } - else + else if(display) q_status_message(SM_ORDER, 0, 1, _("No thread to collapse or expand on this line")); @@ -952,18 +1000,19 @@ count_flags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, long int flags) unsigned long count = 0; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; + unsigned long next = 0L, branch = 0L; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) count += count_flags_in_thread(stream, nthrd, flags); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) count += count_flags_in_thread(stream, bthrd, flags); } @@ -1051,20 +1100,21 @@ int mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) { int count = 0; + long next, branch; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) count += mark_msgs_in_thread(stream, nthrd, msgmap); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) count += mark_msgs_in_thread(stream, bthrd, msgmap); } @@ -1098,7 +1148,7 @@ set_thread_lflags(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int fla /* flags to set or clear */ /* set or clear? */ { - unsigned long msgno; + unsigned long msgno, next, branch; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) @@ -1122,14 +1172,14 @@ set_thread_lflags(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int fla if(msgno > 0L && flags == MN_CHID2 && v == 1) clear_index_cache_ent(stream, msgno, 0); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_thread_lflags(stream, nthrd, msgmap, flags, v); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream,thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_thread_lflags(stream, bthrd, msgmap, flags, v); } @@ -1210,7 +1260,8 @@ status_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, IndexColType type /* * Symbol is * if some message in thread is important, * + if some message is to us, - * - if mark-for-cc and some message is cc to us, else blank. + * - if mark-for-cc and some message is cc to us, + * . if mark-for-group and some message is to us in a group, else blank. */ char to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagged) @@ -1218,45 +1269,48 @@ to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagg char to_us = ' '; char branch_to_us = ' '; PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return to_us; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream,thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); } if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) - && thrd->branch){ + && (branch = get_branch(stream, thrd))){ bthrd = fetch_thread(stream, thrd->branch); if(bthrd) branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); /* use branch to_us symbol if it has higher priority than what we have so far */ if(to_us == ' '){ - if(branch_to_us == '-' || branch_to_us == '+' || branch_to_us == '*') + if(branch_to_us == '-' || branch_to_us == '+' + || branch_to_us == '.' || branch_to_us == '*') to_us = branch_to_us; } else if(to_us == '-'){ - if(branch_to_us == '+' || branch_to_us == '*') + if(branch_to_us == '+' || branch_to_us == '.' || branch_to_us == '*') to_us = branch_to_us; } - else if(to_us == '+'){ + else if(to_us == '+' || to_us == '.'){ if(branch_to_us == '*') to_us = branch_to_us; } } - if((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')){ + if((consider_flagged && to_us != '*') + || (!consider_flagged && to_us != '+' && to_us != '.')){ if(consider_flagged && thrd && thrd->rawno > 0L && stream && thrd->rawno <= stream->nmsgs && (mc = mail_elt(stream, thrd->rawno)) && FLAG_MATCH(F_FLAG, mc, stream)) to_us = '*'; - else if(to_us != '+' && !IS_NEWS(stream)){ + else if(to_us != '+' && to_us != '.' && !IS_NEWS(stream)){ INDEXDATA_S idata; MESSAGECACHE *mc; ADDRESS *addr; @@ -1280,7 +1334,7 @@ to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagg break; } - if(to_us != '+' && resent_to_us(&idata)) + if(to_us != '+' && !idata.bogus && resent_to_us(&idata)) to_us = '+'; if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) @@ -1328,7 +1382,8 @@ set_thread_subtree(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int v, set_lflag(stream, msgmap, msgno, flags, v); - if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ + if(thrd->next + && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_subtree(stream, nthrd, msgmap, v, flags); @@ -1368,8 +1423,8 @@ view_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int set_lfl if(rawno) thrd = fetch_thread(stream, rawno); - if(thrd && thrd->top && thrd->top != thrd->rawno) - thrd = fetch_thread(stream, thrd->top); + if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno) + thrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!thrd) return 0; @@ -1433,7 +1488,7 @@ unview_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap) thrd = fetch_thread(stream, rawno); if(thrd && thrd->top) - topthrd = fetch_thread(stream, thrd->top); + topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!topthrd) return 0; @@ -1539,6 +1594,7 @@ void set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset) { PINETHRD_S *nthrd, *bthrd; + unsigned long next, branch; if(!(stream && thrd)) return; @@ -1547,15 +1603,622 @@ set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgs && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) mm_searched(stream, thrd->rawno); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next= get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_search_bit_for_thread(stream, nthrd, msgset); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_search_bit_for_thread(stream, bthrd, msgset); } } + +/* + * Make a copy of c-client's THREAD tree + */ +THREADNODE * +copy_tree(THREADNODE *tree) +{ + THREADNODE *newtree = NULL; + + if(tree){ + newtree = mail_newthreadnode(NULL); + newtree->num = tree->num; + if(tree->next) + newtree->next = copy_tree(tree->next); + + if(tree->branch) + newtree->branch = copy_tree(tree->branch); + } + return(newtree); +} + +long +top_thread(MAILSTREAM *stream, long rawmsgno) +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return F_ON(F_ENHANCED_THREAD, ps_global) + ? (thrd->toploose ? thrd->toploose : thrd->top) + : thrd->top; +} + +void +move_top_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) +{ + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno))); +} + +long +top_this_thread(MAILSTREAM *stream, long rawmsgno) +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return thrd->top; +} + +void +move_top_this_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) +{ + mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno))); +} + +int +thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) +{ + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig, orig_rawno; + + if(!stream) + return -1; + + orig = mn_get_cur(msgmap); + move_top_thread(stream, msgmap, rawmsgno); + rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno)) + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_rawno != top_thread(stream, rawno))) + break; + + mn_set_cur(msgmap,orig); /* return home */ + + return collapsed; +} + +/* this function tells us if the thread (or branch in the case of loose threads) + * is collapsed + */ + +int +this_thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) +{ + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig; + + if(!stream) + return -1; + + rawno = rawmsgno; + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID); + + if (!thrd->next){ + if (thrd->rawno != top_thread(stream, thrd->rawno)) + collapsed = get_lflag(stream, NULL, rawno, MN_CHID); + else + collapsed = get_lflag(stream, NULL, rawno, MN_COLL); + } + + return collapsed; +} + +/* + * This function assumes that it is called at a top of a thread in its + * first call + */ + +int +count_this_thread(MAILSTREAM *stream, unsigned long rawno) +{ + unsigned long top, orig_top, topnxt; + PINETHRD_S *thrd = NULL; + int count = 1; + + if(!stream) + return 0; + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return 0; + + if (thrd->next) + count += count_this_thread(stream, thrd->next); + + if (thrd->branch) + count += count_this_thread(stream, thrd->branch); + + return count; +} + +int +count_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawno) +{ + unsigned long top, orig, orig_top; + PINETHRD_S *thrd = NULL; + int done = 0, count = 0; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,rawno); + top = orig_top = top_thread(stream, rawno); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (!done){ + count += count_this_thread(stream, top); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig)); + return count; +} + +unsigned long +get_branch(MAILSTREAM *stream, PINETHRD_S *thrd) +{ + PINETHRD_S *nthrd = NULL; + unsigned long top; + + if (thrd->toploose && thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + if (!nthrd) + return thrd->branch; + top = top_thread(stream, thrd->rawno); + return thrd->branch + ? thrd->branch + : (F_ON(F_ENHANCED_THREAD, ps_global) + ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L) + : 0L); +} + +unsigned long +get_next(MAILSTREAM *stream, PINETHRD_S *thrd) +{ + return thrd->next; +} + +long +get_length_branch(MAILSTREAM *stream, long rawno) +{ + int branchp = 0, done = 0; + long top, count = 1L, raw; + PINETHRD_S *thrd, *pthrd = NULL, *nthrd; + + thrd = fetch_thread(stream, rawno); + + if (!thrd) + return -1L; + + top = thrd->top; + + if (thrd->parent) + pthrd = fetch_thread(stream, thrd->parent); + + if (thrd->rawno == top) + branchp++; + + if (!branchp && !pthrd){ /* what!!?? */ + raw = top; + while (!done){ + pthrd = fetch_thread(stream, raw); + if ((pthrd->next == rawno) || (pthrd->branch == rawno)) + done++; + else{ + if (pthrd->next) + raw = pthrd->next; + else if (pthrd->branch) + raw = pthrd->branch; + } + } + } + + if (pthrd && pthrd->next == thrd->rawno && thrd->branch) + branchp++; + + if (pthrd && pthrd->next && pthrd->next != thrd->rawno){ + nthrd = fetch_thread(stream, pthrd->next); + while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno) + nthrd = fetch_thread(stream, nthrd->branch); + if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno) + branchp++; + } + + if(branchp){ + int entry = 0; + while(thrd && thrd->next){ + entry = 1; + count++; + thrd = fetch_thread(stream, thrd->next); + if (thrd->branch) + break; + } + if (entry && thrd->branch) + count--; + } + return branchp ? (count ? count : 1L) : 0L; +} + +int pine_compare_size_thread(const qsort_t *a, const qsort_t *b) +{ + SIZETHREAD_T *s = (SIZETHREAD_T *) a, *t = (SIZETHREAD_T *) b; + + return s->count == t->count ? s->pos - t->pos : s->count - t->count; +} + + + +void +find_msgmap(MAILSTREAM *stream, MSGNO_S *msgmap, int flags, SortOrder ordersort, unsigned is_rev) +{ + long *old_arrival,*new_arrival; + long init_thread, end_thread, current; + long i, j, k; + long tmsg, ntmsg, nthreads; + SIZETHREAD_T *l; + PINETHRD_S *thrd; + + erase_thread_info = 0; + current = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + switch(ordersort){ + case SortSize: + sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); + tmsg = mn_get_total(msgmap) + 1; + + if(tmsg <= 1) + return; + + for (i= 1L, k = 0L; i <= mn_get_total(msgmap); i += count_thread(ps_global, stream, msgmap, msgmap->sort[i]), k++); + l = (SIZETHREAD_T *) fs_get(k*sizeof(SIZETHREAD_T)); + for (j = 0L, i=1L; j < k && i<= mn_get_total(msgmap); ){ + l[j].count = count_thread(ps_global, stream, msgmap, msgmap->sort[i]); + l[j].pos = i; + i += l[j].count; + j++; + } + qsort((void *)l, (size_t) k, sizeof(SIZETHREAD_T), pine_compare_size_thread); + old_arrival = (long *) fs_get(tmsg * sizeof(long)); + for(i = 1L, j = 0; j < k; j++){ /* copy thread of length .count */ + int p; + for(p = 0; p < l[j].count; p++) + old_arrival[i++] = msgmap->sort[l[j].pos + p]; + } + fs_give((void **)&l); + break; + default: + sort_folder(stream, msgmap, ordersort, 0, SRT_VRB, 0); + tmsg = mn_get_total(msgmap) + 1; + + if (tmsg <= 1) + return; + + old_arrival = (long *) fs_get(tmsg * sizeof(long)); + for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++); + /* sort by thread */ + sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); + break; + + } + + ntmsg = mn_get_total(msgmap) + 1; + if (tmsg != ntmsg){ /* oh oh, something happened, we better try again */ + fs_give((void **)&old_arrival); + find_msgmap(stream, msgmap, flags, ordersort, is_rev); + return; + } + + /* reconstruct the msgmap */ + + new_arrival = (long *) fs_get(tmsg * sizeof(long)); + memset(new_arrival, 0, tmsg*sizeof(long)); + i = mn_get_total(msgmap); + /* we copy from the bottom, the last one to be filled is new_arrival[1] */ + while (new_arrival[1] == 0){ + int done = 0; + long n; + + init_thread = top_thread(stream, old_arrival[i]); + thrd = fetch_thread(stream, init_thread); + for (n = mn_get_total(msgmap); new_arrival[n] != 0 && !done; n--) + done = (new_arrival[n] == init_thread); + if (!done){ + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if(move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + end_thread = mn_raw2m(msgmap, init_thread) + j; + for(k = 1L; k <= j; k++) + new_arrival[tmsg - k] = msgmap->sort[end_thread - k]; + tmsg -= j; + } + i--; + } + relink_threads(stream, msgmap, new_arrival); + for (i = 1; (i <= mn_get_total(msgmap)) + && (msgmap->sort[i] = new_arrival[i]); i++); + msgno_reset_isort(msgmap); + + fs_give((void **)&new_arrival); + fs_give((void **)&old_arrival); + + + if(is_rev && (mn_get_total(msgmap) > 1L)){ + long *rev_sort; + long i = 1L, l = mn_get_total(msgmap); + + rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long)); + memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long)); + while (l > 0L){ + if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){ + long init_thread = msgmap->sort[l]; + long j, k; + + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++); + i += j; + } + l--; + } + relink_threads(stream, msgmap, rev_sort); + for (i = 1L; i <= mn_get_total(msgmap); i++) + msgmap->sort[i] = rev_sort[i]; + msgno_reset_isort(msgmap); + fs_give((void **)&rev_sort); + } + mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK, + stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID)); + msgmap->top = -1L; + + sp_set_unsorted_newmail(ps_global->mail_stream, 0); + + for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++) + mail_elt(ps_global->mail_stream, i)->spare7 = 0; + + mn_set_sort(msgmap, SortThread); + mn_set_revsort(msgmap, is_rev); + erase_thread_info = 1; + clear_index_cache(stream, 0); +} + +void +move_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int direction) +{ + long new_cursor, old_cursor = mn_get_cur(msgmap); + int rv; + PINETHRD_S *thrd; + + rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1): + move_prev_thread(state, stream, msgmap, 1); + if (rv > 0 && THRD_INDX_ENABLED()){ + new_cursor = mn_get_cur(msgmap); + mn_set_cur(msgmap, old_cursor); + unview_thread(state, stream, msgmap); + thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor)); + mn_set_cur(msgmap, new_cursor); + view_thread(state, stream, msgmap, 1); + state->next_screen = SCREEN_FUN_NULL; + } +} + +void +relink_threads(MAILSTREAM *stream, MSGNO_S *msgmap, long *new_arrival) +{ + long last_thread = 0L; + long i = 0L, j = 1L, k; + PINETHRD_S *thrd, *nthrd; + + while (j <= mn_get_total(msgmap)){ + i++; + thrd = fetch_thread(stream, new_arrival[j]); + if (!thrd) /* sort failed!, better leave from here now!!! */ + break; + thrd->prevthd = last_thread; + thrd->thrdno = i; + thrd->head = new_arrival[1]; + last_thread = thrd->rawno; + mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top)); + k = mn_get_cur(msgmap); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j += mn_get_total(msgmap) + 1 - k; + else + j += mn_get_cur(msgmap) - k; + if (!thrd->toploose) + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + else{ + int done = 0; + while(thrd->nextthd && !done){ + thrd->thrdno = i; + thrd->head = new_arrival[1]; + if (thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + else + done++; + if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno)) + thrd = nthrd; + else + done++; + } + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + last_thread = thrd->rawno; + } + } +} + +int +move_next_this_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) +{ + PINETHRD_S *thrd = NULL, *thrdnxt; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd; + if (thrdnxt->nextthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd)); + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "No more Threads to advance"); + } + return rv; +} + +int +move_next_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,orig); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (rv > 0 && !done){ + rv = move_next_this_thread(state, stream, msgmap, display); + if (F_OFF(F_ENHANCED_THREAD, state) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + if (display){ + if (rv > 0 && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing next thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to advance"); + } + if(rv <= 0){ + rv = 0; + mn_set_cur(msgmap, mn_raw2m(msgmap, orig)); + } + + return rv; +} + +int +move_prev_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + if (top != rawno) + mn_set_cur(msgmap,mn_raw2m(msgmap, top)); + else if (thrd->prevthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd))); + else + rv = 0; + if (display){ + if (rv && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing previous thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to go back"); + } + + return rv; +} + +/* add more keys to this list */ +int +allowed_thread_key(SortOrder sort) +{ + return sort == SortArrival || sort == SortDate + || sort == SortScore || sort == SortThread + || sort == SortSize; +} + diff --git a/pith/thread.h b/pith/thread.h index 40b09bb0..4c5f1a97 100644 --- a/pith/thread.h +++ b/pith/thread.h @@ -37,6 +37,7 @@ typedef struct pine_thrd { unsigned long nextthd; /* next thread, only tops have this */ unsigned long prevthd; /* previous thread, only tops have this */ unsigned long top; /* top of this thread */ + unsigned long toploose; /* top of this thread, if is loose */ unsigned long head; /* head of the whole thread list */ } PINETHRD_S; @@ -92,7 +93,7 @@ void erase_threading_info(MAILSTREAM *, MSGNO_S *); void sort_thread_callback(MAILSTREAM *, THREADNODE *); void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); -void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long); +void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long, int); 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); @@ -106,6 +107,24 @@ int view_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); - +void find_msgmap(MAILSTREAM *, MSGNO_S *, int, SortOrder, unsigned); +void move_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); +void relink_threads(MAILSTREAM *, MSGNO_S *, long *); +long top_thread(MAILSTREAM *, long); +long top_this_thread(MAILSTREAM *, long); +long get_length_branch(MAILSTREAM *, long); +unsigned long get_next(MAILSTREAM *,PINETHRD_S *); +unsigned long get_branch(MAILSTREAM *,PINETHRD_S *); +int count_thread(struct pine *, MAILSTREAM *, MSGNO_S *, long); +int count_this_thread(MAILSTREAM *, unsigned long); +int this_thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); +int thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); +int move_prev_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); +int move_next_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); +int move_next_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); +void move_top_thread(MAILSTREAM *, MSGNO_S *, long); +void move_top_this_thread(MAILSTREAM *, MSGNO_S *, long); +THREADNODE *copy_tree(THREADNODE *); +int allowed_thread_key(SortOrder sort); #endif /* PITH_THREAD_INCLUDED */ @@ -53,7 +53,7 @@ char * rfc1738_scan(char *line, int *len) { char *colon, *start, *end; - int n; + int n, delim; /* process each : in the line */ for(; (colon = strindex(line, ':')) != NULL; line = end){ @@ -137,6 +137,7 @@ rfc1738_scan(char *line, int *len) if(i != j){ *len = end - start; + delim = start > line && *(start - 1) == '<'; /* * Special case handling for comma. @@ -146,8 +147,8 @@ rfc1738_scan(char *line, int *len) * In most cases any way, that's why we have the * exception. */ - if(*(end - 1) == ',' - || (*(end - 1) == '.' && (!*end || *end == ' '))) + if(delim == 0 && (*(end - 1) == ',' + || (*(end - 1) == '.' && (!*end || *end == ' ')))) (*len)--; if(*len - (colon - start) > 0) |