</sect1>
+<sect1 id="display-munging">
+<title>Display Munging</title>
+
+<para>
+Working within the confines of a console or terminal window, it is
+often useful to be able to modify certain information elements in a
+non-destructive way -- to change how they display, without changing
+the stored value of the information itself. This is especially so of
+message subjects, which may often be polluted with extraneous metadata
+that either is reproduced elsewhere, or is of secondary interest.
+</para>
+
+<cmdsynopsis>
+<command>subjectrx</command>
+<arg choice="plain">
+<replaceable class="parameter">pattern</replaceable>
+</arg>
+<arg choice="plain">
+<replaceable class="parameter">replacement</replaceable>
+</arg>
+
+<command>unsubjectrx</command>
+<arg choice="plain">
+<replaceable class="parameter">pattern</replaceable>
+</arg>
+</cmdsynopsis>
+
+<para>
+<literal>subjectrx</literal> specifies a regular expression
+<quote>pattern</quote> which, if detected in a message subject, causes
+the subject to be replaced with the <quote>replacement</quote> value.
+The replacement is subject to substitutions in the same way as for the
+<link linkend="spam">spam</link> command: <literal>%L</literal> for the text
+to the left of the match, <literal>%R</literal> for text to the right of the
+match, and <literal>%1</literal> for the first subgroup in the match (etc).
+If you simply want to erase the match, set it to <quote>%L%R</quote>.
+Any number of <literal>subjectrx</literal> commands may coexist.
+</para>
+
+<para>
+Note this well: the <quote>replacement</quote> value replaces the
+entire subject, not just the match!
+</para>
+
+<para>
+<literal>unsubjectrx</literal> removes a given subjectrx from the substitution
+list.
+</para>
+
+<example id="ex-subjectrx">
+<title>Subject Munging</title>
+<screen>
+# Erase [rt #12345] tags from Request Tracker (RT) e-mails
+subjectrx '\[rt #[0-9]+\] *' '%L%R'
+
+# Servicedesk is another RT that sends more complex subjects.
+# Keep the ticket number.
+subjectrx '\[servicedesk #([0-9]+)\] ([^.]+)\.([^.]+) - (new|open|pending|update) - ' '%L[#%1] %R'
+
+# Strip out annoying [listname] prefixes in subjects
+subjectrx '\[[^\]]*\]:? *' '%L%R'
+</screen>
+</example>
+
+</sect1>
+
<sect1 id="new-mail">
<title>New Mail Detection</title>
</cmdsynopsis>
</listitem>
+<listitem>
+<cmdsynopsis>
+<command><link linkend="display-munging">subjectrx</link></command>
+<arg choice="plain">
+<replaceable class="parameter">pattern</replaceable>
+</arg>
+<arg choice="plain">
+<replaceable class="parameter">format</replaceable>
+</arg>
+
+<command><link linkend="display-munging">unsubjectrx</link></command>
+<arg choice="plain">
+<replaceable class="parameter">pattern</replaceable>
+</arg>
+</cmdsynopsis>
+</listitem>
+
<listitem>
<cmdsynopsis>
<command><link linkend="subscribe">subscribe</link></command>
WHERE RX_LIST *UnSubscribedLists INITVAL(0);
WHERE REPLACE_LIST *SpamList INITVAL(0);
WHERE RX_LIST *NoSpamList INITVAL(0);
+WHERE REPLACE_LIST *SubjectRxList INITVAL(0);
/* bit vector for boolean variables */
return table->chars[index];
}
+static char *apply_subject_mods (ENVELOPE *env)
+{
+ if (env == NULL)
+ return NULL;
+
+ if (SubjectRxList == NULL)
+ return env->subject;
+
+ if (env->subject == NULL || *env->subject == '\0')
+ return env->disp_subj = NULL;
+
+ env->disp_subj = mutt_apply_replace(NULL, 0, env->subject, SubjectRxList);
+ return env->disp_subj;
+}
+
+
/* %a = address of author
* %A = reply-to address (if present; otherwise: address of author
* %b = filename of the originating folder
break;
case 's':
-
- if (flags & MUTT_FORMAT_TREE && !hdr->collapsed)
{
- if (flags & MUTT_FORMAT_FORCESUBJ)
+ char *subj;
+ if (hdr->env->disp_subj)
+ subj = hdr->env->disp_subj;
+ else if (SubjectRxList)
+ subj = apply_subject_mods(hdr->env);
+ else
+ subj = hdr->env->subject;
+ if (flags & MUTT_FORMAT_TREE && !hdr->collapsed)
+ {
+ if (flags & MUTT_FORMAT_FORCESUBJ)
+ {
+ colorlen = add_index_color (dest, destlen, flags, MT_COLOR_INDEX_SUBJECT);
+ mutt_format_s (dest + colorlen, destlen - colorlen, "", NONULL (subj));
+ add_index_color (dest + colorlen, destlen - colorlen, flags, MT_COLOR_INDEX);
+ snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
+ mutt_format_s_tree (dest, destlen, prefix, buf2);
+ }
+ else
+ mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
+ }
+ else
{
colorlen = add_index_color (dest, destlen, flags, MT_COLOR_INDEX_SUBJECT);
- mutt_format_s (dest + colorlen, destlen - colorlen, "", NONULL (hdr->env->subject));
+ mutt_format_s (dest + colorlen, destlen - colorlen, prefix, NONULL (subj));
add_index_color (dest + colorlen, destlen - colorlen, flags, MT_COLOR_INDEX);
- snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest);
- mutt_format_s_tree (dest, destlen, prefix, buf2);
}
- else
- mutt_format_s_tree (dest, destlen, prefix, hdr->tree);
- }
- else
- {
- colorlen = add_index_color (dest, destlen, flags, MT_COLOR_INDEX_SUBJECT);
- mutt_format_s (dest + colorlen, destlen - colorlen, prefix, NONULL (hdr->env->subject));
- add_index_color (dest + colorlen, destlen - colorlen, flags, MT_COLOR_INDEX);
}
break;
return 0;
}
+static int parse_replace_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ REPLACE_LIST **list = (REPLACE_LIST **)data;
+ BUFFER templ;
+
+ memset(&templ, 0, sizeof(templ));
+
+ /* First token is a regexp. */
+ if (!MoreArgs(s))
+ {
+ strfcpy(err->data, _("not enough arguments"), err->dsize);
+ return -1;
+ }
+ mutt_extract_token(buf, s, 0);
+
+ /* Second token is a replacement template */
+ if (!MoreArgs(s))
+ {
+ strfcpy(err->data, _("not enough arguments"), err->dsize);
+ return -1;
+ }
+ mutt_extract_token(&templ, s, 0);
+
+ if (add_to_replace_list(list, buf->data, templ.data, err) != 0) {
+ FREE(&templ.data);
+ return -1;
+ }
+ FREE(&templ.data);
+
+ return 0;
+}
+
+static int parse_unreplace_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ REPLACE_LIST **list = (REPLACE_LIST **)data;
+
+ /* First token is a regexp. */
+ if (!MoreArgs(s))
+ {
+ strfcpy(err->data, _("not enough arguments"), err->dsize);
+ return -1;
+ }
+
+ mutt_extract_token(buf, s, 0);
+ remove_from_replace_list(list, buf->data);
+ return 0;
+}
+
+
+static void clear_subject_mods (void)
+{
+ int i;
+ if (Context && Context->msgcount)
+ {
+ for (i = 0; i < Context->msgcount; i++)
+ FREE(&Context->hdrs[i]->env->disp_subj);
+ }
+}
+
+
+static int parse_subjectrx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int rc;
+
+ rc = parse_replace_list(buf, s, data, err);
+ if (rc == 0)
+ clear_subject_mods();
+ return rc;
+}
+
+
+static int parse_unsubjectrx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int rc;
+
+ rc = parse_unreplace_list(buf, s, data, err);
+ if (rc == 0)
+ clear_subject_mods();
+ return rc;
+}
+
+
static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
BUFFER templ;
static int parse_attachments (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unattachments (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_replace_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unreplace_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_subjectrx_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unsubjectrx_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_alternates (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unalternates (BUFFER *, BUFFER *, unsigned long, BUFFER *);
{ "shutdown-hook", mutt_parse_hook, MUTT_SHUTDOWNHOOK | MUTT_GLOBALHOOK },
{ "startup-hook", mutt_parse_hook, MUTT_STARTUPHOOK | MUTT_GLOBALHOOK },
{ "subscribe", parse_subscribe, 0 },
+ { "subjectrx", parse_subjectrx_list, UL &SubjectRxList },
+ { "unsubjectrx", parse_unsubjectrx_list, UL &SubjectRxList },
{ "timeout-hook", mutt_parse_hook, MUTT_TIMEOUTHOOK | MUTT_GLOBALHOOK },
{ "toggle", parse_set, MUTT_SET_INV },
{ "unalias", parse_unalias, 0 },
char *list_post; /* this stores a mailto URL, or nothing */
char *subject;
char *real_subj; /* offset of the real subject */
+ char *disp_subj; /* display subject (modified copy of subject) */
char *message_id;
char *supersedes;
char *date;
FREE (&(*p)->list_post);
FREE (&(*p)->subject);
/* real_subj is just an offset to subject and shouldn't be freed */
+ FREE (&(*p)->disp_subj);
FREE (&(*p)->message_id);
FREE (&(*p)->supersedes);
FREE (&(*p)->date);
{
base->subject = (*extra)->subject;
base->real_subj = (*extra)->real_subj;
+ base->disp_subj = (*extra)->disp_subj;
(*extra)->subject = NULL;
(*extra)->real_subj = NULL;
+ (*extra)->disp_subj = NULL;
}
/* spam and user headers should never be hashed, and the new envelope may
* have better values. Use new versions regardless. */