From c7a9a7b5dd1382f12f094644ca82196f7c26162c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 3 Aug 2016 09:30:25 +0900 Subject: [PATCH] [PATCH 5/8] lib: iterator API for message properties --- e7/c235d0d2ecd2178693c1d081b13c877ce6c843 | 336 ++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 e7/c235d0d2ecd2178693c1d081b13c877ce6c843 diff --git a/e7/c235d0d2ecd2178693c1d081b13c877ce6c843 b/e7/c235d0d2ecd2178693c1d081b13c877ce6c843 new file mode 100644 index 000000000..62f79eaab --- /dev/null +++ b/e7/c235d0d2ecd2178693c1d081b13c877ce6c843 @@ -0,0 +1,336 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by arlo.cworth.org (Postfix) with ESMTP id 19F486DE091B + for ; Tue, 2 Aug 2016 21:39:05 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at cworth.org +X-Spam-Flag: NO +X-Spam-Score: -0.005 +X-Spam-Level: +X-Spam-Status: No, score=-0.005 tagged_above=-999 required=5 + tests=[AWL=-0.006, HEADER_FROM_DIFFERENT_DOMAINS=0.001] + 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 K47JhQzC1u2K for ; + Tue, 2 Aug 2016 21:38:57 -0700 (PDT) +Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197]) + by arlo.cworth.org (Postfix) with ESMTPS id 244EB6DE02B5 + for ; Tue, 2 Aug 2016 21:38:41 -0700 (PDT) +Received: from remotemail by fethera.tethera.net with local (Exim 4.84_2) + (envelope-from ) + id 1bUnxJ-0000Gp-CQ; Wed, 03 Aug 2016 00:38:57 -0400 +Received: (nullmailer pid 12787 invoked by uid 1000); + Wed, 03 Aug 2016 00:30:32 -0000 +From: David Bremner +To: notmuch@notmuchmail.org +Subject: [PATCH 5/8] lib: iterator API for message properties +Date: Wed, 3 Aug 2016 09:30:25 +0900 +Message-Id: <1470184228-12517-6-git-send-email-david@tethera.net> +X-Mailer: git-send-email 2.8.1 +In-Reply-To: <1470184228-12517-1-git-send-email-david@tethera.net> +References: <1470184228-12517-1-git-send-email-david@tethera.net> +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: Wed, 03 Aug 2016 04:39:05 -0000 + +This is a thin wrapper around the string map iterator API just introduced. +--- + lib/message-property.cc | 38 +++++++++++++++ + lib/notmuch.h | 95 +++++++++++++++++++++++++++++++++++++ + test/T610-message-property.sh | 107 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 240 insertions(+) + +diff --git a/lib/message-property.cc b/lib/message-property.cc +index 1f04a20..0b13cac 100644 +--- a/lib/message-property.cc ++++ b/lib/message-property.cc +@@ -106,3 +106,41 @@ notmuch_message_remove_all_properties (notmuch_message_t *message, const char *k + + return NOTMUCH_STATUS_SUCCESS; + } ++ ++notmuch_message_properties_t * ++notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact) ++{ ++ notmuch_string_map_t *map; ++ map = _notmuch_message_property_map (message); ++ return _notmuch_string_map_iterator_create (map, key, exact); ++} ++ ++notmuch_bool_t ++notmuch_message_properties_valid (notmuch_message_properties_t *properties) ++{ ++ return _notmuch_string_map_iterator_valid (properties); ++} ++ ++void ++notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties) ++{ ++ return _notmuch_string_map_iterator_move_to_next (properties); ++} ++ ++const char * ++notmuch_message_properties_key (notmuch_message_properties_t *properties) ++{ ++ return _notmuch_string_map_iterator_key (properties); ++} ++ ++const char * ++notmuch_message_properties_value (notmuch_message_properties_t *properties) ++{ ++ return _notmuch_string_map_iterator_value (properties); ++} ++ ++void ++notmuch_message_properties_destroy (notmuch_message_properties_t *properties) ++{ ++ _notmuch_string_map_iterator_destroy (properties); ++} +diff --git a/lib/notmuch.h b/lib/notmuch.h +index b304dca..e03a05d 100644 +--- a/lib/notmuch.h ++++ b/lib/notmuch.h +@@ -1723,6 +1723,101 @@ notmuch_message_remove_property (notmuch_message_t *message, const char *key, co + notmuch_status_t + notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key); + ++/** ++ * Opaque message property iterator ++ */ ++typedef struct _notmuch_string_map_iterator notmuch_message_properties_t; ++ ++/** ++ * Get the properties for *message*, returning a ++ * notmuch_message_properties_t object which can be used to iterate ++ * over all properties. ++ * ++ * The notmuch_message_properties_t object is owned by the message and ++ * as such, will only be valid for as long as the message is valid, ++ * (which is until the query from which it derived is destroyed). ++ * ++ * @param[in] message The message to examine ++ * @param[in] key key or key prefix ++ * @param[in] exact if TRUE, require exact match with key. Otherwise ++ * treat as prefix. ++ * ++ * Typical usage might be: ++ * ++ * notmuch_message_properties_t *list; ++ * ++ * for (list = notmuch_message_get_properties (message, "testkey1", TRUE); ++ * notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { ++ * printf("%s\n", notmuch_message_properties_value(list)); ++ * } ++ * ++ * notmuch_message_properties_destroy (list); ++ * ++ * Note that there's no explicit destructor needed for the ++ * notmuch_message_properties_t object. (For consistency, we do ++ * provide a notmuch_message_properities_destroy function, but there's ++ * no good reason to call it if the message is about to be destroyed). ++ */ ++notmuch_message_properties_t * ++notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact); ++ ++/** ++ * Is the given *properties* iterator pointing at a valid (key,value) ++ * pair. ++ * ++ * When this function returns TRUE, ++ * notmuch_message_properties_{key,value} will return a valid string, ++ * and notmuch_message_properties_move_to_next will do what it ++ * says. Whereas when this function returns FALSE, calling any of ++ * these functions results in undefined behaviour. ++ * ++ * See the documentation of notmuch_message_properties_get for example ++ * code showing how to iterate over a notmuch_message_properties_t ++ * object. ++ */ ++notmuch_bool_t ++notmuch_message_properties_valid (notmuch_message_properties_t *properties); ++ ++/** ++ * Move the *properties* iterator to the next (key,value) pair ++ * ++ * If *properties* is already pointing at the last pair then the iterator ++ * will be moved to a point just beyond that last pair, (where ++ * notmuch_message_properties_valid will return FALSE). ++ * ++ * See the documentation of notmuch_message_get_properties for example ++ * code showing how to iterate over a notmuch_message_properties_t object. ++ */ ++void ++notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties); ++ ++/** ++ * Return the key from the current (key,value) pair. ++ * ++ * this could be useful if iterating for a prefix ++ */ ++const char * ++notmuch_message_properties_key (notmuch_message_properties_t *properties); ++ ++/** ++ * Return the key from the current (key,value) pair. ++ * ++ * This could be useful if iterating for a prefix. ++ */ ++const char * ++notmuch_message_properties_value (notmuch_message_properties_t *properties); ++ ++ ++/** ++ * Destroy a notmuch_message_properties_t object. ++ * ++ * It's not strictly necessary to call this function. All memory from ++ * the notmuch_message_properties_t object will be reclaimed when the ++ * containing message object is destroyed. ++ */ ++void ++notmuch_message_properties_destroy (notmuch_message_properties_t *properties); ++ + /**@}*/ + + /** +diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh +index 0217950..b5ddb7a 100755 +--- a/test/T610-message-property.sh ++++ b/test/T610-message-property.sh +@@ -9,8 +9,18 @@ cat < c_head + #include + #include + #include ++#include + #include + ++void print_properties (notmuch_message_t *message, const char *prefix, notmuch_bool_t exact) { ++ notmuch_message_properties_t *list; ++ for (list = notmuch_message_get_properties (message, prefix, exact); ++ notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { ++ printf("%s\n", notmuch_message_properties_value(list)); ++ } ++ notmuch_message_properties_destroy (list); ++} ++ + int main (int argc, char** argv) + { + notmuch_database_t *db; +@@ -79,6 +89,103 @@ testkey2 = NULL + EOF + test_expect_equal_file EXPECTED OUTPUT + ++test_begin_subtest "notmuch_message_get_properties: empty list" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++{ ++ notmuch_message_properties_t *list; ++ list = notmuch_message_get_properties (message, "nonexistent", TRUE); ++ printf("valid = %d\n", notmuch_message_properties_valid (list)); ++ notmuch_message_properties_destroy (list); ++} ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++valid = 0 ++== stderr == ++EOF ++test_expect_equal_file EXPECTED OUTPUT + ++test_begin_subtest "notmuch_message_properties: one value" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++print_properties (message, "testkey1", TRUE); ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++testvalue1 ++== stderr == ++EOF ++test_expect_equal_file EXPECTED OUTPUT ++ ++test_begin_subtest "notmuch_message_properties: multiple values" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++EXPECT0(notmuch_message_add_property (message, "testkey1", "bob")); ++EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue2")); ++EXPECT0(notmuch_message_add_property (message, "testkey1", "alice")); ++print_properties (message, "testkey1", TRUE); ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++alice ++bob ++testvalue1 ++testvalue2 ++== stderr == ++EOF ++test_expect_equal_file EXPECTED OUTPUT ++ ++test_begin_subtest "notmuch_message_properties: prefix" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++EXPECT0(notmuch_message_add_property (message, "testkey3", "bob3")); ++EXPECT0(notmuch_message_add_property (message, "testkey3", "testvalue3")); ++EXPECT0(notmuch_message_add_property (message, "testkey3", "alice3")); ++print_properties (message, "testkey", FALSE); ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++alice ++bob ++testvalue1 ++testvalue2 ++alice3 ++bob3 ++testvalue3 ++== stderr == ++EOF ++test_expect_equal_file EXPECTED OUTPUT ++ ++test_begin_subtest "notmuch_message_properties: modify during iteration" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++{ ++ const char *keys[1000] = {NULL}; ++ const char *vals[1000] = {NULL}; ++ notmuch_message_properties_t *properties; ++ int i; ++ ++ for (properties = notmuch_message_get_properties (message, "", FALSE), i=0; ++ notmuch_message_properties_valid (properties); ++ notmuch_message_properties_move_to_next (properties), i++) ++ { ++ const char *key, *value; ++ ++ keys[i]=talloc_strdup(message, ++ notmuch_message_properties_key (properties)); ++ vals[i]=talloc_strdup(message, ++ notmuch_message_properties_value (properties)); ++ ++ EXPECT0(notmuch_message_remove_property (message, keys[i], vals[i])); ++ } ++ ++ print_properties (message, "", FALSE); ++ ++ for (i = 0; keys[i] && vals[i]; i++) { ++ EXPECT0(notmuch_message_add_property (message, keys[i], vals[i])); ++ } ++} ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++== stderr == ++EOF ++test_expect_equal_file EXPECTED OUTPUT + + test_done +-- +2.8.1 + -- 2.26.2