From 0663272c47d0a858a4fc3bc14d3e67959292f6ad Mon Sep 17 00:00:00 2001 From: David Champion Date: Mon, 23 Jan 2017 19:01:50 -0800 Subject: [PATCH] Add subjectrx command to replace matching subjects with something else. This lets you define regular expressions-replacement pairs for subject display. When a Subject: matches the regular expression, the replacement value will be displayed instead in the message index. Backreferences are supported. This is especially nice for simplifying subjects that are overly wordy, such as mailing list posts (with [Listname] tags, etc), mail from ticketing systems or bug trackers, etc. It lets you reduce clutter in your mutt display without altering the messages themselves. --- doc/manual.xml.head | 83 +++++++++++++++++++++++++++++++++++++++++++++ globals.h | 1 + hdrline.c | 42 ++++++++++++++++++----- init.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ init.h | 6 ++++ mutt.h | 1 + muttlib.c | 3 ++ 7 files changed, 209 insertions(+), 9 deletions(-) diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 4a99f3de..14a190b4 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -6076,6 +6076,72 @@ threads and quickly find topics of value. + +Display Munging + + +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. + + + +subjectrx + +pattern + + +replacement + + +unsubjectrx + +pattern + + + + +subjectrx specifies a regular expression +pattern which, if detected in a message subject, causes +the subject to be replaced with the replacement value. +The replacement is subject to substitutions in the same way as for the +spam command: %L for the text +to the left of the match, %R for text to the right of the +match, and %1 for the first subgroup in the match (etc). +If you simply want to erase the match, set it to %L%R. +Any number of subjectrx commands may coexist. + + + +Note this well: the replacement value replaces the +entire subject, not just the match! + + + +unsubjectrx removes a given subjectrx from the substitution +list. + + + +Subject Munging + +# 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' + + + + + New Mail Detection @@ -9989,6 +10055,23 @@ The following are the commands understood by Mutt: + + +subjectrx + +pattern + + +format + + +unsubjectrx + +pattern + + + + subscribe diff --git a/globals.h b/globals.h index 97bf0183..a68338a8 100644 --- a/globals.h +++ b/globals.h @@ -184,6 +184,7 @@ WHERE RX_LIST *SubscribedLists INITVAL(0); 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 */ diff --git a/hdrline.c b/hdrline.c index 10248469..ba118bf5 100644 --- a/hdrline.c +++ b/hdrline.c @@ -199,6 +199,22 @@ int mutt_user_is_recipient (HEADER *h) return h->recipient; } +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 @@ -566,20 +582,28 @@ hdr_format_str (char *dest, 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) { - mutt_format_s (dest, destlen, "", NONULL (hdr->env->subject)); - snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree, dest); - mutt_format_s_tree (dest, destlen, prefix, buf2); + if (flags & MUTT_FORMAT_FORCESUBJ) + { + mutt_format_s (dest, destlen, "", NONULL (subj)); + 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 - mutt_format_s_tree (dest, destlen, prefix, hdr->tree); + mutt_format_s (dest, destlen, prefix, NONULL (subj)); } - else - mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->subject)); break; case 'S': diff --git a/init.c b/init.c index ca0d3e89..cdbfb820 100644 --- a/init.c +++ b/init.c @@ -757,6 +757,88 @@ static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFE 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; diff --git a/init.h b/init.h index 6734ded4..5b25761a 100644 --- a/init.h +++ b/init.h @@ -3898,6 +3898,10 @@ 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 *); @@ -3976,6 +3980,8 @@ const struct command_t Commands[] = { { "spam", parse_spam_list, MUTT_SPAM }, { "nospam", parse_spam_list, MUTT_NOSPAM }, { "subscribe", parse_subscribe, 0 }, + { "subjectrx", parse_subjectrx_list, UL &SubjectRxList }, + { "unsubjectrx", parse_unsubjectrx_list, UL &SubjectRxList }, { "toggle", parse_set, MUTT_SET_INV }, { "unalias", parse_unalias, 0 }, { "unalternative_order",parse_unlist, UL &AlternativeOrderList }, diff --git a/mutt.h b/mutt.h index 035c8dfd..c67c260d 100644 --- a/mutt.h +++ b/mutt.h @@ -618,6 +618,7 @@ typedef struct envelope 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; diff --git a/muttlib.c b/muttlib.c index 67bd4d1d..eaf8429d 100644 --- a/muttlib.c +++ b/muttlib.c @@ -735,6 +735,7 @@ void mutt_free_envelope (ENVELOPE **p) 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); @@ -782,8 +783,10 @@ void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) { 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. */ -- 2.40.0