]> git.notmuchmail.org Git - notmuch/commitdiff
bindings: move go bindings to contrib
authorDavid Bremner <david@tethera.net>
Tue, 23 Aug 2016 23:50:05 +0000 (20:50 -0300)
committerDavid Bremner <david@tethera.net>
Sat, 3 Sep 2016 23:13:08 +0000 (20:13 -0300)
This signals two things, an intent to be more liberal about accepting
patches, and an intent to stop distributing the bindings if maintenance
doesn't pick up.

12 files changed:
bindings/go/.gitignore [deleted file]
bindings/go/LICENSE [deleted file]
bindings/go/Makefile [deleted file]
bindings/go/README [deleted file]
bindings/go/src/notmuch-addrlookup/addrlookup.go [deleted file]
bindings/go/src/notmuch/notmuch.go [deleted file]
contrib/go/.gitignore [new file with mode: 0644]
contrib/go/LICENSE [new file with mode: 0644]
contrib/go/Makefile [new file with mode: 0644]
contrib/go/README [new file with mode: 0644]
contrib/go/src/notmuch-addrlookup/addrlookup.go [new file with mode: 0644]
contrib/go/src/notmuch/notmuch.go [new file with mode: 0644]

diff --git a/bindings/go/.gitignore b/bindings/go/.gitignore
deleted file mode 100644 (file)
index c394479..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-src/github.com/
-pkg/
-bin/
diff --git a/bindings/go/LICENSE b/bindings/go/LICENSE
deleted file mode 100644 (file)
index 4362b49..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-                  GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-\f
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-\f
-                  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-\f
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-\f
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-\f
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-\f
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-\f
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-\f
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-                            NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library 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
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/bindings/go/Makefile b/bindings/go/Makefile
deleted file mode 100644 (file)
index 1b9e750..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# Makefile for the go bindings of notmuch
-
-export GOPATH      ?= $(shell pwd)
-export CGO_CFLAGS  ?= -I../../../../lib
-export CGO_LDFLAGS ?= -L../../../../lib
-
-GO         ?= go
-GOFMT      ?= gofmt
-
-all: notmuch notmuch-addrlookup
-
-.PHONY: notmuch
-notmuch:
-       $(GO) install notmuch
-
-.PHONY: goconfig
-goconfig:
-       if [ ! -d github.com/msbranco/goconfig ]; then \
-           $(GO) get github.com/msbranco/goconfig; \
-       fi
-
-.PHONY: notmuch-addrlookup
-notmuch-addrlookup: notmuch goconfig
-       $(GO) install notmuch-addrlookup
-
-.PHONY: format
-format:
-       $(GOFMT) -w=true $(GOFMT_OPTS) src/notmuch
-       $(GOFMT) -w=true $(GOFMT_OPTS) src/notmuch-addrlookup
-
-.PHONY: check-format
-check-format:
-       $(GOFMT) -d=true $(GOFMT_OPTS) src/notmuch
-       $(GOFMT) -d=true $(GOFMT_OPTS) src/notmuch-addrlookup
-
-.PHONY: clean
-clean:
-       $(GO) clean notmuch
-       $(GO) clean notmuch-addrlookup
-       rm -rf pkg bin
diff --git a/bindings/go/README b/bindings/go/README
deleted file mode 100644 (file)
index 1825ae0..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-go-notmuch
-==========
-
-simple go bindings to the libnotmuch library[1].
-
-they are heavily inspired from the vala bindings from Sebastian Spaeth: 
-  https://github.com/spaetz/vala-notmuch
-
-note: the whole library hasn't been wrapped (yet?)
-
-todo
-----
-
- - improve notmuch-addrlookup regexp
-
-[1] https://notmuchmail.org/
diff --git a/bindings/go/src/notmuch-addrlookup/addrlookup.go b/bindings/go/src/notmuch-addrlookup/addrlookup.go
deleted file mode 100644 (file)
index 916e5bb..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-package main
-
-// stdlib imports
-import "os"
-import "path"
-import "log"
-import "fmt"
-import "regexp"
-import "strings"
-import "sort"
-
-// 3rd-party imports
-import "notmuch"
-import "github.com/msbranco/goconfig"
-
-type mail_addr_freq struct {
-       addr  string
-       count [3]uint
-}
-
-type frequencies map[string]uint
-
-/* Used to sort the email addresses from most to least used */
-func sort_by_freq(m1, m2 *mail_addr_freq) int {
-       if m1.count[0] == m2.count[0] &&
-               m1.count[1] == m2.count[1] &&
-               m1.count[2] == m2.count[2] {
-               return 0
-       }
-
-       if m1.count[0] > m2.count[0] ||
-               m1.count[0] == m2.count[0] &&
-                       m1.count[1] > m2.count[1] ||
-               m1.count[0] == m2.count[0] &&
-                       m1.count[1] == m2.count[1] &&
-                       m1.count[2] > m2.count[2] {
-               return -1
-       }
-
-       return 1
-}
-
-type maddresses []*mail_addr_freq
-
-func (self *maddresses) Len() int {
-       return len(*self)
-}
-
-func (self *maddresses) Less(i, j int) bool {
-       m1 := (*self)[i]
-       m2 := (*self)[j]
-       v := sort_by_freq(m1, m2)
-       if v <= 0 {
-               return true
-       }
-       return false
-}
-
-func (self *maddresses) Swap(i, j int) {
-       (*self)[i], (*self)[j] = (*self)[j], (*self)[i]
-}
-
-// find most frequent real name for each mail address
-func frequent_fullname(freqs frequencies) string {
-       var maxfreq uint = 0
-       fullname := ""
-       freqs_sz := len(freqs)
-
-       for mail, freq := range freqs {
-               if (freq > maxfreq && mail != "") || freqs_sz == 1 {
-                       // only use the entry if it has a real name
-                       // or if this is the only entry
-                       maxfreq = freq
-                       fullname = mail
-               }
-       }
-       return fullname
-}
-
-func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {
-
-       freqs := make(frequencies)
-
-       pattern := `\s*(("(\.|[^"])*"|[^,])*<?(?mail\b\w+([-+.]\w+)*\@\w+[-\.\w]*\.([-\.\w]+)*\w\b)>?)`
-       // pattern := "\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" +
-       //      "<?(?P<mail>\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)>?)"
-       pattern = `.*` + strings.ToLower(name) + `.*`
-       var re *regexp.Regexp = nil
-       var err error = nil
-       if re, err = regexp.Compile(pattern); err != nil {
-               log.Printf("error: %v\n", err)
-               return &freqs
-       }
-
-       headers := []string{"from"}
-       if pass == 1 {
-               headers = append(headers, "to", "cc", "bcc")
-       }
-
-       for ; msgs.Valid(); msgs.MoveToNext() {
-               msg := msgs.Get()
-               //println("==> msg [", msg.GetMessageId(), "]")
-               for _, header := range headers {
-                       froms := strings.ToLower(msg.GetHeader(header))
-                       //println("  froms: ["+froms+"]")
-                       for _, from := range strings.Split(froms, ",") {
-                               from = strings.Trim(from, " ")
-                               match := re.FindString(from)
-                               //println("  -> match: ["+match+"]")
-                               occ, ok := freqs[match]
-                               if !ok {
-                                       freqs[match] = 0
-                                       occ = 0
-                               }
-                               freqs[match] = occ + 1
-                       }
-               }
-       }
-       return &freqs
-}
-
-func search_address_passes(queries [3]*notmuch.Query, name string) []string {
-       var val []string
-       addr_freq := make(map[string]*mail_addr_freq)
-       addr_to_realname := make(map[string]*frequencies)
-
-       var pass uint = 0 // 0-based
-       for _, query := range queries {
-               if query == nil {
-                       //println("**warning: idx [",idx,"] contains a nil query")
-                       continue
-               }
-               msgs := query.SearchMessages()
-               ht := addresses_by_frequency(msgs, name, pass, &addr_to_realname)
-               for addr, count := range *ht {
-                       freq, ok := addr_freq[addr]
-                       if !ok {
-                               freq = &mail_addr_freq{addr: addr, count: [3]uint{0, 0, 0}}
-                       }
-                       freq.count[pass] = count
-                       addr_freq[addr] = freq
-               }
-               msgs.Destroy()
-               pass += 1
-       }
-
-       addrs := make(maddresses, len(addr_freq))
-       {
-               iaddr := 0
-               for _, freq := range addr_freq {
-                       addrs[iaddr] = freq
-                       iaddr += 1
-               }
-       }
-       sort.Sort(&addrs)
-
-       for _, addr := range addrs {
-               freqs, ok := addr_to_realname[addr.addr]
-               if ok {
-                       val = append(val, frequent_fullname(*freqs))
-               } else {
-                       val = append(val, addr.addr)
-               }
-       }
-       //println("val:",val)
-       return val
-}
-
-type address_matcher struct {
-       // the notmuch database
-       db *notmuch.Database
-       // full path of the notmuch database
-       user_db_path string
-       // user primary email
-       user_primary_email string
-       // user tag to mark from addresses as in the address book
-       user_addrbook_tag string
-}
-
-func new_address_matcher() *address_matcher {
-       // honor NOTMUCH_CONFIG
-       home := os.Getenv("NOTMUCH_CONFIG")
-       if home == "" {
-               home = os.Getenv("HOME")
-       }
-
-       cfg, err := goconfig.ReadConfigFile(path.Join(home, ".notmuch-config"))
-       if err != nil {
-               log.Fatalf("error loading config file:", err)
-       }
-
-       db_path, _ := cfg.GetString("database", "path")
-       primary_email, _ := cfg.GetString("user", "primary_email")
-       addrbook_tag, err := cfg.GetString("user", "addrbook_tag")
-       if err != nil {
-               addrbook_tag = "addressbook"
-       }
-
-       self := &address_matcher{db: nil,
-               user_db_path:       db_path,
-               user_primary_email: primary_email,
-               user_addrbook_tag:  addrbook_tag}
-       return self
-}
-
-func (self *address_matcher) run(name string) {
-       queries := [3]*notmuch.Query{}
-
-       // open the database
-       if db, status := notmuch.OpenDatabase(self.user_db_path,
-               notmuch.DATABASE_MODE_READ_ONLY); status == notmuch.STATUS_SUCCESS {
-               self.db = db
-       } else {
-               log.Fatalf("Failed to open the database: %v\n", status)
-       }
-
-       // pass 1: look at all from: addresses with the address book tag
-       query := "tag:" + self.user_addrbook_tag
-       if name != "" {
-               query = query + " and from:" + name + "*"
-       }
-       queries[0] = self.db.CreateQuery(query)
-
-       // pass 2: look at all to: addresses sent from our primary mail
-       query = ""
-       if name != "" {
-               query = "to:" + name + "*"
-       }
-       if self.user_primary_email != "" {
-               query = query + " from:" + self.user_primary_email
-       }
-       queries[1] = self.db.CreateQuery(query)
-
-       // if that leads only to a few hits, we check every from too
-       if queries[0].CountMessages()+queries[1].CountMessages() < 10 {
-               query = ""
-               if name != "" {
-                       query = "from:" + name + "*"
-               }
-               queries[2] = self.db.CreateQuery(query)
-       }
-
-       // actually retrieve and sort addresses
-       results := search_address_passes(queries, name)
-       for _, v := range results {
-               if v != "" && v != "\n" {
-                       fmt.Println(v)
-               }
-       }
-       return
-}
-
-func main() {
-       //fmt.Println("args:",os.Args)
-       app := new_address_matcher()
-       name := ""
-       if len(os.Args) > 1 {
-               name = os.Args[1]
-       }
-       app.run(name)
-}
diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go
deleted file mode 100644 (file)
index 4d8c2ce..0000000
+++ /dev/null
@@ -1,1146 +0,0 @@
-// Wrapper around the notmuch library
-
-package notmuch
-
-/*
-#cgo LDFLAGS: -lnotmuch
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include "notmuch.h"
-*/
-import "C"
-import "unsafe"
-
-// Status codes used for the return values of most functions
-type Status C.notmuch_status_t
-
-const (
-       STATUS_SUCCESS Status = iota
-       STATUS_OUT_OF_MEMORY
-       STATUS_READ_ONLY_DATABASE
-       STATUS_XAPIAN_EXCEPTION
-       STATUS_FILE_ERROR
-       STATUS_FILE_NOT_EMAIL
-       STATUS_DUPLICATE_MESSAGE_ID
-       STATUS_NULL_POINTER
-       STATUS_TAG_TOO_LONG
-       STATUS_UNBALANCED_FREEZE_THAW
-       STATUS_UNBALANCED_ATOMIC
-
-       STATUS_LAST_STATUS
-)
-
-func (self Status) String() string {
-       var p *C.char
-
-       // p is read-only
-       p = C.notmuch_status_to_string(C.notmuch_status_t(self))
-       if p != nil {
-               s := C.GoString(p)
-               return s
-       }
-       return ""
-}
-
-/* Various opaque data types. For each notmuch_<foo>_t see the various
- * notmuch_<foo> functions below. */
-
-type Database struct {
-       db *C.notmuch_database_t
-}
-
-type Query struct {
-       query *C.notmuch_query_t
-}
-
-type Threads struct {
-       threads *C.notmuch_threads_t
-}
-
-type Thread struct {
-       thread *C.notmuch_thread_t
-}
-
-type Messages struct {
-       messages *C.notmuch_messages_t
-}
-
-type Message struct {
-       message *C.notmuch_message_t
-}
-
-type Tags struct {
-       tags *C.notmuch_tags_t
-}
-
-type Directory struct {
-       dir *C.notmuch_directory_t
-}
-
-type Filenames struct {
-       fnames *C.notmuch_filenames_t
-}
-
-type DatabaseMode C.notmuch_database_mode_t
-
-const (
-       DATABASE_MODE_READ_ONLY DatabaseMode = 0
-       DATABASE_MODE_READ_WRITE
-)
-
-// Create a new, empty notmuch database located at 'path'
-func NewDatabase(path string) (*Database, Status) {
-
-       var c_path *C.char = C.CString(path)
-       defer C.free(unsafe.Pointer(c_path))
-
-       if c_path == nil {
-               return nil, STATUS_OUT_OF_MEMORY
-       }
-
-       self := &Database{db: nil}
-       st := Status(C.notmuch_database_create(c_path, &self.db))
-       if st != STATUS_SUCCESS {
-               return nil, st
-       }
-       return self, st
-}
-
-/* Open an existing notmuch database located at 'path'.
- *
- * The database should have been created at some time in the past,
- * (not necessarily by this process), by calling
- * notmuch_database_create with 'path'. By default the database should be
- * opened for reading only. In order to write to the database you need to
- * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode.
- *
- * An existing notmuch database can be identified by the presence of a
- * directory named ".notmuch" below 'path'.
- *
- * The caller should call notmuch_database_destroy when finished with
- * this database.
- *
- * In case of any failure, this function returns NULL, (after printing
- * an error message on stderr).
- */
-func OpenDatabase(path string, mode DatabaseMode) (*Database, Status) {
-
-       var c_path *C.char = C.CString(path)
-       defer C.free(unsafe.Pointer(c_path))
-
-       if c_path == nil {
-               return nil, STATUS_OUT_OF_MEMORY
-       }
-
-       self := &Database{db: nil}
-       st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &self.db))
-       if st != STATUS_SUCCESS {
-               return nil, st
-       }
-       return self, st
-}
-
-/* Close the given notmuch database, freeing all associated
- * resources. See notmuch_database_open. */
-func (self *Database) Close() Status {
-       return Status(C.notmuch_database_destroy(self.db))
-}
-
-/* Return the database path of the given database.
- */
-func (self *Database) GetPath() string {
-
-       /* The return value is a string owned by notmuch so should not be
-        * modified nor freed by the caller. */
-       var p *C.char = C.notmuch_database_get_path(self.db)
-       if p != nil {
-               s := C.GoString(p)
-               return s
-       }
-       return ""
-}
-
-/* Return the database format version of the given database. */
-func (self *Database) GetVersion() uint {
-       return uint(C.notmuch_database_get_version(self.db))
-}
-
-/* Does this database need to be upgraded before writing to it?
- *
- * If this function returns TRUE then no functions that modify the
- * database (notmuch_database_add_message, notmuch_message_add_tag,
- * notmuch_directory_set_mtime, etc.) will work unless the function
- * notmuch_database_upgrade is called successfully first. */
-func (self *Database) NeedsUpgrade() bool {
-       do_upgrade := C.notmuch_database_needs_upgrade(self.db)
-       if do_upgrade == 0 {
-               return false
-       }
-       return true
-}
-
-// TODO: notmuch_database_upgrade
-
-/* Retrieve a directory object from the database for 'path'.
- *
- * Here, 'path' should be a path relative to the path of 'database'
- * (see notmuch_database_get_path), or else should be an absolute path
- * with initial components that match the path of 'database'.
- *
- * Can return NULL if a Xapian exception occurs.
- */
-func (self *Database) GetDirectory(path string) (*Directory, Status) {
-       var c_path *C.char = C.CString(path)
-       defer C.free(unsafe.Pointer(c_path))
-
-       if c_path == nil {
-               return nil, STATUS_OUT_OF_MEMORY
-       }
-
-       var c_dir *C.notmuch_directory_t
-       st := Status(C.notmuch_database_get_directory(self.db, c_path, &c_dir))
-       if st != STATUS_SUCCESS || c_dir == nil {
-               return nil, st
-       }
-       return &Directory{dir: c_dir}, st
-}
-
-/* Add a new message to the given notmuch database.
- *
- * Here,'filename' should be a path relative to the path of
- * 'database' (see notmuch_database_get_path), or else should be an
- * absolute filename with initial components that match the path of
- * 'database'.
- *
- * The file should be a single mail message (not a multi-message mbox)
- * that is expected to remain at its current location, (since the
- * notmuch database will reference the filename, and will not copy the
- * entire contents of the file.
- *
- * If 'message' is not NULL, then, on successful return '*message'
- * will be initialized to a message object that can be used for things
- * such as adding tags to the just-added message. The user should call
- * notmuch_message_destroy when done with the message. On any failure
- * '*message' will be set to NULL.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.
- *
- * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
- *     message not added.
- *
- * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: Message has the same message
- *     ID as another message already in the database. The new
- *     filename was successfully added to the message in the database
- *     (if not already present).
- *
- * NOTMUCH_STATUS_FILE_ERROR: an error occurred trying to open the
- *     file, (such as permission denied, or file not found,
- *     etc.). Nothing added to the database.
- *
- * NOTMUCH_STATUS_FILE_NOT_EMAIL: the contents of filename don't look
- *     like an email message. Nothing added to the database.
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so no message can be added.
- */
-func (self *Database) AddMessage(fname string) (*Message, Status) {
-       var c_fname *C.char = C.CString(fname)
-       defer C.free(unsafe.Pointer(c_fname))
-
-       if c_fname == nil {
-               return nil, STATUS_OUT_OF_MEMORY
-       }
-
-       var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)
-       st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))
-
-       return &Message{message: c_msg}, st
-}
-
-/* Remove a message from the given notmuch database.
- *
- * Note that only this particular filename association is removed from
- * the database. If the same message (as determined by the message ID)
- * is still available via other filenames, then the message will
- * persist in the database for those filenames. When the last filename
- * is removed for a particular message, the database content for that
- * message will be entirely removed.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
- *     message was removed from the database.
- *
- * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
- *     message not removed.
- *
- * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but
- *     the message persists in the database with at least one other
- *     filename.
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so no message can be removed.
- */
-func (self *Database) RemoveMessage(fname string) Status {
-
-       var c_fname *C.char = C.CString(fname)
-       defer C.free(unsafe.Pointer(c_fname))
-
-       if c_fname == nil {
-               return STATUS_OUT_OF_MEMORY
-       }
-
-       st := C.notmuch_database_remove_message(self.db, c_fname)
-       return Status(st)
-}
-
-/* Find a message with the given message_id.
- *
- * If the database contains a message with the given message_id, then
- * a new notmuch_message_t object is returned. The caller should call
- * notmuch_message_destroy when done with the message.
- *
- * This function returns NULL in the following situations:
- *
- *     * No message is found with the given message_id
- *     * An out-of-memory situation occurs
- *     * A Xapian exception occurs
- */
-func (self *Database) FindMessage(message_id string) (*Message, Status) {
-
-       var c_msg_id *C.char = C.CString(message_id)
-       defer C.free(unsafe.Pointer(c_msg_id))
-
-       if c_msg_id == nil {
-               return nil, STATUS_OUT_OF_MEMORY
-       }
-
-       msg := &Message{message: nil}
-       st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))
-       if st != STATUS_SUCCESS {
-               return nil, st
-       }
-       return msg, st
-}
-
-/* Return a list of all tags found in the database.
- *
- * This function creates a list of all tags found in the database. The
- * resulting list contains all tags from all messages found in the database.
- *
- * On error this function returns NULL.
- */
-func (self *Database) GetAllTags() *Tags {
-       tags := C.notmuch_database_get_all_tags(self.db)
-       if tags == nil {
-               return nil
-       }
-       return &Tags{tags: tags}
-}
-
-/* Create a new query for 'database'.
- *
- * Here, 'database' should be an open database, (see
- * notmuch_database_open and notmuch_database_create).
- *
- * For the query string, we'll document the syntax here more
- * completely in the future, but it's likely to be a specialized
- * version of the general Xapian query syntax:
- *
- * https://xapian.org/docs/queryparser.html
- *
- * As a special case, passing either a length-zero string, (that is ""),
- * or a string consisting of a single asterisk (that is "*"), will
- * result in a query that returns all messages in the database.
- *
- * See notmuch_query_set_sort for controlling the order of results.
- * See notmuch_query_search_messages and notmuch_query_search_threads
- * to actually execute the query.
- *
- * User should call notmuch_query_destroy when finished with this
- * query.
- *
- * Will return NULL if insufficient memory is available.
- */
-func (self *Database) CreateQuery(query string) *Query {
-
-       var c_query *C.char = C.CString(query)
-       defer C.free(unsafe.Pointer(c_query))
-
-       if c_query == nil {
-               return nil
-       }
-
-       q := C.notmuch_query_create(self.db, c_query)
-       if q == nil {
-               return nil
-       }
-       return &Query{query: q}
-}
-
-/* Sort values for notmuch_query_set_sort */
-type Sort C.notmuch_sort_t
-
-const (
-       SORT_OLDEST_FIRST Sort = 0
-       SORT_NEWEST_FIRST
-       SORT_MESSAGE_ID
-       SORT_UNSORTED
-)
-
-/* Return the query_string of this query. See notmuch_query_create. */
-func (self *Query) String() string {
-       // FIXME: do we own 'q' or not ?
-       q := C.notmuch_query_get_query_string(self.query)
-       //defer C.free(unsafe.Pointer(q))
-
-       if q != nil {
-               s := C.GoString(q)
-               return s
-       }
-
-       return ""
-}
-
-/* Specify the sorting desired for this query. */
-func (self *Query) SetSort(sort Sort) {
-       C.notmuch_query_set_sort(self.query, C.notmuch_sort_t(sort))
-}
-
-/* Return the sort specified for this query. See notmuch_query_set_sort. */
-func (self *Query) GetSort() Sort {
-       return Sort(C.notmuch_query_get_sort(self.query))
-}
-
-/* Execute a query for threads, returning a notmuch_threads_t object
- * which can be used to iterate over the results. The returned threads
- * object is owned by the query and as such, will only be valid until
- * notmuch_query_destroy.
- *
- * Typical usage might be:
- *
- *     notmuch_query_t *query;
- *     notmuch_threads_t *threads;
- *     notmuch_thread_t *thread;
- *
- *     query = notmuch_query_create (database, query_string);
- *
- *     for (threads = notmuch_query_search_threads (query);
- *          notmuch_threads_valid (threads);
- *          notmuch_threads_move_to_next (threads))
- *     {
- *         thread = notmuch_threads_get (threads);
- *         ....
- *         notmuch_thread_destroy (thread);
- *     }
- *
- *     notmuch_query_destroy (query);
- *
- * Note: If you are finished with a thread before its containing
- * query, you can call notmuch_thread_destroy to clean up some memory
- * sooner (as in the above example). Otherwise, if your thread objects
- * are long-lived, then you don't need to call notmuch_thread_destroy
- * and all the memory will still be reclaimed when the query is
- * destroyed.
- *
- * Note that there's no explicit destructor needed for the
- * notmuch_threads_t object. (For consistency, we do provide a
- * notmuch_threads_destroy function, but there's no good reason
- * to call it if the query is about to be destroyed).
- *
- * If a Xapian exception occurs this function will return NULL.
- */
-func (self *Query) SearchThreads() *Threads {
-       threads := C.notmuch_query_search_threads(self.query)
-       if threads == nil {
-               return nil
-       }
-       return &Threads{threads: threads}
-}
-
-/* Execute a query for messages, returning a notmuch_messages_t object
- * which can be used to iterate over the results. The returned
- * messages object is owned by the query and as such, will only be
- * valid until notmuch_query_destroy.
- *
- * Typical usage might be:
- *
- *     notmuch_query_t *query;
- *     notmuch_messages_t *messages;
- *     notmuch_message_t *message;
- *
- *     query = notmuch_query_create (database, query_string);
- *
- *     for (messages = notmuch_query_search_messages (query);
- *          notmuch_messages_valid (messages);
- *          notmuch_messages_move_to_next (messages))
- *     {
- *         message = notmuch_messages_get (messages);
- *         ....
- *         notmuch_message_destroy (message);
- *     }
- *
- *     notmuch_query_destroy (query);
- *
- * Note: If you are finished with a message before its containing
- * query, you can call notmuch_message_destroy to clean up some memory
- * sooner (as in the above example). Otherwise, if your message
- * objects are long-lived, then you don't need to call
- * notmuch_message_destroy and all the memory will still be reclaimed
- * when the query is destroyed.
- *
- * Note that there's no explicit destructor needed for the
- * notmuch_messages_t object. (For consistency, we do provide a
- * notmuch_messages_destroy function, but there's no good
- * reason to call it if the query is about to be destroyed).
- *
- * If a Xapian exception occurs this function will return NULL.
- */
-func (self *Query) SearchMessages() *Messages {
-       msgs := C.notmuch_query_search_messages(self.query)
-       if msgs == nil {
-               return nil
-       }
-       return &Messages{messages: msgs}
-}
-
-/* Destroy a notmuch_query_t along with any associated resources.
- *
- * This will in turn destroy any notmuch_threads_t and
- * notmuch_messages_t objects generated by this query, (and in
- * turn any notmuch_thread_t and notmuch_message_t objects generated
- * from those results, etc.), if such objects haven't already been
- * destroyed.
- */
-func (self *Query) Destroy() {
-       if self.query != nil {
-               C.notmuch_query_destroy(self.query)
-       }
-}
-
-/* Return an estimate of the number of messages matching a search
- *
- * This function performs a search and returns Xapian's best
- * guess as to number of matching messages.
- *
- * If a Xapian exception occurs, this function may return 0 (after
- * printing a message).
- */
-func (self *Query) CountMessages() uint {
-       return uint(C.notmuch_query_count_messages(self.query))
-}
-
-// TODO: wrap threads and thread
-
-/* Is the given 'threads' iterator pointing at a valid thread.
- *
- * When this function returns TRUE, notmuch_threads_get will return a
- * valid object. Whereas when this function returns FALSE,
- * notmuch_threads_get will return NULL.
- *
- * See the documentation of notmuch_query_search_threads for example
- * code showing how to iterate over a notmuch_threads_t object.
- */
-func (self *Threads) Valid() bool {
-       if self.threads == nil {
-               return false
-       }
-       valid := C.notmuch_threads_valid(self.threads)
-       if valid == 0 {
-               return false
-       }
-       return true
-}
-
-/* Destroy a notmuch_threads_t object.
- *
- * It's not strictly necessary to call this function. All memory from
- * the notmuch_threads_t object will be reclaimed when the
- * containg query object is destroyed.
- */
-func (self *Threads) Destroy() {
-       if self.threads != nil {
-               C.notmuch_threads_destroy(self.threads)
-       }
-}
-
-/* Is the given 'messages' iterator pointing at a valid message.
- *
- * When this function returns TRUE, notmuch_messages_get will return a
- * valid object. Whereas when this function returns FALSE,
- * notmuch_messages_get will return NULL.
- *
- * See the documentation of notmuch_query_search_messages for example
- * code showing how to iterate over a notmuch_messages_t object.
- */
-func (self *Messages) Valid() bool {
-       if self.messages == nil {
-               return false
-       }
-       valid := C.notmuch_messages_valid(self.messages)
-       if valid == 0 {
-               return false
-       }
-       return true
-}
-
-/* Get the current message from 'messages' as a notmuch_message_t.
- *
- * Note: The returned message belongs to 'messages' and has a lifetime
- * identical to it (and the query to which it belongs).
- *
- * See the documentation of notmuch_query_search_messages for example
- * code showing how to iterate over a notmuch_messages_t object.
- *
- * If an out-of-memory situation occurs, this function will return
- * NULL.
- */
-func (self *Messages) Get() *Message {
-       if self.messages == nil {
-               return nil
-       }
-       msg := C.notmuch_messages_get(self.messages)
-       if msg == nil {
-               return nil
-       }
-       return &Message{message: msg}
-}
-
-/* Move the 'messages' iterator to the next message.
- *
- * If 'messages' is already pointing at the last message then the
- * iterator will be moved to a point just beyond that last message,
- * (where notmuch_messages_valid will return FALSE and
- * notmuch_messages_get will return NULL).
- *
- * See the documentation of notmuch_query_search_messages for example
- * code showing how to iterate over a notmuch_messages_t object.
- */
-func (self *Messages) MoveToNext() {
-       if self.messages == nil {
-               return
-       }
-       C.notmuch_messages_move_to_next(self.messages)
-}
-
-/* Destroy a notmuch_messages_t object.
- *
- * It's not strictly necessary to call this function. All memory from
- * the notmuch_messages_t object will be reclaimed when the containing
- * query object is destroyed.
- */
-func (self *Messages) Destroy() {
-       if self.messages != nil {
-               C.notmuch_messages_destroy(self.messages)
-       }
-}
-
-/* Return a list of tags from all messages.
- *
- * The resulting list is guaranteed not to contain duplicated tags.
- *
- * WARNING: You can no longer iterate over messages after calling this
- * function, because the iterator will point at the end of the list.
- * We do not have a function to reset the iterator yet and the only
- * way how you can iterate over the list again is to recreate the
- * message list.
- *
- * The function returns NULL on error.
- */
-func (self *Messages) CollectTags() *Tags {
-       if self.messages == nil {
-               return nil
-       }
-       tags := C.notmuch_messages_collect_tags(self.messages)
-       if tags == nil {
-               return nil
-       }
-       return &Tags{tags: tags}
-}
-
-/* Get the message ID of 'message'.
- *
- * The returned string belongs to 'message' and as such, should not be
- * modified by the caller and will only be valid for as long as the
- * message is valid, (which is until the query from which it derived
- * is destroyed).
- *
- * This function will not return NULL since Notmuch ensures that every
- * message has a unique message ID, (Notmuch will generate an ID for a
- * message if the original file does not contain one).
- */
-func (self *Message) GetMessageId() string {
-
-       if self.message == nil {
-               return ""
-       }
-       id := C.notmuch_message_get_message_id(self.message)
-       // we dont own id
-       // defer C.free(unsafe.Pointer(id))
-       if id == nil {
-               return ""
-       }
-       return C.GoString(id)
-}
-
-/* Get the thread ID of 'message'.
- *
- * The returned string belongs to 'message' and as such, should not be
- * modified by the caller and will only be valid for as long as the
- * message is valid, (for example, until the user calls
- * notmuch_message_destroy on 'message' or until a query from which it
- * derived is destroyed).
- *
- * This function will not return NULL since Notmuch ensures that every
- * message belongs to a single thread.
- */
-func (self *Message) GetThreadId() string {
-
-       if self.message == nil {
-               return ""
-       }
-       id := C.notmuch_message_get_thread_id(self.message)
-       // we dont own id
-       // defer C.free(unsafe.Pointer(id))
-
-       if id == nil {
-               return ""
-       }
-
-       return C.GoString(id)
-}
-
-/* Get a notmuch_messages_t iterator for all of the replies to
- * 'message'.
- *
- * Note: This call only makes sense if 'message' was ultimately
- * obtained from a notmuch_thread_t object, (such as by coming
- * directly from the result of calling notmuch_thread_get_
- * toplevel_messages or by any number of subsequent
- * calls to notmuch_message_get_replies).
- *
- * If 'message' was obtained through some non-thread means, (such as
- * by a call to notmuch_query_search_messages), then this function
- * will return NULL.
- *
- * If there are no replies to 'message', this function will return
- * NULL. (Note that notmuch_messages_valid will accept that NULL
- * value as legitimate, and simply return FALSE for it.)
- */
-func (self *Message) GetReplies() *Messages {
-       if self.message == nil {
-               return nil
-       }
-       msgs := C.notmuch_message_get_replies(self.message)
-       if msgs == nil {
-               return nil
-       }
-       return &Messages{messages: msgs}
-}
-
-/* Get a filename for the email corresponding to 'message'.
- *
- * The returned filename is an absolute filename, (the initial
- * component will match notmuch_database_get_path() ).
- *
- * The returned string belongs to the message so should not be
- * modified or freed by the caller (nor should it be referenced after
- * the message is destroyed).
- *
- * Note: If this message corresponds to multiple files in the mail
- * store, (that is, multiple files contain identical message IDs),
- * this function will arbitrarily return a single one of those
- * filenames.
- */
-func (self *Message) GetFileName() string {
-       if self.message == nil {
-               return ""
-       }
-       fname := C.notmuch_message_get_filename(self.message)
-       // we dont own fname
-       // defer C.free(unsafe.Pointer(fname))
-
-       if fname == nil {
-               return ""
-       }
-
-       return C.GoString(fname)
-}
-
-type Flag C.notmuch_message_flag_t
-
-const (
-       MESSAGE_FLAG_MATCH Flag = 0
-)
-
-/* Get a value of a flag for the email corresponding to 'message'. */
-func (self *Message) GetFlag(flag Flag) bool {
-       if self.message == nil {
-               return false
-       }
-       v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))
-       if v == 0 {
-               return false
-       }
-       return true
-}
-
-/* Set a value of a flag for the email corresponding to 'message'. */
-func (self *Message) SetFlag(flag Flag, value bool) {
-       if self.message == nil {
-               return
-       }
-       var v C.notmuch_bool_t = 0
-       if value {
-               v = 1
-       }
-       C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)
-}
-
-/* Get the timestamp (seconds since the epoch) of 'message'.
- *
- * Return status:
- *
- * NOTMUCH_STATUS_SUCCESS: Timestamp successfully retrieved
- *
- * NOTMUCH_STATUS_NULL_POINTER: The 'message' argument is NULL
- *
- */
-func (self *Message) GetDate() (int64, Status) {
-       if self.message == nil {
-               return -1, STATUS_NULL_POINTER
-       }
-       timestamp := C.notmuch_message_get_date(self.message)
-       return int64(timestamp), STATUS_SUCCESS
-}
-
-/* Get the value of the specified header from 'message'.
- *
- * The value will be read from the actual message file, not from the
- * notmuch database. The header name is case insensitive.
- *
- * The returned string belongs to the message so should not be
- * modified or freed by the caller (nor should it be referenced after
- * the message is destroyed).
- *
- * Returns an empty string ("") if the message does not contain a
- * header line matching 'header'. Returns NULL if any error occurs.
- */
-func (self *Message) GetHeader(header string) string {
-       if self.message == nil {
-               return ""
-       }
-
-       var c_header *C.char = C.CString(header)
-       defer C.free(unsafe.Pointer(c_header))
-
-       /* we dont own value */
-       value := C.notmuch_message_get_header(self.message, c_header)
-       if value == nil {
-               return ""
-       }
-
-       return C.GoString(value)
-}
-
-/* Get the tags for 'message', returning a notmuch_tags_t object which
- * can be used to iterate over all tags.
- *
- * The tags 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).
- *
- * Typical usage might be:
- *
- *     notmuch_message_t *message;
- *     notmuch_tags_t *tags;
- *     const char *tag;
- *
- *     message = notmuch_database_find_message (database, message_id);
- *
- *     for (tags = notmuch_message_get_tags (message);
- *          notmuch_tags_valid (tags);
- *          notmuch_result_move_to_next (tags))
- *     {
- *         tag = notmuch_tags_get (tags);
- *         ....
- *     }
- *
- *     notmuch_message_destroy (message);
- *
- * Note that there's no explicit destructor needed for the
- * notmuch_tags_t object. (For consistency, we do provide a
- * notmuch_tags_destroy function, but there's no good reason to call
- * it if the message is about to be destroyed).
- */
-func (self *Message) GetTags() *Tags {
-       if self.message == nil {
-               return nil
-       }
-       tags := C.notmuch_message_get_tags(self.message)
-       if tags == nil {
-               return nil
-       }
-       return &Tags{tags: tags}
-}
-
-/* The longest possible tag value. */
-const TAG_MAX = 200
-
-/* Add a tag to the given message.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
- *
- * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
- *
- * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
- *     (exceeds NOTMUCH_TAG_MAX)
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so message cannot be modified.
- */
-func (self *Message) AddTag(tag string) Status {
-       if self.message == nil {
-               return STATUS_NULL_POINTER
-       }
-       c_tag := C.CString(tag)
-       defer C.free(unsafe.Pointer(c_tag))
-
-       return Status(C.notmuch_message_add_tag(self.message, c_tag))
-}
-
-/* Remove a tag from the given message.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
- *
- * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
- *
- * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
- *     (exceeds NOTMUCH_TAG_MAX)
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so message cannot be modified.
- */
-func (self *Message) RemoveTag(tag string) Status {
-       if self.message == nil {
-               return STATUS_NULL_POINTER
-       }
-       c_tag := C.CString(tag)
-       defer C.free(unsafe.Pointer(c_tag))
-
-       return Status(C.notmuch_message_remove_tag(self.message, c_tag))
-}
-
-/* Remove all tags from the given message.
- *
- * See notmuch_message_freeze for an example showing how to safely
- * replace tag values.
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so message cannot be modified.
- */
-func (self *Message) RemoveAllTags() Status {
-       if self.message == nil {
-               return STATUS_NULL_POINTER
-       }
-       return Status(C.notmuch_message_remove_all_tags(self.message))
-}
-
-/* Freeze the current state of 'message' within the database.
- *
- * This means that changes to the message state, (via
- * notmuch_message_add_tag, notmuch_message_remove_tag, and
- * notmuch_message_remove_all_tags), will not be committed to the
- * database until the message is thawed with notmuch_message_thaw.
- *
- * Multiple calls to freeze/thaw are valid and these calls will
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
- *
- * The ability to do freeze/thaw allows for safe transactions to
- * change tag values. For example, explicitly setting a message to
- * have a given set of tags might look like this:
- *
- *    notmuch_message_freeze (message);
- *
- *    notmuch_message_remove_all_tags (message);
- *
- *    for (i = 0; i < NUM_TAGS; i++)
- *        notmuch_message_add_tag (message, tags[i]);
- *
- *    notmuch_message_thaw (message);
- *
- * With freeze/thaw used like this, the message in the database is
- * guaranteed to have either the full set of original tag values, or
- * the full set of new tag values, but nothing in between.
- *
- * Imagine the example above without freeze/thaw and the operation
- * somehow getting interrupted. This could result in the message being
- * left with no tags if the interruption happened after
- * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *     mode so message cannot be modified.
- */
-func (self *Message) Freeze() Status {
-       if self.message == nil {
-               return STATUS_NULL_POINTER
-       }
-       return Status(C.notmuch_message_freeze(self.message))
-}
-
-/* Thaw the current 'message', synchronizing any changes that may have
- * occurred while 'message' was frozen into the notmuch database.
- *
- * See notmuch_message_freeze for an example of how to use this
- * function to safely provide tag changes.
- *
- * Multiple calls to freeze/thaw are valid and these calls with
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
- *     its frozen count has successfully been reduced by 1).
- *
- * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw
- *     an unfrozen message. That is, there have been an unbalanced
- *     number of calls to notmuch_message_freeze and
- *     notmuch_message_thaw.
- */
-func (self *Message) Thaw() Status {
-       if self.message == nil {
-               return STATUS_NULL_POINTER
-       }
-
-       return Status(C.notmuch_message_thaw(self.message))
-}
-
-/* Destroy a notmuch_message_t object.
- *
- * It can be useful to call this function in the case of a single
- * query object with many messages in the result, (such as iterating
- * over the entire database). Otherwise, it's fine to never call this
- * function and there will still be no memory leaks. (The memory from
- * the messages get reclaimed when the containing query is destroyed.)
- */
-func (self *Message) Destroy() {
-       if self.message == nil {
-               return
-       }
-       C.notmuch_message_destroy(self.message)
-}
-
-/* Is the given 'tags' iterator pointing at a valid tag.
- *
- * When this function returns TRUE, notmuch_tags_get will return a
- * valid string. Whereas when this function returns FALSE,
- * notmuch_tags_get will return NULL.
- *
- * See the documentation of notmuch_message_get_tags for example code
- * showing how to iterate over a notmuch_tags_t object.
- */
-func (self *Tags) Valid() bool {
-       if self.tags == nil {
-               return false
-       }
-       v := C.notmuch_tags_valid(self.tags)
-       if v == 0 {
-               return false
-       }
-       return true
-}
-
-/* Get the current tag from 'tags' as a string.
- *
- * Note: The returned string belongs to 'tags' and has a lifetime
- * identical to it (and the query to which it ultimately belongs).
- *
- * See the documentation of notmuch_message_get_tags for example code
- * showing how to iterate over a notmuch_tags_t object.
- */
-func (self *Tags) Get() string {
-       if self.tags == nil {
-               return ""
-       }
-       s := C.notmuch_tags_get(self.tags)
-       // we dont own 's'
-
-       return C.GoString(s)
-}
-func (self *Tags) String() string {
-       return self.Get()
-}
-
-/* Move the 'tags' iterator to the next tag.
- *
- * If 'tags' is already pointing at the last tag then the iterator
- * will be moved to a point just beyond that last tag, (where
- * notmuch_tags_valid will return FALSE and notmuch_tags_get will
- * return NULL).
- *
- * See the documentation of notmuch_message_get_tags for example code
- * showing how to iterate over a notmuch_tags_t object.
- */
-func (self *Tags) MoveToNext() {
-       if self.tags == nil {
-               return
-       }
-       C.notmuch_tags_move_to_next(self.tags)
-}
-
-/* Destroy a notmuch_tags_t object.
- *
- * It's not strictly necessary to call this function. All memory from
- * the notmuch_tags_t object will be reclaimed when the containing
- * message or query objects are destroyed.
- */
-func (self *Tags) Destroy() {
-       if self.tags == nil {
-               return
-       }
-       C.notmuch_tags_destroy(self.tags)
-}
-
-// TODO: wrap notmuch_directory_<fct>
-
-/* Destroy a notmuch_directory_t object. */
-func (self *Directory) Destroy() {
-       if self.dir == nil {
-               return
-       }
-       C.notmuch_directory_destroy(self.dir)
-}
-
-// TODO: wrap notmuch_filenames_<fct>
-
-/* Destroy a notmuch_filenames_t object.
- *
- * It's not strictly necessary to call this function. All memory from
- * the notmuch_filenames_t object will be reclaimed when the
- * containing directory object is destroyed.
- *
- * It is acceptable to pass NULL for 'filenames', in which case this
- * function will do nothing.
- */
-func (self *Filenames) Destroy() {
-       if self.fnames == nil {
-               return
-       }
-       C.notmuch_filenames_destroy(self.fnames)
-}
-
-/* EOF */
diff --git a/contrib/go/.gitignore b/contrib/go/.gitignore
new file mode 100644 (file)
index 0000000..c394479
--- /dev/null
@@ -0,0 +1,3 @@
+src/github.com/
+pkg/
+bin/
diff --git a/contrib/go/LICENSE b/contrib/go/LICENSE
new file mode 100644 (file)
index 0000000..4362b49
--- /dev/null
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/contrib/go/Makefile b/contrib/go/Makefile
new file mode 100644 (file)
index 0000000..1b9e750
--- /dev/null
@@ -0,0 +1,40 @@
+# Makefile for the go bindings of notmuch
+
+export GOPATH      ?= $(shell pwd)
+export CGO_CFLAGS  ?= -I../../../../lib
+export CGO_LDFLAGS ?= -L../../../../lib
+
+GO         ?= go
+GOFMT      ?= gofmt
+
+all: notmuch notmuch-addrlookup
+
+.PHONY: notmuch
+notmuch:
+       $(GO) install notmuch
+
+.PHONY: goconfig
+goconfig:
+       if [ ! -d github.com/msbranco/goconfig ]; then \
+           $(GO) get github.com/msbranco/goconfig; \
+       fi
+
+.PHONY: notmuch-addrlookup
+notmuch-addrlookup: notmuch goconfig
+       $(GO) install notmuch-addrlookup
+
+.PHONY: format
+format:
+       $(GOFMT) -w=true $(GOFMT_OPTS) src/notmuch
+       $(GOFMT) -w=true $(GOFMT_OPTS) src/notmuch-addrlookup
+
+.PHONY: check-format
+check-format:
+       $(GOFMT) -d=true $(GOFMT_OPTS) src/notmuch
+       $(GOFMT) -d=true $(GOFMT_OPTS) src/notmuch-addrlookup
+
+.PHONY: clean
+clean:
+       $(GO) clean notmuch
+       $(GO) clean notmuch-addrlookup
+       rm -rf pkg bin
diff --git a/contrib/go/README b/contrib/go/README
new file mode 100644 (file)
index 0000000..1825ae0
--- /dev/null
@@ -0,0 +1,16 @@
+go-notmuch
+==========
+
+simple go bindings to the libnotmuch library[1].
+
+they are heavily inspired from the vala bindings from Sebastian Spaeth: 
+  https://github.com/spaetz/vala-notmuch
+
+note: the whole library hasn't been wrapped (yet?)
+
+todo
+----
+
+ - improve notmuch-addrlookup regexp
+
+[1] https://notmuchmail.org/
diff --git a/contrib/go/src/notmuch-addrlookup/addrlookup.go b/contrib/go/src/notmuch-addrlookup/addrlookup.go
new file mode 100644 (file)
index 0000000..916e5bb
--- /dev/null
@@ -0,0 +1,261 @@
+package main
+
+// stdlib imports
+import "os"
+import "path"
+import "log"
+import "fmt"
+import "regexp"
+import "strings"
+import "sort"
+
+// 3rd-party imports
+import "notmuch"
+import "github.com/msbranco/goconfig"
+
+type mail_addr_freq struct {
+       addr  string
+       count [3]uint
+}
+
+type frequencies map[string]uint
+
+/* Used to sort the email addresses from most to least used */
+func sort_by_freq(m1, m2 *mail_addr_freq) int {
+       if m1.count[0] == m2.count[0] &&
+               m1.count[1] == m2.count[1] &&
+               m1.count[2] == m2.count[2] {
+               return 0
+       }
+
+       if m1.count[0] > m2.count[0] ||
+               m1.count[0] == m2.count[0] &&
+                       m1.count[1] > m2.count[1] ||
+               m1.count[0] == m2.count[0] &&
+                       m1.count[1] == m2.count[1] &&
+                       m1.count[2] > m2.count[2] {
+               return -1
+       }
+
+       return 1
+}
+
+type maddresses []*mail_addr_freq
+
+func (self *maddresses) Len() int {
+       return len(*self)
+}
+
+func (self *maddresses) Less(i, j int) bool {
+       m1 := (*self)[i]
+       m2 := (*self)[j]
+       v := sort_by_freq(m1, m2)
+       if v <= 0 {
+               return true
+       }
+       return false
+}
+
+func (self *maddresses) Swap(i, j int) {
+       (*self)[i], (*self)[j] = (*self)[j], (*self)[i]
+}
+
+// find most frequent real name for each mail address
+func frequent_fullname(freqs frequencies) string {
+       var maxfreq uint = 0
+       fullname := ""
+       freqs_sz := len(freqs)
+
+       for mail, freq := range freqs {
+               if (freq > maxfreq && mail != "") || freqs_sz == 1 {
+                       // only use the entry if it has a real name
+                       // or if this is the only entry
+                       maxfreq = freq
+                       fullname = mail
+               }
+       }
+       return fullname
+}
+
+func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {
+
+       freqs := make(frequencies)
+
+       pattern := `\s*(("(\.|[^"])*"|[^,])*<?(?mail\b\w+([-+.]\w+)*\@\w+[-\.\w]*\.([-\.\w]+)*\w\b)>?)`
+       // pattern := "\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" +
+       //      "<?(?P<mail>\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)>?)"
+       pattern = `.*` + strings.ToLower(name) + `.*`
+       var re *regexp.Regexp = nil
+       var err error = nil
+       if re, err = regexp.Compile(pattern); err != nil {
+               log.Printf("error: %v\n", err)
+               return &freqs
+       }
+
+       headers := []string{"from"}
+       if pass == 1 {
+               headers = append(headers, "to", "cc", "bcc")
+       }
+
+       for ; msgs.Valid(); msgs.MoveToNext() {
+               msg := msgs.Get()
+               //println("==> msg [", msg.GetMessageId(), "]")
+               for _, header := range headers {
+                       froms := strings.ToLower(msg.GetHeader(header))
+                       //println("  froms: ["+froms+"]")
+                       for _, from := range strings.Split(froms, ",") {
+                               from = strings.Trim(from, " ")
+                               match := re.FindString(from)
+                               //println("  -> match: ["+match+"]")
+                               occ, ok := freqs[match]
+                               if !ok {
+                                       freqs[match] = 0
+                                       occ = 0
+                               }
+                               freqs[match] = occ + 1
+                       }
+               }
+       }
+       return &freqs
+}
+
+func search_address_passes(queries [3]*notmuch.Query, name string) []string {
+       var val []string
+       addr_freq := make(map[string]*mail_addr_freq)
+       addr_to_realname := make(map[string]*frequencies)
+
+       var pass uint = 0 // 0-based
+       for _, query := range queries {
+               if query == nil {
+                       //println("**warning: idx [",idx,"] contains a nil query")
+                       continue
+               }
+               msgs := query.SearchMessages()
+               ht := addresses_by_frequency(msgs, name, pass, &addr_to_realname)
+               for addr, count := range *ht {
+                       freq, ok := addr_freq[addr]
+                       if !ok {
+                               freq = &mail_addr_freq{addr: addr, count: [3]uint{0, 0, 0}}
+                       }
+                       freq.count[pass] = count
+                       addr_freq[addr] = freq
+               }
+               msgs.Destroy()
+               pass += 1
+       }
+
+       addrs := make(maddresses, len(addr_freq))
+       {
+               iaddr := 0
+               for _, freq := range addr_freq {
+                       addrs[iaddr] = freq
+                       iaddr += 1
+               }
+       }
+       sort.Sort(&addrs)
+
+       for _, addr := range addrs {
+               freqs, ok := addr_to_realname[addr.addr]
+               if ok {
+                       val = append(val, frequent_fullname(*freqs))
+               } else {
+                       val = append(val, addr.addr)
+               }
+       }
+       //println("val:",val)
+       return val
+}
+
+type address_matcher struct {
+       // the notmuch database
+       db *notmuch.Database
+       // full path of the notmuch database
+       user_db_path string
+       // user primary email
+       user_primary_email string
+       // user tag to mark from addresses as in the address book
+       user_addrbook_tag string
+}
+
+func new_address_matcher() *address_matcher {
+       // honor NOTMUCH_CONFIG
+       home := os.Getenv("NOTMUCH_CONFIG")
+       if home == "" {
+               home = os.Getenv("HOME")
+       }
+
+       cfg, err := goconfig.ReadConfigFile(path.Join(home, ".notmuch-config"))
+       if err != nil {
+               log.Fatalf("error loading config file:", err)
+       }
+
+       db_path, _ := cfg.GetString("database", "path")
+       primary_email, _ := cfg.GetString("user", "primary_email")
+       addrbook_tag, err := cfg.GetString("user", "addrbook_tag")
+       if err != nil {
+               addrbook_tag = "addressbook"
+       }
+
+       self := &address_matcher{db: nil,
+               user_db_path:       db_path,
+               user_primary_email: primary_email,
+               user_addrbook_tag:  addrbook_tag}
+       return self
+}
+
+func (self *address_matcher) run(name string) {
+       queries := [3]*notmuch.Query{}
+
+       // open the database
+       if db, status := notmuch.OpenDatabase(self.user_db_path,
+               notmuch.DATABASE_MODE_READ_ONLY); status == notmuch.STATUS_SUCCESS {
+               self.db = db
+       } else {
+               log.Fatalf("Failed to open the database: %v\n", status)
+       }
+
+       // pass 1: look at all from: addresses with the address book tag
+       query := "tag:" + self.user_addrbook_tag
+       if name != "" {
+               query = query + " and from:" + name + "*"
+       }
+       queries[0] = self.db.CreateQuery(query)
+
+       // pass 2: look at all to: addresses sent from our primary mail
+       query = ""
+       if name != "" {
+               query = "to:" + name + "*"
+       }
+       if self.user_primary_email != "" {
+               query = query + " from:" + self.user_primary_email
+       }
+       queries[1] = self.db.CreateQuery(query)
+
+       // if that leads only to a few hits, we check every from too
+       if queries[0].CountMessages()+queries[1].CountMessages() < 10 {
+               query = ""
+               if name != "" {
+                       query = "from:" + name + "*"
+               }
+               queries[2] = self.db.CreateQuery(query)
+       }
+
+       // actually retrieve and sort addresses
+       results := search_address_passes(queries, name)
+       for _, v := range results {
+               if v != "" && v != "\n" {
+                       fmt.Println(v)
+               }
+       }
+       return
+}
+
+func main() {
+       //fmt.Println("args:",os.Args)
+       app := new_address_matcher()
+       name := ""
+       if len(os.Args) > 1 {
+               name = os.Args[1]
+       }
+       app.run(name)
+}
diff --git a/contrib/go/src/notmuch/notmuch.go b/contrib/go/src/notmuch/notmuch.go
new file mode 100644 (file)
index 0000000..4d8c2ce
--- /dev/null
@@ -0,0 +1,1146 @@
+// Wrapper around the notmuch library
+
+package notmuch
+
+/*
+#cgo LDFLAGS: -lnotmuch
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "notmuch.h"
+*/
+import "C"
+import "unsafe"
+
+// Status codes used for the return values of most functions
+type Status C.notmuch_status_t
+
+const (
+       STATUS_SUCCESS Status = iota
+       STATUS_OUT_OF_MEMORY
+       STATUS_READ_ONLY_DATABASE
+       STATUS_XAPIAN_EXCEPTION
+       STATUS_FILE_ERROR
+       STATUS_FILE_NOT_EMAIL
+       STATUS_DUPLICATE_MESSAGE_ID
+       STATUS_NULL_POINTER
+       STATUS_TAG_TOO_LONG
+       STATUS_UNBALANCED_FREEZE_THAW
+       STATUS_UNBALANCED_ATOMIC
+
+       STATUS_LAST_STATUS
+)
+
+func (self Status) String() string {
+       var p *C.char
+
+       // p is read-only
+       p = C.notmuch_status_to_string(C.notmuch_status_t(self))
+       if p != nil {
+               s := C.GoString(p)
+               return s
+       }
+       return ""
+}
+
+/* Various opaque data types. For each notmuch_<foo>_t see the various
+ * notmuch_<foo> functions below. */
+
+type Database struct {
+       db *C.notmuch_database_t
+}
+
+type Query struct {
+       query *C.notmuch_query_t
+}
+
+type Threads struct {
+       threads *C.notmuch_threads_t
+}
+
+type Thread struct {
+       thread *C.notmuch_thread_t
+}
+
+type Messages struct {
+       messages *C.notmuch_messages_t
+}
+
+type Message struct {
+       message *C.notmuch_message_t
+}
+
+type Tags struct {
+       tags *C.notmuch_tags_t
+}
+
+type Directory struct {
+       dir *C.notmuch_directory_t
+}
+
+type Filenames struct {
+       fnames *C.notmuch_filenames_t
+}
+
+type DatabaseMode C.notmuch_database_mode_t
+
+const (
+       DATABASE_MODE_READ_ONLY DatabaseMode = 0
+       DATABASE_MODE_READ_WRITE
+)
+
+// Create a new, empty notmuch database located at 'path'
+func NewDatabase(path string) (*Database, Status) {
+
+       var c_path *C.char = C.CString(path)
+       defer C.free(unsafe.Pointer(c_path))
+
+       if c_path == nil {
+               return nil, STATUS_OUT_OF_MEMORY
+       }
+
+       self := &Database{db: nil}
+       st := Status(C.notmuch_database_create(c_path, &self.db))
+       if st != STATUS_SUCCESS {
+               return nil, st
+       }
+       return self, st
+}
+
+/* Open an existing notmuch database located at 'path'.
+ *
+ * The database should have been created at some time in the past,
+ * (not necessarily by this process), by calling
+ * notmuch_database_create with 'path'. By default the database should be
+ * opened for reading only. In order to write to the database you need to
+ * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode.
+ *
+ * An existing notmuch database can be identified by the presence of a
+ * directory named ".notmuch" below 'path'.
+ *
+ * The caller should call notmuch_database_destroy when finished with
+ * this database.
+ *
+ * In case of any failure, this function returns NULL, (after printing
+ * an error message on stderr).
+ */
+func OpenDatabase(path string, mode DatabaseMode) (*Database, Status) {
+
+       var c_path *C.char = C.CString(path)
+       defer C.free(unsafe.Pointer(c_path))
+
+       if c_path == nil {
+               return nil, STATUS_OUT_OF_MEMORY
+       }
+
+       self := &Database{db: nil}
+       st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &self.db))
+       if st != STATUS_SUCCESS {
+               return nil, st
+       }
+       return self, st
+}
+
+/* Close the given notmuch database, freeing all associated
+ * resources. See notmuch_database_open. */
+func (self *Database) Close() Status {
+       return Status(C.notmuch_database_destroy(self.db))
+}
+
+/* Return the database path of the given database.
+ */
+func (self *Database) GetPath() string {
+
+       /* The return value is a string owned by notmuch so should not be
+        * modified nor freed by the caller. */
+       var p *C.char = C.notmuch_database_get_path(self.db)
+       if p != nil {
+               s := C.GoString(p)
+               return s
+       }
+       return ""
+}
+
+/* Return the database format version of the given database. */
+func (self *Database) GetVersion() uint {
+       return uint(C.notmuch_database_get_version(self.db))
+}
+
+/* Does this database need to be upgraded before writing to it?
+ *
+ * If this function returns TRUE then no functions that modify the
+ * database (notmuch_database_add_message, notmuch_message_add_tag,
+ * notmuch_directory_set_mtime, etc.) will work unless the function
+ * notmuch_database_upgrade is called successfully first. */
+func (self *Database) NeedsUpgrade() bool {
+       do_upgrade := C.notmuch_database_needs_upgrade(self.db)
+       if do_upgrade == 0 {
+               return false
+       }
+       return true
+}
+
+// TODO: notmuch_database_upgrade
+
+/* Retrieve a directory object from the database for 'path'.
+ *
+ * Here, 'path' should be a path relative to the path of 'database'
+ * (see notmuch_database_get_path), or else should be an absolute path
+ * with initial components that match the path of 'database'.
+ *
+ * Can return NULL if a Xapian exception occurs.
+ */
+func (self *Database) GetDirectory(path string) (*Directory, Status) {
+       var c_path *C.char = C.CString(path)
+       defer C.free(unsafe.Pointer(c_path))
+
+       if c_path == nil {
+               return nil, STATUS_OUT_OF_MEMORY
+       }
+
+       var c_dir *C.notmuch_directory_t
+       st := Status(C.notmuch_database_get_directory(self.db, c_path, &c_dir))
+       if st != STATUS_SUCCESS || c_dir == nil {
+               return nil, st
+       }
+       return &Directory{dir: c_dir}, st
+}
+
+/* Add a new message to the given notmuch database.
+ *
+ * Here,'filename' should be a path relative to the path of
+ * 'database' (see notmuch_database_get_path), or else should be an
+ * absolute filename with initial components that match the path of
+ * 'database'.
+ *
+ * The file should be a single mail message (not a multi-message mbox)
+ * that is expected to remain at its current location, (since the
+ * notmuch database will reference the filename, and will not copy the
+ * entire contents of the file.
+ *
+ * If 'message' is not NULL, then, on successful return '*message'
+ * will be initialized to a message object that can be used for things
+ * such as adding tags to the just-added message. The user should call
+ * notmuch_message_destroy when done with the message. On any failure
+ * '*message' will be set to NULL.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.
+ *
+ * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
+ *     message not added.
+ *
+ * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: Message has the same message
+ *     ID as another message already in the database. The new
+ *     filename was successfully added to the message in the database
+ *     (if not already present).
+ *
+ * NOTMUCH_STATUS_FILE_ERROR: an error occurred trying to open the
+ *     file, (such as permission denied, or file not found,
+ *     etc.). Nothing added to the database.
+ *
+ * NOTMUCH_STATUS_FILE_NOT_EMAIL: the contents of filename don't look
+ *     like an email message. Nothing added to the database.
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so no message can be added.
+ */
+func (self *Database) AddMessage(fname string) (*Message, Status) {
+       var c_fname *C.char = C.CString(fname)
+       defer C.free(unsafe.Pointer(c_fname))
+
+       if c_fname == nil {
+               return nil, STATUS_OUT_OF_MEMORY
+       }
+
+       var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)
+       st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))
+
+       return &Message{message: c_msg}, st
+}
+
+/* Remove a message from the given notmuch database.
+ *
+ * Note that only this particular filename association is removed from
+ * the database. If the same message (as determined by the message ID)
+ * is still available via other filenames, then the message will
+ * persist in the database for those filenames. When the last filename
+ * is removed for a particular message, the database content for that
+ * message will be entirely removed.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
+ *     message was removed from the database.
+ *
+ * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
+ *     message not removed.
+ *
+ * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but
+ *     the message persists in the database with at least one other
+ *     filename.
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so no message can be removed.
+ */
+func (self *Database) RemoveMessage(fname string) Status {
+
+       var c_fname *C.char = C.CString(fname)
+       defer C.free(unsafe.Pointer(c_fname))
+
+       if c_fname == nil {
+               return STATUS_OUT_OF_MEMORY
+       }
+
+       st := C.notmuch_database_remove_message(self.db, c_fname)
+       return Status(st)
+}
+
+/* Find a message with the given message_id.
+ *
+ * If the database contains a message with the given message_id, then
+ * a new notmuch_message_t object is returned. The caller should call
+ * notmuch_message_destroy when done with the message.
+ *
+ * This function returns NULL in the following situations:
+ *
+ *     * No message is found with the given message_id
+ *     * An out-of-memory situation occurs
+ *     * A Xapian exception occurs
+ */
+func (self *Database) FindMessage(message_id string) (*Message, Status) {
+
+       var c_msg_id *C.char = C.CString(message_id)
+       defer C.free(unsafe.Pointer(c_msg_id))
+
+       if c_msg_id == nil {
+               return nil, STATUS_OUT_OF_MEMORY
+       }
+
+       msg := &Message{message: nil}
+       st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))
+       if st != STATUS_SUCCESS {
+               return nil, st
+       }
+       return msg, st
+}
+
+/* Return a list of all tags found in the database.
+ *
+ * This function creates a list of all tags found in the database. The
+ * resulting list contains all tags from all messages found in the database.
+ *
+ * On error this function returns NULL.
+ */
+func (self *Database) GetAllTags() *Tags {
+       tags := C.notmuch_database_get_all_tags(self.db)
+       if tags == nil {
+               return nil
+       }
+       return &Tags{tags: tags}
+}
+
+/* Create a new query for 'database'.
+ *
+ * Here, 'database' should be an open database, (see
+ * notmuch_database_open and notmuch_database_create).
+ *
+ * For the query string, we'll document the syntax here more
+ * completely in the future, but it's likely to be a specialized
+ * version of the general Xapian query syntax:
+ *
+ * https://xapian.org/docs/queryparser.html
+ *
+ * As a special case, passing either a length-zero string, (that is ""),
+ * or a string consisting of a single asterisk (that is "*"), will
+ * result in a query that returns all messages in the database.
+ *
+ * See notmuch_query_set_sort for controlling the order of results.
+ * See notmuch_query_search_messages and notmuch_query_search_threads
+ * to actually execute the query.
+ *
+ * User should call notmuch_query_destroy when finished with this
+ * query.
+ *
+ * Will return NULL if insufficient memory is available.
+ */
+func (self *Database) CreateQuery(query string) *Query {
+
+       var c_query *C.char = C.CString(query)
+       defer C.free(unsafe.Pointer(c_query))
+
+       if c_query == nil {
+               return nil
+       }
+
+       q := C.notmuch_query_create(self.db, c_query)
+       if q == nil {
+               return nil
+       }
+       return &Query{query: q}
+}
+
+/* Sort values for notmuch_query_set_sort */
+type Sort C.notmuch_sort_t
+
+const (
+       SORT_OLDEST_FIRST Sort = 0
+       SORT_NEWEST_FIRST
+       SORT_MESSAGE_ID
+       SORT_UNSORTED
+)
+
+/* Return the query_string of this query. See notmuch_query_create. */
+func (self *Query) String() string {
+       // FIXME: do we own 'q' or not ?
+       q := C.notmuch_query_get_query_string(self.query)
+       //defer C.free(unsafe.Pointer(q))
+
+       if q != nil {
+               s := C.GoString(q)
+               return s
+       }
+
+       return ""
+}
+
+/* Specify the sorting desired for this query. */
+func (self *Query) SetSort(sort Sort) {
+       C.notmuch_query_set_sort(self.query, C.notmuch_sort_t(sort))
+}
+
+/* Return the sort specified for this query. See notmuch_query_set_sort. */
+func (self *Query) GetSort() Sort {
+       return Sort(C.notmuch_query_get_sort(self.query))
+}
+
+/* Execute a query for threads, returning a notmuch_threads_t object
+ * which can be used to iterate over the results. The returned threads
+ * object is owned by the query and as such, will only be valid until
+ * notmuch_query_destroy.
+ *
+ * Typical usage might be:
+ *
+ *     notmuch_query_t *query;
+ *     notmuch_threads_t *threads;
+ *     notmuch_thread_t *thread;
+ *
+ *     query = notmuch_query_create (database, query_string);
+ *
+ *     for (threads = notmuch_query_search_threads (query);
+ *          notmuch_threads_valid (threads);
+ *          notmuch_threads_move_to_next (threads))
+ *     {
+ *         thread = notmuch_threads_get (threads);
+ *         ....
+ *         notmuch_thread_destroy (thread);
+ *     }
+ *
+ *     notmuch_query_destroy (query);
+ *
+ * Note: If you are finished with a thread before its containing
+ * query, you can call notmuch_thread_destroy to clean up some memory
+ * sooner (as in the above example). Otherwise, if your thread objects
+ * are long-lived, then you don't need to call notmuch_thread_destroy
+ * and all the memory will still be reclaimed when the query is
+ * destroyed.
+ *
+ * Note that there's no explicit destructor needed for the
+ * notmuch_threads_t object. (For consistency, we do provide a
+ * notmuch_threads_destroy function, but there's no good reason
+ * to call it if the query is about to be destroyed).
+ *
+ * If a Xapian exception occurs this function will return NULL.
+ */
+func (self *Query) SearchThreads() *Threads {
+       threads := C.notmuch_query_search_threads(self.query)
+       if threads == nil {
+               return nil
+       }
+       return &Threads{threads: threads}
+}
+
+/* Execute a query for messages, returning a notmuch_messages_t object
+ * which can be used to iterate over the results. The returned
+ * messages object is owned by the query and as such, will only be
+ * valid until notmuch_query_destroy.
+ *
+ * Typical usage might be:
+ *
+ *     notmuch_query_t *query;
+ *     notmuch_messages_t *messages;
+ *     notmuch_message_t *message;
+ *
+ *     query = notmuch_query_create (database, query_string);
+ *
+ *     for (messages = notmuch_query_search_messages (query);
+ *          notmuch_messages_valid (messages);
+ *          notmuch_messages_move_to_next (messages))
+ *     {
+ *         message = notmuch_messages_get (messages);
+ *         ....
+ *         notmuch_message_destroy (message);
+ *     }
+ *
+ *     notmuch_query_destroy (query);
+ *
+ * Note: If you are finished with a message before its containing
+ * query, you can call notmuch_message_destroy to clean up some memory
+ * sooner (as in the above example). Otherwise, if your message
+ * objects are long-lived, then you don't need to call
+ * notmuch_message_destroy and all the memory will still be reclaimed
+ * when the query is destroyed.
+ *
+ * Note that there's no explicit destructor needed for the
+ * notmuch_messages_t object. (For consistency, we do provide a
+ * notmuch_messages_destroy function, but there's no good
+ * reason to call it if the query is about to be destroyed).
+ *
+ * If a Xapian exception occurs this function will return NULL.
+ */
+func (self *Query) SearchMessages() *Messages {
+       msgs := C.notmuch_query_search_messages(self.query)
+       if msgs == nil {
+               return nil
+       }
+       return &Messages{messages: msgs}
+}
+
+/* Destroy a notmuch_query_t along with any associated resources.
+ *
+ * This will in turn destroy any notmuch_threads_t and
+ * notmuch_messages_t objects generated by this query, (and in
+ * turn any notmuch_thread_t and notmuch_message_t objects generated
+ * from those results, etc.), if such objects haven't already been
+ * destroyed.
+ */
+func (self *Query) Destroy() {
+       if self.query != nil {
+               C.notmuch_query_destroy(self.query)
+       }
+}
+
+/* Return an estimate of the number of messages matching a search
+ *
+ * This function performs a search and returns Xapian's best
+ * guess as to number of matching messages.
+ *
+ * If a Xapian exception occurs, this function may return 0 (after
+ * printing a message).
+ */
+func (self *Query) CountMessages() uint {
+       return uint(C.notmuch_query_count_messages(self.query))
+}
+
+// TODO: wrap threads and thread
+
+/* Is the given 'threads' iterator pointing at a valid thread.
+ *
+ * When this function returns TRUE, notmuch_threads_get will return a
+ * valid object. Whereas when this function returns FALSE,
+ * notmuch_threads_get will return NULL.
+ *
+ * See the documentation of notmuch_query_search_threads for example
+ * code showing how to iterate over a notmuch_threads_t object.
+ */
+func (self *Threads) Valid() bool {
+       if self.threads == nil {
+               return false
+       }
+       valid := C.notmuch_threads_valid(self.threads)
+       if valid == 0 {
+               return false
+       }
+       return true
+}
+
+/* Destroy a notmuch_threads_t object.
+ *
+ * It's not strictly necessary to call this function. All memory from
+ * the notmuch_threads_t object will be reclaimed when the
+ * containg query object is destroyed.
+ */
+func (self *Threads) Destroy() {
+       if self.threads != nil {
+               C.notmuch_threads_destroy(self.threads)
+       }
+}
+
+/* Is the given 'messages' iterator pointing at a valid message.
+ *
+ * When this function returns TRUE, notmuch_messages_get will return a
+ * valid object. Whereas when this function returns FALSE,
+ * notmuch_messages_get will return NULL.
+ *
+ * See the documentation of notmuch_query_search_messages for example
+ * code showing how to iterate over a notmuch_messages_t object.
+ */
+func (self *Messages) Valid() bool {
+       if self.messages == nil {
+               return false
+       }
+       valid := C.notmuch_messages_valid(self.messages)
+       if valid == 0 {
+               return false
+       }
+       return true
+}
+
+/* Get the current message from 'messages' as a notmuch_message_t.
+ *
+ * Note: The returned message belongs to 'messages' and has a lifetime
+ * identical to it (and the query to which it belongs).
+ *
+ * See the documentation of notmuch_query_search_messages for example
+ * code showing how to iterate over a notmuch_messages_t object.
+ *
+ * If an out-of-memory situation occurs, this function will return
+ * NULL.
+ */
+func (self *Messages) Get() *Message {
+       if self.messages == nil {
+               return nil
+       }
+       msg := C.notmuch_messages_get(self.messages)
+       if msg == nil {
+               return nil
+       }
+       return &Message{message: msg}
+}
+
+/* Move the 'messages' iterator to the next message.
+ *
+ * If 'messages' is already pointing at the last message then the
+ * iterator will be moved to a point just beyond that last message,
+ * (where notmuch_messages_valid will return FALSE and
+ * notmuch_messages_get will return NULL).
+ *
+ * See the documentation of notmuch_query_search_messages for example
+ * code showing how to iterate over a notmuch_messages_t object.
+ */
+func (self *Messages) MoveToNext() {
+       if self.messages == nil {
+               return
+       }
+       C.notmuch_messages_move_to_next(self.messages)
+}
+
+/* Destroy a notmuch_messages_t object.
+ *
+ * It's not strictly necessary to call this function. All memory from
+ * the notmuch_messages_t object will be reclaimed when the containing
+ * query object is destroyed.
+ */
+func (self *Messages) Destroy() {
+       if self.messages != nil {
+               C.notmuch_messages_destroy(self.messages)
+       }
+}
+
+/* Return a list of tags from all messages.
+ *
+ * The resulting list is guaranteed not to contain duplicated tags.
+ *
+ * WARNING: You can no longer iterate over messages after calling this
+ * function, because the iterator will point at the end of the list.
+ * We do not have a function to reset the iterator yet and the only
+ * way how you can iterate over the list again is to recreate the
+ * message list.
+ *
+ * The function returns NULL on error.
+ */
+func (self *Messages) CollectTags() *Tags {
+       if self.messages == nil {
+               return nil
+       }
+       tags := C.notmuch_messages_collect_tags(self.messages)
+       if tags == nil {
+               return nil
+       }
+       return &Tags{tags: tags}
+}
+
+/* Get the message ID of 'message'.
+ *
+ * The returned string belongs to 'message' and as such, should not be
+ * modified by the caller and will only be valid for as long as the
+ * message is valid, (which is until the query from which it derived
+ * is destroyed).
+ *
+ * This function will not return NULL since Notmuch ensures that every
+ * message has a unique message ID, (Notmuch will generate an ID for a
+ * message if the original file does not contain one).
+ */
+func (self *Message) GetMessageId() string {
+
+       if self.message == nil {
+               return ""
+       }
+       id := C.notmuch_message_get_message_id(self.message)
+       // we dont own id
+       // defer C.free(unsafe.Pointer(id))
+       if id == nil {
+               return ""
+       }
+       return C.GoString(id)
+}
+
+/* Get the thread ID of 'message'.
+ *
+ * The returned string belongs to 'message' and as such, should not be
+ * modified by the caller and will only be valid for as long as the
+ * message is valid, (for example, until the user calls
+ * notmuch_message_destroy on 'message' or until a query from which it
+ * derived is destroyed).
+ *
+ * This function will not return NULL since Notmuch ensures that every
+ * message belongs to a single thread.
+ */
+func (self *Message) GetThreadId() string {
+
+       if self.message == nil {
+               return ""
+       }
+       id := C.notmuch_message_get_thread_id(self.message)
+       // we dont own id
+       // defer C.free(unsafe.Pointer(id))
+
+       if id == nil {
+               return ""
+       }
+
+       return C.GoString(id)
+}
+
+/* Get a notmuch_messages_t iterator for all of the replies to
+ * 'message'.
+ *
+ * Note: This call only makes sense if 'message' was ultimately
+ * obtained from a notmuch_thread_t object, (such as by coming
+ * directly from the result of calling notmuch_thread_get_
+ * toplevel_messages or by any number of subsequent
+ * calls to notmuch_message_get_replies).
+ *
+ * If 'message' was obtained through some non-thread means, (such as
+ * by a call to notmuch_query_search_messages), then this function
+ * will return NULL.
+ *
+ * If there are no replies to 'message', this function will return
+ * NULL. (Note that notmuch_messages_valid will accept that NULL
+ * value as legitimate, and simply return FALSE for it.)
+ */
+func (self *Message) GetReplies() *Messages {
+       if self.message == nil {
+               return nil
+       }
+       msgs := C.notmuch_message_get_replies(self.message)
+       if msgs == nil {
+               return nil
+       }
+       return &Messages{messages: msgs}
+}
+
+/* Get a filename for the email corresponding to 'message'.
+ *
+ * The returned filename is an absolute filename, (the initial
+ * component will match notmuch_database_get_path() ).
+ *
+ * The returned string belongs to the message so should not be
+ * modified or freed by the caller (nor should it be referenced after
+ * the message is destroyed).
+ *
+ * Note: If this message corresponds to multiple files in the mail
+ * store, (that is, multiple files contain identical message IDs),
+ * this function will arbitrarily return a single one of those
+ * filenames.
+ */
+func (self *Message) GetFileName() string {
+       if self.message == nil {
+               return ""
+       }
+       fname := C.notmuch_message_get_filename(self.message)
+       // we dont own fname
+       // defer C.free(unsafe.Pointer(fname))
+
+       if fname == nil {
+               return ""
+       }
+
+       return C.GoString(fname)
+}
+
+type Flag C.notmuch_message_flag_t
+
+const (
+       MESSAGE_FLAG_MATCH Flag = 0
+)
+
+/* Get a value of a flag for the email corresponding to 'message'. */
+func (self *Message) GetFlag(flag Flag) bool {
+       if self.message == nil {
+               return false
+       }
+       v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))
+       if v == 0 {
+               return false
+       }
+       return true
+}
+
+/* Set a value of a flag for the email corresponding to 'message'. */
+func (self *Message) SetFlag(flag Flag, value bool) {
+       if self.message == nil {
+               return
+       }
+       var v C.notmuch_bool_t = 0
+       if value {
+               v = 1
+       }
+       C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)
+}
+
+/* Get the timestamp (seconds since the epoch) of 'message'.
+ *
+ * Return status:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Timestamp successfully retrieved
+ *
+ * NOTMUCH_STATUS_NULL_POINTER: The 'message' argument is NULL
+ *
+ */
+func (self *Message) GetDate() (int64, Status) {
+       if self.message == nil {
+               return -1, STATUS_NULL_POINTER
+       }
+       timestamp := C.notmuch_message_get_date(self.message)
+       return int64(timestamp), STATUS_SUCCESS
+}
+
+/* Get the value of the specified header from 'message'.
+ *
+ * The value will be read from the actual message file, not from the
+ * notmuch database. The header name is case insensitive.
+ *
+ * The returned string belongs to the message so should not be
+ * modified or freed by the caller (nor should it be referenced after
+ * the message is destroyed).
+ *
+ * Returns an empty string ("") if the message does not contain a
+ * header line matching 'header'. Returns NULL if any error occurs.
+ */
+func (self *Message) GetHeader(header string) string {
+       if self.message == nil {
+               return ""
+       }
+
+       var c_header *C.char = C.CString(header)
+       defer C.free(unsafe.Pointer(c_header))
+
+       /* we dont own value */
+       value := C.notmuch_message_get_header(self.message, c_header)
+       if value == nil {
+               return ""
+       }
+
+       return C.GoString(value)
+}
+
+/* Get the tags for 'message', returning a notmuch_tags_t object which
+ * can be used to iterate over all tags.
+ *
+ * The tags 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).
+ *
+ * Typical usage might be:
+ *
+ *     notmuch_message_t *message;
+ *     notmuch_tags_t *tags;
+ *     const char *tag;
+ *
+ *     message = notmuch_database_find_message (database, message_id);
+ *
+ *     for (tags = notmuch_message_get_tags (message);
+ *          notmuch_tags_valid (tags);
+ *          notmuch_result_move_to_next (tags))
+ *     {
+ *         tag = notmuch_tags_get (tags);
+ *         ....
+ *     }
+ *
+ *     notmuch_message_destroy (message);
+ *
+ * Note that there's no explicit destructor needed for the
+ * notmuch_tags_t object. (For consistency, we do provide a
+ * notmuch_tags_destroy function, but there's no good reason to call
+ * it if the message is about to be destroyed).
+ */
+func (self *Message) GetTags() *Tags {
+       if self.message == nil {
+               return nil
+       }
+       tags := C.notmuch_message_get_tags(self.message)
+       if tags == nil {
+               return nil
+       }
+       return &Tags{tags: tags}
+}
+
+/* The longest possible tag value. */
+const TAG_MAX = 200
+
+/* Add a tag to the given message.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
+ *
+ * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
+ *
+ * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
+ *     (exceeds NOTMUCH_TAG_MAX)
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so message cannot be modified.
+ */
+func (self *Message) AddTag(tag string) Status {
+       if self.message == nil {
+               return STATUS_NULL_POINTER
+       }
+       c_tag := C.CString(tag)
+       defer C.free(unsafe.Pointer(c_tag))
+
+       return Status(C.notmuch_message_add_tag(self.message, c_tag))
+}
+
+/* Remove a tag from the given message.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
+ *
+ * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
+ *
+ * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
+ *     (exceeds NOTMUCH_TAG_MAX)
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so message cannot be modified.
+ */
+func (self *Message) RemoveTag(tag string) Status {
+       if self.message == nil {
+               return STATUS_NULL_POINTER
+       }
+       c_tag := C.CString(tag)
+       defer C.free(unsafe.Pointer(c_tag))
+
+       return Status(C.notmuch_message_remove_tag(self.message, c_tag))
+}
+
+/* Remove all tags from the given message.
+ *
+ * See notmuch_message_freeze for an example showing how to safely
+ * replace tag values.
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so message cannot be modified.
+ */
+func (self *Message) RemoveAllTags() Status {
+       if self.message == nil {
+               return STATUS_NULL_POINTER
+       }
+       return Status(C.notmuch_message_remove_all_tags(self.message))
+}
+
+/* Freeze the current state of 'message' within the database.
+ *
+ * This means that changes to the message state, (via
+ * notmuch_message_add_tag, notmuch_message_remove_tag, and
+ * notmuch_message_remove_all_tags), will not be committed to the
+ * database until the message is thawed with notmuch_message_thaw.
+ *
+ * Multiple calls to freeze/thaw are valid and these calls will
+ * "stack". That is there must be as many calls to thaw as to freeze
+ * before a message is actually thawed.
+ *
+ * The ability to do freeze/thaw allows for safe transactions to
+ * change tag values. For example, explicitly setting a message to
+ * have a given set of tags might look like this:
+ *
+ *    notmuch_message_freeze (message);
+ *
+ *    notmuch_message_remove_all_tags (message);
+ *
+ *    for (i = 0; i < NUM_TAGS; i++)
+ *        notmuch_message_add_tag (message, tags[i]);
+ *
+ *    notmuch_message_thaw (message);
+ *
+ * With freeze/thaw used like this, the message in the database is
+ * guaranteed to have either the full set of original tag values, or
+ * the full set of new tag values, but nothing in between.
+ *
+ * Imagine the example above without freeze/thaw and the operation
+ * somehow getting interrupted. This could result in the message being
+ * left with no tags if the interruption happened after
+ * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
+ *
+ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
+ *     mode so message cannot be modified.
+ */
+func (self *Message) Freeze() Status {
+       if self.message == nil {
+               return STATUS_NULL_POINTER
+       }
+       return Status(C.notmuch_message_freeze(self.message))
+}
+
+/* Thaw the current 'message', synchronizing any changes that may have
+ * occurred while 'message' was frozen into the notmuch database.
+ *
+ * See notmuch_message_freeze for an example of how to use this
+ * function to safely provide tag changes.
+ *
+ * Multiple calls to freeze/thaw are valid and these calls with
+ * "stack". That is there must be as many calls to thaw as to freeze
+ * before a message is actually thawed.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
+ *     its frozen count has successfully been reduced by 1).
+ *
+ * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw
+ *     an unfrozen message. That is, there have been an unbalanced
+ *     number of calls to notmuch_message_freeze and
+ *     notmuch_message_thaw.
+ */
+func (self *Message) Thaw() Status {
+       if self.message == nil {
+               return STATUS_NULL_POINTER
+       }
+
+       return Status(C.notmuch_message_thaw(self.message))
+}
+
+/* Destroy a notmuch_message_t object.
+ *
+ * It can be useful to call this function in the case of a single
+ * query object with many messages in the result, (such as iterating
+ * over the entire database). Otherwise, it's fine to never call this
+ * function and there will still be no memory leaks. (The memory from
+ * the messages get reclaimed when the containing query is destroyed.)
+ */
+func (self *Message) Destroy() {
+       if self.message == nil {
+               return
+       }
+       C.notmuch_message_destroy(self.message)
+}
+
+/* Is the given 'tags' iterator pointing at a valid tag.
+ *
+ * When this function returns TRUE, notmuch_tags_get will return a
+ * valid string. Whereas when this function returns FALSE,
+ * notmuch_tags_get will return NULL.
+ *
+ * See the documentation of notmuch_message_get_tags for example code
+ * showing how to iterate over a notmuch_tags_t object.
+ */
+func (self *Tags) Valid() bool {
+       if self.tags == nil {
+               return false
+       }
+       v := C.notmuch_tags_valid(self.tags)
+       if v == 0 {
+               return false
+       }
+       return true
+}
+
+/* Get the current tag from 'tags' as a string.
+ *
+ * Note: The returned string belongs to 'tags' and has a lifetime
+ * identical to it (and the query to which it ultimately belongs).
+ *
+ * See the documentation of notmuch_message_get_tags for example code
+ * showing how to iterate over a notmuch_tags_t object.
+ */
+func (self *Tags) Get() string {
+       if self.tags == nil {
+               return ""
+       }
+       s := C.notmuch_tags_get(self.tags)
+       // we dont own 's'
+
+       return C.GoString(s)
+}
+func (self *Tags) String() string {
+       return self.Get()
+}
+
+/* Move the 'tags' iterator to the next tag.
+ *
+ * If 'tags' is already pointing at the last tag then the iterator
+ * will be moved to a point just beyond that last tag, (where
+ * notmuch_tags_valid will return FALSE and notmuch_tags_get will
+ * return NULL).
+ *
+ * See the documentation of notmuch_message_get_tags for example code
+ * showing how to iterate over a notmuch_tags_t object.
+ */
+func (self *Tags) MoveToNext() {
+       if self.tags == nil {
+               return
+       }
+       C.notmuch_tags_move_to_next(self.tags)
+}
+
+/* Destroy a notmuch_tags_t object.
+ *
+ * It's not strictly necessary to call this function. All memory from
+ * the notmuch_tags_t object will be reclaimed when the containing
+ * message or query objects are destroyed.
+ */
+func (self *Tags) Destroy() {
+       if self.tags == nil {
+               return
+       }
+       C.notmuch_tags_destroy(self.tags)
+}
+
+// TODO: wrap notmuch_directory_<fct>
+
+/* Destroy a notmuch_directory_t object. */
+func (self *Directory) Destroy() {
+       if self.dir == nil {
+               return
+       }
+       C.notmuch_directory_destroy(self.dir)
+}
+
+// TODO: wrap notmuch_filenames_<fct>
+
+/* Destroy a notmuch_filenames_t object.
+ *
+ * It's not strictly necessary to call this function. All memory from
+ * the notmuch_filenames_t object will be reclaimed when the
+ * containing directory object is destroyed.
+ *
+ * It is acceptable to pass NULL for 'filenames', in which case this
+ * function will do nothing.
+ */
+func (self *Filenames) Destroy() {
+       if self.fnames == nil {
+               return
+       }
+       C.notmuch_filenames_destroy(self.fnames)
+}
+
+/* EOF */