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 B57DE6DE035A for ; Sun, 29 Jan 2017 03:06:42 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at cworth.org X-Spam-Flag: NO X-Spam-Score: 0.076 X-Spam-Level: X-Spam-Status: No, score=0.076 tagged_above=-999 required=5 tests=[AWL=0.096, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-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 TGHpf7t-4J04 for ; Sun, 29 Jan 2017 03:06:39 -0800 (PST) Received: from mail-lf0-f66.google.com (mail-lf0-f66.google.com [209.85.215.66]) by arlo.cworth.org (Postfix) with ESMTPS id 7C8286DE0319 for ; Sun, 29 Jan 2017 03:06:38 -0800 (PST) Received: by mail-lf0-f66.google.com with SMTP id h65so29122087lfi.3 for ; Sun, 29 Jan 2017 03:06:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nikula-org.20150623.gappssmtp.com; s=20150623; h=from:to:subject:in-reply-to:references:date:message-id:mime-version :content-transfer-encoding; bh=8QZ+dk9V5IqtG1QDFLwTWFCt9EqPY5UV0WzS6VLgRzQ=; b=XRhDgPynCJl7KXQTKQvPcro+8qBaWwU1r5prCsb4d32sSHd40GwkyzElc0ITGoe5xo 1jxG2/Qas/BZPodq0a9lXvxf6skOwvLPA40qOsYojDiTh/F2n8kIdwzBKneJ0yje1kWb bP/H0dqwVzQ88ajaFFIEtHomdAuTeO8Pxkqwk3ZBneesRyZ8JFYVxHBHQxtTqAQAmA5N MdxAvYo+fPJOeolgJU8excN2L3jM66pmDH3T5Ef9XEN2n9R+wgr9XFbUCGI0pZBbP92B hqJ8YuTY2FJe8GrSIZNDNjEChTA+KEVrLgDJqJIb4IFgGj38NLMyIs3BUGpSWhECscaY AnFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:in-reply-to:references:date :message-id:mime-version:content-transfer-encoding; bh=8QZ+dk9V5IqtG1QDFLwTWFCt9EqPY5UV0WzS6VLgRzQ=; b=uZlB7IPs7Zsg3gitoGADgqThM1TOn/gKHDegZ/7/spauFPz+WcAO8exo0v4lwOI23j MShIWPmsL1fEd9X4lZBUpy1esIyGDDkXpvAWjg04v4ap49hN7gOszQAqdxRCKjBs5ECN s65yGApwfgqiAOoh4T+RmOkDeloESoTcCk9ivM4tEzRJKChcYhCRyz19AFi9/oVXI3yz UGaDWj5VlnjcQTW0L+ad0dDP0oxUtfmIzRTJHl/UfP/ND5QLkJUaY9pRXnNEzwSYd+b5 7tPRAl4ZRXo4D28GkRhd8QgYLy7zRrMA8W/YayaJykZ97YDKKVmQtSp7NU5K9iNCgNU7 tDlw== X-Gm-Message-State: AIkVDXLGITuzm3j1+eG4jd+w7LV6h4WlKl3CLV838bBezEmclCudVc7+xnr3u8ZVF9YvWw== X-Received: by 10.46.77.9 with SMTP id a9mr6149981ljb.25.1485687996533; Sun, 29 Jan 2017 03:06:36 -0800 (PST) Received: from localhost (mobile-access-5d6a3e-39.dhcp.inet.fi. [93.106.62.39]) by smtp.gmail.com with ESMTPSA id b145sm2743340lfg.46.2017.01.29.03.06.35 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sun, 29 Jan 2017 03:06:35 -0800 (PST) From: Jani Nikula To: Tomi Ollila , David Bremner , notmuch@notmuchmail.org Subject: Re: [Patch v4] lib: regexp matching in 'subject' and 'from' In-Reply-To: References: <20170121032752.6788-1-david@tethera.net> <20170121135917.22062-1-david@tethera.net> Date: Sun, 29 Jan 2017 13:06:34 +0200 Message-ID: <8737g23z2d.fsf@nikula.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.22 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: Sun, 29 Jan 2017 11:06:42 -0000 On Wed, 25 Jan 2017, Tomi Ollila wrote: > On Sat, Jan 21 2017, David Bremner wrote: > >> the idea is that you can run >> >> % notmuch search subject:// >> % notmuch search from:// > > I like this interface. FWIW I think this is superior to the earlier alternatives too. I think people would like to use regexps (or globbing) for path: and folder: queries. Is there a risk of ambiguity between normal path: and folder: searches and regexp searches due to "/"? I suppose the normal queries never begin with "/" for them (due to being relative to database path, not absolute) but is that confusing? BR, Jani. > >> >> or >> >> % notmuch search subject:"your usual phrase search" >> % notmuch search from:"usual phrase search" >> >> This should also work with bindings, since it extends the query parser. >> >> This is trivial to extend for other value slots, but currently the only >> value slots are date, message_id, from, subject, and last_mod. Date is >> already searchable, and message_id is not obviously useful to regex >> match. > > Why would not mesasge_id not be useful to regex match. I can come up quite > a few use cases... but if there are techinal difficulties... then that > should be mentioned instead. > > maybe this commit message should inform that xapian with field processors > (1.4.x) is required for this feature -- and emphasize it a bit better in > manual page ? > > Probably '//' is used to escape '/' -- should such a character ever needed > in regex search. > >> >> This was originally written by Austin Clements, and ported to Xapian >> field processors (from Austin's custom query parser) by yours truly. >> --- >> >> This version impliments the use of // to delimit regular expressions. >> I have not tested the code paths with old (pre field processor) xapian. > > Fedora 25 has 1.2.24 -- T630 tests are skipped. It looks like these chang= es > did not increase the failure count there. > > Some (mostly whitespace nitpicking) comments below: > > >> >> doc/man7/notmuch-search-terms.rst | 27 +++++++- >> lib/Makefile.local | 1 + >> lib/database-private.h | 2 + >> lib/database.cc | 29 +++++++- >> lib/regexp-fields.cc | 142 +++++++++++++++++++++++++++++++= +++++++ >> lib/regexp-fields.h | 77 +++++++++++++++++++++ >> test/T630-regexp-query.sh | 82 ++++++++++++++++++++++ >> 7 files changed, 354 insertions(+), 6 deletions(-) >> create mode 100644 lib/regexp-fields.cc >> create mode 100644 lib/regexp-fields.h >> create mode 100755 test/T630-regexp-query.sh >> >> diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search= -terms.rst >> index de93d733..d8527e18 100644 >> --- a/doc/man7/notmuch-search-terms.rst >> +++ b/doc/man7/notmuch-search-terms.rst >> @@ -34,10 +34,14 @@ indicate user-supplied values): >>=20=20 >> - from: >>=20=20 >> +- from:// >> + >> - to: >>=20=20 >> - subject: >>=20=20 >> +- subject:// >> + >> - attachment: >>=20=20 >> - mimetype: >> @@ -71,6 +75,17 @@ subject of an email. Searching for a phrase in the su= bject is supported >> by including quotation marks around the phrase, immediately following >> **subject:**. >>=20=20 >> +The **from:** and **subject** prefix can be also used to restrict the >> +results to those whose from/subject value matches a regular >> +expression (see **regex(7)**) delimited with //. >> + >> +:: >> + >> + notmuch search 'from:/bob@.*[.]example[.]com/' >> + >> +Regular expression searches are only available if notmuch is built >> +with **Xapian Field Processors** (see below). > > And the poor user stopped reading far before this line, desperately trying > the regex searches... >;/ so IMO this requirement should be notified earl= ier. > >> + >> The **attachment:** prefix can be used to search for specific filenames >> (or extensions) of attachments to email messages. >>=20=20 >> @@ -220,13 +235,18 @@ Boolean and Probabilistic Prefixes >> ---------------------------------- >>=20=20 >> Xapian (and hence notmuch) prefixes are either **boolean**, supporting >> -exact matches like "tag:inbox" or **probabilistic**, supporting a more= flexible **term** based searching. The prefixes currently supported by not= much are as follows. >> - >> +exact matches like "tag:inbox" or **probabilistic**, supporting a more >> +flexible **term** based searching. Certain **special** prefixes are >> +processed by notmuch in a way not stricly fitting either of Xapian's >> +built in styles. The prefixes currently supported by notmuch are as >> +follows. >>=20=20 >> Boolean >> **tag:**, **id:**, **thread:**, **folder:**, **path:**, **property:** >> Probabilistic >> - **from:**, **to:**, **subject:**, **attachment:**, **mimetype:** >> + **to:**, **attachment:**, **mimetype:** >> +Special >> + **from:**, **query:**, **subject:** >>=20=20 >> Terms and phrases >> ----------------- >> @@ -396,6 +416,7 @@ Currently the following features require field proce= ssor support: >>=20=20 >> - non-range date queries, e.g. "date:today" >> - named queries e.g. "query:my_special_query" >> +- regular expression searches, e.g. "subject:/^\\[SPAM\\]/" >>=20=20 >> SEE ALSO >> =3D=3D=3D=3D=3D=3D=3D=3D >> diff --git a/lib/Makefile.local b/lib/Makefile.local >> index b77e5780..ff812b5f 100644 >> --- a/lib/Makefile.local >> +++ b/lib/Makefile.local >> @@ -52,6 +52,7 @@ libnotmuch_cxx_srcs =3D \ >> $(dir)/query.cc \ >> $(dir)/query-fp.cc \ >> $(dir)/config.cc \ >> + $(dir)/regexp-fields.cc \ > > Space instead of TAB above -- tab is used more often (and \:s usually ali= gned) > >> $(dir)/thread.cc >>=20=20 >> libnotmuch_modules :=3D $(libnotmuch_c_srcs:.c=3D.o) $(libnotmuch_cxx_s= rcs:.cc=3D.o) >> diff --git a/lib/database-private.h b/lib/database-private.h >> index ccc1e9a1..9f5659a9 100644 >> --- a/lib/database-private.h >> +++ b/lib/database-private.h >> @@ -190,6 +190,8 @@ struct _notmuch_database { >> #if HAVE_XAPIAN_FIELD_PROCESSOR >> Xapian::FieldProcessor *date_field_processor; >> Xapian::FieldProcessor *query_field_processor; >> + Xapian::FieldProcessor *from_field_processor; >> + Xapian::FieldProcessor *subject_field_processor; >> #endif >> Xapian::ValueRangeProcessor *last_mod_range_processor; >> }; >> diff --git a/lib/database.cc b/lib/database.cc >> index 2d19f20c..8a9ad251 100644 >> --- a/lib/database.cc >> +++ b/lib/database.cc >> @@ -21,6 +21,7 @@ >> #include "database-private.h" >> #include "parse-time-vrp.h" >> #include "query-fp.h" >> +#include "regexp-fields.h" >> #include "string-util.h" >>=20=20 >> #include >> @@ -272,12 +273,16 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] =3D { >> { "folder", "XFOLDER:" }, >> }; >>=20=20 >> -static prefix_t PROBABILISTIC_PREFIX[]=3D { >> +static prefix_t REGEX_PREFIX[]=3D { >> { "from", "XFROM" }, >> + { "subject", "XSUBJECT"}, >> +}; >> + >> +static prefix_t PROBABILISTIC_PREFIX[]=3D { >> + > > empty line ^ > >> { "to", "XTO" }, >> { "attachment", "XATTACHMENT" }, >> { "mimetype", "XMIMETYPE"}, >> - { "subject", "XSUBJECT"}, >> }; >>=20=20 >> const char * >> @@ -295,6 +300,11 @@ _find_prefix (const char *name) >> return BOOLEAN_PREFIX_EXTERNAL[i].prefix; >> } >>=20=20 >> + for (i =3D 0; i < ARRAY_SIZE (REGEX_PREFIX); i++) { >> + if (strcmp (name, REGEX_PREFIX[i].name) =3D=3D 0) >> + return REGEX_PREFIX[i].prefix; >> + } >> + >> for (i =3D 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) { >> if (strcmp (name, PROBABILISTIC_PREFIX[i].name) =3D=3D 0) >> return PROBABILISTIC_PREFIX[i].prefix; >> @@ -1042,6 +1052,10 @@ notmuch_database_open_verbose (const char *path, >> notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_= processor); >> notmuch->query_field_processor =3D new QueryFieldProcessor (*notmuch->= query_parser, notmuch); >> notmuch->query_parser->add_boolean_prefix("query", notmuch->query_fiel= d_processor); >> + notmuch->from_field_processor =3D new RegexpFieldProcessor ("from", *n= otmuch->query_parser, notmuch); >> + notmuch->subject_field_processor =3D new RegexpFieldProcessor ("subjec= t", *notmuch->query_parser, notmuch); >> + notmuch->query_parser->add_boolean_prefix("from", notmuch->from_field_= processor); >> + notmuch->query_parser->add_boolean_prefix("subject", notmuch->subject_= field_processor); >> #endif >> notmuch->last_mod_range_processor =3D new Xapian::NumberValueRangeProc= essor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); >>=20=20 >> @@ -1058,7 +1072,12 @@ notmuch_database_open_verbose (const char *path, >> notmuch->query_parser->add_boolean_prefix (prefix->name, >> prefix->prefix); >> } >> - >> +#if !HAVE_XAPIAN_FIELD_PROCESSOR >> + for (i =3D 0; i < ARRAY_SIZE (REGEX_PREFIX); i++) { >> + prefix_t *prefix =3D ®EX_PREFIX[i]; >> + notmuch->query_parser->add_prefix (prefix->name, prefix->prefix); >> + } >> +#endif >> for (i =3D 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) { >> prefix_t *prefix =3D &PROBABILISTIC_PREFIX[i]; >> notmuch->query_parser->add_prefix (prefix->name, prefix->prefix); >> @@ -1138,6 +1157,10 @@ notmuch_database_close (notmuch_database_t *notmu= ch) >> notmuch->date_field_processor =3D NULL; >> delete notmuch->query_field_processor; >> notmuch->query_field_processor =3D NULL; >> + delete notmuch->from_field_processor; >> + notmuch->from_field_processor =3D NULL; >> + delete notmuch->subject_field_processor; >> + notmuch->subject_field_processor =3D NULL; >> #endif >>=20=20 >> return status; >> diff --git a/lib/regexp-fields.cc b/lib/regexp-fields.cc >> new file mode 100644 >> index 00000000..8cb1cada >> --- /dev/null >> +++ b/lib/regexp-fields.cc >> @@ -0,0 +1,142 @@ >> +/* regexp-fields.cc - field processor glue for regex supporting fields >> + * >> + * This file is part of notmuch. >> + * >> + * Copyright =C2=A9 2015 Austin Clements >> + * Copyright =C2=A9 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 https://www.gnu.org/licenses/ . >> + * >> + * Author: Austin Clements >> + * David Bremner >> + */ >> + >> +#include "regexp-fields.h" >> +#include "notmuch-private.h" >> +#include "database-private.h" >> +#include >> + >> +#if HAVE_XAPIAN_FIELD_PROCESSOR >> +static void >> +compile_regex (regex_t ®exp, const char *str) >> +{ >> + int err =3D regcomp (®exp, str, REG_EXTENDED | REG_NOSUB); >> + >> + if (err !=3D 0) { >> + size_t len =3D regerror (err, ®exp, NULL, 0); >> + char *buffer =3D new char[len]; >> + std::string msg; >> + (void) regerror (err, ®exp, buffer, len); >> + msg.assign (buffer, len); >> + delete buffer; >> + >> + throw Xapian::QueryParserError (msg); >> + > > empty line ^ > >> + } >> +} >> + >> +RegexpPostingSource::RegexpPostingSource (Xapian::valueno slot, const s= td::string ®exp) >> + : slot_ (slot) >> +{ >> + > > ditto > >> + compile_regex (regexp_, regexp.c_str ()); >> +} >> + >> +RegexpPostingSource::~RegexpPostingSource () >> +{ >> + regfree (®exp_); >> +} >> + >> +void >> +RegexpPostingSource::init (const Xapian::Database &db) >> +{ >> + db_ =3D db; >> + it_ =3D db_.valuestream_begin (slot_); >> + end_ =3D db.valuestream_end (slot_); >> + started_ =3D false; >> +} >> + >> +Xapian::doccount >> +RegexpPostingSource::get_termfreq_min () const >> +{ >> + return 0; >> +} >> + >> +Xapian::doccount >> +RegexpPostingSource::get_termfreq_est () const >> +{ >> + return get_termfreq_max () / 2; >> +} >> + >> +Xapian::doccount >> +RegexpPostingSource::get_termfreq_max () const >> +{ >> + return db_.get_value_freq (slot_); >> +} >> + >> +Xapian::docid >> +RegexpPostingSource::get_docid () const >> +{ >> + return it_.get_docid (); >> +} >> + >> +bool >> +RegexpPostingSource::at_end () const >> +{ >> + return it_ =3D=3D end_; >> +} >> + >> +void >> +RegexpPostingSource::next (unused (double min_wt)) >> +{ >> + if (started_ && ! at_end ()) >> + ++it_; >> + started_ =3D true; >> + >> + for (; ! at_end (); ++it_) { >> + std::string value =3D *it_; >> + if (regexec (®exp_, value.c_str (), 0, NULL, 0) =3D=3D 0) >> + break; >> + } >> +} >> + >> +static inline Xapian::valueno _find_slot (std::string prefix) >> +{ >> + if (prefix =3D=3D "from") >> + return NOTMUCH_VALUE_FROM; >> + else if (prefix =3D=3D "subject") >> + return NOTMUCH_VALUE_SUBJECT; >> + else >> + throw Xapian::QueryParserError ("unsupported regexp field '" + prefix = + "'"); >> +} >> + >> +RegexpFieldProcessor::RegexpFieldProcessor (std::string prefix, Xapian:= :QueryParser &parser_, notmuch_database_t *notmuch_) >> + : slot(_find_slot (prefix)), term_prefix(_find_prefix (prefix.c_str ()= )), parser(parser_), notmuch(notmuch_) >> +{ >> +}; >> + >> +Xapian::Query >> +RegexpFieldProcessor::operator() (const std::string & str) >> +{ >> + if (str.at (0) =3D=3D '/' && str.at (str.size () - 1)){ >> + RegexpPostingSource *postings =3D new RegexpPostingSource (slot, str.s= ubstr(1,str.size () - 2)); >> + return Xapian::Query (postings->release ()); >> + } else { >> + /* TODO replace this with a nicer API level triggering of >> + * phrase parsing, when possible */ >> + std::string quoted=3D'"' + str + '"'; >> + return parser.parse_query (quoted, NOTMUCH_QUERY_PARSER_FLAGS, term_pr= efix); >> + } >> +} >> +#endif >> diff --git a/lib/regexp-fields.h b/lib/regexp-fields.h >> new file mode 100644 >> index 00000000..bac11999 >> --- /dev/null >> +++ b/lib/regexp-fields.h >> @@ -0,0 +1,77 @@ >> +/* regex-fields.h - xapian glue for semi-bruteforce regexp search >> + * >> + * This file is part of notmuch. >> + * >> + * Copyright =C2=A9 2015 Austin Clements >> + * Copyright =C2=A9 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 https://www.gnu.org/licenses/ . >> + * >> + * Author: Austin Clements >> + * David Bremner >> + */ >> + >> +#ifndef NOTMUCH_REGEXP_FIELDS_H >> +#define NOTMUCH_REGEXP_FIELDS_H >> +#if HAVE_XAPIAN_FIELD_PROCESSOR >> +#include >> +#include >> +#include "database-private.h" >> +#include "notmuch-private.h" >> + >> +/* A posting source that returns documents where a value matches a >> + * regexp. >> + */ >> +class RegexpPostingSource : public Xapian::PostingSource >> +{ >> + protected: >> + const Xapian::valueno slot_; >> + regex_t regexp_; >> + Xapian::Database db_; >> + bool started_; >> + Xapian::ValueIterator it_, end_; >> + >> +/* No copying */ >> + RegexpPostingSource (const RegexpPostingSource &); >> + RegexpPostingSource &operator=3D (const RegexpPostingSource &); >> + >> + public: >> + RegexpPostingSource (Xapian::valueno slot, const std::string ®ex= p); >> + ~RegexpPostingSource (); >> + void init (const Xapian::Database &db); >> + Xapian::doccount get_termfreq_min () const; >> + Xapian::doccount get_termfreq_est () const; >> + Xapian::doccount get_termfreq_max () const; >> + Xapian::docid get_docid () const; >> + bool at_end () const; >> + void next (unused (double min_wt)); >> +}; >> + >> + >> +class RegexpFieldProcessor : public Xapian::FieldProcessor { >> + protected: >> + Xapian::valueno slot; >> + std::string term_prefix; >> + Xapian::QueryParser &parser; >> + notmuch_database_t *notmuch; >> + >> + public: >> + RegexpFieldProcessor (std::string prefix, Xapian::QueryParser &pars= er_, notmuch_database_t *notmuch_); >> + >> + ~RegexpFieldProcessor () { }; >> + >> + Xapian::Query operator()(const std::string & str); >> +}; >> +#endif >> +#endif /* NOTMUCH_REGEXP_FIELDS_H */ >> diff --git a/test/T630-regexp-query.sh b/test/T630-regexp-query.sh >> new file mode 100755 >> index 00000000..722af715 >> --- /dev/null >> +++ b/test/T630-regexp-query.sh >> @@ -0,0 +1,82 @@ >> +#!/usr/bin/env bash >> +test_description=3D'regular expression searches' >> +. ./test-lib.sh || exit 1 >> + >> +add_email_corpus >> + >> + >> +if [ $NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR -eq 1 ]; then >> + >> + notmuch search --output=3Dmessages from:cworth > cworth.msg-ids >> + >> + test_begin_subtest "regexp from search, case sensitive" >> + notmuch search --output=3Dmessages from:/carl/ > OUTPUT >> + test_expect_equal_file /dev/null OUTPUT >> + >> + test_begin_subtest "empty regexp or query" >> + notmuch search --output=3Dmessages from:/carl/ or from:/cworth/ > O= UTPUT >> + test_expect_equal_file cworth.msg-ids OUTPUT >> + >> + test_begin_subtest "non-empty regexp and query" >> + notmuch search from:/cworth@cworth.org/ and subject:patch > OUTPUT >> + cat < EXPECTED >> +thread:0000000000000008 2009-11-18 [1/2] Carl Worth| Alex Botero-Lowr= y; [notmuch] [PATCH] Error out if no query is supplied to search instead of= going into an infinite loop (attachment inbox unread) >> +thread:0000000000000007 2009-11-18 [1/2] Carl Worth| Ingmar Vanhassel= ; [notmuch] [PATCH] Typsos (inbox unread) >> +thread:0000000000000018 2009-11-18 [1/2] Carl Worth| Jan Janak; [notm= uch] [PATCH] Older versions of install do not support -C. (inbox unread) >> +thread:0000000000000017 2009-11-18 [1/2] Carl Worth| Keith Packard; [= notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and= unread) tags (inbox unread) >> +thread:0000000000000014 2009-11-18 [2/5] Carl Worth| Mikhail Gusarov,= Keith Packard; [notmuch] [PATCH 1/2] Close message file after parsing mess= age headers (inbox unread) >> +thread:0000000000000001 2009-11-18 [1/1] Stewart Smith; [notmuch] [PA= TCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread) >> +EOF >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "regexp from search, duplicate term search" >> + notmuch search --output=3Dmessages from:/cworth/ > OUTPUT >> + test_expect_equal_file cworth.msg-ids OUTPUT >> + >> + test_begin_subtest "long enough regexp matches only desired senders" >> + notmuch search --output=3Dmessages 'from:"/C.* Wo/"' > OUTPUT >> + test_expect_equal_file cworth.msg-ids OUTPUT >> + >> + test_begin_subtest "shorter regexp matches one more sender" >> + notmuch search --output=3Dmessages 'from:"/C.* W/"' > OUTPUT >> + (echo id:1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk= ; cat cworth.msg-ids) > EXPECTED > > The above doesn't need to be executed in subshell:=20 > > { echo id:1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk; c= at cworth.msg-ids; } > EXPECTED > > does it in the same shell > > >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "regexp subject search, non-ASCII" >> + notmuch search --output=3Dmessages subject:/accentu=C3=A9/ > OUTPUT >> + echo id:877h1wv7mg.fsf@inf-8657.int-evry.fr > EXPECTED >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "regexp subject search, punctuation" >> + notmuch search subject:/\'X\'/ > OUTPUT >> + cat < EXPECTED >> +thread:0000000000000017 2009-11-18 [2/2] Keith Packard, Carl Worth; [= notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and= unread) tags (inbox unread) >> +EOF >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "regexp subject search, no punctuation" >> + notmuch search subject:/X/ > OUTPUT >> + cat < EXPECTED >> +thread:0000000000000017 2009-11-18 [2/2] Keith Packard, Carl Worth; [= notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and= unread) tags (inbox unread) >> +thread:000000000000000f 2009-11-18 [4/4] Jjgod Jiang, Alexander Boter= o-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread) >> +EOF >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "combine regexp from and subject" >> + notmuch search subject:/-C/ and from:/.an.k/ > OUTPUT >> + cat < EXPECTED >> +thread:0000000000000018 2009-11-17 [1/2] Jan Janak| Carl Worth; [notm= uch] [PATCH] Older versions of install do not support -C. (inbox unread) >> +EOF >> + test_expect_equal_file EXPECTED OUTPUT >> + >> + test_begin_subtest "regexp error reporting" >> + notmuch search 'from:/unbalanced[/' 1>OUTPUT 2>&1 >> + cat < EXPECTED >> +notmuch search: A Xapian exception occurred >> +A Xapian exception occurred performing query: Invalid regular expression >> +Query string was: from:/unbalanced[/ >> +EOF >> + test_expect_equal_file EXPECTED OUTPUT >> +fi >> + >> +test_done >> --=20 >> 2.11.0 > _______________________________________________ > notmuch mailing list > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch