From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id 394A06DE0926 for ; Mon, 30 May 2016 04:50:36 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org X-Spam-Flag: NO X-Spam-Score: -0.012 X-Spam-Level: X-Spam-Status: No, score=-0.012 tagged_above=-999 required=5 tests=[AWL=-0.001, SPF_PASS=-0.001, T_RP_MATCHES_RCVD=-0.01] autolearn=disabled Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id mMopsA6eUKDo for ; Mon, 30 May 2016 04:50:27 -0700 (PDT) Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197]) by arlo.cworth.org (Postfix) with ESMTPS id E60AE6DE02A6 for ; Mon, 30 May 2016 04:50:12 -0700 (PDT) Received: from remotemail by fethera.tethera.net with local (Exim 4.84) (envelope-from ) id 1b7Lhq-0000Q4-20; Mon, 30 May 2016 07:50:02 -0400 Received: (nullmailer pid 14852 invoked by uid 1000); Mon, 30 May 2016 11:50:06 -0000 From: David Bremner To: notmuch@notmuchmail.org Subject: [RFC2 Patch 3/5] lib: basic message-property API Date: Mon, 30 May 2016 08:49:57 -0300 Message-Id: <1464608999-14774-4-git-send-email-david@tethera.net> X-Mailer: git-send-email 2.8.1 In-Reply-To: <1464608999-14774-1-git-send-email-david@tethera.net> References: <1463927339-5441-1-git-send-email-david@tethera.net> <1464608999-14774-1-git-send-email-david@tethera.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 30 May 2016 11:50:36 -0000 Initially, support get, set and remove of single key/value pair --- lib/Makefile.local | 1 + lib/message-private.h | 10 +++++ lib/message-property.cc | 86 +++++++++++++++++++++++++++++++++++++++ lib/message.cc | 48 ++++++++++++++++++++++ lib/notmuch.h | 14 +++++++ test/T610-message-property.sh | 94 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 lib/message-private.h create mode 100644 lib/message-property.cc create mode 100755 test/T610-message-property.sh diff --git a/lib/Makefile.local b/lib/Makefile.local index 9280880..c012ed1 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \ $(dir)/directory.cc \ $(dir)/index.cc \ $(dir)/message.cc \ + $(dir)/message-property.cc \ $(dir)/query.cc \ $(dir)/query-fp.cc \ $(dir)/config.cc \ diff --git a/lib/message-private.h b/lib/message-private.h new file mode 100644 index 0000000..61e5bac --- /dev/null +++ b/lib/message-private.h @@ -0,0 +1,10 @@ +#ifndef MESSAGE_PRIVATE_H +#define MESSAGE_PRIVATE_H + +notmuch_string_map_t * +_notmuch_message_property_map (notmuch_message_t *message); + +notmuch_bool_t +_notmuch_message_frozen (notmuch_message_t *message); + +#endif diff --git a/lib/message-property.cc b/lib/message-property.cc new file mode 100644 index 0000000..21348a3 --- /dev/null +++ b/lib/message-property.cc @@ -0,0 +1,86 @@ +/* message-property.cc - Properties are like tags, but (key,value) pairs. + * keys are allowed to repeat. + * + * This file is part of notmuch. + * + * Copyright © 2016 David Bremner + * + * 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 3 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, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "notmuch-private.h" +#include "database-private.h" +#include "message-private.h" + +notmuch_status_t +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value) +{ + if (! value) + return NOTMUCH_STATUS_NULL_POINTER; + + *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key); + + return NOTMUCH_STATUS_SUCCESS; +} + +static notmuch_status_t +_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value, + notmuch_bool_t delete_it) +{ + notmuch_private_status_t private_status; + notmuch_status_t status; + char *term = NULL; + + status = _notmuch_database_ensure_writable (_notmuch_message_database (message)); + if (status) + return status; + + if (key == NULL || value == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + + if (index (key, '=') || index (value, '=')) + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; + + term = talloc_asprintf (message, "%s=%s", key, value); + + if (delete_it) + private_status = _notmuch_message_remove_term (message, "property", term); + else + private_status = _notmuch_message_add_term (message, "property", term); + + if (private_status) + return COERCE_STATUS (private_status, + "Unhandled error modifying message property"); + if (! _notmuch_message_frozen (message)) + _notmuch_message_sync (message); + + if (term) + talloc_free (term); + + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value) +{ + return _notmuch_message_modify_property (message, key, value, FALSE); +} + +notmuch_status_t +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value) +{ + return _notmuch_message_modify_property (message, key, value, TRUE); +} diff --git a/lib/message.cc b/lib/message.cc index 47946a3..0eb7e2c 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -20,6 +20,7 @@ #include "notmuch-private.h" #include "database-private.h" +#include "message-private.h" #include @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message) { return message->notmuch; } + +void +_notmuch_message_ensure_property_map (notmuch_message_t *message) +{ + notmuch_string_node_t *node; + + if (message->property_map) + return; + + if (!message->property_term_list) + _notmuch_message_ensure_metadata (message); + + message->property_map = _notmuch_string_map_create (message); + + for (node = message->property_term_list->head; node; node = node->next) { + const char *key; + char *value; + + value = index(node->string, '='); + if (!value) + INTERNAL_ERROR ("malformed property term"); + + *value = '\0'; + value++; + key = node->string; + + _notmuch_string_map_append (message->property_map, key, value); + + } + + talloc_free (message->property_term_list); + message->property_term_list = NULL; +} + +notmuch_string_map_t * +_notmuch_message_property_map (notmuch_message_t *message) +{ + _notmuch_message_ensure_property_map (message); + + return message->property_map; +} + +notmuch_bool_t +_notmuch_message_frozen (notmuch_message_t *message) +{ + return message->frozen; +} diff --git a/lib/notmuch.h b/lib/notmuch.h index bd977c3..c9e654e 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -180,6 +180,11 @@ typedef enum _notmuch_status { */ NOTMUCH_STATUS_PATH_ERROR, /** + * One of the arguments violates the preconditions for the + * function, in a way not covered by a more specific argument. + */ + NOTMUCH_STATUS_ILLEGAL_ARGUMENT, + /** * Not an actual status value. Just a way to find out how many * valid status values there are. */ @@ -1651,6 +1656,15 @@ notmuch_message_thaw (notmuch_message_t *message); void notmuch_message_destroy (notmuch_message_t *message); +notmuch_status_t +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value); + +notmuch_status_t +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value); + +notmuch_status_t +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value); + /** * Is the given 'tags' iterator pointing at a valid tag. * diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh new file mode 100755 index 0000000..45ed66b --- /dev/null +++ b/test/T610-message-property.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +test_description="message property API" + +. ./test-lib.sh || exit 1 + +add_email_corpus + +cat < c_head +#include +#include +#include +#include + +void run(int line, notmuch_status_t ret) +{ + if (ret) { + fprintf (stderr, "line %d: %s\n", line, ret); + exit (1); + } +} + +#define RUN(v) run(__LINE__, v); + +int main (int argc, char** argv) +{ + notmuch_database_t *db; + notmuch_message_t *message = NULL; + const char *val; + notmuch_status_t stat; + + RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + RUN(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message)); + if (message == NULL) { + fprintf (stderr, "unable to find message"); + exit (1); + } +EOF + +cat < c_tail + RUN(notmuch_database_destroy(db)); +} +EOF + +test_begin_subtest "notmuch_message_{add,get,remove}_property" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + RUN(notmuch_message_add_property (message, "testkey1", "testvalue1")); + RUN(notmuch_message_get_property (message, "testkey1", &val)); + printf("testkey1[1] = %s\n", val); + RUN(notmuch_message_add_property (message, "testkey2", "testvalue2")); + RUN(notmuch_message_get_property (message, "testkey1", &val)); + printf("testkey1[2] = %s\n", val); + RUN(notmuch_message_get_property (message, "testkey1", &val)); + + RUN(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* Add second value for key */ + RUN(notmuch_message_add_property (message, "testkey2", "testvalue3")); + RUN(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove first value for key */ + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2")); + RUN(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove non-existant value for key */ + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2")); + RUN(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove only value for key */ + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue3")); + RUN(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val == NULL ? "NULL" : val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +testkey1[1] = testvalue1 +testkey1[2] = testvalue1 +testkey2 = testvalue2 +testkey2 = testvalue2 +testkey2 = testvalue3 +testkey2 = testvalue3 +testkey2 = NULL +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + + + +test_done -- 2.8.1