[PATCH v3 3/3] Use the structured format printer in notmuch search.

2012-07-10 Thread cra...@gmx.net
This patch uses the previously introduced structured format printer in 
notmuch-search.
Most of this patch is necessary to keep the text output unchanged. All tests 
pass, both text and JSON are printed exactly the same as they were before.
---
 notmuch-search.c |  275 +-
 1 file changed, 148 insertions(+), 127 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..321b32a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -19,6 +19,7 @@
  */

 #include "notmuch-client.h"
+#include "structured-output.h"

 typedef enum {
 OUTPUT_SUMMARY,
@@ -28,6 +29,7 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;

+/* legacy, only needed for non-structured text output */
 typedef struct search_format {
 const char *results_start;
 const char *item_start;
@@ -64,6 +66,7 @@ format_thread_text (const void *ctx,
const int total,
const char *authors,
const char *subject);
+
 static const search_format_t format_text = {
 "",
"",
@@ -78,35 +81,6 @@ static const search_format_t format_text = {
 };

 static void
-format_item_id_json (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_json (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-
-/* Any changes to the JSON format should be reflected in the file
- * devel/schemata. */
-static const search_format_t format_json = {
-"[",
-   "{",
-   format_item_id_json,
-   format_thread_json,
-   "\"tags\": [",
-   "\"%s\"", ", ",
-   "]", ",\n",
-   "}",
-"]\n",
-"]\n",
-};
-
-static void
 format_item_id_text (unused (const void *ctx),
 const char *item_type,
 const char *item_id)
@@ -153,50 +127,9 @@ format_thread_text (const void *ctx,
 talloc_free (ctx_quote);
 }

-static void
-format_item_id_json (const void *ctx,
-unused (const char *item_type),
-const char *item_id)
-{
-void *ctx_quote = talloc_new (ctx);
-
-printf ("%s", json_quote_str (ctx_quote, item_id));
-
-talloc_free (ctx_quote);
-
-}
-
-static void
-format_thread_json (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject)
-{
-void *ctx_quote = talloc_new (ctx);
-
-printf ("\"thread\": %s,\n"
-   "\"timestamp\": %ld,\n"
-   "\"date_relative\": \"%s\",\n"
-   "\"matched\": %d,\n"
-   "\"total\": %d,\n"
-   "\"authors\": %s,\n"
-   "\"subject\": %s,\n",
-   json_quote_str (ctx_quote, thread_id),
-   date,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   json_quote_str (ctx_quote, authors),
-   json_quote_str (ctx_quote, subject));
-
-talloc_free (ctx_quote);
-}
-
 static int
-do_search_threads (const search_format_t *format,
+do_search_threads (const structure_printer_t *format,
+  void *state,
   notmuch_query_t *query,
   notmuch_sort_t sort,
   output_t output,
@@ -209,6 +142,8 @@ do_search_threads (const search_format_t *format,
 time_t date;
 int first_thread = 1;
 int i;
+int outermost_level = 0;
+int items_level = 0;

 if (offset < 0) {
offset += notmuch_query_count_threads (query);
@@ -220,7 +155,11 @@ do_search_threads (const search_format_t *format,
 if (threads == NULL)
return 1;

-fputs (format->results_start, stdout);
+if (format == unstructured_text_printer) {
+   fputs(format_text.results_start, stdout);
+} else { /* structured output */
+   outermost_level = format->list(state);
+}

 for (i = 0;
 notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);
@@ -235,43 +174,85 @@ do_search_threads (const search_format_t *format,
continue;
}

-   if (! first_thread)
-   fputs (format->item_sep, stdout);
+   if (format == unstructured_text_printer && ! first_thread)
+   fputs (format_text.item_sep, stdout);

if (output == OUTPUT_THREADS) {
-   format->item_id (thread, "thread:",
-notmuch_thread_get_thread_id (thread));
+   if (format == unstructured_text_printer) {
+   format_text.item_id (thread, "thread:",
+notmuch_thread_get_thread_id (thread));
+   } else { /* 

[PATCH v3 2/3] Adding structured output formatter for JSON.

2012-07-10 Thread cra...@gmx.net
This formatter prints exactly the same structure as the existing JSON 
formatter, but uses the newly introduced structured formatting primitives.
---
 structured-output.c |  159 +++
 structured-output.h |   48 
 2 files changed, 207 insertions(+)
 create mode 100644 structured-output.c

diff --git a/structured-output.c b/structured-output.c
new file mode 100644
index 000..e10fba4
--- /dev/null
+++ b/structured-output.c
@@ -0,0 +1,159 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright ? 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth 
+ */
+
+#include "structured-output.h"
+
+structure_printer_t *
+unstructured_text_printer = NULL;
+
+structure_printer_t 
+json_structure_printer = {
+_map,
+_list,
+_pop,
+_map_key,
+_number,
+_string,
+_bool,
+_initial_state
+};
+static int
+enter_level(void *st, const char *marker, int type) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if (state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(",", output);
+   if (state->level == 1)
+   fputs("\n", output);
+   else
+   fputs(" ", output);
+}
+if (state->stack != NULL) {
+   state->stack->first_already_seen = TRUE;
+}
+fputs(marker, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el->type = type;
+el->first_already_seen = FALSE;
+el->rest = state->stack;
+state->stack = el;
+return state->level++;
+}
+
+int
+json_map(void *st)
+{
+return enter_level(st, "{", TYPE_JSON_MAP);
+}
+
+int
+json_list(void *st)
+{
+return enter_level(st, "[", TYPE_JSON_ARRAY);
+}
+
+void
+json_pop(void *st, int level) 
+{
+int i;
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+for (i = state->level; i > level; i--) {
+   json_list_t *tos = state->stack;
+   if (tos->type == TYPE_JSON_MAP) {
+   fputs("}", output);
+   }
+   if (tos->type == TYPE_JSON_ARRAY) {
+   fputs("]", output);
+   }
+   state->stack = tos->rest;
+   state->level--;
+   talloc_free(tos);
+}
+if (state->level == 0)
+   fputs("\n", output);
+}
+
+void
+json_map_key(void *st, const char *key)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if (state->stack != NULL && state->stack->first_already_seen) {
+   fputs(",\n", output);
+}
+fputs("\"", output);
+fputs(key, output);
+fputs("\": ", output);
+}
+
+void
+json_number(void *st, int val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if (state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(", ", output);
+}
+state->stack->first_already_seen = TRUE;
+fprintf(output, "%i", val);
+}
+
+void
+json_string(void *st, const char *val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+void *ctx = talloc_new(0);
+if (state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(",", output);
+   if (state->level == 1)
+   fputs("\n", output);
+   else
+   fputs(" ", output);
+}
+
+state->stack->first_already_seen = TRUE;
+fprintf(output, "%s", json_quote_str(ctx, val));
+talloc_free(ctx);
+}
+
+void
+json_bool(void *st, notmuch_bool_t val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if (val)
+   fputs("true", output);
+else
+   fputs("false", output);
+}
+
+void *
+json_initial_state(const struct structure_printer *sp, FILE *output)
+{
+(void)sp;
+json_state_t *st = talloc(0, json_state_t);
+st->level = 0;
+st->stack = NULL;
+st->output = output;
+return st;
+}
diff --git a/structured-output.h b/structured-output.h
index b43afe0..cba3882 100644
--- a/structured-output.h
+++ b/structured-output.h
@@ -62,3 +62,51 @@ typedef struct structure_printer {
 } structure_printer_t;


+/* JSON structure printer */
+
+/* single linked list 

[PATCH v3 1/3] Add support for structured output formatters.

2012-07-10 Thread cra...@gmx.net
This patch adds a new type structure_printer, which is used for structured 
formatting, e.g. JSON or S-Expressions.

The structure contains the following function pointers:

- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
- number, string, bool: output one element of the specific type.

All functions should use state to insert delimiters etc. automatically when 
appropriate.

Example:
int top, one;
top = map(state);
map_key(state, "foo");
one = list(state);
number(state, 1);
number(state, 2);
number(state, 3);
pop(state, one);
map_key(state, "bar");
map(state);
map_key(state, "baaz");
string(state, "hello world");
pop(state, top);

would output JSON as follows:

{"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
---
 Makefile.local  |1 +
 structured-output.h |   64 +++
 2 files changed, 65 insertions(+)
 create mode 100644 structured-output.h

diff --git a/Makefile.local b/Makefile.local
index a890df2..9b3db5e 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -286,6 +286,7 @@ notmuch_client_srcs =   \
notmuch-reply.c \
notmuch-restore.c   \
notmuch-search.c\
+   structured-output.c \
notmuch-setup.c \
notmuch-show.c  \
notmuch-tag.c   \
diff --git a/structured-output.h b/structured-output.h
new file mode 100644
index 000..b43afe0
--- /dev/null
+++ b/structured-output.h
@@ -0,0 +1,64 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright ? 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth 
+ */
+
+#include "notmuch-client.h"
+
+/* structured formatting, useful for JSON, S-Expressions, ...
+
+- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
+- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
+- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
+- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
+- number, string, bool: output one element of the specific type.
+
+All functions should use state to insert delimiters etc. automatically when 
appropriate.
+
+Example:
+int top, one;
+top = map(state);
+map_key(state, "foo");
+one = list(state);
+number(state, 1);
+number(state, 2);
+number(state, 3);
+pop(state, one);
+map_key(state, "bar");
+map(state);
+map_key(state, "baaz");
+string(state, "hello world");
+pop(state, top);
+
+would output JSON as follows:
+
+{"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
+
+ */
+typedef struct structure_printer {
+

[PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread Mark Walters
On Tue, 10 Jul 2012, craven at gmx.net wrote:
> As discussed in , this patch adds
> support for new structured output formats (like s-expressions) by using
> stateful structure_printers. An implementation of the JSON structure
> printer that passes all tests is included. The output for JSON (and
> text) is identical to the current output. S-Expressions will be added in
> a later patch.
>

Hi I have some more comments inline below. I agree with Jamie that it
would be nice to split it into smaller chunks but I am not sure that is
practical to do.

> A large part of this patch just implements the differentiation between
> structured and non-structured output (all the code within 
> "if(format == unstructured_text_printer)").

I also agree with Jamie that the text output is annoying. However, at
least until Austin's async json parser goes in even the emacs interface
is using it so I think we have to live with it for a while (at the very
least we would need to give clients a reasonable time to migrate).

Anyway, I think this patch has to support it.

> In a second patch, the structured output code should be isolated in a
> separate file, and also used in all other parts of notmuch.
>
> The interface is a structure structure_printer, which contains the following 
> methods:
>
> - initial_state: is called to create a state object, that is passed to all 
> invocations. This should be used to keep track of the output file and 
> everything else necessary to correctly format output.
> - map: is called when a new map (associative array, dictionary) is started. 
> map_key and the primitives (string, number, bool) are used alternatingly to 
> add key/value pairs. pop is used to close the map (see there). This function 
> must return a nesting level identifier that can be used to close all nested 
> structures (maps and lists), backing out to the returned nesting level.
> - list: is called when a new list (array, vector) is started. the primitives 
> (string, number, bool) are used consecutively to add values to the list. pop 
> is used to close the list. This function must return a nesting level 
> identifier that can be used to close all nested structures (maps and lists), 
> backing out to the returned nesting level.
> - pop: is called to return to a given nesting level. All lists and maps with 
> a deeper nesting level must be closed.
> - number, string, bool: output one element of the specific type.
>
> All functions should use the state object to insert delimiters etc. 
> automatically when appropriate.
>
> Example:
> int top, one;
> top = map(state);
> map_key(state, "foo");
> one = list(state);
> number(state, 1);
> number(state, 2);
> number(state, 3);
> pop(state, i);
> map_key(state, "bar");
> map(state);
> map_key(state, "baaz");
> string(state, "hello world");
> pop(state, top);
>
> would output JSON as follows:
>
> {"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
> ---
>  notmuch-search.c | 491 
> ---
>  1 file changed, 361 insertions(+), 130 deletions(-)
>
> diff --git a/notmuch-search.c b/notmuch-search.c
> index 3be296d..412 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -28,6 +28,210 @@ typedef enum {
>  OUTPUT_TAGS
>  } output_t;
>  
> +/* structured formatting, useful for JSON, S-Expressions, ...
> +
> +- initial_state: is called to create a state object, that is passed to all 
> invocations. This should be used to keep track of the output file and 
> everything else necessary to correctly format output.
> +- map: is called when a new map (associative array, dictionary) is started. 
> map_key and the primitives (string, number, bool) are used alternatingly to 
> add key/value pairs. pop is used to close the map (see there). This function 
> must return a nesting level identifier that can be used to close all nested 
> structures (maps and lists), backing out to the returned nesting level.
> +- list: is called when a new list (array, vector) is started. the primitives 
> (string, number, bool) are used consecutively to add values to the list. pop 
> is used to close the list. This function must return a nesting level 
> identifier that can be used to close all nested structures (maps and lists), 
> backing out to the returned nesting level.
> +- pop: is called to return to a given nesting level. All lists and maps with 
> a deeper nesting level must be closed.
> +- number, string, bool: output one element of the specific type.
> +
> +All functions should use state to insert delimiters etc. automatically when 
> appropriate.
> +
> +Example:
> +int top, one;
> +top = map(state);
> +map_key(state, "foo");
> +one = list(state);
> +number(state, 1);
> +number(state, 2);
> +number(state, 3);
> +pop(state, i);

I think `i` should be `one` or vice versa (and the same in the commit message)? 

> +map_key(state, "bar");
> +map(state);
> +map_key(state, "baaz");
> +string(state, "hello world");
> +pop(state, top);
> +
> +would output 

Tests

2012-07-10 Thread Mark Walters

I have been thinking a little bit about the current situation with
regards to tests. There are quite a lot of tests in the review queue
that have been there for quite some time without much interest, but I do
think we are rather short of tests. (*)

I wonder if we could have a sort of staging area for tests where they
could roughly go in without review and would only be run by something
like make stage-test or make all-tests or something.  The hope is that
this would encourage more tests.

The idea would be not that a patch author would have to make sure they
all pass, but if they previously passed and no longer do then the author
would know *something* about the output had changed.

One example is the emacs elide test by Pieter
id:"1329684990-12504-3-git-send-email-pieter at praet.org" which does still
pass after my substantial change to the way elide is done (and is not
currently covered in the test suite)

Of course if someone does review one of these staging tests then they
can be moved into the real tests.

Best wishes

Mark

(*) for example the structured output patch accidentally changed the
output of notmuch search --output=threads --format=json  but
that was not caught by the tests.







deprecating legacy text output

2012-07-10 Thread David Bremner
On Tue, 10 Jul 2012 10:04:10 -0700, Jameson Graef Rollins  wrote:
> On Tue, Jul 10 2012, Mark Walters  wrote:
> > I think I would also add something saying that the text format is just
> > different (and that a significant chunk of the patch is just that).
> 
> Can we not just dump this output format once and for all?  Does anything
> use it?  And if so can we just modify it to use a more sensible format?

I think we need to at least deprecate the format for a release or so, as
there are definitely people using it in the wild.  As far as I know the
at least the Vim interface uses it (what about notmuch-mutt?), in
addition to whatever homebrewed scripts might use it. Since
scriptability is one of the "notmuch core values", I guess we should
also provide something more script friendly than json. As you say, it
doesn't have to be exactly like what we have now.

d


post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Jani Nikula
On Jul 10, 2012 12:59 PM, "Sebastien Binet"  wrote:
>
> Jani Nikula  writes:
>
> > On Jul 9, 2012 8:12 PM, "Jameson Graef Rollins" <
jrollins at finestructure.net>
> > wrote:
> >>
> >> On Mon, Jul 09 2012, Sebastien Binet  wrote:
> >> > hum... is post-new supposed to be run even if there is no new
message ?
> >>
> >> Hi, Sebastian.  Yes, I think it runs regardless if there are any new
> >> messages or not.
> >
> > That's correct; only errors in notmuch new cause post-new hook to be
> > skipped.
>
> ok. I thought using the post-new hook would have saved some i/o
> resources over my current setup:
>  offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh
>
> where notmuch-tag.sh is (in pseudo-code):
> ##
> /usr/bin/notmuch new
>
> for tag,query in tag-queries:
>   tag_new $tag $query
>
> ##
>
> is there any advantage of using post-new compared to this setup ?

There's no functional advantage. It does keep your initial tagging script
connected with notmuch new rather than offlinemap, if you ever need to run
notmuch new on its own.

If your tagging setup is really complicated, you could have some-tag in
new.tags config, and bail out early if notmuch count tag:some-tag outputs 0
(and obviously notmuch tag -some-tag tag:some-tag later in the script).
Just a thought.

J.

>
> -s
-- next part --
An HTML attachment was scrubbed...
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/c069b7cf/attachment.html>


notmuch-emacs and bbdb

2012-07-10 Thread Daniel Bergey
As far as I can tell, notmuch doesn't integrate as smoothly with bbdb as older
emacs mailclients.  I'm especially looking for a snarf function that
distinguishes sender from recipient.

How do other people use bbdb with notmuch?

Does anyone have lisp code like that which ships with bbdb for other
clients?

If I were to find time to write such code, what would you like it to do?

cheers,
bergey


[PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread cra...@gmx.net
As discussed in , this patch adds
support for new structured output formats (like s-expressions) by using
stateful structure_printers. An implementation of the JSON structure
printer that passes all tests is included. The output for JSON (and
text) is identical to the current output. S-Expressions will be added in
a later patch.

A large part of this patch just implements the differentiation between
structured and non-structured output (all the code within 
"if(format == unstructured_text_printer)").

In a second patch, the structured output code should be isolated in a
separate file, and also used in all other parts of notmuch.

The interface is a structure structure_printer, which contains the following 
methods:

- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
- number, string, bool: output one element of the specific type.

All functions should use the state object to insert delimiters etc. 
automatically when appropriate.

Example:
int top, one;
top = map(state);
map_key(state, "foo");
one = list(state);
number(state, 1);
number(state, 2);
number(state, 3);
pop(state, i);
map_key(state, "bar");
map(state);
map_key(state, "baaz");
string(state, "hello world");
pop(state, top);

would output JSON as follows:

{"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
---
 notmuch-search.c | 491 ---
 1 file changed, 361 insertions(+), 130 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..412 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,6 +28,210 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;

+/* structured formatting, useful for JSON, S-Expressions, ...
+
+- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
+- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
+- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
+- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
+- number, string, bool: output one element of the specific type.
+
+All functions should use state to insert delimiters etc. automatically when 
appropriate.
+
+Example:
+int top, one;
+top = map(state);
+map_key(state, "foo");
+one = list(state);
+number(state, 1);
+number(state, 2);
+number(state, 3);
+pop(state, i);
+map_key(state, "bar");
+map(state);
+map_key(state, "baaz");
+string(state, "hello world");
+pop(state, top);
+
+would output JSON as follows:
+
+{"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
+
+ */
+typedef struct structure_printer {
+int (*map)(void *state);
+int (*list)(void *state);
+void (*pop)(void *state, int level);
+void (*map_key)(void *state, const char *key);
+void (*number)(void *state, int val);
+void (*string)(void *state, const char *val);
+void (*bool)(void *state, notmuch_bool_t val);
+void *(*initial_state)(const struct structure_printer *sp, FILE *output);
+} structure_printer_t;
+
+/* JSON structure printer */
+
+/* single linked list implementation for keeping track of the array/map 
nesting state */
+typedef struct json_list {
+int type;
+int first_already_seen;
+struct json_list *rest;
+} json_list_t;
+
+#define TYPE_JSON_MAP 1
+#define TYPE_JSON_ARRAY 2
+

[PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread Austin Clements
Since it would be great to use the structure printer for show as well,
it would make sense to put it in its own source file, where it can
easily be shared between commands.

There are a few systematic code formatting problems in your patch.  To
be consistent with other notmuch code, there should be a space between
function names and parameter lists (in both declarations and calls)
and there should be a space between a keyword and a paren (e.g., "if
(" and "for (").  In a function definition, the function name should
start on a new line (this makes it easy to grep for a function's
definition).  Also, in general, try to keep lines under 80 characters
wide (we're not very consistent about this one, but it would be nice
to not become even less consistent about it).

More detailed comments below.

Quoth craven at gmx.net on Jul 10 at  3:30 pm:
> As discussed in , this patch adds
> support for new structured output formats (like s-expressions) by using
> stateful structure_printers. An implementation of the JSON structure
> printer that passes all tests is included. The output for JSON (and
> text) is identical to the current output. S-Expressions will be added in
> a later patch.
> 
> A large part of this patch just implements the differentiation between
> structured and non-structured output (all the code within 
> "if(format == unstructured_text_printer)").
> 
> In a second patch, the structured output code should be isolated in a
> separate file, and also used in all other parts of notmuch.
> 
> The interface is a structure structure_printer, which contains the following 
> methods:
> 
> - initial_state: is called to create a state object, that is passed to all 
> invocations. This should be used to keep track of the output file and 
> everything else necessary to correctly format output.
> - map: is called when a new map (associative array, dictionary) is started. 
> map_key and the primitives (string, number, bool) are used alternatingly to 
> add key/value pairs. pop is used to close the map (see there). This function 
> must return a nesting level identifier that can be used to close all nested 
> structures (maps and lists), backing out to the returned nesting level.
> - list: is called when a new list (array, vector) is started. the primitives 
> (string, number, bool) are used consecutively to add values to the list. pop 
> is used to close the list. This function must return a nesting level 
> identifier that can be used to close all nested structures (maps and lists), 
> backing out to the returned nesting level.
> - pop: is called to return to a given nesting level. All lists and maps with 
> a deeper nesting level must be closed.
> - number, string, bool: output one element of the specific type.
> 
> All functions should use the state object to insert delimiters etc. 
> automatically when appropriate.
> 
> Example:
> int top, one;
> top = map(state);
> map_key(state, "foo");
> one = list(state);
> number(state, 1);
> number(state, 2);
> number(state, 3);
> pop(state, i);
> map_key(state, "bar");
> map(state);
> map_key(state, "baaz");
> string(state, "hello world");
> pop(state, top);
> 
> would output JSON as follows:
> 
> {"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
> ---
>  notmuch-search.c | 491 
> ---
>  1 file changed, 361 insertions(+), 130 deletions(-)
> 
> diff --git a/notmuch-search.c b/notmuch-search.c
> index 3be296d..412 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -28,6 +28,210 @@ typedef enum {
>  OUTPUT_TAGS
>  } output_t;
>  
> +/* structured formatting, useful for JSON, S-Expressions, ...
> +
> +- initial_state: is called to create a state object, that is passed to all 
> invocations. This should be used to keep track of the output file and 
> everything else necessary to correctly format output.
> +- map: is called when a new map (associative array, dictionary) is started. 
> map_key and the primitives (string, number, bool) are used alternatingly to 
> add key/value pairs. pop is used to close the map (see there). This function 
> must return a nesting level identifier that can be used to close all nested 
> structures (maps and lists), backing out to the returned nesting level.
> +- list: is called when a new list (array, vector) is started. the primitives 
> (string, number, bool) are used consecutively to add values to the list. pop 
> is used to close the list. This function must return a nesting level 
> identifier that can be used to close all nested structures (maps and lists), 
> backing out to the returned nesting level.
> +- pop: is called to return to a given nesting level. All lists and maps with 
> a deeper nesting level must be closed.
> +- number, string, bool: output one element of the specific type.
> +
> +All functions should use state to insert delimiters etc. automatically when 
> appropriate.
> +
> +Example:
> +int top, one;
> +top = map(state);
> +map_key(state, "foo");
> 

[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Mark Walters

>  notmuch-search.c | 453 
> +++
>  1 file changed, 326 insertions(+), 127 deletions(-)

Hi for such a large patch this was surprisingly easy to follow (though I
am not saying I have worked through all the details). However, there are
some preliminary comments below which I think are worth fixing before a
full review as they will make it easier for other reviewers. One thing
that would help a lot is some comments.

Best wishes

Mark

> As discussed in , this patch adds
> support for new structured output formats (like s-expressions) by using
> stateful structure_printers. An implementation of the JSON structure
> printer that passes all tests is included.

If I understand it correctly, the output should be identical to before?
If that is the case I think it's worth saying. 

> Structure printers have functions for starting a map, a list, closing
> any number of these two, printing a map key, a number value, a string
> value, and boolean values. By threading a state through the
> invocations, arbitrary structured formatting can be achieved.

I think I would also add something saying that the text format is just
different (and that a significant chunk of the patch is just that).

> In a second patch, the structured output code should be isolated in a
> separate file, and also used in all other parts of notmuch.
> ---
>
> diff --git a/notmuch-search.c b/notmuch-search.c
> index 3be296d..3413b79 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -28,6 +28,181 @@ typedef enum {
>  OUTPUT_TAGS
>  } output_t;
>  
> +typedef void * structure_printer_state_t;
> +
> +typedef struct structure_printer {
> +int (*map)(void *state);
> +int (*list)(void *state);
> +void (*pop)(void *state, int level);
> +void (*map_key)(void *state, const char *key);
> +void (*number)(void *state, int val);
> +void (*string)(void *state, const char *val);
> +void (*bool)(void *state, notmuch_bool_t val);
> +void *(*initial_state)(const struct structure_printer *sp, FILE *output);
> +} structure_printer_t;

I think this needs some comments on what these functions do. number,
string and boolean are relatively clear (but saying "output a number"
etc is worthwhile). But what map and list do is much less clear and
definitely deserves a comment. And it is definitely unclear what they
are meant to return.

I would also say something about the variable state: eg "the variable
`state` can contain any state the structure_printer wishes to maintain".

> +
> +/* JSON structure printer */
> +
> +typedef struct json_list {
> +int type;
> +int first_already_seen;
> +struct json_list *rest;
> +} json_list_t;
> +
> +#define TYPE_JSON_MAP 1
> +#define TYPE_JSON_ARRAY 2
> +
> +typedef struct json_state {
> +FILE *output;
> +json_list_t *stack;
> +int level;
> +} json_state_t;
> +
> +int json_map(void *state);
> +int json_list(void *state);
> +void json_pop(void *state, int level);
> +void json_map_key(void *state, const char *key);
> +void json_number(void *state, int val);
> +void json_string(void *state, const char *val);
> +void json_bool(void *state, notmuch_bool_t val);
> +void *json_initial_state(const struct structure_printer *sp, FILE *output);
> +
> +int json_map(void *st) {
> +json_state_t *state = (json_state_t*)st;
> +FILE *output = state->output;
> +if(state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
> state->stack->first_already_seen) {
> + fputs(",", output);
> + if(state->level == 1)
> + fputs("\n", output);
> + else
> + fputs(" ", output);
> +}
> +if(state->stack != NULL) {
> + state->stack->first_already_seen = TRUE;
> +}
> +fputs("{", output);
> +void *ctx_json_map = talloc_new (0);
> +json_list_t *el = talloc(ctx_json_map, json_list_t);
> +el->type = TYPE_JSON_MAP;
> +el->first_already_seen = FALSE;
> +el->rest = state->stack;
> +state->stack = el;
> +return state->level++;
> +}
> +
> +int json_list(void *st) {
> +json_state_t *state = (json_state_t*)st;
> +FILE *output = state->output;
> +if(state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
> state->stack->first_already_seen) {
> + fputs(",", output);
> + if(state->level == 1)
> + fputs("\n", output);
> + else
> + fputs(" ", output);
> +}
> +if(state->stack != NULL) {
> + state->stack->first_already_seen = TRUE;
> +}
> +fputs("[", output);
> +void *ctx_json_map = talloc_new (0);
> +json_list_t *el = talloc(ctx_json_map, json_list_t);
> +el->type = TYPE_JSON_ARRAY;
> +el->first_already_seen = FALSE;
> +el->rest = state->stack;
> +state->stack = el;
> +return state->level++;
> +}
> +
> +void json_pop(void *st, int level) {
> +int i;
> +json_state_t *state = (json_state_t*)st;
> +FILE *output = state->output;
> +for(i = state->level; i > level; 

[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Austin Clements
Quoth Jameson Graef Rollins on Jul 10 at 10:04 am:
> On Tue, Jul 10 2012, Mark Walters  wrote:
> > I think I would also add something saying that the text format is just
> > different (and that a significant chunk of the patch is just that).
> 
> Can we not just dump this output format once and for all?  Does anything
> use it?  And if so can we just modify it to use a more sensible format?

Currently Emacs uses this output format (though
id:"1341870162-17782-1-git-send-email-amdragon at mit.edu" aims to fix
this).  Also, I assume most scripted uses of notmuch use the text
format (probably not the --output=summary text format, but certainly
files, messages, and tags).


[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread cra...@gmx.net
Sorry, the original patch I sent was missing a small part, here the full
patch:

As discussed in , this patch adds
support for new structured output formats (like s-expressions) by using
stateful structure_printers. An implementation of the JSON structure
printer that passes all tests is included.

Structure printers have functions for starting a map, a list, closing
any number of these two, printing a map key, a number value, a string
value, and boolean values. By threading a state through the
invocations, arbitrary structured formatting can be achieved.

In a second patch, the structured output code should be isolated in a
separate file, and also used in all other parts of notmuch.
---
 notmuch-search.c | 453 +++
 1 file changed, 326 insertions(+), 127 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..3413b79 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,6 +28,181 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;

+typedef void * structure_printer_state_t;
+
+typedef struct structure_printer {
+int (*map)(void *state);
+int (*list)(void *state);
+void (*pop)(void *state, int level);
+void (*map_key)(void *state, const char *key);
+void (*number)(void *state, int val);
+void (*string)(void *state, const char *val);
+void (*bool)(void *state, notmuch_bool_t val);
+void *(*initial_state)(const struct structure_printer *sp, FILE *output);
+} structure_printer_t;
+
+/* JSON structure printer */
+
+typedef struct json_list {
+int type;
+int first_already_seen;
+struct json_list *rest;
+} json_list_t;
+
+#define TYPE_JSON_MAP 1
+#define TYPE_JSON_ARRAY 2
+
+typedef struct json_state {
+FILE *output;
+json_list_t *stack;
+int level;
+} json_state_t;
+
+int json_map(void *state);
+int json_list(void *state);
+void json_pop(void *state, int level);
+void json_map_key(void *state, const char *key);
+void json_number(void *state, int val);
+void json_string(void *state, const char *val);
+void json_bool(void *state, notmuch_bool_t val);
+void *json_initial_state(const struct structure_printer *sp, FILE *output);
+
+int json_map(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if(state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(",", output);
+   if(state->level == 1)
+   fputs("\n", output);
+   else
+   fputs(" ", output);
+}
+if(state->stack != NULL) {
+   state->stack->first_already_seen = TRUE;
+}
+fputs("{", output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el->type = TYPE_JSON_MAP;
+el->first_already_seen = FALSE;
+el->rest = state->stack;
+state->stack = el;
+return state->level++;
+}
+
+int json_list(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if(state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(",", output);
+   if(state->level == 1)
+   fputs("\n", output);
+   else
+   fputs(" ", output);
+}
+if(state->stack != NULL) {
+   state->stack->first_already_seen = TRUE;
+}
+fputs("[", output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el->type = TYPE_JSON_ARRAY;
+el->first_already_seen = FALSE;
+el->rest = state->stack;
+state->stack = el;
+return state->level++;
+}
+
+void json_pop(void *st, int level) {
+int i;
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+for(i = state->level; i > level; i--) {
+   json_list_t *tos = state->stack;
+   if(tos->type == TYPE_JSON_MAP) {
+   fputs("}", output);
+   } 
+   if(tos->type == TYPE_JSON_ARRAY) {
+   fputs("]", output);
+   }
+   state->stack = tos->rest;
+   state->level--;
+   talloc_free(tos);
+}
+if(state->level == 0)
+   fputs("\n", output);
+}
+
+void json_map_key(void *st, const char *key) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if(state->stack != NULL && state->stack->first_already_seen) {
+   fputs(",\n", output);
+}
+fputs("\"", output);
+fputs(key, output);
+fputs("\": ", output);
+}
+
+void json_number(void *st, int val) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+if(state->stack != NULL && state->stack->type == TYPE_JSON_ARRAY && 
state->stack->first_already_seen) {
+   fputs(", ", output);
+}
+state->stack->first_already_seen = TRUE;
+fprintf(output, "%i", val);
+}
+
+void json_string(void *st, const char *val) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state->output;
+void *ctx = 

ANN: Speedee, a web-based notmuch client

2012-07-10 Thread elf Pavlik
Hi,

Sounds great!

https://speedee.bugsplat.info/#search/ruby throws at me:

NoMethodError at /
undefined method `close' for nil:NilClass

file: app.rb
location: block in 
line: 16

Looking forward to play with you demo :)

Thanks!
~ elf Pavlik ~

Excerpts from Peter Keen's message of 2012-07-10 05:14:45 +:
> Hi guys,
> 
> Over the weekend I threw together the prototype for a web-based notmuch
> client named Speedee. It's a little Ruby webapp with a Backbone.js front
> end, using Twitter Bootstrap and a free theme from Bootswatch. It doesn't
> do much yet. Basically, it can list threads matching a search and show you
> those threads. It reads the notmuch config file to find the database. It
> also reads the file for a new section named "speedee searches", where you
> can define named searches to be included in the sidebar. The list of All
> Tags is also included in the sidebar, assuming the version of notmuch
> you're running has my ruby bindings patch from yesterday.
> 
> At this point, my plan is to add a super basic mail composer and sending
> facility and then live with it for a few months. I've setup a demo using
> the notmuch mbox file as sample data. Feel free to poke around [1] and let
> me know what you think!
> 
> Source: https://github.com/peterkeen/speedee
> Demo: https://speedee.bugsplat.info/#search/ruby
> 
> 
> [1]: The demo is using a self-signed wildcard cert. SHA1 fingerprint: 01 B6
> 5D 47 EC 4A BB 27 E6 49 A1 0F 5A 48 8F BD CC 33 F2 F8


post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Sebastien Binet
Jani Nikula  writes:

> On Jul 9, 2012 8:12 PM, "Jameson Graef Rollins"  finestructure.net>
> wrote:
>>
>> On Mon, Jul 09 2012, Sebastien Binet  wrote:
>> > hum... is post-new supposed to be run even if there is no new message ?
>>
>> Hi, Sebastian.  Yes, I think it runs regardless if there are any new
>> messages or not.
>
> That's correct; only errors in notmuch new cause post-new hook to be
> skipped.

ok. I thought using the post-new hook would have saved some i/o
resources over my current setup:
 offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh

where notmuch-tag.sh is (in pseudo-code):
##
/usr/bin/notmuch new

for tag,query in tag-queries:
  tag_new $tag $query

##

is there any advantage of using post-new compared to this setup ?

-s
-- 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/20120710/8e3e0334/attachment.pgp>


[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Austin Clements  wrote:
> Quoth Jameson Graef Rollins on Jul 10 at 10:04 am:
>> On Tue, Jul 10 2012, Mark Walters  wrote:
>> > I think I would also add something saying that the text format is just
>> > different (and that a significant chunk of the patch is just that).
>> 
>> Can we not just dump this output format once and for all?  Does anything
>> use it?  And if so can we just modify it to use a more sensible format?
>
> Currently Emacs uses this output format (though
> id:"1341870162-17782-1-git-send-email-amdragon at mit.edu" aims to fix
> this).  Also, I assume most scripted uses of notmuch use the text
> format (probably not the --output=summary text format, but certainly
> files, messages, and tags).

Sorry, in a momentary brain freeze I confused the search and show text
output.  You're right that the text search output is more widely used.

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/1889c9ec/attachment.pgp>


[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Mark Walters  wrote:
> I think I would also add something saying that the text format is just
> different (and that a significant chunk of the patch is just that).

Can we not just dump this output format once and for all?  Does anything
use it?  And if so can we just modify it to use a more sensible format?

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/05948522/attachment.pgp>


[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, craven at gmx.net wrote:
> Sorry, the original patch I sent was missing a small part, here the full
> patch:
>
> As discussed in , this patch adds
> support for new structured output formats (like s-expressions) by using
> stateful structure_printers. An implementation of the JSON structure
> printer that passes all tests is included.
>
> Structure printers have functions for starting a map, a list, closing
> any number of these two, printing a map key, a number value, a string
> value, and boolean values. By threading a state through the
> invocations, arbitrary structured formatting can be achieved.
>
> In a second patch, the structured output code should be isolated in a
> separate file, and also used in all other parts of notmuch.

Hi, Peter.  Thanks for submitting this.

I would really like to see this patch broken up into multiple smaller
and more atomic patches.  Smaller patches are much easier to read and
review.  For instance, all the new formatting functions could be added
in their own patch (and already in their own file if that's
appropriate).  Then the existing json output could then be modified to
use the new formatters.

There is also json output in notmuch show and reply as well, so they
should probably also be modified to use the new formatter.

Thanks!

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/74c64ff7/attachment.pgp>


post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Sebastien Binet  wrote:
> ok. I thought using the post-new hook would have saved some i/o
> resources over my current setup:
>  offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh
>
> where notmuch-tag.sh is (in pseudo-code):
> ##
> /usr/bin/notmuch new
>
> for tag,query in tag-queries:
>   tag_new $tag $query
>
> ##
>
> is there any advantage of using post-new compared to this setup ?

The hooks are just there for convenience, and as Jani says, they're
executed any time notmuch new is run, in any context.  In your setup it
looks like it would also save you having to maintain another script
(notmuch-tag.sh) since that script in it's entirety could just be the
post-new script, and the offlineimap.postsynchook could just execute
notmuch new directly.

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/f68c2784/attachment.pgp>


[PATCH 1/3] emacs: add no-display arg to notmuch-hello-refresh-hook

2012-07-10 Thread Jani Nikula
On Jul 10, 2012 4:49 AM, "Austin Clements"  wrote:
>
> Quoth Jani Nikula on Jun 15 at  6:53 pm:
> > Add no-display arg to notmuch-hello-refresh-hook to allow each hook to
> > decide what is appropriate when no-display is t, which is typically
> > the case when called non-interactively. This is used by the following
> > patch.
> >
> > This breaks existing hooks people might have, which will now need to
> > accept the argument.
> >
> > Signed-off-by: Jani Nikula 
>
> This seems like an overloaded use of no-display.  If I'm reading the
> code right, no-display indicates whether or not the notmuch-hello
> buffer should be switched to and seems like a workaround for some
> particular corner-case (I'm not even sure what).  This seems like a
> strange condition to predicate a hook on (but maybe I just don't
> understand).  What condition, abstractly speaking, is
> notmuch-hello-refresh-status-message trying to run under?

IIUC, no-display is useful for calling refresh from outside of emacs, e.g.
from post-new hook in an automated fashion, so you can have an up-to-date
buffer when you switch to it. There's no point in displaying the refresh
message when you don't also switch to the buffer, is there? And this way
you'll get the diff between the manual (through user interaction) refreshes
of the buffer, not between two cron jobs.

J.
-- next part --
An HTML attachment was scrubbed...
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/cf9914bb/attachment.html>


ANN: Speedee, a web-based notmuch client

2012-07-10 Thread Peter Keen
On Tue, Jul 10, 2012 at 5:08 AM, elf Pavlik
wrote:

> Hi,
>
> Sounds great!
>
> https://speedee.bugsplat.info/#search/ruby throws at me:
>
> NoMethodError at /
> undefined method `close' for nil:NilClass
>
> file: app.rb
> location: block in 
> line: 16
>
> Looking forward to play with you demo :)
>
> Thanks!
> ~ elf Pavlik ~
>


Interesting. Looks like something was leaking file handles. It appears to
be all better after a simple patch and a restart. Thanks!
-- next part --
An HTML attachment was scrubbed...
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/f0282786/attachment.html>


ANN: Speedee, a web-based notmuch client

2012-07-10 Thread Jameson Graef Rollins
On Mon, Jul 09 2012, Peter Keen  wrote:
> Over the weekend I threw together the prototype for a web-based notmuch
> client named Speedee. It's a little Ruby webapp with a Backbone.js front
> end, using Twitter Bootstrap and a free theme from Bootswatch. It doesn't
> do much yet. Basically, it can list threads matching a search and show you
> those threads. It reads the notmuch config file to find the database. It
> also reads the file for a new section named "speedee searches", where you
> can define named searches to be included in the sidebar. The list of All
> Tags is also included in the sidebar, assuming the version of notmuch
> you're running has my ruby bindings patch from yesterday.
>
> At this point, my plan is to add a super basic mail composer and sending
> facility and then live with it for a few months. I've setup a demo using
> the notmuch mbox file as sample data. Feel free to poke around [1] and let
> me know what you think!
>
> Source: https://github.com/peterkeen/speedee
> Demo: https://speedee.bugsplat.info/#search/ruby

Cool!  I like it.  I can imagine adding fancier formatting and coloring
in the show pages, but it's a really nice simple platform to start with.
Very nice.

Looking forward to watching it develop!

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/e3e1e654/attachment.pgp>


S/MIME support

2012-07-10 Thread Jameson Graef Rollins
On Mon, Jul 09 2012, "Bryant, Daniel B."  wrote:
> I was able to get signature verification working with your patchset
> (with a caveat) but not decryption.

Hi, Daniel.  I guess I'm only partially happy to hear that!  I
definitely do appreciate the feedback, though.

> The caveat is that GMime is still borked with handling signatures with
> content type application/x-pkcs7-signature
> (vs. application/pkcs7-signature, which works fine). This is upstream
> GNOME bug #674032 that was supposed to have been fixed in GMime 2.6.9,
> but that original fix is also broken.

Ah, I didn't notice that:

https://bugzilla.gnome.org/show_bug.cgi?id=674032

Encouragingly, it sounds like Jeffery is working on it.

> One possible workaround is to twiddle the content-type of the
> signature part (and the corresponding protocol in the multipart/signed
> part). I implemented this by looping over each message part in
> mime_node_open() and modifying as necessary using the following logic:
>
>
> GMimeContentType *content_type = g_mime_object_get_content_type (part);
>
> const char *subtype = g_mime_content_type_get_media_subtype 
> (content_type);
> const char *protocol = g_mime_content_type_get_parameter (content_type, 
> "protocol");
>
> if (!strcmp(subtype, "x-pkcs7-signature")) {
> g_mime_content_type_set_media_subtype (content_type, 
> "pkcs7-signature");
> }
>
> if (protocol && !strcmp(protocol, "application/x-pkcs7-signature")) {
> g_mime_content_type_set_parameter (content_type, 
> "protocol","application/pkcs7-signature");
> }

We could do this, but I would certainly prefer that we fix gmime to
handle both types properly.

> All of my S/MIME encrypted mail consists of single part messages with
> content-type "application/x-pkcs7-mime". These conform to RFC3851,
> section 3.3/3.4. (sample messages are included in the RFC as
> well). This fails to be decrypted by notmuch because the mime node
> traversal code assumes that every encrypted message is
> multipart/encrypted, which appears to only be true for PGP/MIME.

Thanks for the great example of why we need tests!

Would you (or anyone) be willing to start putting together some tests
that include messages encrypted according to this RFC?  I think adding
some tests to the test/crypto script would be a great place to start.

jamie.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120710/7606e81b/attachment-0001.pgp>


RE: S/MIME support

2012-07-10 Thread Jameson Graef Rollins
On Mon, Jul 09 2012, Bryant, Daniel B. dan.bry...@jhuapl.edu wrote:
 I was able to get signature verification working with your patchset
 (with a caveat) but not decryption.

Hi, Daniel.  I guess I'm only partially happy to hear that!  I
definitely do appreciate the feedback, though.

 The caveat is that GMime is still borked with handling signatures with
 content type application/x-pkcs7-signature
 (vs. application/pkcs7-signature, which works fine). This is upstream
 GNOME bug #674032 that was supposed to have been fixed in GMime 2.6.9,
 but that original fix is also broken.

Ah, I didn't notice that:

https://bugzilla.gnome.org/show_bug.cgi?id=674032

Encouragingly, it sounds like Jeffery is working on it.

 One possible workaround is to twiddle the content-type of the
 signature part (and the corresponding protocol in the multipart/signed
 part). I implemented this by looping over each message part in
 mime_node_open() and modifying as necessary using the following logic:


 GMimeContentType *content_type = g_mime_object_get_content_type (part);

 const char *subtype = g_mime_content_type_get_media_subtype 
 (content_type);
 const char *protocol = g_mime_content_type_get_parameter (content_type, 
 protocol);

 if (!strcmp(subtype, x-pkcs7-signature)) {
 g_mime_content_type_set_media_subtype (content_type, 
 pkcs7-signature);
 }

 if (protocol  !strcmp(protocol, application/x-pkcs7-signature)) {
 g_mime_content_type_set_parameter (content_type, 
 protocol,application/pkcs7-signature);
 }

We could do this, but I would certainly prefer that we fix gmime to
handle both types properly.

 All of my S/MIME encrypted mail consists of single part messages with
 content-type application/x-pkcs7-mime. These conform to RFC3851,
 section 3.3/3.4. (sample messages are included in the RFC as
 well). This fails to be decrypted by notmuch because the mime node
 traversal code assumes that every encrypted message is
 multipart/encrypted, which appears to only be true for PGP/MIME.

Thanks for the great example of why we need tests!

Would you (or anyone) be willing to start putting together some tests
that include messages encrypted according to this RFC?  I think adding
some tests to the test/crypto script would be a great place to start.

jamie.


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


Re: ANN: Speedee, a web-based notmuch client

2012-07-10 Thread Jameson Graef Rollins
On Mon, Jul 09 2012, Peter Keen peter.k...@bugsplat.info wrote:
 Over the weekend I threw together the prototype for a web-based notmuch
 client named Speedee. It's a little Ruby webapp with a Backbone.js front
 end, using Twitter Bootstrap and a free theme from Bootswatch. It doesn't
 do much yet. Basically, it can list threads matching a search and show you
 those threads. It reads the notmuch config file to find the database. It
 also reads the file for a new section named speedee searches, where you
 can define named searches to be included in the sidebar. The list of All
 Tags is also included in the sidebar, assuming the version of notmuch
 you're running has my ruby bindings patch from yesterday.

 At this point, my plan is to add a super basic mail composer and sending
 facility and then live with it for a few months. I've setup a demo using
 the notmuch mbox file as sample data. Feel free to poke around [1] and let
 me know what you think!

 Source: https://github.com/peterkeen/speedee
 Demo: https://speedee.bugsplat.info/#search/ruby

Cool!  I like it.  I can imagine adding fancier formatting and coloring
in the show pages, but it's a really nice simple platform to start with.
Very nice.

Looking forward to watching it develop!

jamie.


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


Re: ANN: Speedee, a web-based notmuch client

2012-07-10 Thread Christophe-Marie Duquesne
On Tue, Jul 10, 2012 at 7:14 AM, Peter Keen peter.k...@bugsplat.info wrote:
 let me know what you think!

This is awesome, I love it!
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Sebastien Binet
Jani Nikula j...@nikula.org writes:

 On Jul 9, 2012 8:12 PM, Jameson Graef Rollins jroll...@finestructure.net
 wrote:

 On Mon, Jul 09 2012, Sebastien Binet bi...@cern.ch wrote:
  hum... is post-new supposed to be run even if there is no new message ?

 Hi, Sebastian.  Yes, I think it runs regardless if there are any new
 messages or not.

 That's correct; only errors in notmuch new cause post-new hook to be
 skipped.

ok. I thought using the post-new hook would have saved some i/o
resources over my current setup:
 offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh

where notmuch-tag.sh is (in pseudo-code):
##
/usr/bin/notmuch new

for tag,query in tag-queries:
  tag_new $tag $query

##

is there any advantage of using post-new compared to this setup ?

-s


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


[PATCH] Added better support for multiple structured output formats.

2012-07-10 Thread craven
As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
support for new structured output formats (like s-expressions) by using
stateful structure_printers. An implementation of the JSON structure
printer that passes all tests is included.

Structure printers have functions for starting a map, a list, closing
any number of these two, printing a map key, a number value, a string
value, and boolean values. By threading a state through the
invocations, arbitrary structured formatting can be achieved.

In a second patch, the structured output code should be isolated in a
separate file, and also used in all other parts of notmuch.
---
 notmuch-search.c | 458 ++-
 1 file changed, 322 insertions(+), 136 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index fa5086e..3413b79 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,19 +28,181 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
+typedef void * structure_printer_state_t;
+
 typedef struct structure_printer {
-int (*map)(struct structure_printer_t *sp);
-int (*list)(struct structure_printer_t *sp);
-void (*pop)(struct structure_printer_t *sp, int level);
-void (*map_key)(struct structure_printer_t *sp, const char *key);
-void (*number)(struct structure_printer_t *sp, int val);
-void (*string)(struct structure_printer_t *sp, const char *val);
-void (*bool)(struct structure_printer_t *sp, notmuch_bool_t val);
-void *(*initial_context)(struct structure_printer_t *sp);
+int (*map)(void *state);
+int (*list)(void *state);
+void (*pop)(void *state, int level);
+void (*map_key)(void *state, const char *key);
+void (*number)(void *state, int val);
+void (*string)(void *state, const char *val);
+void (*bool)(void *state, notmuch_bool_t val);
+void *(*initial_state)(const struct structure_printer *sp, FILE *output);
 } structure_printer_t;
 
-struct structure_printer_t *new_search_format_structure_printer(FILE *stream, 
struct search_format_t *search_format);
+/* JSON structure printer */
+
+typedef struct json_list {
+int type;
+int first_already_seen;
+struct json_list *rest;
+} json_list_t;
+
+#define TYPE_JSON_MAP 1
+#define TYPE_JSON_ARRAY 2
+
+typedef struct json_state {
+FILE *output;
+json_list_t *stack;
+int level;
+} json_state_t;
+
+int json_map(void *state);
+int json_list(void *state);
+void json_pop(void *state, int level);
+void json_map_key(void *state, const char *key);
+void json_number(void *state, int val);
+void json_string(void *state, const char *val);
+void json_bool(void *state, notmuch_bool_t val);
+void *json_initial_state(const struct structure_printer *sp, FILE *output);
+
+int json_map(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if(state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+if(state-stack != NULL) {
+   state-stack-first_already_seen = TRUE;
+}
+fputs({, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el-type = TYPE_JSON_MAP;
+el-first_already_seen = FALSE;
+el-rest = state-stack;
+state-stack = el;
+return state-level++;
+}
+
+int json_list(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if(state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+if(state-stack != NULL) {
+   state-stack-first_already_seen = TRUE;
+}
+fputs([, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el-type = TYPE_JSON_ARRAY;
+el-first_already_seen = FALSE;
+el-rest = state-stack;
+state-stack = el;
+return state-level++;
+}
+
+void json_pop(void *st, int level) {
+int i;
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+for(i = state-level; i  level; i--) {
+   json_list_t *tos = state-stack;
+   if(tos-type == TYPE_JSON_MAP) {
+   fputs(}, output);
+   } 
+   if(tos-type == TYPE_JSON_ARRAY) {
+   fputs(], output);
+   }
+   state-stack = tos-rest;
+   state-level--;
+   talloc_free(tos);
+}
+if(state-level == 0)
+   fputs(\n, output);
+}
+
+void json_map_key(void *st, const char *key) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-first_already_seen) {
+   fputs(,\n, output);
+}
+fputs(\, output);
+fputs(key, output);
+fputs(\: , output);
+}
+
+void 

[PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread craven
Sorry, the original patch I sent was missing a small part, here the full
patch:

As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
support for new structured output formats (like s-expressions) by using
stateful structure_printers. An implementation of the JSON structure
printer that passes all tests is included.

Structure printers have functions for starting a map, a list, closing
any number of these two, printing a map key, a number value, a string
value, and boolean values. By threading a state through the
invocations, arbitrary structured formatting can be achieved.

In a second patch, the structured output code should be isolated in a
separate file, and also used in all other parts of notmuch.
---
 notmuch-search.c | 453 +++
 1 file changed, 326 insertions(+), 127 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..3413b79 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,6 +28,181 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
+typedef void * structure_printer_state_t;
+
+typedef struct structure_printer {
+int (*map)(void *state);
+int (*list)(void *state);
+void (*pop)(void *state, int level);
+void (*map_key)(void *state, const char *key);
+void (*number)(void *state, int val);
+void (*string)(void *state, const char *val);
+void (*bool)(void *state, notmuch_bool_t val);
+void *(*initial_state)(const struct structure_printer *sp, FILE *output);
+} structure_printer_t;
+
+/* JSON structure printer */
+
+typedef struct json_list {
+int type;
+int first_already_seen;
+struct json_list *rest;
+} json_list_t;
+
+#define TYPE_JSON_MAP 1
+#define TYPE_JSON_ARRAY 2
+
+typedef struct json_state {
+FILE *output;
+json_list_t *stack;
+int level;
+} json_state_t;
+
+int json_map(void *state);
+int json_list(void *state);
+void json_pop(void *state, int level);
+void json_map_key(void *state, const char *key);
+void json_number(void *state, int val);
+void json_string(void *state, const char *val);
+void json_bool(void *state, notmuch_bool_t val);
+void *json_initial_state(const struct structure_printer *sp, FILE *output);
+
+int json_map(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if(state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+if(state-stack != NULL) {
+   state-stack-first_already_seen = TRUE;
+}
+fputs({, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el-type = TYPE_JSON_MAP;
+el-first_already_seen = FALSE;
+el-rest = state-stack;
+state-stack = el;
+return state-level++;
+}
+
+int json_list(void *st) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if(state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+if(state-stack != NULL) {
+   state-stack-first_already_seen = TRUE;
+}
+fputs([, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el-type = TYPE_JSON_ARRAY;
+el-first_already_seen = FALSE;
+el-rest = state-stack;
+state-stack = el;
+return state-level++;
+}
+
+void json_pop(void *st, int level) {
+int i;
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+for(i = state-level; i  level; i--) {
+   json_list_t *tos = state-stack;
+   if(tos-type == TYPE_JSON_MAP) {
+   fputs(}, output);
+   } 
+   if(tos-type == TYPE_JSON_ARRAY) {
+   fputs(], output);
+   }
+   state-stack = tos-rest;
+   state-level--;
+   talloc_free(tos);
+}
+if(state-level == 0)
+   fputs(\n, output);
+}
+
+void json_map_key(void *st, const char *key) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-first_already_seen) {
+   fputs(,\n, output);
+}
+fputs(\, output);
+fputs(key, output);
+fputs(\: , output);
+}
+
+void json_number(void *st, int val) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(, , output);
+}
+state-stack-first_already_seen = TRUE;
+fprintf(output, %i, val);
+}
+
+void json_string(void *st, const char *val) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+void *ctx = talloc_new(0);
+if(state-stack != NULL  state-stack-type == 

Re: ANN: Speedee, a web-based notmuch client

2012-07-10 Thread elf Pavlik
Hi,

Sounds great!

https://speedee.bugsplat.info/#search/ruby throws at me:

NoMethodError at /
undefined method `close' for nil:NilClass

file: app.rb
location: block in class:App
line: 16

Looking forward to play with you demo :)

Thanks!
~ elf Pavlik ~

Excerpts from Peter Keen's message of 2012-07-10 05:14:45 +:
 Hi guys,
 
 Over the weekend I threw together the prototype for a web-based notmuch
 client named Speedee. It's a little Ruby webapp with a Backbone.js front
 end, using Twitter Bootstrap and a free theme from Bootswatch. It doesn't
 do much yet. Basically, it can list threads matching a search and show you
 those threads. It reads the notmuch config file to find the database. It
 also reads the file for a new section named speedee searches, where you
 can define named searches to be included in the sidebar. The list of All
 Tags is also included in the sidebar, assuming the version of notmuch
 you're running has my ruby bindings patch from yesterday.
 
 At this point, my plan is to add a super basic mail composer and sending
 facility and then live with it for a few months. I've setup a demo using
 the notmuch mbox file as sample data. Feel free to poke around [1] and let
 me know what you think!
 
 Source: https://github.com/peterkeen/speedee
 Demo: https://speedee.bugsplat.info/#search/ruby
 
 
 [1]: The demo is using a self-signed wildcard cert. SHA1 fingerprint: 01 B6
 5D 47 EC 4A BB 27 E6 49 A1 0F 5A 48 8F BD CC 33 F2 F8
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Mark Walters

  notmuch-search.c | 453 
 +++
  1 file changed, 326 insertions(+), 127 deletions(-)

Hi for such a large patch this was surprisingly easy to follow (though I
am not saying I have worked through all the details). However, there are
some preliminary comments below which I think are worth fixing before a
full review as they will make it easier for other reviewers. One thing
that would help a lot is some comments.

Best wishes

Mark

 As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
 support for new structured output formats (like s-expressions) by using
 stateful structure_printers. An implementation of the JSON structure
 printer that passes all tests is included.

If I understand it correctly, the output should be identical to before?
If that is the case I think it's worth saying. 

 Structure printers have functions for starting a map, a list, closing
 any number of these two, printing a map key, a number value, a string
 value, and boolean values. By threading a state through the
 invocations, arbitrary structured formatting can be achieved.

I think I would also add something saying that the text format is just
different (and that a significant chunk of the patch is just that).

 In a second patch, the structured output code should be isolated in a
 separate file, and also used in all other parts of notmuch.
 ---

 diff --git a/notmuch-search.c b/notmuch-search.c
 index 3be296d..3413b79 100644
 --- a/notmuch-search.c
 +++ b/notmuch-search.c
 @@ -28,6 +28,181 @@ typedef enum {
  OUTPUT_TAGS
  } output_t;
  
 +typedef void * structure_printer_state_t;
 +
 +typedef struct structure_printer {
 +int (*map)(void *state);
 +int (*list)(void *state);
 +void (*pop)(void *state, int level);
 +void (*map_key)(void *state, const char *key);
 +void (*number)(void *state, int val);
 +void (*string)(void *state, const char *val);
 +void (*bool)(void *state, notmuch_bool_t val);
 +void *(*initial_state)(const struct structure_printer *sp, FILE *output);
 +} structure_printer_t;

I think this needs some comments on what these functions do. number,
string and boolean are relatively clear (but saying output a number
etc is worthwhile). But what map and list do is much less clear and
definitely deserves a comment. And it is definitely unclear what they
are meant to return.

I would also say something about the variable state: eg the variable
`state` can contain any state the structure_printer wishes to maintain.

 +
 +/* JSON structure printer */
 +
 +typedef struct json_list {
 +int type;
 +int first_already_seen;
 +struct json_list *rest;
 +} json_list_t;
 +
 +#define TYPE_JSON_MAP 1
 +#define TYPE_JSON_ARRAY 2
 +
 +typedef struct json_state {
 +FILE *output;
 +json_list_t *stack;
 +int level;
 +} json_state_t;
 +
 +int json_map(void *state);
 +int json_list(void *state);
 +void json_pop(void *state, int level);
 +void json_map_key(void *state, const char *key);
 +void json_number(void *state, int val);
 +void json_string(void *state, const char *val);
 +void json_bool(void *state, notmuch_bool_t val);
 +void *json_initial_state(const struct structure_printer *sp, FILE *output);
 +
 +int json_map(void *st) {
 +json_state_t *state = (json_state_t*)st;
 +FILE *output = state-output;
 +if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
 state-stack-first_already_seen) {
 + fputs(,, output);
 + if(state-level == 1)
 + fputs(\n, output);
 + else
 + fputs( , output);
 +}
 +if(state-stack != NULL) {
 + state-stack-first_already_seen = TRUE;
 +}
 +fputs({, output);
 +void *ctx_json_map = talloc_new (0);
 +json_list_t *el = talloc(ctx_json_map, json_list_t);
 +el-type = TYPE_JSON_MAP;
 +el-first_already_seen = FALSE;
 +el-rest = state-stack;
 +state-stack = el;
 +return state-level++;
 +}
 +
 +int json_list(void *st) {
 +json_state_t *state = (json_state_t*)st;
 +FILE *output = state-output;
 +if(state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
 state-stack-first_already_seen) {
 + fputs(,, output);
 + if(state-level == 1)
 + fputs(\n, output);
 + else
 + fputs( , output);
 +}
 +if(state-stack != NULL) {
 + state-stack-first_already_seen = TRUE;
 +}
 +fputs([, output);
 +void *ctx_json_map = talloc_new (0);
 +json_list_t *el = talloc(ctx_json_map, json_list_t);
 +el-type = TYPE_JSON_ARRAY;
 +el-first_already_seen = FALSE;
 +el-rest = state-stack;
 +state-stack = el;
 +return state-level++;
 +}
 +
 +void json_pop(void *st, int level) {
 +int i;
 +json_state_t *state = (json_state_t*)st;
 +FILE *output = state-output;
 +for(i = state-level; i  level; i--) {
 + json_list_t *tos = state-stack;
 + if(tos-type == TYPE_JSON_MAP) {
 + fputs(}, output);
 + } 
 + if(tos-type == 

Re: post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Jani Nikula
On Jul 10, 2012 12:59 PM, Sebastien Binet bi...@cern.ch wrote:

 Jani Nikula j...@nikula.org writes:

  On Jul 9, 2012 8:12 PM, Jameson Graef Rollins 
jroll...@finestructure.net
  wrote:
 
  On Mon, Jul 09 2012, Sebastien Binet bi...@cern.ch wrote:
   hum... is post-new supposed to be run even if there is no new
message ?
 
  Hi, Sebastian.  Yes, I think it runs regardless if there are any new
  messages or not.
 
  That's correct; only errors in notmuch new cause post-new hook to be
  skipped.

 ok. I thought using the post-new hook would have saved some i/o
 resources over my current setup:
  offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh

 where notmuch-tag.sh is (in pseudo-code):
 ##
 /usr/bin/notmuch new

 for tag,query in tag-queries:
   tag_new $tag $query

 ##

 is there any advantage of using post-new compared to this setup ?

There's no functional advantage. It does keep your initial tagging script
connected with notmuch new rather than offlinemap, if you ever need to run
notmuch new on its own.

If your tagging setup is really complicated, you could have some-tag in
new.tags config, and bail out early if notmuch count tag:some-tag outputs 0
(and obviously notmuch tag -some-tag tag:some-tag later in the script).
Just a thought.

J.


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


[PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread craven
As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
support for new structured output formats (like s-expressions) by using
stateful structure_printers. An implementation of the JSON structure
printer that passes all tests is included. The output for JSON (and
text) is identical to the current output. S-Expressions will be added in
a later patch.

A large part of this patch just implements the differentiation between
structured and non-structured output (all the code within 
if(format == unstructured_text_printer)).

In a second patch, the structured output code should be isolated in a
separate file, and also used in all other parts of notmuch.

The interface is a structure structure_printer, which contains the following 
methods:

- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
- number, string, bool: output one element of the specific type.

All functions should use the state object to insert delimiters etc. 
automatically when appropriate.

Example:
int top, one;
top = map(state);
map_key(state, foo);
one = list(state);
number(state, 1);
number(state, 2);
number(state, 3);
pop(state, i);
map_key(state, bar);
map(state);
map_key(state, baaz);
string(state, hello world);
pop(state, top);

would output JSON as follows:

{foo: [1, 2, 3], bar: { baaz: hello world}}
---
 notmuch-search.c | 491 ---
 1 file changed, 361 insertions(+), 130 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..412 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,6 +28,210 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
+/* structured formatting, useful for JSON, S-Expressions, ...
+
+- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
+- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
+- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
+- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
+- number, string, bool: output one element of the specific type.
+
+All functions should use state to insert delimiters etc. automatically when 
appropriate.
+
+Example:
+int top, one;
+top = map(state);
+map_key(state, foo);
+one = list(state);
+number(state, 1);
+number(state, 2);
+number(state, 3);
+pop(state, i);
+map_key(state, bar);
+map(state);
+map_key(state, baaz);
+string(state, hello world);
+pop(state, top);
+
+would output JSON as follows:
+
+{foo: [1, 2, 3], bar: { baaz: hello world}}
+
+ */
+typedef struct structure_printer {
+int (*map)(void *state);
+int (*list)(void *state);
+void (*pop)(void *state, int level);
+void (*map_key)(void *state, const char *key);
+void (*number)(void *state, int val);
+void (*string)(void *state, const char *val);
+void (*bool)(void *state, notmuch_bool_t val);
+void *(*initial_state)(const struct structure_printer *sp, FILE *output);
+} structure_printer_t;
+
+/* JSON structure printer */
+
+/* single linked list implementation for keeping track of the array/map 
nesting state */
+typedef struct json_list {
+int type;
+int first_already_seen;
+struct json_list *rest;
+} json_list_t;
+
+#define TYPE_JSON_MAP 1
+#define TYPE_JSON_ARRAY 2
+

Re: ANN: Speedee, a web-based notmuch client

2012-07-10 Thread Peter Keen
On Tue, Jul 10, 2012 at 5:08 AM, elf Pavlik
perpetual-trip...@wwelves.orgwrote:

 Hi,

 Sounds great!

 https://speedee.bugsplat.info/#search/ruby throws at me:

 NoMethodError at /
 undefined method `close' for nil:NilClass

 file: app.rb
 location: block in class:App
 line: 16

 Looking forward to play with you demo :)

 Thanks!
 ~ elf Pavlik ~



Interesting. Looks like something was leaking file handles. It appears to
be all better after a simple patch and a restart. Thanks!
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: post-new [was: Re: query on a subset of messages ?]

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Sebastien Binet bi...@cern.ch wrote:
 ok. I thought using the post-new hook would have saved some i/o
 resources over my current setup:
  offlineimap.postsynchook = ~/emacs/notmuch-lib/notmuch-tag.sh

 where notmuch-tag.sh is (in pseudo-code):
 ##
 /usr/bin/notmuch new

 for tag,query in tag-queries:
   tag_new $tag $query

 ##

 is there any advantage of using post-new compared to this setup ?

The hooks are just there for convenience, and as Jani says, they're
executed any time notmuch new is run, in any context.  In your setup it
looks like it would also save you having to maintain another script
(notmuch-tag.sh) since that script in it's entirety could just be the
post-new script, and the offlineimap.postsynchook could just execute
notmuch new directly.

jamie.


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


Tests

2012-07-10 Thread Mark Walters

I have been thinking a little bit about the current situation with
regards to tests. There are quite a lot of tests in the review queue
that have been there for quite some time without much interest, but I do
think we are rather short of tests. (*)

I wonder if we could have a sort of staging area for tests where they
could roughly go in without review and would only be run by something
like make stage-test or make all-tests or something.  The hope is that
this would encourage more tests.

The idea would be not that a patch author would have to make sure they
all pass, but if they previously passed and no longer do then the author
would know *something* about the output had changed.

One example is the emacs elide test by Pieter
id:1329684990-12504-3-git-send-email-pie...@praet.org which does still
pass after my substantial change to the way elide is done (and is not
currently covered in the test suite)

Of course if someone does review one of these staging tests then they
can be moved into the real tests.

Best wishes

Mark

(*) for example the structured output patch accidentally changed the
output of notmuch search --output=threads --format=json search but
that was not caught by the tests.





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


Re: [PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Mark Walters markwalters1...@gmail.com wrote:
 I think I would also add something saying that the text format is just
 different (and that a significant chunk of the patch is just that).

Can we not just dump this output format once and for all?  Does anything
use it?  And if so can we just modify it to use a more sensible format?

jamie.


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


Re: [PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Austin Clements
Quoth Jameson Graef Rollins on Jul 10 at 10:04 am:
 On Tue, Jul 10 2012, Mark Walters markwalters1...@gmail.com wrote:
  I think I would also add something saying that the text format is just
  different (and that a significant chunk of the patch is just that).
 
 Can we not just dump this output format once and for all?  Does anything
 use it?  And if so can we just modify it to use a more sensible format?

Currently Emacs uses this output format (though
id:1341870162-17782-1-git-send-email-amdra...@mit.edu aims to fix
this).  Also, I assume most scripted uses of notmuch use the text
format (probably not the --output=summary text format, but certainly
files, messages, and tags).
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread Mark Walters
On Tue, 10 Jul 2012, cra...@gmx.net wrote:
 As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
 support for new structured output formats (like s-expressions) by using
 stateful structure_printers. An implementation of the JSON structure
 printer that passes all tests is included. The output for JSON (and
 text) is identical to the current output. S-Expressions will be added in
 a later patch.


Hi I have some more comments inline below. I agree with Jamie that it
would be nice to split it into smaller chunks but I am not sure that is
practical to do.

 A large part of this patch just implements the differentiation between
 structured and non-structured output (all the code within 
 if(format == unstructured_text_printer)).

I also agree with Jamie that the text output is annoying. However, at
least until Austin's async json parser goes in even the emacs interface
is using it so I think we have to live with it for a while (at the very
least we would need to give clients a reasonable time to migrate).

Anyway, I think this patch has to support it.

 In a second patch, the structured output code should be isolated in a
 separate file, and also used in all other parts of notmuch.

 The interface is a structure structure_printer, which contains the following 
 methods:

 - initial_state: is called to create a state object, that is passed to all 
 invocations. This should be used to keep track of the output file and 
 everything else necessary to correctly format output.
 - map: is called when a new map (associative array, dictionary) is started. 
 map_key and the primitives (string, number, bool) are used alternatingly to 
 add key/value pairs. pop is used to close the map (see there). This function 
 must return a nesting level identifier that can be used to close all nested 
 structures (maps and lists), backing out to the returned nesting level.
 - list: is called when a new list (array, vector) is started. the primitives 
 (string, number, bool) are used consecutively to add values to the list. pop 
 is used to close the list. This function must return a nesting level 
 identifier that can be used to close all nested structures (maps and lists), 
 backing out to the returned nesting level.
 - pop: is called to return to a given nesting level. All lists and maps with 
 a deeper nesting level must be closed.
 - number, string, bool: output one element of the specific type.

 All functions should use the state object to insert delimiters etc. 
 automatically when appropriate.

 Example:
 int top, one;
 top = map(state);
 map_key(state, foo);
 one = list(state);
 number(state, 1);
 number(state, 2);
 number(state, 3);
 pop(state, i);
 map_key(state, bar);
 map(state);
 map_key(state, baaz);
 string(state, hello world);
 pop(state, top);

 would output JSON as follows:

 {foo: [1, 2, 3], bar: { baaz: hello world}}
 ---
  notmuch-search.c | 491 
 ---
  1 file changed, 361 insertions(+), 130 deletions(-)

 diff --git a/notmuch-search.c b/notmuch-search.c
 index 3be296d..412 100644
 --- a/notmuch-search.c
 +++ b/notmuch-search.c
 @@ -28,6 +28,210 @@ typedef enum {
  OUTPUT_TAGS
  } output_t;
  
 +/* structured formatting, useful for JSON, S-Expressions, ...
 +
 +- initial_state: is called to create a state object, that is passed to all 
 invocations. This should be used to keep track of the output file and 
 everything else necessary to correctly format output.
 +- map: is called when a new map (associative array, dictionary) is started. 
 map_key and the primitives (string, number, bool) are used alternatingly to 
 add key/value pairs. pop is used to close the map (see there). This function 
 must return a nesting level identifier that can be used to close all nested 
 structures (maps and lists), backing out to the returned nesting level.
 +- list: is called when a new list (array, vector) is started. the primitives 
 (string, number, bool) are used consecutively to add values to the list. pop 
 is used to close the list. This function must return a nesting level 
 identifier that can be used to close all nested structures (maps and lists), 
 backing out to the returned nesting level.
 +- pop: is called to return to a given nesting level. All lists and maps with 
 a deeper nesting level must be closed.
 +- number, string, bool: output one element of the specific type.
 +
 +All functions should use state to insert delimiters etc. automatically when 
 appropriate.
 +
 +Example:
 +int top, one;
 +top = map(state);
 +map_key(state, foo);
 +one = list(state);
 +number(state, 1);
 +number(state, 2);
 +number(state, 3);
 +pop(state, i);

I think `i` should be `one` or vice versa (and the same in the commit message)? 

 +map_key(state, bar);
 +map(state);
 +map_key(state, baaz);
 +string(state, hello world);
 +pop(state, top);
 +
 +would output JSON as follows:
 +
 +{foo: [1, 2, 3], bar: { baaz: hello world}}
 +
 + */
 +typedef struct 

Re: [PATCH] FIXED: Added better support for multiple structured output formats.

2012-07-10 Thread Jameson Graef Rollins
On Tue, Jul 10 2012, Austin Clements amdra...@mit.edu wrote:
 Quoth Jameson Graef Rollins on Jul 10 at 10:04 am:
 On Tue, Jul 10 2012, Mark Walters markwalters1...@gmail.com wrote:
  I think I would also add something saying that the text format is just
  different (and that a significant chunk of the patch is just that).
 
 Can we not just dump this output format once and for all?  Does anything
 use it?  And if so can we just modify it to use a more sensible format?

 Currently Emacs uses this output format (though
 id:1341870162-17782-1-git-send-email-amdra...@mit.edu aims to fix
 this).  Also, I assume most scripted uses of notmuch use the text
 format (probably not the --output=summary text format, but certainly
 files, messages, and tags).

Sorry, in a momentary brain freeze I confused the search and show text
output.  You're right that the text search output is more widely used.

jamie.


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


[PATCH v3 1/3] Add support for structured output formatters.

2012-07-10 Thread craven
This patch adds a new type structure_printer, which is used for structured 
formatting, e.g. JSON or S-Expressions.

The structure contains the following function pointers:

- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
- number, string, bool: output one element of the specific type.

All functions should use state to insert delimiters etc. automatically when 
appropriate.

Example:
int top, one;
top = map(state);
map_key(state, foo);
one = list(state);
number(state, 1);
number(state, 2);
number(state, 3);
pop(state, one);
map_key(state, bar);
map(state);
map_key(state, baaz);
string(state, hello world);
pop(state, top);

would output JSON as follows:

{foo: [1, 2, 3], bar: { baaz: hello world}}
---
 Makefile.local  |1 +
 structured-output.h |   64 +++
 2 files changed, 65 insertions(+)
 create mode 100644 structured-output.h

diff --git a/Makefile.local b/Makefile.local
index a890df2..9b3db5e 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -286,6 +286,7 @@ notmuch_client_srcs =   \
notmuch-reply.c \
notmuch-restore.c   \
notmuch-search.c\
+   structured-output.c \
notmuch-setup.c \
notmuch-show.c  \
notmuch-tag.c   \
diff --git a/structured-output.h b/structured-output.h
new file mode 100644
index 000..b43afe0
--- /dev/null
+++ b/structured-output.h
@@ -0,0 +1,64 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth cwo...@cworth.org
+ */
+
+#include notmuch-client.h
+
+/* structured formatting, useful for JSON, S-Expressions, ...
+
+- initial_state: is called to create a state object, that is passed to all 
invocations. This should be used to keep track of the output file and 
everything else necessary to correctly format output.
+- map: is called when a new map (associative array, dictionary) is started. 
map_key and the primitives (string, number, bool) are used alternatingly to add 
key/value pairs. pop is used to close the map (see there). This function must 
return a nesting level identifier that can be used to close all nested 
structures (maps and lists), backing out to the returned nesting level.
+- list: is called when a new list (array, vector) is started. the primitives 
(string, number, bool) are used consecutively to add values to the list. pop is 
used to close the list. This function must return a nesting level identifier 
that can be used to close all nested structures (maps and lists), backing out 
to the returned nesting level.
+- pop: is called to return to a given nesting level. All lists and maps with a 
deeper nesting level must be closed.
+- number, string, bool: output one element of the specific type.
+
+All functions should use state to insert delimiters etc. automatically when 
appropriate.
+
+Example:
+int top, one;
+top = map(state);
+map_key(state, foo);
+one = list(state);
+number(state, 1);
+number(state, 2);
+number(state, 3);
+pop(state, one);
+map_key(state, bar);
+map(state);
+map_key(state, baaz);
+string(state, hello world);
+pop(state, top);
+
+would output JSON as follows:
+
+{foo: [1, 2, 3], bar: { baaz: hello world}}
+
+ */
+typedef struct structure_printer {
+int (*map)(void 

[PATCH v3 2/3] Adding structured output formatter for JSON.

2012-07-10 Thread craven
This formatter prints exactly the same structure as the existing JSON 
formatter, but uses the newly introduced structured formatting primitives.
---
 structured-output.c |  159 +++
 structured-output.h |   48 
 2 files changed, 207 insertions(+)
 create mode 100644 structured-output.c

diff --git a/structured-output.c b/structured-output.c
new file mode 100644
index 000..e10fba4
--- /dev/null
+++ b/structured-output.c
@@ -0,0 +1,159 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth cwo...@cworth.org
+ */
+
+#include structured-output.h
+
+structure_printer_t *
+unstructured_text_printer = NULL;
+
+structure_printer_t 
+json_structure_printer = {
+json_map,
+json_list,
+json_pop,
+json_map_key,
+json_number,
+json_string,
+json_bool,
+json_initial_state
+};
+static int
+enter_level(void *st, const char *marker, int type) {
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if (state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if (state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+if (state-stack != NULL) {
+   state-stack-first_already_seen = TRUE;
+}
+fputs(marker, output);
+void *ctx_json_map = talloc_new (0);
+json_list_t *el = talloc(ctx_json_map, json_list_t);
+el-type = type;
+el-first_already_seen = FALSE;
+el-rest = state-stack;
+state-stack = el;
+return state-level++;
+}
+
+int
+json_map(void *st)
+{
+return enter_level(st, {, TYPE_JSON_MAP);
+}
+
+int
+json_list(void *st)
+{
+return enter_level(st, [, TYPE_JSON_ARRAY);
+}
+
+void
+json_pop(void *st, int level) 
+{
+int i;
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+for (i = state-level; i  level; i--) {
+   json_list_t *tos = state-stack;
+   if (tos-type == TYPE_JSON_MAP) {
+   fputs(}, output);
+   }
+   if (tos-type == TYPE_JSON_ARRAY) {
+   fputs(], output);
+   }
+   state-stack = tos-rest;
+   state-level--;
+   talloc_free(tos);
+}
+if (state-level == 0)
+   fputs(\n, output);
+}
+
+void
+json_map_key(void *st, const char *key)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if (state-stack != NULL  state-stack-first_already_seen) {
+   fputs(,\n, output);
+}
+fputs(\, output);
+fputs(key, output);
+fputs(\: , output);
+}
+
+void
+json_number(void *st, int val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if (state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(, , output);
+}
+state-stack-first_already_seen = TRUE;
+fprintf(output, %i, val);
+}
+
+void
+json_string(void *st, const char *val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+void *ctx = talloc_new(0);
+if (state-stack != NULL  state-stack-type == TYPE_JSON_ARRAY  
state-stack-first_already_seen) {
+   fputs(,, output);
+   if (state-level == 1)
+   fputs(\n, output);
+   else
+   fputs( , output);
+}
+
+state-stack-first_already_seen = TRUE;
+fprintf(output, %s, json_quote_str(ctx, val));
+talloc_free(ctx);
+}
+
+void
+json_bool(void *st, notmuch_bool_t val)
+{
+json_state_t *state = (json_state_t*)st;
+FILE *output = state-output;
+if (val)
+   fputs(true, output);
+else
+   fputs(false, output);
+}
+
+void *
+json_initial_state(const struct structure_printer *sp, FILE *output)
+{
+(void)sp;
+json_state_t *st = talloc(0, json_state_t);
+st-level = 0;
+st-stack = NULL;
+st-output = output;
+return st;
+}
diff --git a/structured-output.h b/structured-output.h
index b43afe0..cba3882 100644
--- a/structured-output.h
+++ b/structured-output.h
@@ -62,3 +62,51 @@ typedef struct structure_printer {
 } structure_printer_t;
 
 
+/* JSON structure printer */
+
+/* single linked list implementation for keeping track of the array/map 
nesting 

[PATCH v3 3/3] Use the structured format printer in notmuch search.

2012-07-10 Thread craven
This patch uses the previously introduced structured format printer in 
notmuch-search.
Most of this patch is necessary to keep the text output unchanged. All tests 
pass, both text and JSON are printed exactly the same as they were before.
---
 notmuch-search.c |  275 +-
 1 file changed, 148 insertions(+), 127 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..321b32a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -19,6 +19,7 @@
  */
 
 #include notmuch-client.h
+#include structured-output.h
 
 typedef enum {
 OUTPUT_SUMMARY,
@@ -28,6 +29,7 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
+/* legacy, only needed for non-structured text output */
 typedef struct search_format {
 const char *results_start;
 const char *item_start;
@@ -64,6 +66,7 @@ format_thread_text (const void *ctx,
const int total,
const char *authors,
const char *subject);
+
 static const search_format_t format_text = {
 ,
,
@@ -78,35 +81,6 @@ static const search_format_t format_text = {
 };
 
 static void
-format_item_id_json (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_json (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-
-/* Any changes to the JSON format should be reflected in the file
- * devel/schemata. */
-static const search_format_t format_json = {
-[,
-   {,
-   format_item_id_json,
-   format_thread_json,
-   \tags\: [,
-   \%s\, , ,
-   ], ,\n,
-   },
-]\n,
-]\n,
-};
-
-static void
 format_item_id_text (unused (const void *ctx),
 const char *item_type,
 const char *item_id)
@@ -153,50 +127,9 @@ format_thread_text (const void *ctx,
 talloc_free (ctx_quote);
 }
 
-static void
-format_item_id_json (const void *ctx,
-unused (const char *item_type),
-const char *item_id)
-{
-void *ctx_quote = talloc_new (ctx);
-
-printf (%s, json_quote_str (ctx_quote, item_id));
-
-talloc_free (ctx_quote);
-
-}
-
-static void
-format_thread_json (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject)
-{
-void *ctx_quote = talloc_new (ctx);
-
-printf (\thread\: %s,\n
-   \timestamp\: %ld,\n
-   \date_relative\: \%s\,\n
-   \matched\: %d,\n
-   \total\: %d,\n
-   \authors\: %s,\n
-   \subject\: %s,\n,
-   json_quote_str (ctx_quote, thread_id),
-   date,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   json_quote_str (ctx_quote, authors),
-   json_quote_str (ctx_quote, subject));
-
-talloc_free (ctx_quote);
-}
-
 static int
-do_search_threads (const search_format_t *format,
+do_search_threads (const structure_printer_t *format,
+  void *state,
   notmuch_query_t *query,
   notmuch_sort_t sort,
   output_t output,
@@ -209,6 +142,8 @@ do_search_threads (const search_format_t *format,
 time_t date;
 int first_thread = 1;
 int i;
+int outermost_level = 0;
+int items_level = 0;
 
 if (offset  0) {
offset += notmuch_query_count_threads (query);
@@ -220,7 +155,11 @@ do_search_threads (const search_format_t *format,
 if (threads == NULL)
return 1;
 
-fputs (format-results_start, stdout);
+if (format == unstructured_text_printer) {
+   fputs(format_text.results_start, stdout);
+} else { /* structured output */
+   outermost_level = format-list(state);
+}
 
 for (i = 0;
 notmuch_threads_valid (threads)  (limit  0 || i  offset + limit);
@@ -235,43 +174,85 @@ do_search_threads (const search_format_t *format,
continue;
}
 
-   if (! first_thread)
-   fputs (format-item_sep, stdout);
+   if (format == unstructured_text_printer  ! first_thread)
+   fputs (format_text.item_sep, stdout);
 
if (output == OUTPUT_THREADS) {
-   format-item_id (thread, thread:,
-notmuch_thread_get_thread_id (thread));
+   if (format == unstructured_text_printer) {
+   format_text.item_id (thread, thread:,
+notmuch_thread_get_thread_id (thread));
+   } else { /* structured output */
+   format-string(state, 

Re: [PATCH] v2: Added better support for multiple structured output formats.

2012-07-10 Thread Austin Clements
Since it would be great to use the structure printer for show as well,
it would make sense to put it in its own source file, where it can
easily be shared between commands.

There are a few systematic code formatting problems in your patch.  To
be consistent with other notmuch code, there should be a space between
function names and parameter lists (in both declarations and calls)
and there should be a space between a keyword and a paren (e.g., if
( and for ().  In a function definition, the function name should
start on a new line (this makes it easy to grep for a function's
definition).  Also, in general, try to keep lines under 80 characters
wide (we're not very consistent about this one, but it would be nice
to not become even less consistent about it).

More detailed comments below.

Quoth cra...@gmx.net on Jul 10 at  3:30 pm:
 As discussed in id:20120121220407.gk16...@mit.edu, this patch adds
 support for new structured output formats (like s-expressions) by using
 stateful structure_printers. An implementation of the JSON structure
 printer that passes all tests is included. The output for JSON (and
 text) is identical to the current output. S-Expressions will be added in
 a later patch.
 
 A large part of this patch just implements the differentiation between
 structured and non-structured output (all the code within 
 if(format == unstructured_text_printer)).
 
 In a second patch, the structured output code should be isolated in a
 separate file, and also used in all other parts of notmuch.
 
 The interface is a structure structure_printer, which contains the following 
 methods:
 
 - initial_state: is called to create a state object, that is passed to all 
 invocations. This should be used to keep track of the output file and 
 everything else necessary to correctly format output.
 - map: is called when a new map (associative array, dictionary) is started. 
 map_key and the primitives (string, number, bool) are used alternatingly to 
 add key/value pairs. pop is used to close the map (see there). This function 
 must return a nesting level identifier that can be used to close all nested 
 structures (maps and lists), backing out to the returned nesting level.
 - list: is called when a new list (array, vector) is started. the primitives 
 (string, number, bool) are used consecutively to add values to the list. pop 
 is used to close the list. This function must return a nesting level 
 identifier that can be used to close all nested structures (maps and lists), 
 backing out to the returned nesting level.
 - pop: is called to return to a given nesting level. All lists and maps with 
 a deeper nesting level must be closed.
 - number, string, bool: output one element of the specific type.
 
 All functions should use the state object to insert delimiters etc. 
 automatically when appropriate.
 
 Example:
 int top, one;
 top = map(state);
 map_key(state, foo);
 one = list(state);
 number(state, 1);
 number(state, 2);
 number(state, 3);
 pop(state, i);
 map_key(state, bar);
 map(state);
 map_key(state, baaz);
 string(state, hello world);
 pop(state, top);
 
 would output JSON as follows:
 
 {foo: [1, 2, 3], bar: { baaz: hello world}}
 ---
  notmuch-search.c | 491 
 ---
  1 file changed, 361 insertions(+), 130 deletions(-)
 
 diff --git a/notmuch-search.c b/notmuch-search.c
 index 3be296d..412 100644
 --- a/notmuch-search.c
 +++ b/notmuch-search.c
 @@ -28,6 +28,210 @@ typedef enum {
  OUTPUT_TAGS
  } output_t;
  
 +/* structured formatting, useful for JSON, S-Expressions, ...
 +
 +- initial_state: is called to create a state object, that is passed to all 
 invocations. This should be used to keep track of the output file and 
 everything else necessary to correctly format output.
 +- map: is called when a new map (associative array, dictionary) is started. 
 map_key and the primitives (string, number, bool) are used alternatingly to 
 add key/value pairs. pop is used to close the map (see there). This function 
 must return a nesting level identifier that can be used to close all nested 
 structures (maps and lists), backing out to the returned nesting level.
 +- list: is called when a new list (array, vector) is started. the primitives 
 (string, number, bool) are used consecutively to add values to the list. pop 
 is used to close the list. This function must return a nesting level 
 identifier that can be used to close all nested structures (maps and lists), 
 backing out to the returned nesting level.
 +- pop: is called to return to a given nesting level. All lists and maps with 
 a deeper nesting level must be closed.
 +- number, string, bool: output one element of the specific type.
 +
 +All functions should use state to insert delimiters etc. automatically when 
 appropriate.
 +
 +Example:
 +int top, one;
 +top = map(state);
 +map_key(state, foo);
 +one = list(state);
 +number(state, 1);
 +number(state, 2);
 +number(state, 3);
 

[PATCH] contib/nmbug/nmbug-status: leftover whitespaces, indentation quoting

2012-07-10 Thread Tomi Ollila
The initial nmbug-status was pretty consistent in it's whitespacing
but a few lines had some leftover slips. Those are now corrected.

Also, most of the code used ' as quoting char. As in Python one can
use ' and  interchangeably some code used  instead of '. However
the usage of those were inconsistent. Now all quotes that python
parses are ':s (only quoted content uses :s).

No functional changes.
---

I tested that this produces identical output compared to the unpatched version.

 contrib/nmbug/nmbug-status |   41 -
 1 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/contrib/nmbug/nmbug-status b/contrib/nmbug/nmbug-status
index 6aa86a0..2abb3e4 100755
--- a/contrib/nmbug/nmbug-status
+++ b/contrib/nmbug/nmbug-status
@@ -18,10 +18,10 @@ import subprocess
 # parse command line arguments
 
 parser = argparse.ArgumentParser()
-parser.add_argument(--text, help=output plain text format,
-action=store_true)
+parser.add_argument('--text', help='output plain text format',
+action='store_true')
 
-parser.add_argument(--config, help=load config from given file)
+parser.add_argument('--config', help='load config from given file')
 
 
 args = parser.parse_args()
@@ -31,18 +31,18 @@ args = parser.parse_args()
 if args.config != None:
 fp = open(args.config)
 else:
-nmbhome = os.getenv('NMBGIT', os.path.expanduser(~/.nmbug))
+nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
 
 # read only the first line from the pipe
-sha1 =  subprocess.Popen(['git', '--git-dir', nmbhome,
- 'show-ref', '-s', 'config'],
-  stdout=subprocess.PIPE).stdout.readline()
+sha1 = subprocess.Popen(['git', '--git-dir', nmbhome,
+ 'show-ref', '-s', 'config'],
+stdout=subprocess.PIPE).stdout.readline()
 
 sha1 = sha1.rstrip()
 
-fp =  subprocess.Popen(['git', '--git-dir', nmbhome,
-'cat-file', 'blob', sha1+':status-config.json'],
-   stdout=subprocess.PIPE).stdout
+fp = subprocess.Popen(['git', '--git-dir', nmbhome,
+   'cat-file', 'blob', sha1+':status-config.json'],
+  stdout=subprocess.PIPE).stdout
 
 config = json.load(fp)
 
@@ -60,11 +60,10 @@ def clear_last():
 
 def print_view(title, query, comment):
 
-query_string =  and .join(query)
+query_string = ' and '.join(query)
 q_new = notmuch.Query(db, query_string)
 q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
 
-
 last['thread_id'] = ''
 
 if output_format == 'html':
@@ -94,7 +93,7 @@ def print_view(title, query, comment):
 val = rfc822.parseaddr(val)[0]
 
 if last[header] == val:
-out[header] = 
+out[header] = ''
 else:
 out[header] = val.encode('utf-8')
 last[header] = val
@@ -109,14 +108,14 @@ def print_view(title, query, comment):
 else:
 br = ''
 out['subject'] = 'a href=http://mid.gmane.org/%s;%s/a' \
-% (urllib.quote(mid), out['subject'])
-
-print  trtd%s %s % (br, out['date'])
-print /tdtd%s %s % (br, out['id'])
-print /td/tr
-print  trtd%s % out['from']
-print /tdtd%s % out['subject']
-print /td/tr\n
+% (urllib.quote(mid), out['subject'])
+
+print ' trtd%s %s' % (br, out['date'])
+print '/tdtd%s %s' % (br, out['id'])
+print '/td/tr'
+print ' trtd%s' % out['from']
+print '/tdtd%s' % out['subject']
+print '/td/tr\n'
 else:
 print '%(date)-10.10s %(from)-20.20s 
%(subject)-40.40s\n%(id)72s\n' % out
 
-- 
1.7.1

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


deprecating legacy text output

2012-07-10 Thread David Bremner
On Tue, 10 Jul 2012 10:04:10 -0700, Jameson Graef Rollins 
jroll...@finestructure.net wrote:
 On Tue, Jul 10 2012, Mark Walters markwalters1...@gmail.com wrote:
  I think I would also add something saying that the text format is just
  different (and that a significant chunk of the patch is just that).
 
 Can we not just dump this output format once and for all?  Does anything
 use it?  And if so can we just modify it to use a more sensible format?

I think we need to at least deprecate the format for a release or so, as
there are definitely people using it in the wild.  As far as I know the
at least the Vim interface uses it (what about notmuch-mutt?), in
addition to whatever homebrewed scripts might use it. Since
scriptability is one of the notmuch core values, I guess we should
also provide something more script friendly than json. As you say, it
doesn't have to be exactly like what we have now.

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


notmuch-emacs and bbdb

2012-07-10 Thread Daniel Bergey
As far as I can tell, notmuch doesn't integrate as smoothly with bbdb as older
emacs mailclients.  I'm especially looking for a snarf function that
distinguishes sender from recipient.

How do other people use bbdb with notmuch?

Does anyone have lisp code like that which ships with bbdb for other
clients?

If I were to find time to write such code, what would you like it to do?

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