This change make the notmuch tags generic.
This is in preparation of reusing it for imap keywords feature.
query.c recvattach.c recvcmd.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \
rfc822.c safe_asprintf.c score.c send.c sendlib.c sidebar.c signal.c \
smtp.c sort.c state.c state.h status.c system.c thread.c thread.h url.c \
- version.c where.h
+ version.c where.h mutt_tags.c
nodist_neomutt_SOURCES = $(BUILT_SOURCES)
mutt_socket.h mutt_ssl.h mutt_tunnel.h mx.h myvar.h nntp.h opcodes.h pager.h \
pgpewrap.c pop.h protos.h queue.h README.md README.SSL remailer.c remailer.h \
rfc1524.h rfc2047.h rfc2231.h rfc3676.h rfc822.h sidebar.h \
- sort.h txt2c.c txt2c.sh version.h
+ sort.h txt2c.c txt2c.sh version.h mutt_tags.h
EXTRA_SCRIPTS =
struct ColorLine *ColorIndexAuthorList = NULL;
struct ColorLine *ColorIndexFlagsList = NULL;
struct ColorLine *ColorIndexSubjectList = NULL;
-#ifdef USE_NOTMUCH
struct ColorLine *ColorIndexTagList = NULL;
-#endif
/* local to this file */
static int ColorQuoteSize;
{ "index_number", MT_COLOR_INDEX_NUMBER },
{ "index_size", MT_COLOR_INDEX_SIZE },
{ "index_subject", MT_COLOR_INDEX_SUBJECT },
-#ifdef USE_NOTMUCH
{ "index_tag", MT_COLOR_INDEX_TAG },
{ "index_tags", MT_COLOR_INDEX_TAGS },
-#endif
{ "prompt", MT_COLOR_PROMPT },
#ifdef USE_SIDEBAR
{ "sidebar_divider", MT_COLOR_DIVIDER },
do_uncolor(buf, s, &ColorIndexFlagsList, &do_cache, parse_uncolor);
else if (object == MT_COLOR_INDEX_SUBJECT)
do_uncolor(buf, s, &ColorIndexSubjectList, &do_cache, parse_uncolor);
-#ifdef USE_NOTMUCH
else if (object == MT_COLOR_INDEX_TAG)
do_uncolor(buf, s, &ColorIndexTagList, &do_cache, parse_uncolor);
-#endif
if (do_cache && !option(OPT_NO_CURSES))
{
if ((object == MT_COLOR_BODY) || (object == MT_COLOR_HEADER) ||
(object == MT_COLOR_ATTACH_HEADERS) || (object == MT_COLOR_INDEX) ||
(object == MT_COLOR_INDEX_AUTHOR) || (object == MT_COLOR_INDEX_FLAGS) ||
-#ifdef USE_NOTMUCH
- (object == MT_COLOR_INDEX_TAG) ||
-#endif
- (object == MT_COLOR_INDEX_SUBJECT))
+ (object == MT_COLOR_INDEX_TAG) || (object == MT_COLOR_INDEX_SUBJECT))
{
if (!MoreArgs(s))
{
r = add_pattern(&ColorIndexSubjectList, buf->data, 1, fg, bg, attr, err, 1, match);
mutt_set_menu_redraw_full(MENU_MAIN);
}
-#ifdef USE_NOTMUCH
else if (object == MT_COLOR_INDEX_TAG)
{
r = add_pattern(&ColorIndexTagList, buf->data, 1, fg, bg, attr, err, 1, match);
mutt_set_menu_redraw_full(MENU_MAIN);
}
-#endif
else if (object == MT_COLOR_QUOTED)
{
if (q_level >= ColorQuoteSize)
#include "mime.h"
#include "mutt_curses.h"
#include "mutt_idna.h"
+#include "mutt_tags.h"
#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
fputs(buf, out);
fputc('\n', out);
}
- char *tags = nm_header_get_tags(h);
+ const char *tags = hdr_tags_get(h);
if (tags && !(option(OPT_WEED) && mutt_matches_ignore("tags")))
{
fputs("Tags: ", out);
{ "sidebar-toggle-virtual", OP_SIDEBAR_TOGGLE_VIRTUAL, NULL },
{ "sidebar-toggle-visible", OP_SIDEBAR_TOGGLE_VISIBLE, NULL },
#endif
+ { "modify-labels", OP_MAIN_MODIFY_LABELS, NULL },
+ { "modify-labels-then-hide", OP_MAIN_MODIFY_LABELS_THEN_HIDE, NULL },
+
#ifdef USE_NOTMUCH
{ "change-vfolder", OP_MAIN_CHANGE_VFOLDER, NULL },
{ "vfolder-from-query", OP_MAIN_VFOLDER_FROM_QUERY, NULL },
{ "vfolder-window-backward", OP_MAIN_WINDOWED_VFOLDER_BACKWARD, NULL },
{ "vfolder-window-forward", OP_MAIN_WINDOWED_VFOLDER_FORWARD, NULL },
- { "modify-labels", OP_MAIN_MODIFY_LABELS, NULL },
- { "modify-labels-then-hide", OP_MAIN_MODIFY_LABELS_THEN_HIDE, NULL },
{ "entire-thread", OP_MAIN_ENTIRE_THREAD, NULL },
#endif
{ NULL, 0, NULL },
{ "sidebar-toggle-virtual", OP_SIDEBAR_TOGGLE_VIRTUAL, NULL },
{ "sidebar-toggle-visible", OP_SIDEBAR_TOGGLE_VISIBLE, NULL },
#endif
+ { "modify-labels", OP_MAIN_MODIFY_LABELS, NULL },
+ { "modify-labels-then-hide", OP_MAIN_MODIFY_LABELS_THEN_HIDE, NULL },
+
#ifdef USE_NOTMUCH
{ "change-vfolder", OP_MAIN_CHANGE_VFOLDER, NULL },
{ "vfolder-from-query", OP_MAIN_VFOLDER_FROM_QUERY, NULL },
- { "modify-labels", OP_MAIN_MODIFY_LABELS, NULL },
- { "modify-labels-then-hide", OP_MAIN_MODIFY_LABELS_THEN_HIDE, NULL },
{ "entire-thread", OP_MAIN_ENTIRE_THREAD, NULL },
#endif
{ NULL, 0, NULL },
WHERE struct Hash *Groups;
WHERE struct Hash *ReverseAliases;
-#ifdef USE_NOTMUCH
+WHERE char *HiddenTags;
WHERE struct Hash *TagTransforms;
WHERE struct Hash *TagFormats;
-#endif
WHERE struct ListHead AutoViewList INITVAL(STAILQ_HEAD_INITIALIZER(AutoViewList));
WHERE struct ListHead AlternativeOrderList INITVAL(STAILQ_HEAD_INITIALIZER(AlternativeOrderList));
WHERE char *NmDefaultUri;
WHERE char *NmExcludeTags;
WHERE char *NmUnreadTag;
-WHERE char *NmHiddenTags;
WHERE char *VfolderFormat;
WHERE int NmDbLimit;
WHERE char *NmQueryType;
nh.path = NULL;
nh.tree = NULL;
nh.thread = NULL;
+ nh.tags = NULL;
#ifdef MIXMASTER
STAILQ_INIT(&nh.chain);
#endif
#include "mbtable.h"
#include "mutt_curses.h"
#include "mutt_idna.h"
+#include "mutt_tags.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#include "protos.h"
* | \%E | number of messages in current thread
* | \%f | entire from line
* | \%F | like %n, unless from self
- * | \%g | message labels (e.g. notmuch tags)
+ * | \%g | message tags (e.g. notmuch tags)
+ * | \%Gx | individual message tag (e.g. notmuch tags)
* | \%i | message-id
* | \%I | initials of author
* | \%K | the list to which the letter was sent (if any; otherwise: empty)
optional = 0;
break;
-#ifdef USE_NOTMUCH
case 'g':
if (!optional)
{
colorlen = add_index_color(dest, destlen, flags, MT_COLOR_INDEX_TAGS);
mutt_format_s(dest + colorlen, destlen - colorlen, prefix,
- nm_header_get_tags_transformed(hdr));
+ hdr_tags_get_transformed(hdr));
add_index_color(dest + colorlen, destlen - colorlen, flags, MT_COLOR_INDEX);
}
- else if (!nm_header_get_tags_transformed(hdr))
+ else if (!hdr_tags_get_transformed(hdr))
optional = 0;
break;
- case 'G':
- {
- char *tag_transformed = NULL;
+ case 'G':;
+ const char *tag_transformed = NULL;
char format[3];
char *tag = NULL;
tag = hash_find(TagFormats, format);
if (tag)
{
- tag_transformed = nm_header_get_tag_transformed(tag, hdr);
-
+ tag_transformed = hdr_tags_get_transformed_for(tag, hdr);
colorlen = add_index_color(dest, destlen, flags, MT_COLOR_INDEX_TAG);
mutt_format_s(dest + colorlen, destlen - colorlen, prefix,
(tag_transformed) ? tag_transformed : "");
add_index_color(dest + colorlen, destlen - colorlen, flags, MT_COLOR_INDEX);
}
-
src++;
}
else
tag = hash_find(TagFormats, format);
if (tag)
- if (nm_header_get_tag_transformed(tag, hdr) == NULL)
+ if (hdr_tags_get_transformed_for(tag, hdr) == NULL)
optional = 0;
}
break;
- }
-#endif
case 'H':
/* (Hormel) spam score */
int refno; /**< message number on server */
#endif
+ void *tags; /* for driver that support server tagging */
#if defined(USE_POP) || defined(USE_IMAP) || defined(USE_NNTP) || defined(USE_NOTMUCH)
void *data; /**< driver-specific data */
void (*free_cb)(struct Header *); /**< driver-specific data free function */
/* reverse alias keys need to be strdup'ed because of idna conversions */
ReverseAliases =
hash_create(1031, MUTT_HASH_STRCASECMP | MUTT_HASH_STRDUP_KEYS | MUTT_HASH_ALLOW_DUPS);
-#ifdef USE_NOTMUCH
TagTransforms = hash_create(64, 1);
TagFormats = hash_create(64, 0);
-#endif
mutt_menu_init();
return -1;
}
-#ifdef USE_NOTMUCH
static int parse_tag_transforms(struct Buffer *b, struct Buffer *s,
unsigned long data, struct Buffer *err)
{
}
return 0;
}
-#endif
#ifdef USE_IMAP
/**
** This variable specifies the default Notmuch database in format
** notmuch://<absolute path>.
*/
- { "nm_hidden_tags", DT_STRING, R_NONE, UL &NmHiddenTags, UL "unread,draft,flagged,passed,replied,attachment,signed,encrypted" },
- /*
- ** .pp
- ** This variable specifies private notmuch tags which should not be printed
- ** on screen.
- */
{ "nm_exclude_tags", DT_STRING, R_NONE, UL &NmExcludeTags, 0 },
/*
** .pp
** directly from the pager, and screen resizes cause lines longer than
** the screen width to be badly formatted in the help menu.
*/
+ { "hidden_tags", DT_STRING, R_NONE, UL &HiddenTags, UL "unread,draft,flagged,passed,replied,attachment,signed,encrypted" },
+ /*
+ ** .pp
+ ** This variable specifies private notmuch tags which should not be printed
+ ** on screen.
+ */
{ "pager_context", DT_NUMBER, R_NONE, UL &PagerContext, 0 },
/*
** .pp
static int parse_stailq (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_subjectrx_list (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_subscribe (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
+static int parse_tag_formats (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
+static int parse_tag_transforms (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_unalias (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_unalternates (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_unattachments (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_subscribe_to (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
#endif
-#ifdef USE_NOTMUCH
-static int parse_tag_formats (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
-static int parse_tag_transforms (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
-#endif
#ifdef USE_SIDEBAR
static int parse_path_list (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
static int parse_path_unlist (struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err);
#ifdef USE_IMAP
{ "subscribe-to", parse_subscribe_to, 0 },
#endif
-#ifdef USE_NOTMUCH
{ "tag-formats", parse_tag_formats, 0 },
{ "tag-transforms", parse_tag_transforms, 0 },
-#endif
{ "timeout-hook", mutt_parse_hook, MUTT_TIMEOUTHOOK | MUTT_GLOBALHOOK },
{ "toggle", parse_set, MUTT_SET_INV },
{ "unalias", parse_unalias, 0 },
* | mutt_strncmp() | Compare two strings (to a maximum), safely
* | mutt_str_adjust() | Shrink-to-fit a string
* | mutt_str_replace() | Replace one string with another
+ * | mutt_str_append_item() | Add a string to another
* | mutt_substrcpy() | Copy a sub-string into a buffer
* | mutt_substrdup() | Duplicate a sub-string
* | rfc822_dequote_comment() | Un-escape characters in an email address comment
*p = safe_strdup(s);
}
+/**
+ * mutt_str_append_item - Add string to another seprated by sep
+ * @param str String appened
+ * @param item String to append
+ * @param sep separator between string item
+ *
+ * This function appends a string to another separate them by sep
+ * if needed
+ *
+ * This function alters the pointer of the caller.
+ */
+void mutt_str_append_item(char **str, const char *item, int sep)
+{
+ char *p = NULL;
+ size_t sz = strlen(item);
+ size_t ssz = *str ? strlen(*str) : 0;
+
+ safe_realloc(str, ssz + ((ssz && sep) ? 1 : 0) + sz + 1);
+ p = *str + ssz;
+ if (sep && ssz)
+ *p++ = sep;
+ memcpy(p, item, sz + 1);
+}
+
/**
* mutt_str_adjust - Shrink-to-fit a string
* @param p String to alter
void mutt_remove_trailing_ws(char *s);
char * mutt_skip_whitespace(char *p);
void mutt_str_adjust(char **p);
+void mutt_str_append_item(char **p, const char *item, int sep);
int mutt_strcasecmp(const char *a, const char *b);
const char *mutt_strchrnul(const char *s, char c);
int mutt_strcmp(const char *a, const char *b);
case MT_COLOR_INDEX_SUBJECT:
color = ColorIndexSubjectList;
break;
-#ifdef USE_NOTMUCH
case MT_COLOR_INDEX_TAG:
for (color = ColorIndexTagList; color; color = color->next)
{
return color->pair;
}
return 0;
-#endif
default:
return ColorDefs[type];
}
MUTT_PGP_KEY,
MUTT_XLABEL,
MUTT_SERVERSEARCH,
-#ifdef USE_NOTMUCH
- MUTT_NOTMUCH_LABEL,
-#endif
+ MUTT_DRIVER_LABEL,
MUTT_MIMEATTACH,
#ifdef USE_NNTP
MUTT_NEWSGROUPS,
MT_COLOR_INDEX,
MT_COLOR_INDEX_AUTHOR,
MT_COLOR_INDEX_FLAGS,
-#ifdef USE_NOTMUCH
MT_COLOR_INDEX_TAG,
-#endif
MT_COLOR_INDEX_SUBJECT,
/* below here - only index coloring stuff that doesn't have a pattern */
MT_COLOR_INDEX_COLLAPSED,
MT_COLOR_INDEX_LABEL,
MT_COLOR_INDEX_NUMBER,
MT_COLOR_INDEX_SIZE,
-#ifdef USE_NOTMUCH
MT_COLOR_INDEX_TAGS,
-#endif
MT_COLOR_COMPOSE_HEADER,
MT_COLOR_COMPOSE_SECURITY_ENCRYPT,
MT_COLOR_COMPOSE_SECURITY_SIGN,
extern struct ColorLine *ColorIndexAuthorList;
extern struct ColorLine *ColorIndexFlagsList;
extern struct ColorLine *ColorIndexSubjectList;
-
-#ifdef USE_NOTMUCH
extern struct ColorLine *ColorIndexTagList;
-#endif
void ci_start_color(void);
#include "header.h"
#include "mailbox.h"
#include "mutt_curses.h"
+#include "mutt_tags.h"
#include "mx.h"
#include "protos.h"
#include "thread.h"
struct UriTag *next;
};
-/**
- * struct NmHdrTag - NotMuch Mail Header Tags
- *
- * Keep a linked list of header tags and their transformed values.
- * Textual tags can be transformed to symbols to save space.
- *
- * @sa NmHdrData#tag_list
- */
-struct NmHdrTag
-{
- char *tag;
- char *transformed;
- struct NmHdrTag *next;
-};
-
-/**
+/*
* struct NmHdrData - NotMuch data attached to an email
*
* This stores all the NotMuch data associated with an email.
struct NmHdrData
{
char *folder; /**< Location of the email */
- char *tags;
- char *tags_transformed;
- struct NmHdrTag *tag_list;
char *oldpath;
char *virtual_id; /**< Unique NotMuch Id */
int magic; /**< Type of mailbox the email is in */
return false;
}
-/**
- * free_tag_list - Free a list of tags
- * @param tag_list List of tags
- *
- * Take a nm_hdrtag struct (a singly-linked list) and free the attached strings
- * and the list itself.
- */
-static void free_tag_list(struct NmHdrTag **tag_list)
-{
- struct NmHdrTag *tmp = NULL;
-
- while ((tmp = *tag_list) != NULL)
- {
- *tag_list = tmp->next;
- FREE(&tmp->tag);
- FREE(&tmp->transformed);
- FREE(&tmp);
- }
-
- *tag_list = 0;
-}
-
-/**
- * free_hdrdata - Free header data attached to an email
- * @param data Header data
- *
- * Each email can have an attached nm_hdrdata struct, which contains things
- * like the tags (labels). This function frees all the resources and the
- * nm_hdrdata struct itself.
- */
static void free_hdrdata(struct NmHdrData *data)
{
if (!data)
mutt_debug(2, "nm: freeing header %p\n", (void *) data);
FREE(&data->folder);
- FREE(&data->tags);
- FREE(&data->tags_transformed);
- free_tag_list(&data->tag_list);
FREE(&data->oldpath);
FREE(&data->virtual_id);
FREE(&data);
return NULL;
}
-static void append_str_item(char **str, const char *item, int sep)
-{
- char *p = NULL;
- size_t sz = strlen(item);
- size_t ssz = *str ? strlen(*str) : 0;
-
- safe_realloc(str, ssz + ((ssz && sep) ? 1 : 0) + sz + 1);
- p = *str + ssz;
- if (sep && ssz)
- *p++ = sep;
- memcpy(p, item, sz + 1);
-}
-
static int update_header_tags(struct Header *h, notmuch_message_t *msg)
{
struct NmHdrData *data = h->data;
notmuch_tags_t *tags = NULL;
- char *tstr = NULL, *ttstr = NULL;
- struct NmHdrTag *tag_list = NULL, *tmp = NULL;
+ char *new_tags = NULL;
mutt_debug(2, "nm: tags update requested (%s)\n", data->virtual_id);
notmuch_tags_move_to_next(tags))
{
const char *t = notmuch_tags_get(tags);
- const char *tt = NULL;
if (!t || !*t)
continue;
- tt = hash_find(TagTransforms, t);
- if (!tt)
- tt = t;
-
- /* tags list contains all tags */
- tmp = safe_calloc(1, sizeof(*tmp));
- tmp->tag = safe_strdup(t);
- tmp->transformed = safe_strdup(tt);
- tmp->next = tag_list;
- tag_list = tmp;
-
- /* filter out hidden tags */
- if (NmHiddenTags)
- {
- char *p = strstr(NmHiddenTags, t);
- size_t xsz = p ? strlen(t) : 0;
-
- if (p && ((p == NmHiddenTags) || (*(p - 1) == ',') || (*(p - 1) == ' ')) &&
- ((*(p + xsz) == '\0') || (*(p + xsz) == ',') || (*(p + xsz) == ' ')))
- continue;
- }
-
- /* expand the transformed tag string */
- append_str_item(&ttstr, tt, ' ');
-
- /* expand the un-transformed tag string */
- append_str_item(&tstr, t, ' ');
+ mutt_str_append_item(&new_tags, t, ' ');
}
- free_tag_list(&data->tag_list);
- data->tag_list = tag_list;
-
- if (data->tags && tstr && (strcmp(data->tags, tstr) == 0))
+ if (hdr_tags_get(h) && new_tags && (strcmp(hdr_tags_get(h), new_tags) == 0))
{
- FREE(&tstr);
- FREE(&ttstr);
+ FREE(&new_tags);
mutt_debug(2, "nm: tags unchanged\n");
return 1;
}
- /* free old version */
- FREE(&data->tags);
- FREE(&data->tags_transformed);
-
/* new version */
- data->tags = tstr;
- mutt_debug(2, "nm: new tags: '%s'\n", tstr);
-
- data->tags_transformed = ttstr;
- mutt_debug(2, "nm: new tag transforms: '%s'\n", ttstr);
+ hdr_tags_replace(h, new_tags);
+ mutt_debug(2, "nm: new tags: '%s'\n", hdr_tags_get(h));
+ mutt_debug(2, "nm: new tag transforms: '%s'\n", hdr_tags_get_transformed(h));
return 0;
}
h->data = safe_calloc(1, sizeof(struct NmHdrData));
h->free_cb = deinit_header;
+ hdr_tags_init(h);
/*
* Notmuch ensures that message Id exists (if not notmuch Notmuch will
{
notmuch_message_maildir_flags_to_tags(msg);
update_header_tags(h, msg);
- update_tags(msg, nm_header_get_tags(h));
+ update_tags(msg, hdr_tags_get(h));
}
rc = 0;
return (h && h->data) ? ((struct NmHdrData *) h->data)->folder : NULL;
}
-char *nm_header_get_tags(struct Header *h)
-{
- return (h && h->data) ? ((struct NmHdrData *) h->data)->tags : NULL;
-}
-
-char *nm_header_get_tags_transformed(struct Header *h)
-{
- return (h && h->data) ? ((struct NmHdrData *) h->data)->tags_transformed : NULL;
-}
-
-char *nm_header_get_tag_transformed(char *tag, struct Header *h)
-{
- struct NmHdrTag *tmp = NULL;
-
- if (!h || !h->data)
- return NULL;
-
- for (tmp = ((struct NmHdrData *) h->data)->tag_list; tmp != NULL; tmp = tmp->next)
- {
- if (strcmp(tag, tmp->tag) == 0)
- return tmp->transformed;
- }
-
- return NULL;
-}
-
void nm_longrun_init(struct Context *ctx, bool writable)
{
struct NmCtxData *data = get_ctxdata(ctx);
id = notmuch_message_get_thread_id(msg);
if (!id)
goto done;
- append_str_item(&qstr, "thread:", 0);
- append_str_item(&qstr, id, 0);
+ mutt_str_append_item(&qstr, "thread:", 0);
+ mutt_str_append_item(&qstr, id, 0);
q = notmuch_query_create(db, qstr);
FREE(&qstr);
{
notmuch_message_maildir_flags_to_tags(msg);
if (h)
- update_tags(msg, nm_header_get_tags(h));
+ update_tags(msg, hdr_tags_get(h));
if (NmRecordTags)
update_tags(msg, NmRecordTags);
}
--- /dev/null
+/*
+ * Copyright (C) 2017 Mehdi Abaakouk <sileht@sileht.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "mutt_tags.h"
+#include "globals.h"
+#include "lib/hash.h"
+#include "lib/string2.h"
+#include "header.h"
+
+#define HEADER_TAGS(ph) ((struct HeaderTags *) ((ph)->tags))
+
+/**
+ * hdr_tags_free_tag_list - free tag
+ * @param[in] h: pointer to a header struct
+ *
+ * Free tag
+ */
+void hdr_tags_free_tag_list(struct HeaderTag **kw_list)
+{
+ struct HeaderTag *tmp = NULL;
+
+ while ((tmp = *kw_list) != NULL)
+ {
+ *kw_list = tmp->next;
+ FREE(&tmp->name);
+ FREE(&tmp->transformed);
+ FREE(&tmp);
+ }
+
+ *kw_list = 0;
+}
+
+/**
+ * hdr_tags_free - Free tags from a header
+ * @param[in] h: pointer to a header struct
+ *
+ * Free the whole tags structure
+ */
+void hdr_tags_free(struct Header *h)
+{
+ if (!HEADER_TAGS(h))
+ return;
+ FREE(&HEADER_TAGS(h)->tags);
+ FREE(&HEADER_TAGS(h)->tags_transformed);
+ FREE(&HEADER_TAGS(h)->tags_with_hidden);
+ hdr_tags_free_tag_list(&HEADER_TAGS(h)->tag_list);
+}
+
+/**
+ * hdr_tags_get_transformed - Get transformed tags from a header
+ * @param[in] h: pointer to a header struct
+ *
+ * @return string transformed tags
+ *
+ * Return a string containing all transformed tags separated by space
+ * without hidden tags
+ */
+const char *hdr_tags_get_transformed(struct Header *h)
+{
+ if (!h || !HEADER_TAGS(h) || !HEADER_TAGS(h)->tags_transformed)
+ return NULL;
+ return HEADER_TAGS(h)->tags_transformed;
+}
+
+/*
+ * hdr_tags_get - Get tags from a header
+ * @param[in] h: pointer to a header struct
+ *
+ * @return string tags
+ *
+ * Return a string containing all tags separated by space with
+ * hidden tags
+ */
+const char *hdr_tags_get(struct Header *h)
+{
+ if (!h || !HEADER_TAGS(h) || !HEADER_TAGS(h)->tags)
+ return NULL;
+ return HEADER_TAGS(h)->tags;
+}
+
+/*
+ * hdr_tags_get - Get tags with hiddens from a header
+ * @param[in] h: pointer to a header struct
+ *
+ * @return string tags
+ *
+ * Return a string containing all tags separated by space
+ * even the hiddens.
+ */
+const char *hdr_tags_get_with_hidden(struct Header *h)
+{
+ if (!h || !HEADER_TAGS(h) || !HEADER_TAGS(h)->tags_with_hidden)
+ return NULL;
+ return HEADER_TAGS(h)->tags_with_hidden;
+}
+
+/**
+ * hdr_tags_get_transformed_for - Get tranformed tag for a tag name from a header
+ * @param[in] tag: char* to the tag to get the transformed version
+ * @param[in] h: pointer to a header struct
+ *
+ * @return string tag
+ *
+ * Return a string containing transformed tag that match the tag
+ * even if this is a hidden tags
+ */
+const char *hdr_tags_get_transformed_for(char *name, struct Header *h)
+{
+ if (!h || !HEADER_TAGS(h) || !HEADER_TAGS(h)->tag_list)
+ return NULL;
+
+ struct HeaderTag *tag = HEADER_TAGS(h)->tag_list;
+ while (tag)
+ {
+ if (strcmp(tag->name, name) == 0)
+ return tag->transformed;
+ tag = tag->next;
+ }
+ return NULL;
+}
+
+void hdr_tags_init(struct Header *h)
+{
+ h->tags = safe_calloc(1, sizeof(struct HeaderTags));
+ HEADER_TAGS(h)->tags = NULL;
+ HEADER_TAGS(h)->tags_transformed = NULL;
+ HEADER_TAGS(h)->tags_with_hidden = NULL;
+ HEADER_TAGS(h)->tag_list = NULL;
+}
+/**
+ * hdr_tags_replace - Add a tag to header
+ * @param[in] h: pointer to a header struct
+ * @param[in] new_tag: string representing the new tag
+ *
+ * Add a tag to the header tags
+ */
+void hdr_tags_add(struct Header *h, char *new_tag)
+{
+ struct HeaderTag *ttmp = NULL;
+ char *new_tag_transformed = NULL;
+
+ new_tag_transformed = hash_find(TagTransforms, new_tag);
+ if (!new_tag_transformed)
+ new_tag_transformed = new_tag;
+
+ ttmp = safe_calloc(1, sizeof(*ttmp));
+ ttmp->name = safe_strdup(new_tag);
+ ttmp->transformed = safe_strdup(new_tag_transformed);
+ ttmp->next = HEADER_TAGS(h)->tag_list;
+ HEADER_TAGS(h)->tag_list = ttmp;
+
+ /* expand the all un-transformed tag string */
+ mutt_str_append_item(&HEADER_TAGS(h)->tags_with_hidden, new_tag, ' ');
+
+ /* filter out hidden tags */
+ if (HiddenTags)
+ {
+ char *p = strstr(HiddenTags, new_tag);
+ size_t xsz = p ? strlen(new_tag) : 0;
+
+ if (p && ((p == HiddenTags) || (*(p - 1) == ',') || (*(p - 1) == ' ')) &&
+ ((*(p + xsz) == '\0') || (*(p + xsz) == ',') || (*(p + xsz) == ' ')))
+ return;
+ }
+
+ /* expand the visible un-transformed tag string */
+ mutt_str_append_item(&HEADER_TAGS(h)->tags, new_tag, ' ');
+
+ /* expand the transformed tag string */
+ mutt_str_append_item(&HEADER_TAGS(h)->tags_transformed, new_tag_transformed, ' ');
+}
+
+/**
+ * hdr_tags_replace - Replace all tags
+ * @param[in] h: pointer to a header struct
+ * @param[in] tags: string of all tags separated by space
+ *
+ * @retval 0 If no change are made
+ * @retval 1 If tags are updated
+ *
+ *
+ * Free current tags structures and replace it by
+ * new tags
+ */
+int hdr_tags_replace(struct Header *h, char *tags)
+{
+ if (!h)
+ return 0;
+ if (tags && HEADER_TAGS(h) && HEADER_TAGS(h)->tags &&
+ mutt_strcmp(HEADER_TAGS(h)->tags, tags) == 0)
+ return 0;
+
+ hdr_tags_free(h);
+
+ if (tags)
+ {
+ char *tag;
+ while ((tag = strsep(&tags, " ")))
+ hdr_tags_add(h, tag);
+ FREE(&tags);
+ }
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 Mehdi Abaakouk <sileht@sileht.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _MUTT_TAG_H
+#define _MUTT_TAG_H
+
+#include "header.h"
+#include "context.h"
+
+/**
+ * hdr_tag - Mail Header Tags
+ *
+ * Keep a linked list of header tags and their transformed values.
+ * Textual tags can be transformed to symbols to save space.
+ *
+ * @sa hdr_tags#tag_list
+ */
+struct HeaderTag
+{
+ char *name;
+ char *transformed;
+ struct HeaderTag *next;
+};
+
+/**
+ * struct HeaderTags - tags data attached to an email
+ *
+ * This stores all tags data associated with an email.
+ *
+ */
+struct HeaderTags
+{
+ /* Without hidden tags */
+ char *tags;
+ char *tags_transformed;
+
+ /* With hidden tags */
+ char *tags_with_hidden;
+ struct HeaderTag *tag_list;
+};
+
+void hdr_tags_free_tag_list(struct HeaderTag **kw_list);
+void hdr_tags_free(struct Header *h);
+const char *hdr_tags_get(struct Header *h);
+const char *hdr_tags_get_with_hidden(struct Header *h);
+const char *hdr_tags_get_transformed(struct Header *h);
+const char *hdr_tags_get_transformed_for(char *name, struct Header *h);
+void hdr_tags_init(struct Header *h);
+void hdr_tags_add(struct Header *h, char *new_tag);
+int hdr_tags_replace(struct Header *h, char *tags);
+
+#endif /* _MUTT_TAG_H */
#include "mime.h"
#include "mutt_curses.h"
#include "mutt_regex.h"
+#if defined USE_IMAP || defined USE_NOTMUCH
+#include "mutt_tags.h"
+#endif
+
#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#ifdef MIXMASTER
mutt_list_free(&(*h)->chain);
#endif
+ hdr_tags_free(*h);
+ FREE(&(*h)->tags);
#if defined(USE_POP) || defined(USE_IMAP) || defined(USE_NNTP) || defined(USE_NOTMUCH)
if ((*h)->free_cb)
(*h)->free_cb(*h);
_fmt(OP_MAIN_SHOW_LIMIT, N_("show currently active limit pattern")) \
_fmt(OP_MAIN_COLLAPSE_THREAD, N_("collapse/uncollapse current thread")) \
_fmt(OP_MAIN_COLLAPSE_ALL, N_("collapse/uncollapse all threads")) \
+ _fmt(OP_MAIN_MODIFY_LABELS, N_("modify (notmuch) tags")) \
+ _fmt(OP_MAIN_MODIFY_LABELS_THEN_HIDE, N_("modify labels and then hide message")) \
#define OPS_CRYPT(_fmt) \
_fmt(OP_DECRYPT_SAVE, N_("make decrypted copy and delete")) \
_fmt(OP_MAIN_VFOLDER_FROM_QUERY, N_("generate virtual folder from query")) \
_fmt(OP_MAIN_WINDOWED_VFOLDER_FORWARD, N_("shifts virtual folder time window forwards")) \
_fmt(OP_MAIN_WINDOWED_VFOLDER_BACKWARD, N_("shifts virtual folder time window backwards")) \
- _fmt(OP_MAIN_MODIFY_LABELS, N_("modify (notmuch) tags")) \
- _fmt(OP_MAIN_MODIFY_LABELS_THEN_HIDE, N_("modify labels and then hide message")) \
_fmt(OP_MAIN_ENTIRE_THREAD, N_("read entire thread of the current message"))
#else
#define OPS_NOTMUCH(_)
#include "thread.h"
#ifdef USE_IMAP
#include "imap/imap.h"
-#include "mx.h"
#endif
+#include "mutt_tags.h"
+#include "mx.h"
#ifdef USE_NOTMUCH
#include "mutt_notmuch.h"
#endif
{ 'x', MUTT_REFERENCE, 0, eat_regex },
{ 'X', MUTT_MIMEATTACH, 0, eat_range },
{ 'y', MUTT_XLABEL, 0, eat_regex },
-#ifdef USE_NOTMUCH
- { 'Y', MUTT_NOTMUCH_LABEL, 0, eat_regex },
-#endif
+ { 'Y', MUTT_DRIVER_LABEL, 0, eat_regex },
{ 'z', MUTT_SIZE, 0, eat_range },
{ '=', MUTT_DUPLICATED, 0, NULL },
{ '$', MUTT_UNREFERENCED, 0, NULL },
return (pat->not ^ ((h->security & APPLICATION_PGP) && (h->security & PGPKEY)));
case MUTT_XLABEL:
return (pat->not ^ (h->env->x_label && patmatch(pat, h->env->x_label) == 0));
-#ifdef USE_NOTMUCH
- case MUTT_NOTMUCH_LABEL:
- if (ctx && (ctx->magic == MUTT_NOTMUCH))
- {
- char *tags = nm_header_get_tags(h);
- return (pat->not ^ (tags && patmatch(pat, tags) == 0));
- }
- else
- {
- return 0;
- }
-#endif
+ case MUTT_DRIVER_LABEL:
+ {
+ const char *tags = hdr_tags_get(h);
+ return (pat->not ^ (tags && patmatch(pat, tags) == 0));
+ }
case MUTT_HORMEL:
return (pat->not ^ (h->env->spam && h->env->spam->data &&
patmatch(pat, h->env->spam->data) == 0));