[PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread Tomi Ollila
On Fri, Jul 20 2012, craven at gmx.net wrote:

> From: 
>
> This patch switches from the current ad-hoc printer to the structured
> formatters in sprinter.h, sprinter-text.c and sprinter-json.c.
>
> The JSON tests are changed slightly in order to make them PASS for the
> new structured output formatter.
>
> The text tests pass without adaptation.
> ---
>  notmuch-search.c | 301 
> ---
>  test/json|  34 ---
>  2 files changed, 103 insertions(+), 232 deletions(-)
>
> diff --git a/notmuch-search.c b/notmuch-search.c
> index 3be296d..07211e8 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -19,6 +19,7 @@
>   */
>  
>  #include "notmuch-client.h"
> +#include "sprinter.h"
>  
>  typedef enum {
>  OUTPUT_SUMMARY,
> @@ -28,92 +29,6 @@ typedef enum {
>  OUTPUT_TAGS
>  } output_t;
>  
> -typedef struct search_format {
> -const char *results_start;
> -const char *item_start;
> -void (*item_id) (const void *ctx,
> -  const char *item_type,
> -  const char *item_id);
> -void (*thread_summary) (const void *ctx,
> - const char *thread_id,
> - const time_t date,
> - const int matched,
> - const int total,
> - const char *authors,
> - const char *subject);
> -const char *tag_start;
> -const char *tag;
> -const char *tag_sep;
> -const char *tag_end;
> -const char *item_sep;
> -const char *item_end;
> -const char *results_end;
> -const char *results_null;
> -} search_format_t;
> -
> -static void
> -format_item_id_text (const void *ctx,
> -  const char *item_type,
> -  const char *item_id);
> -
> -static void
> -format_thread_text (const void *ctx,
> - const char *thread_id,
> - const time_t date,
> - const int matched,
> - const int total,
> - const char *authors,
> - const char *subject);
> -static const search_format_t format_text = {
> -"",
> - "",
> - format_item_id_text,
> - format_thread_text,
> - " (",
> - "%s", " ",
> - ")", "\n",
> - "",
> -"\n",
> -"",
> -};
> -
> -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)
> -{
> -printf ("%s%s", item_type, item_id);
> -}
> -
>  static char *
>  sanitize_string (const void *ctx, const char *str)
>  {
> @@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
>  return out;
>  }
>  
> -static void
> -format_thread_text (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 %12s [%d/%d] %s; %s",
> - thread_id,
> - notmuch_time_relative_date (ctx, date),
> - matched,
> - total,
> - sanitize_string (ctx_quote, authors),
> - sanitize_string (ctx_quote, subject));
> -
> -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"
> - 

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

2012-07-20 Thread Tomi Ollila
On Fri, Jul 20 2012, craven at gmx.net wrote:

> From: 
>
> This patch adds a new struct type sprinter_t, which is used for
> structured formatting, e.g. JSON or S-Expressions. The structure printer
> is heavily based on code from Austin Clements
> (id:87d34hsdx8.fsf at awakening.csail.mit.edu).
>
> It includes the following functions:
>
> /* Start a new map/dictionary structure. This should be followed by
>  * a sequence of alternating calls to map_key and one of the
>  * value-printing functions until the map is ended by end.
>  */
> void (*begin_map) (struct sprinter *);
>
> /* Start a new list/array structure.
>  */
> void (*begin_list) (struct sprinter *);
>
> /* End the last opened list or map structure.
>  */
> void (*end) (struct sprinter *);
>
> /* Print one string/integer/boolean/null element (possibly inside a
>  * list or map, followed or preceded by separators).
>  * For string, the char * must be UTF-8 encoded.
>  */
> void (*string) (struct sprinter *, const char *);
> void (*integer) (struct sprinter *, int);
> void (*boolean) (struct sprinter *, notmuch_bool_t);
> void (*null) (struct sprinter *);
>
> /* Print the key of a map's key/value pair. The char * must be UTF-8
>  * encoded.
>  */
> void (*map_key) (struct sprinter *, const char *);
>
> /* Insert a separator (usually extra whitespace) for improved
>  * readability without affecting the abstract syntax of the
>  * structure being printed.
>  * For JSON, this could simply be a line break.
>  */
> void (*separator) (struct sprinter *);
>
> /* Set the current string prefix. This only affects the text
>  * printer, which will print this string, followed by a colon,
>  * before any string. For other printers, this does nothing.
>  */
> void (*set_prefix) (struct sprinter *, const char *);

The above block duplicated below. Otherwise this LGTM.

I presume the patch 3/3 emails

id:"1342766173-1344-4-git-send-email-craven at gmx.net"
id:"1342772624-23329-1-git-send-email-craven at gmx.net"

have identical content ?

> To support the plain text format properly, the following additional
> function must also be implemented:
>
> /* Set the current string prefix. This only affects the text
>  * printer, which will print this string, followed by a colon,
>  * before any string. For other printers, this does nothing.
>  */
> void (*set_prefix) (struct sprinter *, const char *);
>
> The structure also contains a flag that should be set to FALSE in all
> custom printers and to TRUE in the plain text formatter.
>
> /* True if this is the special-cased plain text printer.
>  */
> notmuch_bool_t is_text_printer;
>
> The printer can (and should) use internal state to insert delimiters
> and syntax at the correct places.
>
> Example:
>
> format->begin_map(format);
> format->map_key(format, "foo");
> format->begin_list(format);
> format->integer(format, 1);
> format->integer(format, 2);
> format->integer(format, 3);
> format->end(format);
> format->map_key(format, "bar");
> format->begin_map(format);
> format->map_key(format, "baaz");
> format->string(format, "hello world");
> format->end(format);
> format->end(format);
>
> would output JSON as follows:
>
> {"foo": [1, 2, 3], "bar": { "baaz": "hello world"}}
> ---
>  sprinter.h | 58 ++
>  1 file changed, 58 insertions(+)
>  create mode 100644 sprinter.h
>
> diff --git a/sprinter.h b/sprinter.h
> new file mode 100644
> index 000..77dc26f
> --- /dev/null
> +++ b/sprinter.h
> @@ -0,0 +1,58 @@
> +#ifndef NOTMUCH_SPRINTER_H
> +#define NOTMUCH_SPRINTER_H
> +
> +/* Necessary for notmuch_bool_t */
> +#include "notmuch-client.h"
> +
> +/* Structure printer interface. This is used to create output
> + * structured as maps (with key/value pairs), lists and primitives
> + * (strings, integers and booleans).
> + */
> +typedef struct sprinter {
> +/* Start a new map/dictionary structure. This should be followed by
> + * a sequence of alternating calls to map_key and one of the
> + * value-printing functions until the map is ended by end.
> + */
> +void (*begin_map) (struct sprinter *);
> +
> +/* Start a new list/array structure.
> + */
> +void (*begin_list) (struct sprinter *);
> +
> +/* End the last opened list or map structure.
> + */
> +void (*end) (struct sprinter *);
> +
> +/* Print one string/integer/boolean/null element (possibly inside a
> + * list or map, followed or preceded by separators).
> + * For string, the char * must be UTF-8 encoded.
> + */
> +void (*string) (struct sprinter *, const char *);
> +void (*integer) (struct sprinter *, int);
> +void (*boolean) (struct sprinter *, notmuch_bool_t);
> +void (*null) (struct sprinter *);
> +
> +/* Print the key of a map's key/value pair. The char * must be 

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

2012-07-20 Thread cra...@gmx.net
> I presume the patch 3/3 emails
> 
> id:"1342766173-1344-4-git-send-email-craven at gmx.net"
> id:"1342772624-23329-1-git-send-email-craven at gmx.net"
> 
> have identical content ?

Yes, they do, there was a problem with the Content-Type header having
^P^P, so I re-sent correctly, but the actual e-mail content is exactly
the same.

Greetings,
Peter


bug in parsing?

2012-07-20 Thread Austin Clements
On Fri, 15 Jun 2012, David Belohrad  wrote:
> Dear all,
>
> recently I got an email from matplotlib list, which fails to parse in
> notmuch-saved-search-unread.
> It looks like this:
>
> Yest. 21:04 [3/5]   Jesse Rosenthal, Tomi Ollila  [PATCHv2] emacs: derive 
> correct timestamp in FCC unique name (inbox notmuch)
>  Yest. 20:58 [1/1]   Allin Cottrell   mouse and update 23.4 -> 24.1 
> (emacs inbox)
>  Yest. 20:56 [3/6]   Milan Kratochv?l, Jaroslav Lukesh, David Obdrzalek, 
> Pavel Kutina  [OT:] Javascript alert jinak (hw inbox)
> Error: Unexpected output from notmuch search:
> thread:6a68  Yest. 20:39 [1/1] R. OGara; [Matplotlib-users] 
> (no subject) (matplotlib mightread unread)
>
>  Yest. 19:38 [1/1]   Baaba Maal - Avaaz. org  Stop the African hunger games 
> (inbox unread)
>  Yest. 19:27 [1/2]   Bob Proulx, sariatucla at Safe-mail.net   next-line 
> at window end without recenter (deleted emacs inbox unread)
>  Yest. 19:12 [1/12]  Ji?? Ku?era, hutta.j at seznam.cz, balu at home, Pavel
>  Hudecek  LCR meter - konstrukce (deleted hw inbox unread)
>
>
>
> It seems that the weird characted in sender does it. Should those
> characters be somehow escaped?

This should be fixed in master, as of 9c5ea07c.


[PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread cra...@gmx.net
From: 

This patch switches from the current ad-hoc printer to the structured
formatters in sprinter.h, sprinter-text.c and sprinter-json.c.

The JSON tests are changed slightly in order to make them PASS for the
new structured output formatter.

The text tests pass without adaptation.
---
 notmuch-search.c | 301 ---
 test/json|  34 ---
 2 files changed, 103 insertions(+), 232 deletions(-)

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

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

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

-typedef struct search_format {
-const char *results_start;
-const char *item_start;
-void (*item_id) (const void *ctx,
-const char *item_type,
-const char *item_id);
-void (*thread_summary) (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-const char *tag_start;
-const char *tag;
-const char *tag_sep;
-const char *tag_end;
-const char *item_sep;
-const char *item_end;
-const char *results_end;
-const char *results_null;
-} search_format_t;
-
-static void
-format_item_id_text (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_text (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-static const search_format_t format_text = {
-"",
-   "",
-   format_item_id_text,
-   format_thread_text,
-   " (",
-   "%s", " ",
-   ")", "\n",
-   "",
-"\n",
-"",
-};
-
-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)
-{
-printf ("%s%s", item_type, item_id);
-}
-
 static char *
 sanitize_string (const void *ctx, const char *str)
 {
@@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
 return out;
 }

-static void
-format_thread_text (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 %12s [%d/%d] %s; %s",
-   thread_id,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   sanitize_string (ctx_quote, authors),
-   sanitize_string (ctx_quote, subject));
-
-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,
-   

[PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread cra...@gmx.net
From: 

This patch switches from the current ad-hoc printer to the structured
formatters in sprinter.h, sprinter-text.c and sprinter-json.c.

The JSON tests are changed slightly in order to make them PASS for the
new structured output formatter.

The text tests pass without adaptation.
---
 notmuch-search.c | 301 ---
 test/json|  34 ---
 2 files changed, 103 insertions(+), 232 deletions(-)

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

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

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

-typedef struct search_format {
-const char *results_start;
-const char *item_start;
-void (*item_id) (const void *ctx,
-const char *item_type,
-const char *item_id);
-void (*thread_summary) (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-const char *tag_start;
-const char *tag;
-const char *tag_sep;
-const char *tag_end;
-const char *item_sep;
-const char *item_end;
-const char *results_end;
-const char *results_null;
-} search_format_t;
-
-static void
-format_item_id_text (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_text (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-static const search_format_t format_text = {
-"",
-   "",
-   format_item_id_text,
-   format_thread_text,
-   " (",
-   "%s", " ",
-   ")", "\n",
-   "",
-"\n",
-"",
-};
-
-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)
-{
-printf ("%s%s", item_type, item_id);
-}
-
 static char *
 sanitize_string (const void *ctx, const char *str)
 {
@@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
 return out;
 }

-static void
-format_thread_text (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 %12s [%d/%d] %s; %s",
-   thread_id,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   sanitize_string (ctx_quote, authors),
-   sanitize_string (ctx_quote, subject));
-
-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,
-   

[PATCH v7 2/3] Add structured output formatter for JSON and plain text (but don't use them yet).

2012-07-20 Thread cra...@gmx.net
From: 

Using the new structured printer support in sprinter.h, implement
sprinter_json_create, which returns a new JSON structured output
formatter. The formatter prints output similar to the existing JSON, but
with differences in whitespace (mostly newlines, --output=summary prints
the entire message summary on one line, not split across multiple lines).

Also implement a "structured" formatter for plain text that prints
prefixed strings, to be used with notmuch-search.c plain text output.
---
 Makefile.local  |   2 +
 sprinter-json.c | 185 
 sprinter-text.c | 126 ++
 sprinter.h  |  10 +++
 4 files changed, 323 insertions(+)
 create mode 100644 sprinter-json.c
 create mode 100644 sprinter-text.c

diff --git a/Makefile.local b/Makefile.local
index a890df2..296995d 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -290,6 +290,8 @@ notmuch_client_srcs =   \
notmuch-show.c  \
notmuch-tag.c   \
notmuch-time.c  \
+   sprinter-json.c \
+   sprinter-text.c \
query-string.c  \
mime-node.c \
crypto.c\
diff --git a/sprinter-json.c b/sprinter-json.c
new file mode 100644
index 000..32daa5a
--- /dev/null
+++ b/sprinter-json.c
@@ -0,0 +1,185 @@
+#include 
+#include 
+#include 
+#include "sprinter.h"
+
+struct sprinter_json {
+struct sprinter vtable;
+FILE *stream;
+/* Top of the state stack, or NULL if the printer is not currently
+ * inside any aggregate types. */
+struct json_state *state;
+
+/* A flag to signify that a separator should be inserted in the
+ * output as soon as possible.
+ */
+notmuch_bool_t insert_separator;
+};
+
+struct json_state {
+struct json_state *parent;
+/* True if nothing has been printed in this aggregate yet.
+ * Suppresses the comma before a value. */
+notmuch_bool_t first;
+/* The character that closes the current aggregate. */
+char close;
+};
+
+/* Helper function to set up the stream to print a value.  If this
+ * value follows another value, prints a comma. */
+static struct sprinter_json *
+json_begin_value (struct sprinter *sp)
+{
+struct sprinter_json *spj = (struct sprinter_json *) sp;
+
+if (spj->state) {
+   if (! spj->state->first) {
+   fputc (',', spj->stream);
+   if (spj->insert_separator) {
+   fputc ('\n', spj->stream);
+   spj->insert_separator = FALSE;
+   } else
+   fputc (' ', spj->stream);
+   } else
+   spj->state->first = FALSE;
+}
+return spj;
+}
+
+/* Helper function to begin an aggregate type.  Prints the open
+ * character and pushes a new state frame. */
+static void
+json_begin_aggregate (struct sprinter *sp, char open, char close)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+struct json_state *state = talloc (spj, struct json_state);
+
+fputc (open, spj->stream);
+state->parent = spj->state;
+state->first = TRUE;
+state->close = close;
+spj->state = state;
+}
+
+static void
+json_begin_map (struct sprinter *sp)
+{
+json_begin_aggregate (sp, '{', '}');
+}
+
+static void
+json_begin_list (struct sprinter *sp)
+{
+json_begin_aggregate (sp, '[', ']');
+}
+
+static void
+json_end (struct sprinter *sp)
+{
+struct sprinter_json *spj = (struct sprinter_json *) sp;
+struct json_state *state = spj->state;
+
+fputc (spj->state->close, spj->stream);
+spj->state = state->parent;
+talloc_free (state);
+if (spj->state == NULL)
+   fputc ('\n', spj->stream);
+}
+
+static void
+json_string (struct sprinter *sp, const char *val)
+{
+static const char *const escapes[] = {
+   ['\"'] = "\\\"", ['\\'] = "", ['\b'] = "\\b",
+   ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"
+};
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputc ('"', spj->stream);
+for (; *val; ++val) {
+   unsigned char ch = *val;
+   if (ch < ARRAY_SIZE (escapes) && escapes[ch])
+   fputs (escapes[ch], spj->stream);
+   else if (ch >= 32)
+   fputc (ch, spj->stream);
+   else
+   fprintf (spj->stream, "\\u%04x", ch);
+}
+fputc ('"', spj->stream);
+}
+
+static void
+json_integer (struct sprinter *sp, int val)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fprintf (spj->stream, "%d", val);
+}
+
+static void
+json_boolean (struct sprinter *sp, notmuch_bool_t val)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputs (val ? "true" : "false", spj->stream);
+}
+
+static void
+json_null (struct sprinter *sp)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputs ("null", spj->stream);
+}
+
+static void
+json_map_key (struct sprinter *sp, const char *key)
+{
+struct sprinter_json *spj = 

notmuch-reply: Structured Formatters

2012-07-20 Thread cra...@gmx.net
Currently there is no easy way to add support for different structured
formatters (like JSON). For example, adding support for S-Expressions
would result in code duplication.

This patch series amends the situation by introducing structured
formatters, which allow different implementations of structures like
lists, maps, strings and numbers.

The new code in sprinter.h and sprinter-json.c can be used instead of
the current ad-hoc output in all parts of notmuch, a patch for
notmuch-search.c is included.

In a later patch, all other parts of notmuch should be adapted to the
structured formatters, and the creation of formatters should be
centralised (to make adding new formatters easier).

A "structured" formatter is provided for notmuch-search that prints the
current text format. This removes almost all the special-casing from
notmuch-search.c.

Overall diff --stat:

 Makefile.local   |   2 +
 notmuch-search.c | 301 +--
 sprinter-json.c  | 185 +
 sprinter-text.c  | 126 
 sprinter.h   |  68 +++
 test/json|  34 +++---
 6 files changed, 484 insertions(+), 232 deletions(-)

Changes versus v6 of this patch:
- is_text_printer is now a field, not a function.
- minor formatting
- sprinter_text_search has been renamed to sprinter_text (as it contains
  no search-specific code).
- string sanitization removed from sprinter_text, the caller should
  sanitize the strings.



[PATCH 3/7] go: Allow notmuch objects to be garbage collected

2012-07-20 Thread Austin Clements
Quoth Adrien Bustany on Jul 19 at  9:25 pm:
> Le 18/07/2012 23:40, Austin Clements a ?crit :
> >This is subtle enough that I think it deserves a comment in the source
> >code explaining that tracking the talloc owner reference, combined
> >with the fact that Go finalizers are run in dependency order, ensures
> >that the C objects will always be destroyed from the talloc leaves up.
> 
> Definitely, I tend to comment in the commit message and forget about
> the code...
> 
> >
> >Just one inline comment below.  Otherwise, I think this is all
> >correct.
> 
> Agree with the comment, the Database should be the parent. I guess I
> wasn't sure of the talloc parenting.
> 
> >
> >Is reproducing the talloc hierarchy in all of the bindings really the
> >right approach?  I feel like there has to be a better way (or that the
> >way we use talloc in the library is slightly broken).  What if the
> >bindings created an additional talloc reference to each managed
> >object, just to keep the object alive, and used talloc_unlink instead
> >of the destroy functions?
> 
> Reproducing the hierarchy is probably error prone, and not that
> simple indeed :/
> I haven't checked at all the way you suggest, but if we use
> talloc_reference/unlink, we get the same issue no?
> - If we do for each new wrapped object talloc_reference(NULL,
> wrapped_object), the the object will be kept alive until we
> talloc_unlink(NULL, wrapped_object), but what about its parents? For
> example will doing that on a notmuch_message_t keep the
> notmuch_messages_t alive?

Hmm.  This is what I was thinking.  You have an interesting point; I
think it's slightly wrong, but it exposes something deeper.  I believe
there are two different things going on here: some of the talloc
relationships are for convenience, while some are structural.  In the
former case, I'm pretty sure my suggestion will work, but in the
latter case the objects should *never* be freed by the finalizer!

For example, notmuch_query_search_messages returns a new
notmuch_messages_t with the query as the talloc parent, but that
notmuch_messages_t doesn't depend on the query object; this is just so
you can conveniently delete everything retrieved from the query by
deleting the query.  In this case, you can either use parent
references like you did---which will prevent a double-free by forcing
destruction to happen from the leaves up but at the cost of having to
encode these relationships and of extending the parent object
lifetimes beyond what's strictly necessary---or you can use my
suggestion of creating an additional talloc reference.

However, in your example, the notmuch_message_t's are structurally
related to the notmuch_messages_t from whence they came.  They're all
part of one data structure and hence it *never* makes sense for a
caller to delete the notmuch_message_t's.  For example, even with the
code in this patch, I think the following could lead to a crash:

1. Obtain a Messages object, say ms.
2. m1 := ms.Get()
3. m1 = nil
4. m2 := ms.Get()
5. m2.whatever()

If a garbage collection happens between steps 3 and 4, the Message in
m1 will get finalized and destroyed.  But step 4 will return the same,
now dangling, pointer, leading to a potential crash in step 5.

Maybe the answer in the structural case is to include the parent
pointer in the Go struct and not set a finalizer on the child?  That
way, if there's a Go reference to the parent wrapper, it won't go away
and the children won't get destroyed (collecting wrappers of children
is fine) and if there's a Go reference to the child wrapper, it will
keep the parent alive so it won't get destroyed and neither will the
child.

> - If we do talloc_reference(parent, wrapped), then we reproduce the
> hierarchy again?
> 
> Note that I have 0 experience with talloc, so I might as well be
> getting things wrong here.
> 
> >
> >Quoth Adrien Bustany on Jul 18 at  9:34 pm:
> >>This makes notmuch appropriately free the underlying notmuch C objects
> >>when garbage collecting their Go wrappers. To make sure we don't break
> >>the underlying links between objects (for example, a notmuch_messages_t
> >>being GC'ed before a notmuch_message_t belonging to it), we add for each
> >>wraper struct a pointer to the owner object (Go objects with a reference
> >>pointing to them don't get garbage collected).
> >>---
> >>  bindings/go/src/notmuch/notmuch.go |  153 
> >> +++-
> >>  1 files changed, 134 insertions(+), 19 deletions(-)
> >>
> >>diff --git a/bindings/go/src/notmuch/notmuch.go 
> >>b/bindings/go/src/notmuch/notmuch.go
> >>index 1d77fd2..3f436a0 100644
> >>--- a/bindings/go/src/notmuch/notmuch.go
> >>+++ b/bindings/go/src/notmuch/notmuch.go
> >>@@ -11,6 +11,7 @@ package notmuch
> >>  #include "notmuch.h"
> >>  */
> >>  import "C"
> >>+import "runtime"
> >>  import "unsafe"
> >>
> >>  // Status codes used for the return values of most functions
> >>@@ -47,40 +48,152 @@ func (self Status) String() string {
> >>  /* 

notmuch-reply: Structured Formatters

2012-07-20 Thread craven
Currently there is no easy way to add support for different structured
formatters (like JSON). For example, adding support for S-Expressions
would result in code duplication.

This patch series amends the situation by introducing structured
formatters, which allow different implementations of structures like
lists, maps, strings and numbers.

The new code in sprinter.h and sprinter-json.c can be used instead of
the current ad-hoc output in all parts of notmuch, a patch for
notmuch-search.c is included.

In a later patch, all other parts of notmuch should be adapted to the
structured formatters, and the creation of formatters should be
centralised (to make adding new formatters easier).

A structured formatter is provided for notmuch-search that prints the
current text format. This removes almost all the special-casing from
notmuch-search.c.

Overall diff --stat:

 Makefile.local   |   2 +
 notmuch-search.c | 301 +--
 sprinter-json.c  | 185 +
 sprinter-text.c  | 126 
 sprinter.h   |  68 +++
 test/json|  34 +++---
 6 files changed, 484 insertions(+), 232 deletions(-)

Changes versus v6 of this patch:
- is_text_printer is now a field, not a function.
- minor formatting
- sprinter_text_search has been renamed to sprinter_text (as it contains
  no search-specific code).
- string sanitization removed from sprinter_text, the caller should
  sanitize the strings.

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


[PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread craven
From: cra...@gmx.net

This patch switches from the current ad-hoc printer to the structured
formatters in sprinter.h, sprinter-text.c and sprinter-json.c.

The JSON tests are changed slightly in order to make them PASS for the
new structured output formatter.

The text tests pass without adaptation.
---
 notmuch-search.c | 301 ---
 test/json|  34 ---
 2 files changed, 103 insertions(+), 232 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..07211e8 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -19,6 +19,7 @@
  */
 
 #include notmuch-client.h
+#include sprinter.h
 
 typedef enum {
 OUTPUT_SUMMARY,
@@ -28,92 +29,6 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
-typedef struct search_format {
-const char *results_start;
-const char *item_start;
-void (*item_id) (const void *ctx,
-const char *item_type,
-const char *item_id);
-void (*thread_summary) (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-const char *tag_start;
-const char *tag;
-const char *tag_sep;
-const char *tag_end;
-const char *item_sep;
-const char *item_end;
-const char *results_end;
-const char *results_null;
-} search_format_t;
-
-static void
-format_item_id_text (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_text (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-static const search_format_t format_text = {
-,
-   ,
-   format_item_id_text,
-   format_thread_text,
-(,
-   %s,  ,
-   ), \n,
-   ,
-\n,
-,
-};
-
-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)
-{
-printf (%s%s, item_type, item_id);
-}
-
 static char *
 sanitize_string (const void *ctx, const char *str)
 {
@@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
 return out;
 }
 
-static void
-format_thread_text (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 %12s [%d/%d] %s; %s,
-   thread_id,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   sanitize_string (ctx_quote, authors),
-   sanitize_string (ctx_quote, subject));
-
-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,
-   

[PATCH v7 2/3] Add structured output formatter for JSON and plain text (but don't use them yet).

2012-07-20 Thread craven
From: cra...@gmx.net

Using the new structured printer support in sprinter.h, implement
sprinter_json_create, which returns a new JSON structured output
formatter. The formatter prints output similar to the existing JSON, but
with differences in whitespace (mostly newlines, --output=summary prints
the entire message summary on one line, not split across multiple lines).

Also implement a structured formatter for plain text that prints
prefixed strings, to be used with notmuch-search.c plain text output.
---
 Makefile.local  |   2 +
 sprinter-json.c | 185 
 sprinter-text.c | 126 ++
 sprinter.h  |  10 +++
 4 files changed, 323 insertions(+)
 create mode 100644 sprinter-json.c
 create mode 100644 sprinter-text.c

diff --git a/Makefile.local b/Makefile.local
index a890df2..296995d 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -290,6 +290,8 @@ notmuch_client_srcs =   \
notmuch-show.c  \
notmuch-tag.c   \
notmuch-time.c  \
+   sprinter-json.c \
+   sprinter-text.c \
query-string.c  \
mime-node.c \
crypto.c\
diff --git a/sprinter-json.c b/sprinter-json.c
new file mode 100644
index 000..32daa5a
--- /dev/null
+++ b/sprinter-json.c
@@ -0,0 +1,185 @@
+#include stdbool.h
+#include stdio.h
+#include talloc.h
+#include sprinter.h
+
+struct sprinter_json {
+struct sprinter vtable;
+FILE *stream;
+/* Top of the state stack, or NULL if the printer is not currently
+ * inside any aggregate types. */
+struct json_state *state;
+
+/* A flag to signify that a separator should be inserted in the
+ * output as soon as possible.
+ */
+notmuch_bool_t insert_separator;
+};
+
+struct json_state {
+struct json_state *parent;
+/* True if nothing has been printed in this aggregate yet.
+ * Suppresses the comma before a value. */
+notmuch_bool_t first;
+/* The character that closes the current aggregate. */
+char close;
+};
+
+/* Helper function to set up the stream to print a value.  If this
+ * value follows another value, prints a comma. */
+static struct sprinter_json *
+json_begin_value (struct sprinter *sp)
+{
+struct sprinter_json *spj = (struct sprinter_json *) sp;
+
+if (spj-state) {
+   if (! spj-state-first) {
+   fputc (',', spj-stream);
+   if (spj-insert_separator) {
+   fputc ('\n', spj-stream);
+   spj-insert_separator = FALSE;
+   } else
+   fputc (' ', spj-stream);
+   } else
+   spj-state-first = FALSE;
+}
+return spj;
+}
+
+/* Helper function to begin an aggregate type.  Prints the open
+ * character and pushes a new state frame. */
+static void
+json_begin_aggregate (struct sprinter *sp, char open, char close)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+struct json_state *state = talloc (spj, struct json_state);
+
+fputc (open, spj-stream);
+state-parent = spj-state;
+state-first = TRUE;
+state-close = close;
+spj-state = state;
+}
+
+static void
+json_begin_map (struct sprinter *sp)
+{
+json_begin_aggregate (sp, '{', '}');
+}
+
+static void
+json_begin_list (struct sprinter *sp)
+{
+json_begin_aggregate (sp, '[', ']');
+}
+
+static void
+json_end (struct sprinter *sp)
+{
+struct sprinter_json *spj = (struct sprinter_json *) sp;
+struct json_state *state = spj-state;
+
+fputc (spj-state-close, spj-stream);
+spj-state = state-parent;
+talloc_free (state);
+if (spj-state == NULL)
+   fputc ('\n', spj-stream);
+}
+
+static void
+json_string (struct sprinter *sp, const char *val)
+{
+static const char *const escapes[] = {
+   ['\'] = \\\, ['\\'] = , ['\b'] = \\b,
+   ['\f'] = \\f,  ['\n'] = \\n,  ['\t'] = \\t
+};
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputc ('', spj-stream);
+for (; *val; ++val) {
+   unsigned char ch = *val;
+   if (ch  ARRAY_SIZE (escapes)  escapes[ch])
+   fputs (escapes[ch], spj-stream);
+   else if (ch = 32)
+   fputc (ch, spj-stream);
+   else
+   fprintf (spj-stream, \\u%04x, ch);
+}
+fputc ('', spj-stream);
+}
+
+static void
+json_integer (struct sprinter *sp, int val)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fprintf (spj-stream, %d, val);
+}
+
+static void
+json_boolean (struct sprinter *sp, notmuch_bool_t val)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputs (val ? true : false, spj-stream);
+}
+
+static void
+json_null (struct sprinter *sp)
+{
+struct sprinter_json *spj = json_begin_value (sp);
+
+fputs (null, spj-stream);
+}
+
+static void
+json_map_key (struct sprinter *sp, const char *key)
+{
+struct sprinter_json *spj = (struct sprinter_json *) sp;
+
+

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

2012-07-20 Thread craven
From: cra...@gmx.net

This patch adds a new struct type sprinter_t, which is used for
structured formatting, e.g. JSON or S-Expressions. The structure printer
is heavily based on code from Austin Clements
(id:87d34hsdx8@awakening.csail.mit.edu).

It includes the following functions:

/* Start a new map/dictionary structure. This should be followed by
 * a sequence of alternating calls to map_key and one of the
 * value-printing functions until the map is ended by end.
 */
void (*begin_map) (struct sprinter *);

/* Start a new list/array structure.
 */
void (*begin_list) (struct sprinter *);

/* End the last opened list or map structure.
 */
void (*end) (struct sprinter *);

/* Print one string/integer/boolean/null element (possibly inside a
 * list or map, followed or preceded by separators).
 * For string, the char * must be UTF-8 encoded.
 */
void (*string) (struct sprinter *, const char *);
void (*integer) (struct sprinter *, int);
void (*boolean) (struct sprinter *, notmuch_bool_t);
void (*null) (struct sprinter *);

/* Print the key of a map's key/value pair. The char * must be UTF-8
 * encoded.
 */
void (*map_key) (struct sprinter *, const char *);

/* Insert a separator (usually extra whitespace) for improved
 * readability without affecting the abstract syntax of the
 * structure being printed.
 * For JSON, this could simply be a line break.
 */
void (*separator) (struct sprinter *);

/* Set the current string prefix. This only affects the text
 * printer, which will print this string, followed by a colon,
 * before any string. For other printers, this does nothing.
 */
void (*set_prefix) (struct sprinter *, const char *);

To support the plain text format properly, the following additional
function must also be implemented:

/* Set the current string prefix. This only affects the text
 * printer, which will print this string, followed by a colon,
 * before any string. For other printers, this does nothing.
 */
void (*set_prefix) (struct sprinter *, const char *);

The structure also contains a flag that should be set to FALSE in all
custom printers and to TRUE in the plain text formatter.

/* True if this is the special-cased plain text printer.
 */
notmuch_bool_t is_text_printer;

The printer can (and should) use internal state to insert delimiters
and syntax at the correct places.

Example:

format-begin_map(format);
format-map_key(format, foo);
format-begin_list(format);
format-integer(format, 1);
format-integer(format, 2);
format-integer(format, 3);
format-end(format);
format-map_key(format, bar);
format-begin_map(format);
format-map_key(format, baaz);
format-string(format, hello world);
format-end(format);
format-end(format);

would output JSON as follows:

{foo: [1, 2, 3], bar: { baaz: hello world}}
---
 sprinter.h | 58 ++
 1 file changed, 58 insertions(+)
 create mode 100644 sprinter.h

diff --git a/sprinter.h b/sprinter.h
new file mode 100644
index 000..77dc26f
--- /dev/null
+++ b/sprinter.h
@@ -0,0 +1,58 @@
+#ifndef NOTMUCH_SPRINTER_H
+#define NOTMUCH_SPRINTER_H
+
+/* Necessary for notmuch_bool_t */
+#include notmuch-client.h
+
+/* Structure printer interface. This is used to create output
+ * structured as maps (with key/value pairs), lists and primitives
+ * (strings, integers and booleans).
+ */
+typedef struct sprinter {
+/* Start a new map/dictionary structure. This should be followed by
+ * a sequence of alternating calls to map_key and one of the
+ * value-printing functions until the map is ended by end.
+ */
+void (*begin_map) (struct sprinter *);
+
+/* Start a new list/array structure.
+ */
+void (*begin_list) (struct sprinter *);
+
+/* End the last opened list or map structure.
+ */
+void (*end) (struct sprinter *);
+
+/* Print one string/integer/boolean/null element (possibly inside a
+ * list or map, followed or preceded by separators).
+ * For string, the char * must be UTF-8 encoded.
+ */
+void (*string) (struct sprinter *, const char *);
+void (*integer) (struct sprinter *, int);
+void (*boolean) (struct sprinter *, notmuch_bool_t);
+void (*null) (struct sprinter *);
+
+/* Print the key of a map's key/value pair. The char * must be UTF-8
+ * encoded.
+ */
+void (*map_key) (struct sprinter *, const char *);
+
+/* Insert a separator (usually extra whitespace) for improved
+ * readability without affecting the abstract syntax of the
+ * structure being printed.
+ * For JSON, this could simply be a line break.
+ */
+void (*separator) (struct sprinter *);
+
+/* Set the current string prefix. This only affects the text
+ * printer, which will print this string, followed by a colon,
+ * before any string. For other printers, 

[PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread craven
From: cra...@gmx.net

This patch switches from the current ad-hoc printer to the structured
formatters in sprinter.h, sprinter-text.c and sprinter-json.c.

The JSON tests are changed slightly in order to make them PASS for the
new structured output formatter.

The text tests pass without adaptation.
---
 notmuch-search.c | 301 ---
 test/json|  34 ---
 2 files changed, 103 insertions(+), 232 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3be296d..07211e8 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -19,6 +19,7 @@
  */
 
 #include notmuch-client.h
+#include sprinter.h
 
 typedef enum {
 OUTPUT_SUMMARY,
@@ -28,92 +29,6 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
-typedef struct search_format {
-const char *results_start;
-const char *item_start;
-void (*item_id) (const void *ctx,
-const char *item_type,
-const char *item_id);
-void (*thread_summary) (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-const char *tag_start;
-const char *tag;
-const char *tag_sep;
-const char *tag_end;
-const char *item_sep;
-const char *item_end;
-const char *results_end;
-const char *results_null;
-} search_format_t;
-
-static void
-format_item_id_text (const void *ctx,
-const char *item_type,
-const char *item_id);
-
-static void
-format_thread_text (const void *ctx,
-   const char *thread_id,
-   const time_t date,
-   const int matched,
-   const int total,
-   const char *authors,
-   const char *subject);
-static const search_format_t format_text = {
-,
-   ,
-   format_item_id_text,
-   format_thread_text,
-(,
-   %s,  ,
-   ), \n,
-   ,
-\n,
-,
-};
-
-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)
-{
-printf (%s%s, item_type, item_id);
-}
-
 static char *
 sanitize_string (const void *ctx, const char *str)
 {
@@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
 return out;
 }
 
-static void
-format_thread_text (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 %12s [%d/%d] %s; %s,
-   thread_id,
-   notmuch_time_relative_date (ctx, date),
-   matched,
-   total,
-   sanitize_string (ctx_quote, authors),
-   sanitize_string (ctx_quote, subject));
-
-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,
-   

Re: [PATCH v7 1/3] Add support for structured output formatters.

2012-07-20 Thread Tomi Ollila
On Fri, Jul 20 2012, cra...@gmx.net wrote:

 From: cra...@gmx.net

 This patch adds a new struct type sprinter_t, which is used for
 structured formatting, e.g. JSON or S-Expressions. The structure printer
 is heavily based on code from Austin Clements
 (id:87d34hsdx8@awakening.csail.mit.edu).

 It includes the following functions:

 /* Start a new map/dictionary structure. This should be followed by
  * a sequence of alternating calls to map_key and one of the
  * value-printing functions until the map is ended by end.
  */
 void (*begin_map) (struct sprinter *);

 /* Start a new list/array structure.
  */
 void (*begin_list) (struct sprinter *);

 /* End the last opened list or map structure.
  */
 void (*end) (struct sprinter *);

 /* Print one string/integer/boolean/null element (possibly inside a
  * list or map, followed or preceded by separators).
  * For string, the char * must be UTF-8 encoded.
  */
 void (*string) (struct sprinter *, const char *);
 void (*integer) (struct sprinter *, int);
 void (*boolean) (struct sprinter *, notmuch_bool_t);
 void (*null) (struct sprinter *);

 /* Print the key of a map's key/value pair. The char * must be UTF-8
  * encoded.
  */
 void (*map_key) (struct sprinter *, const char *);

 /* Insert a separator (usually extra whitespace) for improved
  * readability without affecting the abstract syntax of the
  * structure being printed.
  * For JSON, this could simply be a line break.
  */
 void (*separator) (struct sprinter *);

 /* Set the current string prefix. This only affects the text
  * printer, which will print this string, followed by a colon,
  * before any string. For other printers, this does nothing.
  */
 void (*set_prefix) (struct sprinter *, const char *);

The above block duplicated below. Otherwise this LGTM.

I presume the patch 3/3 emails

id:1342766173-1344-4-git-send-email-cra...@gmx.net
id:1342772624-23329-1-git-send-email-cra...@gmx.net

have identical content ?

 To support the plain text format properly, the following additional
 function must also be implemented:

 /* Set the current string prefix. This only affects the text
  * printer, which will print this string, followed by a colon,
  * before any string. For other printers, this does nothing.
  */
 void (*set_prefix) (struct sprinter *, const char *);

 The structure also contains a flag that should be set to FALSE in all
 custom printers and to TRUE in the plain text formatter.

 /* True if this is the special-cased plain text printer.
  */
 notmuch_bool_t is_text_printer;

 The printer can (and should) use internal state to insert delimiters
 and syntax at the correct places.

 Example:

 format-begin_map(format);
 format-map_key(format, foo);
 format-begin_list(format);
 format-integer(format, 1);
 format-integer(format, 2);
 format-integer(format, 3);
 format-end(format);
 format-map_key(format, bar);
 format-begin_map(format);
 format-map_key(format, baaz);
 format-string(format, hello world);
 format-end(format);
 format-end(format);

 would output JSON as follows:

 {foo: [1, 2, 3], bar: { baaz: hello world}}
 ---
  sprinter.h | 58 ++
  1 file changed, 58 insertions(+)
  create mode 100644 sprinter.h

 diff --git a/sprinter.h b/sprinter.h
 new file mode 100644
 index 000..77dc26f
 --- /dev/null
 +++ b/sprinter.h
 @@ -0,0 +1,58 @@
 +#ifndef NOTMUCH_SPRINTER_H
 +#define NOTMUCH_SPRINTER_H
 +
 +/* Necessary for notmuch_bool_t */
 +#include notmuch-client.h
 +
 +/* Structure printer interface. This is used to create output
 + * structured as maps (with key/value pairs), lists and primitives
 + * (strings, integers and booleans).
 + */
 +typedef struct sprinter {
 +/* Start a new map/dictionary structure. This should be followed by
 + * a sequence of alternating calls to map_key and one of the
 + * value-printing functions until the map is ended by end.
 + */
 +void (*begin_map) (struct sprinter *);
 +
 +/* Start a new list/array structure.
 + */
 +void (*begin_list) (struct sprinter *);
 +
 +/* End the last opened list or map structure.
 + */
 +void (*end) (struct sprinter *);
 +
 +/* Print one string/integer/boolean/null element (possibly inside a
 + * list or map, followed or preceded by separators).
 + * For string, the char * must be UTF-8 encoded.
 + */
 +void (*string) (struct sprinter *, const char *);
 +void (*integer) (struct sprinter *, int);
 +void (*boolean) (struct sprinter *, notmuch_bool_t);
 +void (*null) (struct sprinter *);
 +
 +/* Print the key of a map's key/value pair. The char * must be UTF-8
 + * encoded.
 + */
 +void (*map_key) (struct sprinter *, const char *);
 +
 +/* Insert a separator (usually extra whitespace) for improved
 +

Re: [PATCH v7 1/3] Add support for structured output formatters.

2012-07-20 Thread craven
 I presume the patch 3/3 emails
 
 id:1342766173-1344-4-git-send-email-cra...@gmx.net
 id:1342772624-23329-1-git-send-email-cra...@gmx.net
 
 have identical content ?

Yes, they do, there was a problem with the Content-Type header having
^P^P, so I re-sent correctly, but the actual e-mail content is exactly
the same.

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


Re: [PATCH v7 3/3] Use the structured formatters in notmuch-search.c.

2012-07-20 Thread Tomi Ollila
On Fri, Jul 20 2012, cra...@gmx.net wrote:

 From: cra...@gmx.net

 This patch switches from the current ad-hoc printer to the structured
 formatters in sprinter.h, sprinter-text.c and sprinter-json.c.

 The JSON tests are changed slightly in order to make them PASS for the
 new structured output formatter.

 The text tests pass without adaptation.
 ---
  notmuch-search.c | 301 
 ---
  test/json|  34 ---
  2 files changed, 103 insertions(+), 232 deletions(-)

 diff --git a/notmuch-search.c b/notmuch-search.c
 index 3be296d..07211e8 100644
 --- a/notmuch-search.c
 +++ b/notmuch-search.c
 @@ -19,6 +19,7 @@
   */
  
  #include notmuch-client.h
 +#include sprinter.h
  
  typedef enum {
  OUTPUT_SUMMARY,
 @@ -28,92 +29,6 @@ typedef enum {
  OUTPUT_TAGS
  } output_t;
  
 -typedef struct search_format {
 -const char *results_start;
 -const char *item_start;
 -void (*item_id) (const void *ctx,
 -  const char *item_type,
 -  const char *item_id);
 -void (*thread_summary) (const void *ctx,
 - const char *thread_id,
 - const time_t date,
 - const int matched,
 - const int total,
 - const char *authors,
 - const char *subject);
 -const char *tag_start;
 -const char *tag;
 -const char *tag_sep;
 -const char *tag_end;
 -const char *item_sep;
 -const char *item_end;
 -const char *results_end;
 -const char *results_null;
 -} search_format_t;
 -
 -static void
 -format_item_id_text (const void *ctx,
 -  const char *item_type,
 -  const char *item_id);
 -
 -static void
 -format_thread_text (const void *ctx,
 - const char *thread_id,
 - const time_t date,
 - const int matched,
 - const int total,
 - const char *authors,
 - const char *subject);
 -static const search_format_t format_text = {
 -,
 - ,
 - format_item_id_text,
 - format_thread_text,
 -  (,
 - %s,  ,
 - ), \n,
 - ,
 -\n,
 -,
 -};
 -
 -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)
 -{
 -printf (%s%s, item_type, item_id);
 -}
 -
  static char *
  sanitize_string (const void *ctx, const char *str)
  {
 @@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
  return out;
  }
  
 -static void
 -format_thread_text (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 %12s [%d/%d] %s; %s,
 - thread_id,
 - notmuch_time_relative_date (ctx, date),
 - matched,
 - total,
 - sanitize_string (ctx_quote, authors),
 - sanitize_string (ctx_quote, subject));
 -
 -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,
 - 

Re: bug in parsing?

2012-07-20 Thread Austin Clements
On Fri, 15 Jun 2012, David Belohrad da...@belohrad.ch wrote:
 Dear all,

 recently I got an email from matplotlib list, which fails to parse in
 notmuch-saved-search-unread.
 It looks like this:

 Yest. 21:04 [3/5]   Jesse Rosenthal, Tomi Ollila  [PATCHv2] emacs: derive 
 correct timestamp in FCC unique name (inbox notmuch)
  Yest. 20:58 [1/1]   Allin Cottrell   mouse and update 23.4 - 24.1 
 (emacs inbox)
  Yest. 20:56 [3/6]   Milan Kratochvíl, Jaroslav Lukesh, David Obdrzalek, 
 Pavel Kutina  [OT:] Javascript alert jinak (hw inbox)
 Error: Unexpected output from notmuch search:
 thread:6a68  Yest. 20:39 [1/1] R. O#39;Gara; [Matplotlib-users] 
 (no subject) (matplotlib mightread unread)

  Yest. 19:38 [1/1]   Baaba Maal - Avaaz. org  Stop the African hunger games 
 (inbox unread)
  Yest. 19:27 [1/2]   Bob Proulx, sariatu...@safe-mail.net   next-line at 
 window end without recenter (deleted emacs inbox unread)
  Yest. 19:12 [1/12]  Jiří Kučera, hutt...@seznam.cz, balu@home, Pavel
  Hudecek  LCR meter - konstrukce (deleted hw inbox unread)



 It seems that the weird characted in sender does it. Should those
 characters be somehow escaped?

This should be fixed in master, as of 9c5ea07c.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch