ANNOUNCE: nottoomuch-addresses.pl

2011-12-01 Thread Tomi Ollila

In addition to http://notmuchmail.org/emacstips/#index11h2
and Jani's 'goobook' in id:87zkfuh3i0@nikula.org
you can now also use

http://www.iki.fi/too/nottoomuch/nottoomuch-addresses.pl 

for your address completion needs when composing mail/reply.

This program is 1) easy to install and 2) pretty fast to use
when completing.

How to install

1) Download the program from above url on the machine you're running
   notmuch and find suitable location to it.

2) Verify it's sha1 checksum is 125f3917b3f2dc68bdf30af04ab623d53321e50c

3) chmod 755 /path/to/nottoomuch-addresses.pl

4) Run  /path/to/nottoomuch-addresses.pl --update
   When run first time this gathers email addresses from all of your mail.
   This may take a long while to complete -- depends on the amount of email
   you have. Further --updates are much faster as those just take addresses
   from new mail.

5) Test that it works: Run  /path/to/nottoomuch-addresses.pl notmuchmail

6) In case you're using emacs mua with notmuch, edit your notmuch
   configuration for emacs with the following content:

   (require 'notmuch-address)
   (setq notmuch-address-command /path/to/nottoomuch-addresses.pl)
   (notmuch-address-message-insinuate)

7) Restart emacs notmuch mua (or eval above lines) and start composing
   new mail. When adding recipient to To: field. press TAB after 3
   or more characters have been added. In case you get 2 or more address
   matches, use arrow keys in minibuffer to choose desired recipient...

8) Enjoy!

/path/to/nottoomuch-addresses.pl --help  provides more detailed information.

BR,
Tomi
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


python-notmuch crash with threads

2011-12-01 Thread James Westby
Hi,

I've been seeing a race with python-notmuch, where it will crash due to
pointers being invalidated when threads are used.

I've attached a script which shows the problem some of the time. It's
about the smallest script I can make, but it's hampered by the fact that
making it simpler seems to make the race less likely, so it's hard to
know when it is gone.

The typical backtrace is:

Program terminated with signal 11, Segmentation fault.
#0  0x7f7b19c34b59 in talloc_named_const () from 
/usr/lib/x86_64-linux-gnu/libtalloc.so.2
(gdb) up
#1  0x7f7b1a5f78dc in notmuch_query_search_threads (query=0x14001c70) at 
lib/query.cc:322
322 lib/query.cc: No such file or directory.
in lib/query.cc
(gdb) p *query
Cannot access memory at address 0x14001c70

Where something is invalidating the pointer between creation in
db.create_query() and calling it in query.search_threads()

I've seen other similar things when using other code in the thread.

http://talloc.samba.org/talloc/doc/html/index.html talks about the
thread-safety of talloc, and I don't think it's any of those issues
here.

Any suggestions for how to debug this further would be most welcome.

Thanks,

James

import threading


class NotmuchThread(threading.Thread):

def __init__(self):
super(NotmuchThread, self).__init__()
self.job_waiting = threading.Condition()
self.job_queue = []

def run(self):
from notmuch.database import Database
self.db = Database()
job = None
print Aquiring lock
with self.job_waiting:
if len(self.job_queue)  1:
print Job queue empty so waiting
while True:
ready = self.job_waiting.wait(1)
if ready:
break
if len(self.job_queue)  0:
break
print Got job, releasing lock
self.search_threads(tag:inbox)

def search_threads(self, query_string):
query = self.db.create_query(query_string)
print(%X % query._query)
threads = query.search_threads()
return threads

test_thread = NotmuchThread()
test_thread.start()
with test_thread.job_waiting:
test_thread.job_queue.append()
test_thread.job_waiting.notify()
import time; time.sleep(1)
test_thread.join()
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: ANNOUNCE: nottoomuch-addresses.pl

2011-12-01 Thread Bart Bunting
Hi Tomi,

I have had a quick try of setting up nottoomuch-addresses addresses.

Here are a couple of issues I have hit and I think it's still not quite
 right.

I attach the shell output from emacs below.

- firstly it appears that if the term is set to DUMB then the script
assumes it's being called from emacs and exits if there is no search
string.  This is problematic for me as I run all my stuff including the
--update command from a shell.  
- The instructions don't tell you to create the .config/nottoomuch
directory.  Perhaps you could check for it's existance and create if
required?
- Even when I got this far I had an odd result when running the script
as shown below.  First time I got a strange error.  Second run it added
900 or so addresses.  Subsiquent runs added no more addresses.  I find
this strange as I have over 180k emails.

Anyway great work.  Heading in a cool direction.  Hope this somewhat
rambling report helps.

Bart

bart@bit:~$
bart@bit:~$shasum ~/bin/nottoomuch-addresses.pl 
125f3917b3f2dc68bdf30af04ab623d53321e50c  
/Users/bart/bin/nottoomuch-addresses.pl
bart@bit:~$chmod 755 ~/bin/nottoomuch-addresses.pl 
bart@bit:~$~/bin/nottoomuch-addresses.pl  --update
Cannot open database, maybe not created yet.
run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
bart@bit:~$cd
bart@bit:~$cd .config
bash: cd: .config: No such file or directory
bart@bit:~$
bart@bit:~$mkdir .config
bart@bit:~$nottoomuch --update
bash: nottoomuch: command not found
bart@bit:~$~/bin/nottoomuch-addresses.pl  --update
Cannot open database, maybe not created yet.
run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
bart@bit:~$
bart@bit:~$cd
bart@bit:~$cd .config
bart@bit:~/.config$mkdir nottoomuch
bart@bit:~/.config$
bart@bit:~/.config$~/bin/nottoomuch-addresses.pl  --update
Cannot open database, maybe not created yet.
run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
bart@bit:~/.config$cd nottoomuch/
bart@bit:~/.config/nottoomuch$touch addresses
bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
bart@bit:~/.config/nottoomuch$ls
addresses
bart@bit:~/.config/nottoomuch$ls -s addresses 
0 addresses
bart@bit:~/.config/nottoomuch$echo $TERM
dumb
bart@bit:~/.config/nottoomuch$export TERM=emacs
bart@bit:~/.config/nottoomuch$ls -s addresses 
0 addresses
bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
Updating '/Users/bart/.config/nottoomuch/addresses', since -604800.
Unrecognized option: -604800..
Added 0 addresses in 0 seconds. Total number of addresses: 0.
bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
Updating '/Users/bart/.config/nottoomuch/addresses', since 1322159542.
Added 993 addresses in 4 seconds. Total number of addresses: 993.
bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
Updating '/Users/bart/.config/nottoomuch/addresses', since 1322159560.
Added 0 addresses in 1 seconds. Total number of addresses: 993.
bart@bit:~/.config/nottoomuch$ On Thu, 01 Dec 2011 17:36:24 +0200, Tomi Ollila 
tomi.oll...@iki.fi wrote:
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Bugs on adding flag D

2011-12-01 Thread kedals0
Hi,

I've a recurrent problem on syncing.
To solve it, I've to remove internal files (rm -rf ~/.offlineimap/) and mails 
in .Sent folder.

With offlineimap 6.3.5-rc1, the output is the following :
Account sync toto_irisa:
 * Processing account toto_irisa
 Copying folder structure from IMAP to Maildir
 Establishing connection to zimbra.inria.fr:993.
Folder sync [toto_irisa]:
 Syncing INBOX: IMAP - Maildir
 Syncing Drafts: IMAP - Maildir
 Syncing Sent: IMAP - Maildir
 Adding flag D to 1 messages on Sent
 ERROR [InstanceLimitedThread(Folder sync [toto_irisa], started daemon 
140489914951424)]: Syncing folder .Sent [acc: toto_irisa]
  Error with store: UID STORE failed
 ERROR [InstanceLimitedThread(Folder sync [toto_irisa], started daemon 
140489914951424)]: ERROR in syncfolder for toto_irisa folder .Sent: Traceback 
(most recent call last):
  File /home/toto/comp/offlineimap/offlineimap/accounts.py, line 358, in 
syncfolder
localfolder.syncmessagesto(remotefolder, statusfolder)
  File /home/toto/comp/offlineimap/offlineimap/folder/Base.py, line 431, in 
syncmessagesto
action(dstfolder, statusfolder)
  File /home/toto/comp/offlineimap/offlineimap/folder/Base.py, line 388, in 
syncmessagesto_flags
dstfolder.addmessagesflags(uids, set(flag))
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 627, in 
addmessagesflags
self.addmessagesflags_noconvert(uidlist, flags)
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 621, in 
addmessagesflags_noconvert
self.processmessagesflags('+', uidlist, flags)
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 653, in 
processmessagesflags
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
AssertionError: Error with store: UID STORE failed

  Error with store: UID STORE failed
 Syncing Trash: IMAP - Maildir
Account sync toto_irisa:
 * Finished processing account toto_irisa
 
ERROR: Exceptions occurred during the run!
 ERROR: Syncing folder .Sent [acc: toto_irisa]
  Error with store: UID STORE failed
 ERROR: ERROR in syncfolder for toto_irisa folder .Sent: Traceback (most recent 
call last):
  File /home/toto/comp/offlineimap/offlineimap/accounts.py, line 358, in 
syncfolder
localfolder.syncmessagesto(remotefolder, statusfolder)
  File /home/toto/comp/offlineimap/offlineimap/folder/Base.py, line 431, in 
syncmessagesto
action(dstfolder, statusfolder)
  File /home/toto/comp/offlineimap/offlineimap/folder/Base.py, line 388, in 
syncmessagesto_flags
dstfolder.addmessagesflags(uids, set(flag))
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 627, in 
addmessagesflags
self.addmessagesflags_noconvert(uidlist, flags)
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 621, in 
addmessagesflags_noconvert
self.processmessagesflags('+', uidlist, flags)
  File /home/toto/comp/offlineimap/offlineimap/folder/IMAP.py, line 653, in 
processmessagesflags
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
AssertionError: Error with store: UID STORE failed

  Error with store: UID STORE failed



Of course, I can give you more detailled information if you need it.

Dal.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 1/2] python: add classes to wrap all notmuch_*_t types

2011-12-01 Thread Sebastian Spaeth
On Mon, 28 Nov 2011 13:36:44 +0100, Justus Winter wrote:
 Well, quoting my commit message:
 
  Add type information to the ctypes._FuncPtr wrappers and
  use the wrapper classes instead of c_void_p for pointers
  to notmuch_*_t.
 
  This enables the ctypes library to type check parameters
  being handed to functions from the notmuch library.


This strikes me as a rather good thing, so the patches went in.

Sebastian


pgp6adgNBIklt.pgp
Description: PGP signature
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: python-notmuch decoding error on a message

2011-12-01 Thread Sebastian Spaeth
On Thu, 24 Nov 2011 12:13:22 -0400, David Bremner da...@tethera.net wrote:
 I get a set of critical errors about forgetting to call g_type_init.
 We actually call g_type_init in the CLI now, thanks to 

Oooh, ahh, I just saw these message on doing 'notmuch.py search moo'
myself. I would prefer if I (the binding) would not have to deal with
g_type_init stuff myself, it would mean loading more C libraries and
doing stuff that no libnotmuch documentation has told me about :-).

Sebastian


pgpP9Bq2aADLi.pgp
Description: PGP signature
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: cleanup gdb external dependency in atomicity tests

2011-12-01 Thread Dmitry Kurochkin
Change atomicity tests to use the new external binary dependencies.
This simplifies the code and makes output consistent.
---
 test/atomicity |   12 
 1 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/test/atomicity b/test/atomicity
index ad7d4a3..6df0a00 100755
--- a/test/atomicity
+++ b/test/atomicity
@@ -7,8 +7,7 @@ test_description='atomicity'
 # final database contents should be the same regardless of when (or
 # if) it is killed and restarted.
 
-if which gdb 1/dev/null 21; then
-test_set_prereq GDB
+if test_require_external_prereq gdb; then
 
 # Create a maildir structure to also stress flag synchronization
 mkdir $MAIL_DIR/cur
@@ -91,14 +90,11 @@ if which gdb 1/dev/null 21; then
i=$(expr $end - 1)
fi
 done
-else
-say_color info %-6s WARNING
-echo  Missing test prerequisite GDB
-fi  
+fi
 
 test_begin_subtest 'notmuch new is idempotent under arbitrary aborts'
-test_expect_equal_file GDB searchall expectall
+test_expect_equal_file searchall expectall
 
-test_expect_success GDB detected $outcount10 abort points test $outcount 
-gt 10
+test_expect_success detected $outcount10 abort points test $outcount -gt 
10
 
 test_done
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] python: Store pointers as c_void_p to keep references

2011-12-01 Thread James Westby
On Thu,  1 Dec 2011 18:13:05 -0500, James Westby jw+deb...@jameswestby.net 
wrote:
 From: James Westby james.wes...@linaro.org
 
 ctypes doesn't return c_void_p return values as that, it returns them as
 32-bit integers instead. This has two problems:
 
   1 - On 64-bit machines anything higher than the max 32-bit integer
   will overflow when passed back in to another function expecting,
   a pointer giving the wrong value.
 
   2 - If the value isn't stored as a pointer then the memory can be
   re-used and so the object will be corrupted.
 
 The fix for both of these is to store the values as pointers.

http://osdir.com/ml/python.ctypes/2006-12/msg00048.html states that this
is expected behaviour of ctypes.

Thanks,

James
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 1/2] python: add classes to wrap all notmuch_*_t types

2011-12-01 Thread James Westby
On Thu, 01 Dec 2011 22:25:41 +0100, Sebastian Spaeth sebast...@sspaeth.de 
wrote:
 This strikes me as a rather good thing, so the patches went in.

Hah, I've just seen this, and I'm going to guess that it fixes my
problems too.

...

I've tested and it seems to work, so my patch is unneeded witht his one.

Thanks,

James
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[RFC Patch v2 3/3] notmuch-dump: add --format option

2011-12-01 Thread David Bremner
From: David Bremner brem...@debian.org

Note that the new option does not control anything at the moment.
---
 notmuch-dump.c |4 
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 9e0c91d..d0ab360 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -42,10 +42,14 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
 if (notmuch == NULL)
return 1;
 
+int output_format = 's';
 char *output_file_name = NULL;
 int opt_index;
 
 notmuch_opt_desc_t options[] = {
+   { format, 'f', NOTMUCH_OPT_KEYWORD,
+ (notmuch_keyword_t []){ { sup, 's' }, { path, 'p' }, {0, 0} },
+ output_format },
{ out-file, 'o', NOTMUCH_OPT_POSITION, 0, output_file_name },
{ 0, 0, 0, 0, 0 }
 };
-- 
1.7.5.4

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[RFC Patch v2 2/3] notmuch-dump: convert to notmuch-opts argument handling.

2011-12-01 Thread David Bremner
From: David Bremner brem...@debian.org

The output file is handled via positional arguments. There are
currently no normal options.
---
 notmuch-dump.c |   27 +++
 1 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 0475eb9..9e0c91d 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -19,6 +19,7 @@
  */
 
 #include notmuch-client.h
+#include notmuch-opts.h
 
 int
 notmuch_dump_command (unused (void *ctx), int argc, char *argv[])
@@ -41,27 +42,29 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
 if (notmuch == NULL)
return 1;
 
-argc--; argv++; /* skip subcommand argument */
+char *output_file_name = NULL;
+int opt_index;
 
-if (argc  strcmp (argv[0], --) != 0) {
+notmuch_opt_desc_t options[] = {
+   { out-file, 'o', NOTMUCH_OPT_POSITION, 0, output_file_name },
+   { 0, 0, 0, 0, 0 }
+};
+
+opt_index = notmuch_parse_args (argc, argv, options, 1);
+
+if (output_file_name) {
fprintf (stderr, Warning: the output file argument of dump is 
deprecated.\n);
-   output = fopen (argv[0], w);
+   output = fopen (output_file_name, w);
if (output == NULL) {
fprintf (stderr, Error opening %s for writing: %s\n,
-argv[0], strerror (errno));
+output_file_name, strerror (errno));
return 1;
}
-   argc--;
-   argv++;
 }
 
-if (argc  strcmp (argv[0], --) == 0){
-   argc--;
-   argv++;
-}
 
-if (argc) {
-   query_str = query_string_from_args (notmuch, argc, argv);
+if (opt_index  argc) {
+   query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
if (query_str == NULL) {
fprintf (stderr, Out of memory.\n);
return 1;
-- 
1.7.5.4

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[RFC Patch v2 1/3] notmuch-opts.[ch]: new argument parsing framework for notmuch.

2011-12-01 Thread David Bremner
From: David Bremner brem...@debian.org

As we noticed when Jani kindly converted things to getopt_long, much
of the work in argument parsing in notmuch is due to the the key-value
style arguments like --format=(raw|json|text).

In this version I implement Austin Clements' suggestion of basing the
main API on taking pointers to output variables.
---
 Makefile.local |1 +
 notmuch-opts.c |  120 
 notmuch-opts.h |   50 +++
 3 files changed, 171 insertions(+), 0 deletions(-)
 create mode 100644 notmuch-opts.c
 create mode 100644 notmuch-opts.h

diff --git a/Makefile.local b/Makefile.local
index c94402b..6606be8 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -303,6 +303,7 @@ notmuch_client_srcs =   \
notmuch-count.c \
notmuch-dump.c  \
notmuch-new.c   \
+   notmuch-opts.c  \
notmuch-reply.c \
notmuch-restore.c   \
notmuch-search.c\
diff --git a/notmuch-opts.c b/notmuch-opts.c
new file mode 100644
index 000..a8c5223
--- /dev/null
+++ b/notmuch-opts.c
@@ -0,0 +1,120 @@
+#include assert.h
+#include string.h
+#include stdio.h
+#include error_util.h
+#include notmuch-opts.h
+
+/*
+  Search the list of keywords for a given argument, assigning the
+  output variable to the corresponding value.  Return FALSE if nothing
+  matches.
+*/
+
+static notmuch_bool_t
+_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, const char *arg_str) 
{
+
+if (arg_str[0] != ':'  arg_str[0] != '=') {
+   return FALSE;
+}
+
+/* skip delimiter */
+arg_str++;
+
+notmuch_keyword_t *keywords = arg_desc-keywords;
+
+while (keywords-name) {
+   if (strcmp (arg_str, keywords-name) == 0) {
+   if (arg_desc-output_var) {
+   *((int *)arg_desc-output_var) = keywords-value;
+   }
+   return TRUE;
+   }
+   keywords++;
+}
+return FALSE;
+}
+
+/*
+   Search for the {pos_arg_index}th position argument, return FALSE if
+   that does not exist.
+*/
+
+notmuch_bool_t
+parse_position_arg (const char *arg_str, int pos_arg_index, const 
notmuch_opt_desc_t *arg_desc) {
+
+int pos_arg_counter = 0;
+while (arg_desc-name){
+   if (arg_desc-opt_type == NOTMUCH_OPT_POSITION) {
+   if (pos_arg_counter == pos_arg_index) {
+   if (arg_desc-output_var) {
+   *((const char **)arg_desc-output_var) = arg_str;
+   }
+   return TRUE;
+   }
+   pos_arg_counter++;
+   }
+   arg_desc++;
+}
+return FALSE;
+}
+
+notmuch_opt_result_t
+parse_option (const char *arg,
+ const notmuch_opt_desc_t *options){
+
+assert(arg);
+assert(options);
+
+arg += 2;
+
+const notmuch_opt_desc_t *try = options;
+while (try-name) {
+   if (strncmp (arg, try-name, strlen(try-name)) == 0) {
+
+   switch (try-opt_type) {
+   case NOTMUCH_OPT_KEYWORD:
+   return  _process_keyword_arg (try, arg+strlen(try-name));
+   break;
+   case NOTMUCH_OPT_BOOLEAN:
+   return TRUE;
+   break;
+   case NOTMUCH_OPT_POSITION:
+   case NOTMUCH_OPT_NULL:
+   default:
+   INTERNAL_ERROR (unknown or unhandled option type %d, 
try-opt_type);
+   /*UNREACHED*/
+   }
+   }
+   try++;
+}
+return FALSE;
+}
+
+int
+notmuch_parse_args (int argc, char **argv, const notmuch_opt_desc_t *options, 
int opt_index){
+
+int pos_arg_index = 0;
+notmuch_bool_t more_args = TRUE;
+
+while (more_args  opt_index  argc) {
+   if (strncmp (argv[opt_index],--,2) != 0) {
+
+   more_args = parse_position_arg (argv[opt_index], pos_arg_index, 
options);
+
+   if (more_args) {
+   pos_arg_index++;
+   opt_index++;
+   }
+
+   } else {
+
+   if (strlen (argv[opt_index]) == 2)
+   return opt_index+1;
+
+   more_args = parse_option (argv[opt_index], options);
+   opt_index++;
+   }
+}
+
+return opt_index;
+}
diff --git a/notmuch-opts.h b/notmuch-opts.h
new file mode 100644
index 000..75d65cb
--- /dev/null
+++ b/notmuch-opts.h
@@ -0,0 +1,50 @@
+#ifndef NOTMUCH_OPTS_H
+#define NOTMUCH_OPTS_H
+
+#include notmuch.h
+
+enum notmuch_opt_type {
+NOTMUCH_OPT_NULL = 0,
+NOTMUCH_OPT_BOOLEAN,
+NOTMUCH_OPT_KEYWORD,
+NOTMUCH_OPT_POSITION
+};
+
+typedef enum notmuch_opt_result {
+NOTMUCH_OPT_ERROR = -2,
+NOTMUCH_OPT_GOT_POS = -1,
+NOTMUCH_OPT_STOP = 0,
+NOTMUCH_OPT_SUCCESS = 1
+} notmuch_opt_result_t;
+
+typedef struct notmuch_keyword {
+const char *name;
+int value;
+} notmuch_keyword_t;
+
+typedef struct notmuch_opt_desc {
+const char *name;
+int  arg_id;
+enum notmuch_opt_type opt_type;
+struct notmuch_keyword *keywords;
+

Re: ANNOUNCE: nottoomuch-addresses.pl

2011-12-01 Thread Tomi Ollila
On Fri, 02 Dec 2011 05:38:01 +1100, Bart Bunting b...@ursys.com.au wrote:
 Hi Tomi,
 
 I have had a quick try of setting up nottoomuch-addresses addresses.
 
 Here are a couple of issues I have hit and I think it's still not quite
  right.
 
 I attach the shell output from emacs below.
 
 - firstly it appears that if the term is set to DUMB then the script
 assumes it's being called from emacs and exits if there is no search
 string.  This is problematic for me as I run all my stuff including the
 --update command from a shell.  

Good point. I'll remove that 'optimization' -- mysterious are the ways
users run commands :) (Well, I occasionally run commands from emacs
myself, too).

You could try removing part '''$ENV{TERM} eq 'dumb' or ''' in line 25

 - The instructions don't tell you to create the .config/nottoomuch
 directory.  Perhaps you could check for it's existance and create if
 required?

It does that (in line 91):

mv .config .xconfig
~/mail/nottoomuch/nottoomuch-addresses.pl --update
Creating '/home/too/.config/nottoomuch/addresses'. This may take some time...
Added 3335 addresses in 14 seconds. Total number of addresses: 3335.

It seems your other problems make it seem the directories are not created,
however I check this... 

 - Even when I got this far I had an odd result when running the script
 as shown below.  First time I got a strange error.  Second run it added
 900 or so addresses.  Subsiquent runs added no more addresses.  I find
 this strange as I have over 180k emails.

Haa, the ls output '0 addresses' tells me something:

rm .config/nottoomuch/addresses
touch .config/nottoomuch/addresses
guru$ ~/mail/nottoomuch/nottoomuch-addresses.pl --update
Updating '/home/too/.config/nottoomuch/addresses', since -604800.
Unrecognized option: -604800..
Added 0 addresses in 0 seconds. Total number of addresses: 0.

 Anyway great work.  Heading in a cool direction.  Hope this somewhat
 rambling report helps.

Thanks a lot for your bug report, it's been very helpful. please retry
doing the following:

TERM=emacs /Users/bart/bin/nottoomuch-addresses.pl --update --rebuild

And report if you get more problems.

 Bart

Tomi

 
 bart@bit:~$
 bart@bit:~$shasum ~/bin/nottoomuch-addresses.pl 
 125f3917b3f2dc68bdf30af04ab623d53321e50c  
 /Users/bart/bin/nottoomuch-addresses.pl
 bart@bit:~$chmod 755 ~/bin/nottoomuch-addresses.pl 
 bart@bit:~$~/bin/nottoomuch-addresses.pl  --update
 Cannot open database, maybe not created yet.
 run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
 bart@bit:~$cd
 bart@bit:~$cd .config
 bash: cd: .config: No such file or directory
 bart@bit:~$
 bart@bit:~$mkdir .config
 bart@bit:~$nottoomuch --update
 bash: nottoomuch: command not found
 bart@bit:~$~/bin/nottoomuch-addresses.pl  --update
 Cannot open database, maybe not created yet.
 run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
 bart@bit:~$
 bart@bit:~$cd
 bart@bit:~$cd .config
 bart@bit:~/.config$mkdir nottoomuch
 bart@bit:~/.config$
 bart@bit:~/.config$~/bin/nottoomuch-addresses.pl  --update
 Cannot open database, maybe not created yet.
 run /Users/bart/bin/nottoomuch-addresses.pl --update from command line first.
 bart@bit:~/.config$cd nottoomuch/
 bart@bit:~/.config/nottoomuch$touch addresses
 bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
 bart@bit:~/.config/nottoomuch$ls
 addresses
 bart@bit:~/.config/nottoomuch$ls -s addresses 
 0 addresses
 bart@bit:~/.config/nottoomuch$echo $TERM
 dumb
 bart@bit:~/.config/nottoomuch$export TERM=emacs
 bart@bit:~/.config/nottoomuch$ls -s addresses 
 0 addresses
 bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
 Updating '/Users/bart/.config/nottoomuch/addresses', since -604800.
 Unrecognized option: -604800..
 Added 0 addresses in 0 seconds. Total number of addresses: 0.
 bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
 Updating '/Users/bart/.config/nottoomuch/addresses', since 1322159542.
 Added 993 addresses in 4 seconds. Total number of addresses: 993.
 bart@bit:~/.config/nottoomuch$~/bin/nottoomuch-addresses.pl  --update
 Updating '/Users/bart/.config/nottoomuch/addresses', since 1322159560.
 Added 0 addresses in 1 seconds. Total number of addresses: 993.
 bart@bit:~/.config/nottoomuch$ On Thu, 01 Dec 2011 17:36:24 +0200, Tomi 
 Ollila tomi.oll...@iki.fi wrote:
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Having trouble displaying some html emails

2011-12-01 Thread Bart Bunting
Thanks tomi, 

I missed your reply.

This works  beautifully!

Thanks

Bart

On Tue, 15 Nov 2011 09:51:26 +0200, Tomi Ollila  wrote:
> On Tue, 15 Nov 2011 10:37:42 +1100, Bart Bunting  
> wrote:
> > Hi everyone,
> > 
> > I am having an issue with html emails displaying correctly.
> > 
> > For some reason the first time I view a html email it displays
> > correctly.  All subsequent attempts to view html emails result in the
> > following error:
> > 
> > mm-shr: Wrong type argument: bufferp, "*Summary*"
> > 
> > 
> > The message opens but the parts are hidden like this:
> > 
> > [ multipart/mixed ]
> > [ text/html ]
> > 
> > 
> > I'm not exactly sure what is going on here or what the best way to debug
> > is?
> > 
> > Any one have any ideas or suggestions?
> 
> You could try
> 
> (setq mm-text-html-renderer 'w3m)
> 
> or
> 
> (setq mm-text-html-renderer 'w3m-standalone)
> 
> 
> For w3m-standalone you need w3m binary to be installed
> and with w3m you also need some w3m.el elisp packages
> (which may or may not be installed along emacs)
> (or something)
> 
> > 
> > Kind regards
> > 
> > Bart
> 
> Tomi


ANNOUNCE: nottoomuch-addresses.pl

2011-12-01 Thread Tomi Ollila

In addition to http://notmuchmail.org/emacstips/#index11h2
and Jani's 'goobook' in id:"87zkfuh3i0.fsf at nikula.org"
you can now also use

http://www.iki.fi/too/nottoomuch/nottoomuch-addresses.pl 

for your address completion needs when composing mail/reply.

This program is 1) easy to install and 2) pretty fast to use
when completing.

How to install

1) Download the program from above url on the machine you're running
   notmuch and find suitable location to it.

2) Verify it's sha1 checksum is 125f3917b3f2dc68bdf30af04ab623d53321e50c

3) chmod 755 /path/to/nottoomuch-addresses.pl

4) Run  /path/to/nottoomuch-addresses.pl --update
   When run first time this gathers email addresses from all of your mail.
   This may take a long while to complete -- depends on the amount of email
   you have. Further --updates are much faster as those just take addresses
   from new mail.

5) Test that it works: Run  /path/to/nottoomuch-addresses.pl notmuchmail

6) In case you're using emacs mua with notmuch, edit your notmuch
   configuration for emacs with the following content:

   (require 'notmuch-address)
   (setq notmuch-address-command "/path/to/nottoomuch-addresses.pl")
   (notmuch-address-message-insinuate)

7) Restart emacs notmuch mua (or eval above lines) and start composing
   new mail. When adding recipient to To: field. press  after 3
   or more characters have been added. In case you get 2 or more address
   matches, use arrow keys in minibuffer to choose desired recipient...

8) Enjoy!

/path/to/nottoomuch-addresses.pl --help  provides more detailed information.

BR,
Tomi


python-notmuch crash with threads

2011-12-01 Thread James Westby
Hi,

I've been seeing a race with python-notmuch, where it will crash due to
pointers being invalidated when threads are used.

I've attached a script which shows the problem some of the time. It's
about the smallest script I can make, but it's hampered by the fact that
making it simpler seems to make the race less likely, so it's hard to
know when it is gone.

The typical backtrace is:

Program terminated with signal 11, Segmentation fault.
#0  0x7f7b19c34b59 in talloc_named_const () from 
/usr/lib/x86_64-linux-gnu/libtalloc.so.2
(gdb) up
#1  0x7f7b1a5f78dc in notmuch_query_search_threads (query=0x14001c70) at 
lib/query.cc:322
322 lib/query.cc: No such file or directory.
in lib/query.cc
(gdb) p *query
Cannot access memory at address 0x14001c70

Where something is invalidating the pointer between creation in
db.create_query() and calling it in query.search_threads()

I've seen other similar things when using other code in the thread.

http://talloc.samba.org/talloc/doc/html/index.html talks about the
thread-safety of talloc, and I don't think it's any of those issues
here.

Any suggestions for how to debug this further would be most welcome.

Thanks,

James

-- next part --
A non-text attachment was scrubbed...
Name: test.py
Type: text/x-python
Size: 1170 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20111201/33032025/attachment-0001.py>


Bugs on adding flag D

2011-12-01 Thread keda...@gmail.com
Hi,

I've a recurrent problem on syncing.
To solve it, I've to remove internal files (rm -rf ~/.offlineimap/) and mails 
in .Sent folder.

With offlineimap 6.3.5-rc1, the output is the following :
Account sync toto_irisa:
 * Processing account toto_irisa
 Copying folder structure from IMAP to Maildir
 Establishing connection to zimbra.inria.fr:993.
Folder sync [toto_irisa]:
 Syncing INBOX: IMAP -> Maildir
 Syncing Drafts: IMAP -> Maildir
 Syncing Sent: IMAP -> Maildir
 Adding flag D to 1 messages on Sent
 ERROR []: Syncing folder .Sent [acc: toto_irisa]
  Error with store: UID STORE failed
 ERROR []: ERROR in syncfolder for toto_irisa folder .Sent: Traceback 
(most recent call last):
  File "/home/toto/comp/offlineimap/offlineimap/accounts.py", line 358, in 
syncfolder
localfolder.syncmessagesto(remotefolder, statusfolder)
  File "/home/toto/comp/offlineimap/offlineimap/folder/Base.py", line 431, in 
syncmessagesto
action(dstfolder, statusfolder)
  File "/home/toto/comp/offlineimap/offlineimap/folder/Base.py", line 388, in 
syncmessagesto_flags
dstfolder.addmessagesflags(uids, set(flag))
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 627, in 
addmessagesflags
self.addmessagesflags_noconvert(uidlist, flags)
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 621, in 
addmessagesflags_noconvert
self.processmessagesflags('+', uidlist, flags)
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 653, in 
processmessagesflags
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
AssertionError: Error with store: UID STORE failed

  Error with store: UID STORE failed
 Syncing Trash: IMAP -> Maildir
Account sync toto_irisa:
 * Finished processing account toto_irisa

ERROR: Exceptions occurred during the run!
 ERROR: Syncing folder .Sent [acc: toto_irisa]
  Error with store: UID STORE failed
 ERROR: ERROR in syncfolder for toto_irisa folder .Sent: Traceback (most recent 
call last):
  File "/home/toto/comp/offlineimap/offlineimap/accounts.py", line 358, in 
syncfolder
localfolder.syncmessagesto(remotefolder, statusfolder)
  File "/home/toto/comp/offlineimap/offlineimap/folder/Base.py", line 431, in 
syncmessagesto
action(dstfolder, statusfolder)
  File "/home/toto/comp/offlineimap/offlineimap/folder/Base.py", line 388, in 
syncmessagesto_flags
dstfolder.addmessagesflags(uids, set(flag))
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 627, in 
addmessagesflags
self.addmessagesflags_noconvert(uidlist, flags)
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 621, in 
addmessagesflags_noconvert
self.processmessagesflags('+', uidlist, flags)
  File "/home/toto/comp/offlineimap/offlineimap/folder/IMAP.py", line 653, in 
processmessagesflags
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
AssertionError: Error with store: UID STORE failed

  Error with store: UID STORE failed



Of course, I can give you more detailled information if you need it.

Dal.


[PATCH 1/2] python: add classes to wrap all notmuch_*_t types

2011-12-01 Thread Sebastian Spaeth
On Mon, 28 Nov 2011 13:36:44 +0100, Justus Winter wrote:
> Well, quoting my commit message:
> 
> > Add type information to the ctypes._FuncPtr wrappers and
> > use the wrapper classes instead of c_void_p for pointers
> > to notmuch_*_t.
> >
> > This enables the ctypes library to type check parameters
> > being handed to functions from the notmuch library.


This strikes me as a rather good thing, so the patches went in.

Sebastian
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20111201/34f4fe1a/attachment.pgp>


python-notmuch decoding error on a message

2011-12-01 Thread Sebastian Spaeth
On Thu, 24 Nov 2011 12:13:22 -0400, David Bremner  wrote:
> I get a set of critical errors about forgetting to call g_type_init.
> We actually call g_type_init in the CLI now, thanks to 

Oooh, ahh, I just saw these message on doing 'notmuch.py search "moo"'
myself. I would prefer if I (the binding) would not have to deal with
g_type_init stuff myself, it would mean loading more C libraries and
doing stuff that no libnotmuch documentation has told me about :-).

Sebastian
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20111201/27a46d03/attachment.pgp>


[PATCH] python: Store pointers as c_void_p to keep references

2011-12-01 Thread James Westby
From: James Westby 

ctypes doesn't return c_void_p return values as that, it returns them as
32-bit integers instead. This has two problems:

  1 - On 64-bit machines anything higher than the max 32-bit integer
  will overflow when passed back in to another function expecting,
  a pointer giving the wrong value.

  2 - If the value isn't stored as a pointer then the memory can be
  re-used and so the object will be corrupted.

The fix for both of these is to store the values as pointers.
---
 bindings/python/notmuch/database.py |   18 +-
 bindings/python/notmuch/message.py  |   10 +-
 bindings/python/notmuch/thread.py   |6 +++---
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/bindings/python/notmuch/database.py 
b/bindings/python/notmuch/database.py
index f4bc53e..e5188fb 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -152,7 +152,7 @@ class Database(object):
 if res is None:
 raise NotmuchError(
 message="Could not create the specified database")
-self._db = res
+self._db = c_void_p(res)

 def open(self, path, mode=0):
 """Opens an existing database
@@ -171,7 +171,7 @@ class Database(object):

 if res is None:
 raise NotmuchError(message="Could not open the specified database")
-self._db = res
+self._db = c_void_p(res)

 def get_path(self):
 """Returns the file path of an open database"""
@@ -297,7 +297,7 @@ class Database(object):
 dir_p = Database._get_directory(self._db, _str(path))

 # return the Directory, init it with the absolute path
-return Directory(_str(abs_dirpath), dir_p, self)
+return Directory(_str(abs_dirpath), c_void_p(dir_p), self)

 def add_message(self, filename, sync_maildir_flags=False):
 """Adds a new message to the database
@@ -466,7 +466,7 @@ class Database(object):
 tags_p = Database._get_all_tags(self._db)
 if tags_p == None:
 raise NotmuchError(STATUS.NULL_POINTER)
-return Tags(tags_p, self)
+return Tags(c_void_p(tags_p), self)

 def create_query(self, querystring):
 """Returns a :class:`Query` derived from this database
@@ -600,7 +600,7 @@ class Query(object):
 query_p = Query._create(db.db_p, _str(querystr))
 if query_p is None:
 raise NullPointerError
-self._query = query_p
+self._query = c_void_p(query_p)

 def set_sort(self, sort):
 """Set the sort order future results will be delivered in
@@ -630,7 +630,7 @@ class Query(object):

 if threads_p is None:
 raise NullPointerError
-return Threads(threads_p, self)
+return Threads(c_void_p(threads_p), self)

 def search_messages(self):
 """Filter messages according to the query and return
@@ -644,7 +644,7 @@ class Query(object):

 if msgs_p is None:
 raise NullPointerError
-return Messages(msgs_p, self)
+return Messages(c_void_p(msgs_p), self)

 def count_messages(self):
 """Estimate the number of messages matching the query
@@ -793,7 +793,7 @@ class Directory(object):
 """
 self._assert_dir_is_initialized()
 files_p = Directory._get_child_files(self._dir_p)
-return Filenames(files_p, self)
+return Filenames(c_void_p(files_p), self)

 def get_child_directories(self):
 """Gets a :class:`Filenames` iterator listing all the filenames of
@@ -804,7 +804,7 @@ class Directory(object):
 """
 self._assert_dir_is_initialized()
 files_p = Directory._get_child_directories(self._dir_p)
-return Filenames(files_p, self)
+return Filenames(c_void_p(files_p), self)

 @property
 def path(self):
diff --git a/bindings/python/notmuch/message.py 
b/bindings/python/notmuch/message.py
index 4bf90c2..672a169 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -140,7 +140,7 @@ class Messages(object):

 if tags_p == None:
 raise NotmuchError(STATUS.NULL_POINTER)
-return Tags(tags_p, self)
+return Tags(c_void_p(tags_p), self)

 def __iter__(self):
 """ Make Messages an iterator """
@@ -154,7 +154,7 @@ class Messages(object):
 self._msgs = None
 raise StopIteration

-msg = Message(Messages._get(self._msgs), self)
+msg = Message(c_void_p(Messages._get(self._msgs)), self)
 nmlib.notmuch_messages_move_to_next(self._msgs)
 return msg

@@ -350,7 +350,7 @@ class Message(object):
 if msgs_p is None:
 return None

-return Messages(msgs_p, self)
+return Messages(c_void_p(msgs_p), self)

 def get_date(self):
 """Returns time_t of the message date
@@ -418,7 +418,7 @@ class Message(object):

  

[RFC Patch v2 3/3] notmuch-dump: add --format option

2011-12-01 Thread David Bremner
From: David Bremner 

Note that the new option does not control anything at the moment.
---
 notmuch-dump.c |4 
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 9e0c91d..d0ab360 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -42,10 +42,14 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
 if (notmuch == NULL)
return 1;

+int output_format = 's';
 char *output_file_name = NULL;
 int opt_index;

 notmuch_opt_desc_t options[] = {
+   { "format", 'f', NOTMUCH_OPT_KEYWORD,
+ (notmuch_keyword_t []){ { "sup", 's' }, { "path", 'p' }, {0, 0} },
+ _format },
{ "out-file", 'o', NOTMUCH_OPT_POSITION, 0, _file_name },
{ 0, 0, 0, 0, 0 }
 };
-- 
1.7.5.4



[RFC Patch v2 2/3] notmuch-dump: convert to notmuch-opts argument handling.

2011-12-01 Thread David Bremner
From: David Bremner 

The output file is handled via positional arguments. There are
currently no "normal" options.
---
 notmuch-dump.c |   27 +++
 1 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 0475eb9..9e0c91d 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -19,6 +19,7 @@
  */

 #include "notmuch-client.h"
+#include "notmuch-opts.h"

 int
 notmuch_dump_command (unused (void *ctx), int argc, char *argv[])
@@ -41,27 +42,29 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
 if (notmuch == NULL)
return 1;

-argc--; argv++; /* skip subcommand argument */
+char *output_file_name = NULL;
+int opt_index;

-if (argc && strcmp (argv[0], "--") != 0) {
+notmuch_opt_desc_t options[] = {
+   { "out-file", 'o', NOTMUCH_OPT_POSITION, 0, _file_name },
+   { 0, 0, 0, 0, 0 }
+};
+
+opt_index = notmuch_parse_args (argc, argv, options, 1);
+
+if (output_file_name) {
fprintf (stderr, "Warning: the output file argument of dump is 
deprecated.\n");
-   output = fopen (argv[0], "w");
+   output = fopen (output_file_name, "w");
if (output == NULL) {
fprintf (stderr, "Error opening %s for writing: %s\n",
-argv[0], strerror (errno));
+output_file_name, strerror (errno));
return 1;
}
-   argc--;
-   argv++;
 }

-if (argc && strcmp (argv[0], "--") == 0){
-   argc--;
-   argv++;
-}

-if (argc) {
-   query_str = query_string_from_args (notmuch, argc, argv);
+if (opt_index < argc) {
+   query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return 1;
-- 
1.7.5.4



[RFC Patch v2 1/3] notmuch-opts.[ch]: new argument parsing framework for notmuch.

2011-12-01 Thread David Bremner
From: David Bremner 

As we noticed when Jani kindly converted things to getopt_long, much
of the work in argument parsing in notmuch is due to the the key-value
style arguments like --format=(raw|json|text).

In this version I implement Austin Clements' suggestion of basing the
main API on taking pointers to output variables.
---
 Makefile.local |1 +
 notmuch-opts.c |  120 
 notmuch-opts.h |   50 +++
 3 files changed, 171 insertions(+), 0 deletions(-)
 create mode 100644 notmuch-opts.c
 create mode 100644 notmuch-opts.h

diff --git a/Makefile.local b/Makefile.local
index c94402b..6606be8 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -303,6 +303,7 @@ notmuch_client_srcs =   \
notmuch-count.c \
notmuch-dump.c  \
notmuch-new.c   \
+   notmuch-opts.c  \
notmuch-reply.c \
notmuch-restore.c   \
notmuch-search.c\
diff --git a/notmuch-opts.c b/notmuch-opts.c
new file mode 100644
index 000..a8c5223
--- /dev/null
+++ b/notmuch-opts.c
@@ -0,0 +1,120 @@
+#include 
+#include 
+#include 
+#include "error_util.h"
+#include "notmuch-opts.h"
+
+/*
+  Search the list of keywords for a given argument, assigning the
+  output variable to the corresponding value.  Return FALSE if nothing
+  matches.
+*/
+
+static notmuch_bool_t
+_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, const char *arg_str) 
{
+
+if (arg_str[0] != ':' && arg_str[0] != '=') {
+   return FALSE;
+}
+
+/* skip delimiter */
+arg_str++;
+
+notmuch_keyword_t *keywords = arg_desc->keywords;
+
+while (keywords->name) {
+   if (strcmp (arg_str, keywords->name) == 0) {
+   if (arg_desc->output_var) {
+   *((int *)arg_desc->output_var) = keywords->value;
+   }
+   return TRUE;
+   }
+   keywords++;
+}
+return FALSE;
+}
+
+/*
+   Search for the {pos_arg_index}th position argument, return FALSE if
+   that does not exist.
+*/
+
+notmuch_bool_t
+parse_position_arg (const char *arg_str, int pos_arg_index, const 
notmuch_opt_desc_t *arg_desc) {
+
+int pos_arg_counter = 0;
+while (arg_desc->name){
+   if (arg_desc->opt_type == NOTMUCH_OPT_POSITION) {
+   if (pos_arg_counter == pos_arg_index) {
+   if (arg_desc->output_var) {
+   *((const char **)arg_desc->output_var) = arg_str;
+   }
+   return TRUE;
+   }
+   pos_arg_counter++;
+   }
+   arg_desc++;
+}
+return FALSE;
+}
+
+notmuch_opt_result_t
+parse_option (const char *arg,
+ const notmuch_opt_desc_t *options){
+
+assert(arg);
+assert(options);
+
+arg += 2;
+
+const notmuch_opt_desc_t *try = options;
+while (try->name) {
+   if (strncmp (arg, try->name, strlen(try->name)) == 0) {
+
+   switch (try->opt_type) {
+   case NOTMUCH_OPT_KEYWORD:
+   return  _process_keyword_arg (try, arg+strlen(try->name));
+   break;
+   case NOTMUCH_OPT_BOOLEAN:
+   return TRUE;
+   break;
+   case NOTMUCH_OPT_POSITION:
+   case NOTMUCH_OPT_NULL:
+   default:
+   INTERNAL_ERROR ("unknown or unhandled option type %d", 
try->opt_type);
+   /*UNREACHED*/
+   }
+   }
+   try++;
+}
+return FALSE;
+}
+
+int
+notmuch_parse_args (int argc, char **argv, const notmuch_opt_desc_t *options, 
int opt_index){
+
+int pos_arg_index = 0;
+notmuch_bool_t more_args = TRUE;
+
+while (more_args && opt_index < argc) {
+   if (strncmp (argv[opt_index],"--",2) != 0) {
+
+   more_args = parse_position_arg (argv[opt_index], pos_arg_index, 
options);
+
+   if (more_args) {
+   pos_arg_index++;
+   opt_index++;
+   }
+
+   } else {
+
+   if (strlen (argv[opt_index]) == 2)
+   return opt_index+1;
+
+   more_args = parse_option (argv[opt_index], options);
+   opt_index++;
+   }
+}
+
+return opt_index;
+}
diff --git a/notmuch-opts.h b/notmuch-opts.h
new file mode 100644
index 000..75d65cb
--- /dev/null
+++ b/notmuch-opts.h
@@ -0,0 +1,50 @@
+#ifndef NOTMUCH_OPTS_H
+#define NOTMUCH_OPTS_H
+
+#include "notmuch.h"
+
+enum notmuch_opt_type {
+NOTMUCH_OPT_NULL = 0,
+NOTMUCH_OPT_BOOLEAN,
+NOTMUCH_OPT_KEYWORD,
+NOTMUCH_OPT_POSITION
+};
+
+typedef enum notmuch_opt_result {
+NOTMUCH_OPT_ERROR = -2,
+NOTMUCH_OPT_GOT_POS = -1,
+NOTMUCH_OPT_STOP = 0,
+NOTMUCH_OPT_SUCCESS = 1
+} notmuch_opt_result_t;
+
+typedef struct notmuch_keyword {
+const char *name;
+int value;
+} notmuch_keyword_t;
+
+typedef struct notmuch_opt_desc {
+const char *name;
+int  arg_id;
+enum notmuch_opt_type opt_type;
+struct notmuch_keyword