From 81e994d7907f850506ddc248f84761a54995e58c Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Fri, 31 May 2013 17:08:22 -0600 Subject: * Fix not allow remote execution by adding PIPE_NOSHELL to the opening of a url by a browser. --- pith/Makefile.am | 2 +- pith/Makefile.in | 5 +- pith/adrbklib.c | 10 +- pith/charconv/utf8.c | 50 ++ pith/charconv/utf8.h | 1 + pith/color.c | 113 +++- pith/color.h | 19 + pith/conf.c | 183 ++++- pith/conf.h | 57 ++ pith/conftype.h | 34 + pith/detoken.c | 33 +- pith/filter.c | 378 ++++++++++- pith/filter.h | 1 + pith/filttype.h | 2 + pith/flag.c | 6 +- pith/imap.c | 11 + pith/imap.h | 1 + pith/indxtype.h | 34 +- pith/init.c | 3 + pith/mailcap.c | 66 ++ pith/mailcap.h | 1 + pith/mailcmd.c | 435 ++++++++---- pith/mailcmd.h | 8 + pith/mailindx.c | 440 ++++++++---- pith/mailindx.h | 3 + pith/mailview.c | 465 ++++++++++++- pith/mailview.h | 16 + pith/makefile.wnt | 4 +- pith/msgno.c | 3 + pith/osdep/color.c | 1256 +++++++++++++++++++++++++++++++++- pith/osdep/color.h | 25 +- pith/pattern.c | 28 +- pith/pine.hlp | 1825 +++++++++++++++++++++++++++++++++++++++++++++++++- pith/pineelt.h | 1 + pith/reply.c | 264 +++++++- pith/save.c | 3 +- pith/send.c | 71 +- pith/send.h | 2 + pith/sort.c | 88 ++- pith/sort.h | 8 +- pith/state.c | 14 +- pith/state.h | 37 +- pith/store.c | 8 + pith/string.c | 58 ++ pith/string.h | 2 + pith/text.c | 45 +- pith/thread.c | 787 ++++++++++++++++++++-- pith/thread.h | 23 +- pith/url.c | 7 +- 49 files changed, 6447 insertions(+), 489 deletions(-) (limited to 'pith') 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 @@ -1048,6 +1048,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 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) @@ -279,12 +283,20 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f gf_url_hilite_opt(&uh,handlesp,0))); } + 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 #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_ @@ -158,6 +159,14 @@ Version ()
Copyright 2006-2008 University of Washington +

+This version was modified from its original source code. More information +about some of the patches applied to this version can be found here. + +The patch level of this version, including creation date of the patch is: +. + +

Alpine is an "Alternatively Licensed Program for Internet News and Email" produced until 2008 by the University of Washington. @@ -198,7 +207,7 @@ message, as specified by original sender. Bugs that have been addressed include: