Proof of concept: S-Expression format
craven at gmx.net
craven at gmx.net
Fri Jul 13 01:17:56 PDT 2012
This patch shows how to add a new output format to notmuch-search.c.
As an example, it adds S-Expressions. The concrete formatting can
easily be changed, this is meant as a proof of concept that the
changes to core notmuch code are very few and all formatting state is
kept inside sprinter-sexp.c.
---
Makefile.local | 1 +
notmuch-search.c | 6 +-
sprinter-sexp.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
sprinter.h | 4 ++
4 files changed, 195 insertions(+), 1 deletion(-)
create mode 100644 sprinter-sexp.c
diff --git a/Makefile.local b/Makefile.local
index b6c7e0c..cc1d58a 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -292,6 +292,7 @@ notmuch_client_srcs = \
notmuch-time.c \
sprinter-json.c \
sprinter-text-search.c \
+ sprinter-sexp.c \
query-string.c \
mime-node.c \
crypto.c \
diff --git a/notmuch-search.c b/notmuch-search.c
index 99fddac..2db58a5 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -256,7 +256,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[] = {
@@ -267,6 +267,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
{ NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',
(notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
{ "text", NOTMUCH_FORMAT_TEXT },
+ { "sexp", NOTMUCH_FORMAT_SEXP },
{ 0, 0 } } },
{ NOTMUCH_OPT_KEYWORD, &output, "output", 'o',
(notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
@@ -298,6 +299,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;
}
config = notmuch_config_open (ctx, NULL, NULL);
diff --git a/sprinter-sexp.c b/sprinter-sexp.c
new file mode 100644
index 0000000..68a5db5
--- /dev/null
+++ b/sprinter-sexp.c
@@ -0,0 +1,185 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <talloc.h>
+#include "sprinter.h"
+
+typedef enum { MAP, LIST } aggregate_t;
+
+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;
+};
+
+struct sexp_state {
+ struct sexp_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. */
+ aggregate_t type;
+};
+
+static struct sprinter_sexp *
+sexp_begin_value (struct sprinter *sp)
+{
+ struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;
+
+ if (spsx->state) {
+ if (! spsx->state->first)
+ fputc (' ', spsx->stream);
+ else
+ spsx->state->first = FALSE;
+ }
+ return spsx;
+}
+
+static void
+sexp_begin_aggregate (struct sprinter *sp, aggregate_t type)
+{
+ struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;
+ struct sexp_state *state = talloc (spsx, struct sexp_state);
+
+ fputc ('(', spsx->stream);
+ state->parent = spsx->state;
+ state->first = TRUE;
+ state->type = type;
+
+ spsx->state = state;
+}
+
+static void
+sexp_begin_map (struct sprinter *sp)
+{
+ sexp_begin_aggregate (sp, MAP);
+}
+
+static void
+sexp_begin_list (struct sprinter *sp)
+{
+ sexp_begin_aggregate (sp, LIST);
+}
+
+static void
+sexp_end (struct sprinter *sp)
+{
+ struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;
+ struct sexp_state *state = spsx->state;
+
+ fputc (')', spsx->stream);
+ spsx->state = state->parent;
+ talloc_free (state);
+ if (spsx->state == NULL)
+ fputc ('\n', spsx->stream);
+ else
+ if (spsx->state->type == MAP)
+ fputc (')', spsx->stream);
+}
+
+static void
+sexp_string (struct sprinter *sp, const char *val)
+{
+ static const char *const escapes[] = {
+ ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",
+ ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t"
+ };
+ struct sprinter_sexp *spsx = sexp_begin_value (sp);
+
+ fputc ('"', spsx->stream);
+ for (; *val; ++val) {
+ unsigned char ch = *val;
+ if (ch < ARRAY_SIZE (escapes) && escapes[ch])
+ fputs (escapes[ch], spsx->stream);
+ else if (ch >= 32)
+ fputc (ch, spsx->stream);
+ else
+ fprintf (spsx->stream, "\\u%04x", ch);
+ }
+ fputc ('"', spsx->stream);
+ if (spsx->state != NULL && spsx->state->type == MAP)
+ fputc (')', spsx->stream);
+ spsx->state->first = FALSE;
+}
+
+static void
+sexp_integer (struct sprinter *sp, int val)
+{
+ struct sprinter_sexp *spsx = sexp_begin_value (sp);
+
+ fprintf (spsx->stream, "%d", val);
+ if (spsx->state != NULL && spsx->state->type == MAP)
+ fputc (')', spsx->stream);
+}
+
+static void
+sexp_boolean (struct sprinter *sp, notmuch_bool_t val)
+{
+ struct sprinter_sexp *spsx = sexp_begin_value (sp);
+
+ fputs (val ? "#t" : "#f", spsx->stream);
+ if (spsx->state != NULL && spsx->state->type == MAP)
+ fputc (')', spsx->stream);
+}
+
+static void
+sexp_null (struct sprinter *sp)
+{
+ struct sprinter_sexp *spsx = sexp_begin_value (sp);
+
+ fputs ("'()", spsx->stream);
+ spsx->state->first = FALSE;
+}
+
+static void
+sexp_map_key (struct sprinter *sp, const char *key)
+{
+ struct sprinter_sexp *spsx = sexp_begin_value (sp);
+
+ fputc ('(', spsx->stream);
+ fputs (key, spsx->stream);
+ fputs (" . ", spsx->stream);
+ spsx->state->first = TRUE;
+}
+
+static void
+sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))
+{
+}
+
+static void
+sexp_separator (struct sprinter *sp)
+{
+ struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;
+
+ fputc ('\n', spsx->stream);
+}
+
+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,
+ .integer = sexp_integer,
+ .boolean = sexp_boolean,
+ .null = sexp_null,
+ .map_key = sexp_map_key,
+ .separator = sexp_separator,
+ .set_prefix = sexp_set_prefix,
+ }
+ };
+ 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 4241d65..c0146f6 100644
--- a/sprinter.h
+++ b/sprinter.h
@@ -55,4 +55,8 @@ sprinter_text_search_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.7.11.1
More information about the notmuch
mailing list