From 808c480182be39fbe6401db0ef4bdf24b0b48765 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sat, 29 Dec 2012 13:26:25 +1900 Subject: [PATCH] [PATCH v3 1/5] util: Factor out boolean term quoting routine --- af/46e8576e9351d9aaee3376ce46d5494ebb1add | 287 ++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 af/46e8576e9351d9aaee3376ce46d5494ebb1add diff --git a/af/46e8576e9351d9aaee3376ce46d5494ebb1add b/af/46e8576e9351d9aaee3376ce46d5494ebb1add new file mode 100644 index 000000000..3ed6b065c --- /dev/null +++ b/af/46e8576e9351d9aaee3376ce46d5494ebb1add @@ -0,0 +1,287 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 72C47431FBF + for ; Fri, 28 Dec 2012 10:26:49 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: 1.151 +X-Spam-Level: * +X-Spam-Status: No, score=1.151 tagged_above=-999 required=5 + tests=[FUZZY_AMBIEN=1.851, RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id c4PYkrHDZpsp for ; + Fri, 28 Dec 2012 10:26:48 -0800 (PST) +Received: from dmz-mailsec-scanner-8.mit.edu (DMZ-MAILSEC-SCANNER-8.MIT.EDU + [18.7.68.37]) + by olra.theworths.org (Postfix) with ESMTP id 219F3431FAF + for ; Fri, 28 Dec 2012 10:26:48 -0800 (PST) +X-AuditID: 12074425-b7ff26d000007f8d-60-50dde467b2ba +Received: from mailhub-auth-2.mit.edu ( [18.7.62.36]) + by dmz-mailsec-scanner-8.mit.edu (Symantec Messaging Gateway) with SMTP + id AB.51.32653.764EDD05; Fri, 28 Dec 2012 13:26:47 -0500 (EST) +Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103]) + by mailhub-auth-2.mit.edu (8.13.8/8.9.2) with ESMTP id qBSIQkro003219; + Fri, 28 Dec 2012 13:26:46 -0500 +Received: from drake.dyndns.org (c-76-21-105-205.hsd1.ca.comcast.net + [76.21.105.205]) (authenticated bits=0) + (User authenticated as amdragon@ATHENA.MIT.EDU) + by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id qBSIQfOL012713 + (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); + Fri, 28 Dec 2012 13:26:43 -0500 (EST) +Received: from amthrax by drake.dyndns.org with local (Exim 4.77) + (envelope-from ) + id 1Toedj-0000sQ-Nl; Fri, 28 Dec 2012 13:26:39 -0500 +From: Austin Clements +To: notmuch@notmuchmail.org +Subject: [PATCH v3 1/5] util: Factor out boolean term quoting routine +Date: Fri, 28 Dec 2012 13:26:25 -0500 +Message-Id: <1356719189-2837-2-git-send-email-amdragon@mit.edu> +X-Mailer: git-send-email 1.7.10.4 +In-Reply-To: <1356719189-2837-1-git-send-email-amdragon@mit.edu> +References: <1356719189-2837-1-git-send-email-amdragon@mit.edu> +X-Brightmail-Tracker: + H4sIAAAAAAAAA+NgFrrAIsWRmVeSWpSXmKPExsUixG6nopv+5G6AwZtvwhY3WrsZLZqmO1us + nstjcf3mTGYHFo+ds+6ye9y6/5rd49mqW8weWw69Zw5gieKySUnNySxLLdK3S+DKmHt1LXvB + HqOK23sXszQwvlXtYuTkkBAwkXj5ZCo7hC0mceHeerYuRi4OIYF9jBIvN25igXA2MErcv9zP + DOFcZJJYdnUWlDOXUeJfO0gZJwebgIbEtv3LGUFsEQFpiZ13Z7OC2MwCeRIPH20F2yEs4Cpx + 8t9bNhCbRUBVYuu1uWA2r4C9xL69qxgh7lCU6H42ASzOKeAg8WrKOTBbCKjm1IzlrBMY+Rcw + MqxilE3JrdLNTczMKU5N1i1OTszLSy3StdDLzSzRS00p3cQIDjgX1R2MEw4pHWIU4GBU4uE9 + cftugBBrYllxZe4hRkkOJiVR3uUPgEJ8SfkplRmJxRnxRaU5qcWHGCU4mJVEePuagXK8KYmV + ValF+TApaQ4WJXHeGyk3/YUE0hNLUrNTUwtSi2CyMhwcShK8PI+BGgWLUtNTK9Iyc0oQ0kwc + nCDDeYCGS4LU8BYXJOYWZ6ZD5E8xKkqJ84qDJARAEhmleXC9sITwilEc6BVhXjWQKh5gMoHr + fgU0mAlosDXPHZDBJYkIKakGxt3bwgtmPb+i867K8Azvsvr063Ucp2y6iwWe2Zx3Vm37sSRK + QlHIgVuhc/Hjjo+yehryqTxGa9s1G+9PP8P04c80vwl+blVMsfF9nA9nvGFeXM58tdgl23jj + xoeOpWxahbML9xRtuxhecuWvYOAS9/R590//WBuYZs/O+JPv+Z68ZSZRu3f1KbEUZyQaajEX + FScCANmM/cvjAgAA +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +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: Fri, 28 Dec 2012 18:26:49 -0000 + +From: Austin Clements + +This is now a generic boolean term quoting function. It performs +minimal quoting to produce user-friendly queries. + +This could live in tag-util as well, but it is really nothing specific +to tags (although the conventions are specific to Xapian). + +The API is changed from "caller-allocates" to "readline-like". The +scan for max tag length is pushed down into the quoting routine. +Furthermore, this now combines the term prefix with the quoted term; +arguably this is just as easy to do in the caller, but this will +nicely parallel the boolean term parsing function to be introduced +shortly. + +This is an amalgamation of code written by David Bremner and myself. +--- + notmuch-tag.c | 48 ++++++++++++--------------------------- + util/string-util.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + util/string-util.h | 14 ++++++++++++ + 3 files changed, 92 insertions(+), 34 deletions(-) + +diff --git a/notmuch-tag.c b/notmuch-tag.c +index 88d559b..fc9d43a 100644 +--- a/notmuch-tag.c ++++ b/notmuch-tag.c +@@ -19,6 +19,7 @@ + */ + + #include "notmuch-client.h" ++#include "string-util.h" + + static volatile sig_atomic_t interrupted; + +@@ -35,25 +36,6 @@ handle_sigint (unused (int sig)) + interrupted = 1; + } + +-static char * +-_escape_tag (char *buf, const char *tag) +-{ +- const char *in = tag; +- char *out = buf; +- +- /* Boolean terms surrounded by double quotes can contain any +- * character. Double quotes are quoted by doubling them. */ +- *out++ = '"'; +- while (*in) { +- if (*in == '"') +- *out++ = '"'; +- *out++ = *in++; +- } +- *out++ = '"'; +- *out = 0; +- return buf; +-} +- + typedef struct { + const char *tag; + notmuch_bool_t remove; +@@ -71,25 +53,16 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, + * parenthesize and the exclusion part of the query must not use + * the '-' operator (though the NOT operator is fine). */ + +- char *escaped, *query_string; ++ char *escaped = NULL; ++ size_t escaped_len = 0; ++ char *query_string; + const char *join = ""; +- int i; +- unsigned int max_tag_len = 0; ++ size_t i; + + /* Don't optimize if there are no tag changes. */ + if (tag_ops[0].tag == NULL) + return talloc_strdup (ctx, orig_query_string); + +- /* Allocate a buffer for escaping tags. This is large enough to +- * hold a fully escaped tag with every character doubled plus +- * enclosing quotes and a NUL. */ +- for (i = 0; tag_ops[i].tag; i++) +- if (strlen (tag_ops[i].tag) > max_tag_len) +- max_tag_len = strlen (tag_ops[i].tag); +- escaped = talloc_array (ctx, char, max_tag_len * 2 + 3); +- if (! escaped) +- return NULL; +- + /* Build the new query string */ + if (strcmp (orig_query_string, "*") == 0) + query_string = talloc_strdup (ctx, "("); +@@ -97,10 +70,17 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, + query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string); + + for (i = 0; tag_ops[i].tag && query_string; i++) { ++ /* XXX in case of OOM, query_string will be deallocated when ++ * ctx is, which might be at shutdown */ ++ if (make_boolean_term (ctx, ++ "tag", tag_ops[i].tag, ++ &escaped, &escaped_len)) ++ return NULL; ++ + query_string = talloc_asprintf_append_buffer ( +- query_string, "%s%stag:%s", join, ++ query_string, "%s%s%s", join, + tag_ops[i].remove ? "" : "not ", +- _escape_tag (escaped, tag_ops[i].tag)); ++ escaped); + join = " or "; + } + +diff --git a/util/string-util.c b/util/string-util.c +index 44f8cd3..e4bea21 100644 +--- a/util/string-util.c ++++ b/util/string-util.c +@@ -20,6 +20,7 @@ + + + #include "string-util.h" ++#include "talloc.h" + + char * + strtok_len (char *s, const char *delim, size_t *len) +@@ -32,3 +33,66 @@ strtok_len (char *s, const char *delim, size_t *len) + + return *len ? s : NULL; + } ++ ++int ++make_boolean_term (void *ctx, const char *prefix, const char *term, ++ char **buf, size_t *len) ++{ ++ const char *in; ++ char *out; ++ size_t needed = 3; ++ int need_quoting = 0; ++ ++ /* Do we need quoting? To be paranoid, we quote anything ++ * containing a quote, even though it only matters at the ++ * beginning, and anything containing non-ASCII text. */ ++ for (in = term; *in && !need_quoting; in++) ++ if (*in <= ' ' || *in == ')' || *in == '"' || (unsigned char)*in > 127) ++ need_quoting = 1; ++ ++ if (need_quoting) ++ for (in = term; *in; in++) ++ needed += (*in == '"') ? 2 : 1; ++ else ++ needed = strlen (term) + 1; ++ ++ /* Reserve space for the prefix */ ++ if (prefix) ++ needed += strlen (prefix) + 1; ++ ++ if ((*buf == NULL) || (needed > *len)) { ++ *len = 2 * needed; ++ *buf = talloc_realloc (ctx, *buf, char, *len); ++ } ++ ++ if (! *buf) ++ return 1; ++ ++ out = *buf; ++ ++ /* Copy in the prefix */ ++ if (prefix) { ++ strcpy (out, prefix); ++ out += strlen (prefix); ++ *out++ = ':'; ++ } ++ ++ if (! need_quoting) { ++ strcpy (out, term); ++ return 0; ++ } ++ ++ /* Quote term by enclosing it in double quotes and doubling any ++ * internal double quotes. */ ++ *out++ = '"'; ++ in = term; ++ while (*in) { ++ if (*in == '"') ++ *out++ = '"'; ++ *out++ = *in++; ++ } ++ *out++ = '"'; ++ *out = '\0'; ++ ++ return 0; ++} +diff --git a/util/string-util.h b/util/string-util.h +index ac7676c..b8844a3 100644 +--- a/util/string-util.h ++++ b/util/string-util.h +@@ -19,4 +19,18 @@ + + char *strtok_len (char *s, const char *delim, size_t *len); + ++/* Construct a boolean term query with the specified prefix (e.g., ++ * "id") and search term, quoting term as necessary. Specifically, if ++ * term contains any non-printable ASCII characters, non-ASCII ++ * characters, close parenthesis or double quotes, it will be enclosed ++ * in double quotes and any internal double quotes will be doubled ++ * (e.g. a"b -> "a""b"). The result will be a valid notmuch query and ++ * can be parsed by parse_boolean_term. ++ * ++ * Output is into buf; it may be talloc_realloced. ++ * Return: 0 on success, non-zero on memory allocation failure. ++ */ ++int make_boolean_term (void *talloc_ctx, const char *prefix, const char *term, ++ char **buf, size_t *len); ++ + #endif +-- +1.7.10.4 + -- 2.26.2