unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH v5 0/5] New output format sexp (Lisp S-Expressions)
@ 2012-12-06 21:12 Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 1/5] Adding an S-expression structured output printer Peter Feigl
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

This patch series adds a new output format "sexp" to notmuch-reply,
notmuch-show and notmuch-search. These are useful for the Android mobile
client, Emacs and perhaps other Lisp programs as well.
After the switch to a generic structured output printer, which was
committed some months ago, these patches just add another one (like the
json structured output printer).
Basic tests and updates to the man pages are also included.

This version adresses comments by Tomi Ollila and Austin Clements.

Old versions:
v4: id:cover.1354794428.git.craven@gmx.net
v3: id:1354779189-12231-1-git-send-email-craven@gmx.net
v2: id:1354632382-15609-1-git-send-email-craven@gmx.net
v1: 1354264143-30173-1-git-send-email-craven@gmx.net

Peter Feigl (5):
  Adding an S-expression structured output printer.
  Rename the -json printer functions in notmuch-reply and notmuch-show
    to generic -sprinter functions.
  Use the S-Expression structured printer in notmuch-show,
    notmuch-reply and notmuch-search.
  Adding tests for --format=sexp.
  Updating man pages for new S-Expression output format.

 Makefile.local            |   1 +
 devel/schemata            |  24 +++--
 man/man1/notmuch-reply.1  |  14 ++-
 man/man1/notmuch-search.1 |  15 +--
 man/man1/notmuch-show.1   |  36 +++++--
 notmuch-client.h          |   8 +-
 notmuch-reply.c           |  48 ++++++----
 notmuch-search.c          |   6 +-
 notmuch-show.c            |  76 ++++++++-------
 sprinter-sexp.c           | 235 ++++++++++++++++++++++++++++++++++++++++++++++
 sprinter.h                |   4 +
 test/notmuch-test         |   1 +
 test/sexp                 |  48 ++++++++++
 13 files changed, 434 insertions(+), 82 deletions(-)
 create mode 100644 sprinter-sexp.c
 create mode 100755 test/sexp

-- 
1.8.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 1/5] Adding an S-expression structured output printer.
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
@ 2012-12-06 21:12 ` Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 2/5] Rename the -json printer functions in notmuch-reply and notmuch-show to generic -sprinter functions Peter Feigl
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

This commit adds a structured output printer for Lisp
S-Expressions. Later commits will use this printer in notmuch search,
show and reply.

The structure is the same as json, but:
- arrays are written as lists: ("foo" "bar" "baaz" 1 2 3)
- maps are written as p-lists: (:key "value" :other-key "other-value")
- true is written as t
- false is written as nil
- null is written as nil
---
 Makefile.local  |   1 +
 sprinter-sexp.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sprinter.h      |   4 +
 3 files changed, 240 insertions(+)
 create mode 100644 sprinter-sexp.c

diff --git a/Makefile.local b/Makefile.local
index 2b91946..0db1713 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -270,6 +270,7 @@ notmuch_client_srcs =		\
 	notmuch-tag.c		\
 	notmuch-time.c		\
 	sprinter-json.c		\
+	sprinter-sexp.c		\
 	sprinter-text.c		\
 	query-string.c		\
 	mime-node.c		\
diff --git a/sprinter-sexp.c b/sprinter-sexp.c
new file mode 100644
index 0000000..06856f4
--- /dev/null
+++ b/sprinter-sexp.c
@@ -0,0 +1,235 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2012 Peter Feigl
+ *
+ * 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: Peter Feigl <peter.feigl@gmx.at>
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <talloc.h>
+#include "sprinter.h"
+#include <ctype.h>
+
+struct sprinter_sexp {
+    struct sprinter vtable;
+    FILE *stream;
+    /* Top of the state stack, or NULL if the printer is not currently
+     * inside any aggregate types. */
+    struct sexp_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 sexp_state {
+    struct sexp_state *parent;
+
+    /* True if nothing has been printed in this aggregate yet.
+     * Suppresses the space before a value. */
+    notmuch_bool_t first;
+};
+
+/* Helper function to set up the stream to print a value.  If this
+ * value follows another value, prints a space. */
+static struct sprinter_sexp *
+sexp_begin_value (struct sprinter *sp)
+{
+    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
+
+    if (sps->state) {
+	if (! sps->state->first) {
+	    if (sps->insert_separator) {
+		fputc ('\n', sps->stream);
+		sps->insert_separator = FALSE;
+	    } else {
+		fputc (' ', sps->stream);
+	    }
+	} else {
+	    sps->state->first = FALSE;
+	}
+    }
+    return sps;
+}
+
+/* Helper function to begin an aggregate type.  Prints the open
+ * character and pushes a new state frame. */
+static void
+sexp_begin_aggregate (struct sprinter *sp)
+{
+    struct sprinter_sexp *sps = sexp_begin_value (sp);
+    struct sexp_state *state = talloc (sps, struct sexp_state);
+    fputc ('(', sps->stream);
+    state->parent = sps->state;
+    state->first = TRUE;
+    sps->state = state;
+}
+
+static void
+sexp_begin_map (struct sprinter *sp)
+{
+    sexp_begin_aggregate (sp);
+}
+
+static void
+sexp_begin_list (struct sprinter *sp)
+{
+    sexp_begin_aggregate (sp);
+}
+
+static void
+sexp_end (struct sprinter *sp)
+{
+    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
+    struct sexp_state *state = sps->state;
+
+    fputc (')', sps->stream);
+    sps->state = state->parent;
+    talloc_free (state);
+    if (sps->state == NULL)
+	fputc ('\n', sps->stream);
+}
+
+static void
+sexp_string_len (struct sprinter *sp, const char *val, size_t len)
+{
+    /* Some characters need escaping. " and \ work fine in all Lisps,
+     * \n is not supported in CL, but all others work fine.
+     * Characters below 32 are printed as \123o (three-digit
+     * octals), which work fine in most Schemes and Emacs. */
+    static const char *const escapes[] = {
+	['\"'] = "\\\"", ['\\'] = "\\\\",  ['\n'] = "\\n"
+    };
+    struct sprinter_sexp *sps = sexp_begin_value (sp);
+
+    fputc ('"', sps->stream);
+    for (; len; ++val, --len) {
+	unsigned char ch = *val;
+	if (ch < ARRAY_SIZE (escapes) && escapes[ch])
+	    fputs (escapes[ch], sps->stream);
+	else if (ch >= 32)
+	    fputc (ch, sps->stream);
+	else
+	    fprintf (sps->stream, "\\%03o", ch);
+    }
+    fputc ('"', sps->stream);
+}
+
+static void
+sexp_string (struct sprinter *sp, const char *val)
+{
+    if (val == NULL)
+	val = "";
+    sexp_string_len (sp, val, strlen (val));
+}
+
+/* Prints a symbol, i.e. the name preceded by a colon. This should work
+ * in all Lisps, at least as a symbol, if not as a proper keyword */
+static void
+sexp_keyword (struct sprinter *sp, const char *val)
+{
+    unsigned int i = 0;
+    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
+    char ch;
+
+    if (val == NULL)
+	INTERNAL_ERROR ("illegal symbol NULL");
+
+    for (i = 0; i < strlen (val); i++) {
+	ch = val[i];
+	if (! (isalnum(ch) || (ch == '-') || (ch == '_'))) {
+	   INTERNAL_ERROR ("illegal character in symbol %s: %c", val, ch);
+	}
+    }
+    fputc (':', sps->stream);
+    fputs (val, sps->stream);
+}
+
+static void
+sexp_integer (struct sprinter *sp, int val)
+{
+    struct sprinter_sexp *sps = sexp_begin_value (sp);
+
+    fprintf (sps->stream, "%d", val);
+}
+
+static void
+sexp_boolean (struct sprinter *sp, notmuch_bool_t val)
+{
+    struct sprinter_sexp *sps = sexp_begin_value (sp);
+
+    fputs (val ? "t" : "nil", sps->stream);
+}
+
+static void
+sexp_null (struct sprinter *sp)
+{
+    struct sprinter_sexp *sps = sexp_begin_value (sp);
+
+    fputs ("nil", sps->stream);
+}
+
+static void
+sexp_map_key (struct sprinter *sp, const char *key)
+{
+    sexp_begin_value (sp);
+    
+    sexp_keyword (sp, key);
+}
+
+static void
+sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))
+{
+}
+
+static void
+sexp_separator (struct sprinter *sp)
+{
+    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
+
+    sps->insert_separator = TRUE;
+}
+
+struct sprinter *
+sprinter_sexp_create (const void *ctx, FILE *stream)
+{
+    static const struct sprinter_sexp template = {
+	.vtable = {
+	    .begin_map = sexp_begin_map,
+	    .begin_list = sexp_begin_list,
+	    .end = sexp_end,
+	    .string = sexp_string,
+	    .string_len = sexp_string_len,
+	    .integer = sexp_integer,
+	    .boolean = sexp_boolean,
+	    .null = sexp_null,
+	    .map_key = sexp_map_key,
+	    .separator = sexp_separator,
+	    .set_prefix = sexp_set_prefix,
+	    .is_text_printer = FALSE,
+	}
+    };
+    struct sprinter_sexp *res;
+
+    res = talloc (ctx, struct sprinter_sexp);
+    if (! res)
+	return NULL;
+
+    *res = template;
+    res->stream = stream;
+    return &res->vtable;
+}
diff --git a/sprinter.h b/sprinter.h
index 912a526..59776a9 100644
--- a/sprinter.h
+++ b/sprinter.h
@@ -70,4 +70,8 @@ sprinter_text_create (const void *ctx, FILE *stream);
 struct sprinter *
 sprinter_json_create (const void *ctx, FILE *stream);
 
+/* Create a new structure printer that emits S-Expressions. */
+struct sprinter *
+sprinter_sexp_create (const void *ctx, FILE *stream);
+
 #endif // NOTMUCH_SPRINTER_H
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 2/5] Rename the -json printer functions in notmuch-reply and notmuch-show to generic -sprinter functions.
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 1/5] Adding an S-expression structured output printer Peter Feigl
@ 2012-12-06 21:12 ` Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 3/5] Use the S-Expression structured printer in notmuch-show, notmuch-reply and notmuch-search Peter Feigl
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

All the structured output functions in notmuch-reply and notmuch-show
are renamed to a generic name (as they do not contain any json-specific
code anyway). This patch is a preparation to actually using the new
S-Expression sprinter in notmuch-reply and notmuch-show.
---
 devel/schemata   | 16 ++++++++--------
 notmuch-client.h |  8 ++++----
 notmuch-reply.c  | 43 ++++++++++++++++++++++++++-----------------
 notmuch-show.c   | 40 ++++++++++++++++++++--------------------
 4 files changed, 58 insertions(+), 49 deletions(-)

diff --git a/devel/schemata b/devel/schemata
index e44da71..e5c5505 100644
--- a/devel/schemata
+++ b/devel/schemata
@@ -36,9 +36,9 @@ thread_node = [
     [thread_node*]            # children of message
 ]
 
-# A message (format_part_json)
+# A message (format_part_sprinter)
 message = {
-    # (format_message_json)
+    # (format_message_sprinter)
     id:             messageid,
     match:          bool,
     filename:	    string,
@@ -50,7 +50,7 @@ message = {
     body?:          [part]    # omitted if --body=false
 }
 
-# A MIME part (format_part_json)
+# A MIME part (format_part_sprinter)
 part = {
     id:             int|string, # part id (currently DFS part number)
 
@@ -72,7 +72,7 @@ part = {
     content?:       string
 }
 
-# The headers of a message or part (format_headers_json with reply = FALSE)
+# The headers of a message or part (format_headers_sprinter with reply = FALSE)
 headers = {
     Subject:        string,
     From:           string,
@@ -83,10 +83,10 @@ headers = {
     Date:           string
 }
 
-# Encryption status (format_part_json)
+# Encryption status (format_part_sprinter)
 encstatus = [{status: "good"|"bad"}]
 
-# Signature status (format_part_sigstatus_json)
+# Signature status (format_part_sigstatus_sprinter)
 sigstatus = [signature*]
 
 signature = {
@@ -140,11 +140,11 @@ reply = {
     # The headers of the constructed reply
     reply-headers: reply_headers,
 
-    # As in the show format (format_part_json)
+    # As in the show format (format_part_sprinter)
     original: message
 }
 
-# Reply headers (format_headers_json with reply = TRUE)
+# Reply headers (format_headers_sprinter with reply = TRUE)
 reply_headers = {
     Subject:        string,
     From:           string,
diff --git a/notmuch-client.h b/notmuch-client.h
index ae9344b..1c336dc 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -175,12 +175,12 @@ notmuch_status_t
 show_one_part (const char *filename, int part);
 
 void
-format_part_json (const void *ctx, struct sprinter *sp, mime_node_t *node,
-		  notmuch_bool_t first, notmuch_bool_t output_body);
+format_part_sprinter (const void *ctx, struct sprinter *sp, mime_node_t *node,
+		      notmuch_bool_t first, notmuch_bool_t output_body);
 
 void
-format_headers_json (struct sprinter *sp, GMimeMessage *message,
-		     notmuch_bool_t reply);
+format_headers_sprinter (struct sprinter *sp, GMimeMessage *message,
+			 notmuch_bool_t reply);
 
 typedef enum {
     NOTMUCH_SHOW_TEXT_PART_REPLY = 1 << 0,
diff --git a/notmuch-reply.c b/notmuch-reply.c
index e60a264..ef450b2 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -548,7 +548,8 @@ notmuch_reply_format_default(void *ctx,
 			     notmuch_config_t *config,
 			     notmuch_query_t *query,
 			     notmuch_show_params_t *params,
-			     notmuch_bool_t reply_all)
+			     notmuch_bool_t reply_all,
+			     unused (sprinter_t *sp))
 {
     GMimeMessage *reply;
     notmuch_messages_t *messages;
@@ -587,17 +588,17 @@ notmuch_reply_format_default(void *ctx,
 }
 
 static int
-notmuch_reply_format_json(void *ctx,
-			  notmuch_config_t *config,
-			  notmuch_query_t *query,
-			  notmuch_show_params_t *params,
-			  notmuch_bool_t reply_all)
+notmuch_reply_format_sprinter(void *ctx,
+			      notmuch_config_t *config,
+			      notmuch_query_t *query,
+			      notmuch_show_params_t *params,
+			      notmuch_bool_t reply_all,
+			      sprinter_t *sp)
 {
     GMimeMessage *reply;
     notmuch_messages_t *messages;
     notmuch_message_t *message;
     mime_node_t *node;
-    sprinter_t *sp;
 
     if (notmuch_query_count_messages (query) != 1) {
 	fprintf (stderr, "Error: search term did not match precisely one message.\n");
@@ -613,18 +614,17 @@ notmuch_reply_format_json(void *ctx,
     if (!reply)
 	return 1;
 
-    sp = sprinter_json_create (ctx, stdout);
     sp->begin_map (sp);
 
     /* The headers of the reply message we've created */
     sp->map_key (sp, "reply-headers");
-    format_headers_json (sp, reply, TRUE);
+    format_headers_sprinter (sp, reply, TRUE);
     g_object_unref (G_OBJECT (reply));
     reply = NULL;
 
     /* Start the original */
     sp->map_key (sp, "original");
-    format_part_json (ctx, sp, node, TRUE, TRUE);
+    format_part_sprinter (ctx, sp, node, TRUE, TRUE);
 
     /* End */
     sp->end (sp);
@@ -639,7 +639,8 @@ notmuch_reply_format_headers_only(void *ctx,
 				  notmuch_config_t *config,
 				  notmuch_query_t *query,
 				  unused (notmuch_show_params_t *params),
-				  notmuch_bool_t reply_all)
+				  notmuch_bool_t reply_all,
+				  unused (sprinter_t *sp))
 {
     GMimeMessage *reply;
     notmuch_messages_t *messages;
@@ -707,7 +708,12 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
     notmuch_query_t *query;
     char *query_string;
     int opt_index, ret = 0;
-    int (*reply_format_func)(void *ctx, notmuch_config_t *config, notmuch_query_t *query, notmuch_show_params_t *params, notmuch_bool_t reply_all);
+    int (*reply_format_func) (void *ctx,
+			      notmuch_config_t *config,
+			      notmuch_query_t *query,
+			      notmuch_show_params_t *params,
+			      notmuch_bool_t reply_all,
+			      struct sprinter *sp);
     notmuch_show_params_t params = {
 	.part = -1,
 	.crypto = {
@@ -717,6 +723,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
     };
     int format = FORMAT_DEFAULT;
     int reply_all = TRUE;
+    struct sprinter *sp = NULL;
 
     notmuch_opt_desc_t options[] = {
 	{ NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
@@ -738,12 +745,14 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
 	return 1;
     }
 
-    if (format == FORMAT_HEADERS_ONLY)
+    if (format == FORMAT_HEADERS_ONLY) {
 	reply_format_func = notmuch_reply_format_headers_only;
-    else if (format == FORMAT_JSON)
-	reply_format_func = notmuch_reply_format_json;
-    else
+    } else if (format == FORMAT_JSON) {
+	reply_format_func = notmuch_reply_format_sprinter;
+	sp = sprinter_json_create (ctx, stdout);
+    } else {
 	reply_format_func = notmuch_reply_format_default;
+    }
 
     config = notmuch_config_open (ctx, NULL, NULL);
     if (config == NULL)
@@ -770,7 +779,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
 	return 1;
     }
 
-    if (reply_format_func (ctx, config, query, &params, reply_all) != 0)
+    if (reply_format_func (ctx, config, query, &params, reply_all, sp) != 0)
 	return 1;
 
     notmuch_crypto_cleanup (&params.crypto);
diff --git a/notmuch-show.c b/notmuch-show.c
index 2fa2292..d3f08bc 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -32,12 +32,12 @@ static const notmuch_show_format_t format_text = {
 };
 
 static notmuch_status_t
-format_part_json_entry (const void *ctx, sprinter_t *sp, mime_node_t *node,
-			int indent, const notmuch_show_params_t *params);
+format_part_sprinter_entry (const void *ctx, sprinter_t *sp, mime_node_t *node,
+			    int indent, const notmuch_show_params_t *params);
 
 static const notmuch_show_format_t format_json = {
     .new_sprinter = sprinter_json_create,
-    .part = format_part_json_entry,
+    .part = format_part_sprinter_entry,
 };
 
 static notmuch_status_t
@@ -108,7 +108,7 @@ _get_one_line_summary (const void *ctx, notmuch_message_t *message)
 /* Emit a sequence of key/value pairs for the metadata of message.
  * The caller should begin a map before calling this. */
 static void
-format_message_json (sprinter_t *sp, notmuch_message_t *message)
+format_message_sprinter (sprinter_t *sp, notmuch_message_t *message)
 {
     /* Any changes to the JSON format should be reflected in the file
      * devel/schemata. */
@@ -208,8 +208,8 @@ _is_from_line (const char *line)
 }
 
 void
-format_headers_json (sprinter_t *sp, GMimeMessage *message,
-		     notmuch_bool_t reply)
+format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
+			 notmuch_bool_t reply)
 {
     /* Any changes to the JSON format should be reflected in the file
      * devel/schemata. */
@@ -363,7 +363,7 @@ signer_status_to_string (GMimeSignerStatus x)
 
 #ifdef GMIME_ATLEAST_26
 static void
-format_part_sigstatus_json (sprinter_t *sp, mime_node_t *node)
+format_part_sigstatus_sprinter (sprinter_t *sp, mime_node_t *node)
 {
     /* Any changes to the JSON format should be reflected in the file
      * devel/schemata. */
@@ -438,7 +438,7 @@ format_part_sigstatus_json (sprinter_t *sp, mime_node_t *node)
 }
 #else
 static void
-format_part_sigstatus_json (sprinter_t *sp, mime_node_t *node)
+format_part_sigstatus_sprinter (sprinter_t *sp, mime_node_t *node)
 {
     const GMimeSignatureValidity* validity = node->sig_validity;
 
@@ -595,23 +595,23 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node,
 }
 
 void
-format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
-		  notmuch_bool_t first, notmuch_bool_t output_body)
+format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
+		      notmuch_bool_t first, notmuch_bool_t output_body)
 {
     /* Any changes to the JSON format should be reflected in the file
      * devel/schemata. */
 
     if (node->envelope_file) {
 	sp->begin_map (sp);
-	format_message_json (sp, node->envelope_file);
+	format_message_sprinter (sp, node->envelope_file);
 
 	sp->map_key (sp, "headers");
-	format_headers_json (sp, GMIME_MESSAGE (node->part), FALSE);
+	format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
 
 	if (output_body) {
 	    sp->map_key (sp, "body");
 	    sp->begin_list (sp);
-	    format_part_json (ctx, sp, mime_node_child (node, 0), first, TRUE);
+	    format_part_sprinter (ctx, sp, mime_node_child (node, 0), first, TRUE);
 	    sp->end (sp);
 	}
 	sp->end (sp);
@@ -646,7 +646,7 @@ format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
 
     if (node->verify_attempted) {
 	sp->map_key (sp, "sigstatus");
-	format_part_sigstatus_json (sp, node);
+	format_part_sigstatus_sprinter (sp, node);
     }
 
     sp->map_key (sp, "content-type");
@@ -698,7 +698,7 @@ format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
 	sp->begin_map (sp);
 
 	sp->map_key (sp, "headers");
-	format_headers_json (sp, GMIME_MESSAGE (node->part), FALSE);
+	format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
 
 	sp->map_key (sp, "body");
 	sp->begin_list (sp);
@@ -706,7 +706,7 @@ format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
     }
 
     for (i = 0; i < node->nchildren; i++)
-	format_part_json (ctx, sp, mime_node_child (node, i), i == 0, TRUE);
+	format_part_sprinter (ctx, sp, mime_node_child (node, i), i == 0, TRUE);
 
     /* Close content structures */
     for (i = 0; i < nclose; i++)
@@ -716,11 +716,11 @@ format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
 }
 
 static notmuch_status_t
-format_part_json_entry (const void *ctx, sprinter_t *sp,
-			mime_node_t *node, unused (int indent),
-			const notmuch_show_params_t *params)
+format_part_sprinter_entry (const void *ctx, sprinter_t *sp,
+			    mime_node_t *node, unused (int indent),
+			    const notmuch_show_params_t *params)
 {
-    format_part_json (ctx, sp, node, TRUE, params->output_body);
+    format_part_sprinter (ctx, sp, node, TRUE, params->output_body);
 
     return NOTMUCH_STATUS_SUCCESS;
 }
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 3/5] Use the S-Expression structured printer in notmuch-show, notmuch-reply and notmuch-search.
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 1/5] Adding an S-expression structured output printer Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 2/5] Rename the -json printer functions in notmuch-reply and notmuch-show to generic -sprinter functions Peter Feigl
@ 2012-12-06 21:12 ` Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 4/5] Adding tests for --format=sexp Peter Feigl
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

This patch uses the new S-Expression printer in the notmuch CLI (show,
search and reply). You can now use --format=sexp for any of them.
---
 devel/schemata   |  8 +++++++-
 notmuch-reply.c  |  5 +++++
 notmuch-search.c |  6 +++++-
 notmuch-show.c   | 36 ++++++++++++++++++++++++------------
 4 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/devel/schemata b/devel/schemata
index e5c5505..d1ab983 100644
--- a/devel/schemata
+++ b/devel/schemata
@@ -1,5 +1,5 @@
 This file describes the schemata used for notmuch's structured output
-format (currently JSON).
+format (currently JSON and S-Expressions).
 
 []'s indicate lists.  List items can be marked with a '?', meaning
 they are optional; or a '*', meaning there can be zero or more of that
@@ -8,6 +8,12 @@ values.  An object field marked '?' is optional.  |'s indicate
 alternates (e.g., int|string means something can be an int or a
 string).
 
+For S-Expression output, lists are printed delimited by () instead of
+[]. Objects are printed as p-lists, i.e. lists where the keys and values
+are interleaved. Keys are printed as keywords (symbols preceded by a
+colon), e.g. (:id "123" :time 54321 :from "foobar"). Null is printed as
+nil, true as t and false as nil.
+
 Common non-terminals
 --------------------
 
diff --git a/notmuch-reply.c b/notmuch-reply.c
index ef450b2..720749d 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -697,6 +697,7 @@ notmuch_reply_format_headers_only(void *ctx,
 enum {
     FORMAT_DEFAULT,
     FORMAT_JSON,
+    FORMAT_SEXP,
     FORMAT_HEADERS_ONLY,
 };
 
@@ -729,6 +730,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
 	{ NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
 	  (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },
 				  { "json", FORMAT_JSON },
+				  { "sexp", FORMAT_SEXP },
 				  { "headers-only", FORMAT_HEADERS_ONLY },
 				  { 0, 0 } } },
 	{ NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',
@@ -750,6 +752,9 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
     } else if (format == FORMAT_JSON) {
 	reply_format_func = notmuch_reply_format_sprinter;
 	sp = sprinter_json_create (ctx, stdout);
+    } else if (format == FORMAT_SEXP) {
+	reply_format_func = notmuch_reply_format_sprinter;
+	sp = sprinter_sexp_create (ctx, stdout);
     } else {
 	reply_format_func = notmuch_reply_format_default;
     }
diff --git a/notmuch-search.c b/notmuch-search.c
index 830c4e4..6218622 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -305,7 +305,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
     int exclude = EXCLUDE_TRUE;
     unsigned int i;
 
-    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT }
+    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT, NOTMUCH_FORMAT_SEXP }
 	format_sel = NOTMUCH_FORMAT_TEXT;
 
     notmuch_opt_desc_t options[] = {
@@ -315,6 +315,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
 				  { 0, 0 } } },
 	{ NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',
 	  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
+				  { "sexp", NOTMUCH_FORMAT_SEXP },
 				  { "text", NOTMUCH_FORMAT_TEXT },
 				  { 0, 0 } } },
 	{ NOTMUCH_OPT_KEYWORD, &output, "output", 'o',
@@ -347,6 +348,9 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
     case NOTMUCH_FORMAT_JSON:
 	format = sprinter_json_create (ctx, stdout);
 	break;
+    case NOTMUCH_FORMAT_SEXP:
+	format = sprinter_sexp_create (ctx, stdout);
+	break;
     default:
 	/* this should never happen */
 	INTERNAL_ERROR("no output format selected");
diff --git a/notmuch-show.c b/notmuch-show.c
index d3f08bc..34a0355 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -40,6 +40,11 @@ static const notmuch_show_format_t format_json = {
     .part = format_part_sprinter_entry,
 };
 
+static const notmuch_show_format_t format_sexp = {
+    .new_sprinter = sprinter_sexp_create,
+    .part = format_part_sprinter_entry,
+};
+
 static notmuch_status_t
 format_part_mbox (const void *ctx, sprinter_t *sp, mime_node_t *node,
 		  int indent, const notmuch_show_params_t *params);
@@ -110,8 +115,8 @@ _get_one_line_summary (const void *ctx, notmuch_message_t *message)
 static void
 format_message_sprinter (sprinter_t *sp, notmuch_message_t *message)
 {
-    /* Any changes to the JSON format should be reflected in the file
-     * devel/schemata. */
+    /* Any changes to the JSON or S-Expression format should be
+     * reflected in the file devel/schemata. */
 
     void *local = talloc_new (NULL);
     notmuch_tags_t *tags;
@@ -211,8 +216,8 @@ void
 format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
 			 notmuch_bool_t reply)
 {
-    /* Any changes to the JSON format should be reflected in the file
-     * devel/schemata. */
+    /* Any changes to the JSON or S-Expression format should be
+     * reflected in the file devel/schemata. */
 
     InternetAddressList *recipients;
     const char *recipients_string;
@@ -365,8 +370,8 @@ signer_status_to_string (GMimeSignerStatus x)
 static void
 format_part_sigstatus_sprinter (sprinter_t *sp, mime_node_t *node)
 {
-    /* Any changes to the JSON format should be reflected in the file
-     * devel/schemata. */
+    /* Any changes to the JSON or S-Expression format should be
+     * reflected in the file devel/schemata. */
 
     GMimeSignatureList *siglist = node->sig_list;
 
@@ -598,8 +603,8 @@ void
 format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
 		      notmuch_bool_t first, notmuch_bool_t output_body)
 {
-    /* Any changes to the JSON format should be reflected in the file
-     * devel/schemata. */
+    /* Any changes to the JSON or S-Expression format should be
+     * reflected in the file devel/schemata. */
 
     if (node->envelope_file) {
 	sp->begin_map (sp);
@@ -1012,6 +1017,7 @@ do_show (void *ctx,
 enum {
     NOTMUCH_FORMAT_NOT_SPECIFIED,
     NOTMUCH_FORMAT_JSON,
+    NOTMUCH_FORMAT_SEXP,
     NOTMUCH_FORMAT_TEXT,
     NOTMUCH_FORMAT_MBOX,
     NOTMUCH_FORMAT_RAW
@@ -1056,6 +1062,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 	{ NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',
 	  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
 				  { "text", NOTMUCH_FORMAT_TEXT },
+				  { "sexp", NOTMUCH_FORMAT_SEXP },
 				  { "mbox", NOTMUCH_FORMAT_MBOX },
 				  { "raw", NOTMUCH_FORMAT_RAW },
 				  { 0, 0 } } },
@@ -1100,6 +1107,9 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
     case NOTMUCH_FORMAT_TEXT:
 	format = &format_text;
 	break;
+    case NOTMUCH_FORMAT_SEXP:
+	format = &format_sexp;
+	break;
     case NOTMUCH_FORMAT_MBOX:
 	if (params.part > 0) {
 	    fprintf (stderr, "Error: specifying parts is incompatible with mbox output format.\n");
@@ -1118,9 +1128,10 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 	break;
     }
 
-    /* Default is entire-thread = FALSE except for format=json. */
+    /* Default is entire-thread = FALSE except for format=json and 
+     * format=sexp. */
     if (entire_thread == ENTIRE_THREAD_DEFAULT) {
-	if (format == &format_json)
+	if (format == &format_json || format == &format_sexp)
 	    entire_thread = ENTIRE_THREAD_TRUE;
 	else
 	    entire_thread = ENTIRE_THREAD_FALSE;
@@ -1131,8 +1142,9 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 	    fprintf (stderr, "Warning: --body=false is incompatible with --part > 0. Disabling.\n");
 	    params.output_body = TRUE;
 	} else {
-	    if (format != &format_json)
-		fprintf (stderr, "Warning: --body=false only implemented for format=json\n");
+	    if (format != &format_json && format != &format_sexp)
+		fprintf (stderr,
+			 "Warning: --body=false only implemented for format=json and format=sexp\n");
 	}
     }
 
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 4/5] Adding tests for --format=sexp.
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
                   ` (2 preceding siblings ...)
  2012-12-06 21:12 ` [PATCH v5 3/5] Use the S-Expression structured printer in notmuch-show, notmuch-reply and notmuch-search Peter Feigl
@ 2012-12-06 21:12 ` Peter Feigl
  2012-12-06 21:12 ` [PATCH v5 5/5] Updating man pages for new S-Expression output format Peter Feigl
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

Add basic tests, the same as for json, for the S-Expression output
format.
---
 test/notmuch-test |  1 +
 test/sexp         | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)
 create mode 100755 test/sexp

diff --git a/test/notmuch-test b/test/notmuch-test
index a6ef34f..ca9c3dc 100755
--- a/test/notmuch-test
+++ b/test/notmuch-test
@@ -31,6 +31,7 @@ TESTS="
   excludes
   tagging
   json
+  sexp
   text
   multipart
   thread-naming
diff --git a/test/sexp b/test/sexp
new file mode 100755
index 0000000..b804942
--- /dev/null
+++ b/test/sexp
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+test_description="--format=sexp output"
+. ./test-lib.sh
+
+test_begin_subtest "Show message: sexp"
+add_message "[subject]=\"sexp-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[bcc]=\"test_suite+bcc@notmuchmail.org\"" "[reply-to]=\"test_suite+replyto@notmuchmail.org\"" "[body]=\"sexp-show-message\""
+output=$(notmuch show --format=sexp "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\"))) ())))"
+
+# This should be the same output as above.
+test_begin_subtest "Show message: sexp --body=true"
+output=$(notmuch show --format=sexp --body=true "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\"))) ())))"
+
+test_begin_subtest "Show message: sexp --body=false"
+output=$(notmuch show --format=sexp --body=false "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+
+test_begin_subtest "Search message: sexp"
+add_message "[subject]=\"sexp-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"sexp-search-message\""
+output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_search_sanitize)
+test_expect_equal "$output" "((:thread \"0000000000000002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :tags (\"inbox\" \"unread\")))"
+
+test_begin_subtest "Show message: sexp, utf-8"
+add_message "[subject]=\"sexp-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
+output=$(notmuch show --format=sexp "jsön-show-méssage")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-utf8-body-sübjéct\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"jsön-show-méssage\n\"))) ())))"
+
+test_begin_subtest "Show message: sexp, inline attachment filename"
+subject='sexp-show-inline-attachment-filename'
+id="sexp-show-inline-attachment-filename@notmuchmail.org"
+emacs_deliver_message \
+    "$subject" \
+    'This is a test message with inline attachment with a filename' \
+    "(mml-attach-file \"$TEST_DIRECTORY/README\" nil nil \"inline\")
+     (message-goto-eoh)
+     (insert \"Message-ID: <$id>\n\")"
+output=$(notmuch show --format=sexp "id:$id")
+filename=$(notmuch search --output=files "id:$id")
+test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename \"$filename\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :filename \"README\"))))) ())))"
+
+test_begin_subtest "Search message: sexp, utf-8"
+add_message "[subject]=\"sexp-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
+output=$(notmuch search --format=sexp "jsön-search-méssage" | notmuch_search_sanitize)
+test_expect_equal "$output" "((:thread \"0000000000000005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-sübjéct\" :tags (\"inbox\" \"unread\")))"
+
+
+test_done
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 5/5] Updating man pages for new S-Expression output format.
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
                   ` (3 preceding siblings ...)
  2012-12-06 21:12 ` [PATCH v5 4/5] Adding tests for --format=sexp Peter Feigl
@ 2012-12-06 21:12 ` Peter Feigl
  2012-12-06 22:13 ` [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Mark Walters
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Peter Feigl @ 2012-12-06 21:12 UTC (permalink / raw)
  To: notmuch

Add sections about the new S-Expression output format (--format=sexp) to
the notmuch-search, notmuch-reply and notmuch-show man pages.
---
 man/man1/notmuch-reply.1  | 14 ++++++++++----
 man/man1/notmuch-search.1 | 15 ++++++++-------
 man/man1/notmuch-show.1   | 36 ++++++++++++++++++++++++++++--------
 3 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/man/man1/notmuch-reply.1 b/man/man1/notmuch-reply.1
index d264060..fa04c9e 100644
--- a/man/man1/notmuch-reply.1
+++ b/man/man1/notmuch-reply.1
@@ -37,7 +37,7 @@ Supported options for
 include
 .RS
 .TP 4
-.BR \-\-format= ( default | json | headers\-only )
+.BR \-\-format= ( default | json | sexp | headers\-only )
 .RS
 .TP 4
 .BR default
@@ -48,6 +48,11 @@ Produces JSON output containing headers for a reply message and the
 contents of the original message. This output can be used by a client
 to create a reply message intelligently.
 .TP
+.BR sexp
+Produces S-Expression output containing headers for a reply message and
+the contents of the original message. This output can be used by a client
+to create a reply message intelligently.
+.TP
 .BR headers\-only
 Only produces In\-Reply\-To, References, To, Cc, and Bcc headers.
 .RE
@@ -74,8 +79,8 @@ user's addresses.
 
 Decrypt any MIME encrypted parts found in the selected content
 (ie. "multipart/encrypted" parts). Status of the decryption will be
-reported (currently only supported with --format=json) and the
-multipart/encrypted part will be replaced by the decrypted
+reported (currently only supported with --format=json and --format=sexp)
+and the multipart/encrypted part will be replaced by the decrypted
 content.
 .RE
 
@@ -89,7 +94,8 @@ id:<message-id>), but it can be useful to reply to several messages at
 once. For example, when a series of patches are sent in a single
 thread, replying to the entire thread allows for the reply to comment
 on issues found in multiple patches. The default format supports
-replying to multiple messages at once, but the JSON format does not.
+replying to multiple messages at once, but the JSON and S-Expression
+formats do not.
 .RE
 .RE
 
diff --git a/man/man1/notmuch-search.1 b/man/man1/notmuch-search.1
index 6ccd3b8..0aff348 100644
--- a/man/man1/notmuch-search.1
+++ b/man/man1/notmuch-search.1
@@ -25,9 +25,9 @@ Supported options for
 include
 .RS 4
 .TP 4
-.BR \-\-format= ( json | text )
+.BR \-\-format= ( json | sexp | text )
 
-Presents the results in either JSON or plain-text (default).
+Presents the results in either JSON, S-Expressions or plain-text (default).
 .RE
 
 .RS 4
@@ -49,7 +49,7 @@ the authors of the thread and the subject.
 
 Output the thread IDs of all threads with any message matching the
 search terms, either one per line (\-\-format=text) or as a JSON array
-(\-\-format=json).
+(\-\-format=json) or an S-Expression list (\-\-format=sexp).
 .RE
 .RS 4
 .TP 4
@@ -57,22 +57,23 @@ search terms, either one per line (\-\-format=text) or as a JSON array
 
 Output the message IDs of all messages matching the search terms,
 either one per line (\-\-format=text) or as a JSON array
-(\-\-format=json).
+(\-\-format=json) or as an S-Expression list (\-\-format=sexp).
 .RE
 .RS 4
 .TP 4
 .B files
 
 Output the filenames of all messages matching the search terms, either
-one per line (\-\-format=text) or as a JSON array (\-\-format=json).
+one per line (\-\-format=text) or as a JSON array (\-\-format=json) or
+as an S-Expression list (\-\-format=sexp).
 .RE
 .RS 4
 .TP 4
 .B tags
 
 Output all tags that appear on any message matching the search terms,
-either one per line (\-\-format=text) or as a JSON array
-(\-\-format=json).
+either one per line (\-\-format=text) or as a JSON array (\-\-format=json)
+or as an S-Expression list (\-\-format=sexp).
 .RE
 .RE
 
diff --git a/man/man1/notmuch-show.1 b/man/man1/notmuch-show.1
index 4481f21..bd41c48 100644
--- a/man/man1/notmuch-show.1
+++ b/man/man1/notmuch-show.1
@@ -31,12 +31,14 @@ If true,
 outputs all messages in the thread of any message matching the search
 terms; if false, it outputs only the matching messages. For
 .B --format=json
+and
+.B --format=sexp
 this defaults to true.  For other formats, this defaults to false.
 .RE
 
 .RS 4
 .TP 4
-.B \-\-format=(text|json|mbox|raw)
+.B \-\-format=(text|json|sexp|mbox|raw)
 
 .RS 4
 .TP 4
@@ -60,11 +62,29 @@ format is more robust than the text format for automated
 processing. The nested structure of multipart MIME messages is
 reflected in nested JSON output. By default JSON output includes all
 messages in a matching thread; that is, by default,
+
 .B \-\-format=json
 sets
 .B "\-\-entire\-thread"
 The caller can disable this behaviour by setting
 .B \-\-entire\-thread=false
+.RE
+.RS 4
+.TP 4
+.B sexp
+
+The output is formatted as an S-Expression (sexp). This
+format is more robust than the text format for automated
+processing. The nested structure of multipart MIME messages is
+reflected in nested S-Expression output. By default,
+S-Expression output includes all messages in a matching thread;
+that is, by default,
+
+.B \-\-format=sexp
+sets
+.B "\-\-entire\-thread"
+The caller can disable this behaviour by setting
+.B \-\-entire\-thread=false
 
 .RE
 .RS 4
@@ -113,7 +133,7 @@ message.
 Output the single decoded MIME part N of a single message.  The search
 terms must match only a single message.  Message parts are numbered in
 a depth-first walk of the message MIME structure, and are identified
-in the 'json' or 'text' output formats.
+in the 'json', 'sexp' or 'text' output formats.
 .RE
 
 .RS 4
@@ -123,8 +143,8 @@ in the 'json' or 'text' output formats.
 Compute and report the validity of any MIME cryptographic signatures
 found in the selected content (ie. "multipart/signed" parts). Status
 of the signature will be reported (currently only supported with
---format=json), and the multipart/signed part will be replaced by the
-signed data.
+--format=json and --format=sexp), and the multipart/signed part
+will be replaced by the signed data.
 .RE
 
 .RS 4
@@ -133,9 +153,9 @@ signed data.
 
 Decrypt any MIME encrypted parts found in the selected content
 (ie. "multipart/encrypted" parts). Status of the decryption will be
-reported (currently only supported with --format=json) and the
-multipart/encrypted part will be replaced by the decrypted
-content.  Implies --verify.
+reported (currently only supported with --format=json and
+--format=sexp) and the multipart/encrypted part will be replaced
+by the decrypted content.  Implies --verify.
 .RE
 
 .RS 4
@@ -166,7 +186,7 @@ If true (the default)
 includes the bodies of the messages in the output; if false,
 bodies are omitted.
 .B --body=false
-is only implemented for the json format and it is incompatible with
+is only implemented for the json and sexp formats and it is incompatible with
 .B --part > 0.
 
 This is useful if the caller only needs the headers as body-less
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 0/5] New output format sexp (Lisp S-Expressions)
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
                   ` (4 preceding siblings ...)
  2012-12-06 21:12 ` [PATCH v5 5/5] Updating man pages for new S-Expression output format Peter Feigl
@ 2012-12-06 22:13 ` Mark Walters
  2012-12-07 13:11   ` David Bremner
  2012-12-07 16:03 ` Austin Clements
  2012-12-08 13:34 ` David Bremner
  7 siblings, 1 reply; 10+ messages in thread
From: Mark Walters @ 2012-12-06 22:13 UTC (permalink / raw)
  To: Peter Feigl, notmuch


On Thu, 06 Dec 2012, Peter Feigl <craven@gmx.net> wrote:
> This patch series adds a new output format "sexp" to notmuch-reply,
> notmuch-show and notmuch-search. These are useful for the Android mobile
> client, Emacs and perhaps other Lisp programs as well.
> After the switch to a generic structured output printer, which was
> committed some months ago, these patches just add another one (like the
> json structured output printer).
> Basic tests and updates to the man pages are also included.
>
> This version adresses comments by Tomi Ollila and Austin Clements.

This looks good to me except there are a couple of trailing whitespace
errors

Best wishes

Mark


>
> Old versions:
> v4: id:cover.1354794428.git.craven@gmx.net
> v3: id:1354779189-12231-1-git-send-email-craven@gmx.net
> v2: id:1354632382-15609-1-git-send-email-craven@gmx.net
> v1: 1354264143-30173-1-git-send-email-craven@gmx.net
>
> Peter Feigl (5):
>   Adding an S-expression structured output printer.
>   Rename the -json printer functions in notmuch-reply and notmuch-show
>     to generic -sprinter functions.
>   Use the S-Expression structured printer in notmuch-show,
>     notmuch-reply and notmuch-search.
>   Adding tests for --format=sexp.
>   Updating man pages for new S-Expression output format.
>
>  Makefile.local            |   1 +
>  devel/schemata            |  24 +++--
>  man/man1/notmuch-reply.1  |  14 ++-
>  man/man1/notmuch-search.1 |  15 +--
>  man/man1/notmuch-show.1   |  36 +++++--
>  notmuch-client.h          |   8 +-
>  notmuch-reply.c           |  48 ++++++----
>  notmuch-search.c          |   6 +-
>  notmuch-show.c            |  76 ++++++++-------
>  sprinter-sexp.c           | 235 ++++++++++++++++++++++++++++++++++++++++++++++
>  sprinter.h                |   4 +
>  test/notmuch-test         |   1 +
>  test/sexp                 |  48 ++++++++++
>  13 files changed, 434 insertions(+), 82 deletions(-)
>  create mode 100644 sprinter-sexp.c
>  create mode 100755 test/sexp
>
> -- 
> 1.8.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 0/5] New output format sexp (Lisp S-Expressions)
  2012-12-06 22:13 ` [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Mark Walters
@ 2012-12-07 13:11   ` David Bremner
  0 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2012-12-07 13:11 UTC (permalink / raw)
  To: Mark Walters, Peter Feigl, notmuch

Mark Walters <markwalters1009@gmail.com> writes:

>
> This looks good to me except there are a couple of trailing whitespace
> errors
>

If the whitespace is the only problem, I can fix that without another
series.

d

P.S. Peter, you probably want to do something like

% cd .git/hooks && mv pre-commit.sample pre-commit && chmod 755  pre-commit

this will prevent commits with trailing whitespace.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 0/5] New output format sexp (Lisp S-Expressions)
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
                   ` (5 preceding siblings ...)
  2012-12-06 22:13 ` [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Mark Walters
@ 2012-12-07 16:03 ` Austin Clements
  2012-12-08 13:34 ` David Bremner
  7 siblings, 0 replies; 10+ messages in thread
From: Austin Clements @ 2012-12-07 16:03 UTC (permalink / raw)
  To: Peter Feigl, notmuch

On Thu, 06 Dec 2012, Peter Feigl <craven@gmx.net> wrote:
> This patch series adds a new output format "sexp" to notmuch-reply,
> notmuch-show and notmuch-search. These are useful for the Android mobile
> client, Emacs and perhaps other Lisp programs as well.
> After the switch to a generic structured output printer, which was
> committed some months ago, these patches just add another one (like the
> json structured output printer).
> Basic tests and updates to the man pages are also included.

LGTM.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 0/5] New output format sexp (Lisp S-Expressions)
  2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
                   ` (6 preceding siblings ...)
  2012-12-07 16:03 ` Austin Clements
@ 2012-12-08 13:34 ` David Bremner
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2012-12-08 13:34 UTC (permalink / raw)
  To: Peter Feigl, notmuch

Peter Feigl <craven@gmx.net> writes:

> This patch series adds a new output format "sexp" to notmuch-reply,
> notmuch-show and notmuch-search. These are useful for the Android mobile
> client, Emacs and perhaps other Lisp programs as well.

pushed, looking forward to an android client ;)

d

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-12-08 13:34 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-12-06 21:12 [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Peter Feigl
2012-12-06 21:12 ` [PATCH v5 1/5] Adding an S-expression structured output printer Peter Feigl
2012-12-06 21:12 ` [PATCH v5 2/5] Rename the -json printer functions in notmuch-reply and notmuch-show to generic -sprinter functions Peter Feigl
2012-12-06 21:12 ` [PATCH v5 3/5] Use the S-Expression structured printer in notmuch-show, notmuch-reply and notmuch-search Peter Feigl
2012-12-06 21:12 ` [PATCH v5 4/5] Adding tests for --format=sexp Peter Feigl
2012-12-06 21:12 ` [PATCH v5 5/5] Updating man pages for new S-Expression output format Peter Feigl
2012-12-06 22:13 ` [PATCH v5 0/5] New output format sexp (Lisp S-Expressions) Mark Walters
2012-12-07 13:11   ` David Bremner
2012-12-07 16:03 ` Austin Clements
2012-12-08 13:34 ` David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).