]> git.sur5r.net Git - i3/i3/commitdiff
i3bar: be less strict about the {"version":1} JSON header
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 12 Aug 2012 16:40:15 +0000 (18:40 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 12 Aug 2012 16:40:15 +0000 (18:40 +0200)
i3bar/include/common.h
i3bar/include/determine_json_version.h [new file with mode: 0644]
i3bar/src/child.c
i3bar/src/determine_json_version.c [new file with mode: 0644]

index 212b9dd1d8d55d887de02c2aa6f693a21b4d6b3e..59fcc44a621650cbeb68ea95a8940f536c3fc4ff 100644 (file)
@@ -56,5 +56,6 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
 #include "xcb.h"
 #include "config.h"
 #include "libi3.h"
+#include "determine_json_version.h"
 
 #endif
diff --git a/i3bar/include/determine_json_version.h b/i3bar/include/determine_json_version.h
new file mode 100644 (file)
index 0000000..52c6f5d
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3bar - an xcb-based status- and ws-bar for i3
+ * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ *
+ * determine_json_version.c: Determines the JSON protocol version based on the
+ *                           first line of input from a child program.
+ *
+ */
+#ifndef DETERMINE_JSON_VERSION_H_
+#define DETERMINE_JSON_VERSION_H_
+
+#include <stdint.h>
+
+/*
+ * Determines the JSON i3bar protocol version from the given buffer. In case
+ * the buffer does not contain valid JSON, or no version field is found, this
+ * function returns -1. The amount of bytes consumed by parsing the header is
+ * returned in *consumed (if non-NULL).
+ *
+ * The return type is an int32_t to avoid machines with different sizes of
+ * 'int' to allow different values here. It’s highly unlikely we ever exceed
+ * even an int8_t, but still…
+ *
+ */
+int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed);
+
+#endif
index 12782caf130490755f6363f2abb9baf83f6042b8..51f5fe82515422dee84ea5ed5860afc3a4fef11e 100644 (file)
@@ -192,19 +192,18 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
     if (first_line) {
         DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
         /* Detect whether this is JSON or plain text. */
-        plaintext = (strncasecmp((char*)buffer, "{\"version\":", strlen("{\"version\":")) != 0);
+        unsigned int consumed = 0;
+        /* At the moment, we don’t care for the version. This might change
+         * in the future, but for now, we just discard it. */
+        plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1);
         if (plaintext) {
             /* In case of plaintext, we just add a single block and change its
              * full_text pointer later. */
             struct status_block *new_block = scalloc(sizeof(struct status_block));
             TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
         } else {
-            /* At the moment, we don’t care for the version. This might change
-             * in the future, but for now, we just discard it. */
-            while (*json_input != '\n' && *json_input != '\0') {
-                json_input++;
-                rec--;
-            }
+            json_input += consumed;
+            rec -= consumed;
         }
         first_line = false;
     }
diff --git a/i3bar/src/determine_json_version.c b/i3bar/src/determine_json_version.c
new file mode 100644 (file)
index 0000000..abd4303
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3bar - an xcb-based status- and ws-bar for i3
+ * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ *
+ * determine_json_version.c: Determines the JSON protocol version based on the
+ *                           first line of input from a child program.
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+#include <ev.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <yajl/yajl_common.h>
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_version.h>
+
+static bool version_key;
+static int32_t version_number;
+
+#if YAJL_MAJOR >= 2
+static int version_integer(void *ctx, long long val) {
+#else
+static int version_integer(void *ctx, long val) {
+#endif
+    if (version_key)
+        version_number = (uint32_t)val;
+    return 1;
+}
+
+#if YAJL_MAJOR >= 2
+static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
+#else
+static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
+#endif
+    version_key = (stringlen == strlen("version") &&
+                   strncmp((const char*)stringval, "version", strlen("version")) == 0);
+    return 1;
+}
+
+static yajl_callbacks version_callbacks = {
+    NULL, /* null */
+    NULL, /* boolean */
+    &version_integer,
+    NULL, /* double */
+    NULL, /* number */
+    NULL, /* string */
+    NULL, /* start_map */
+    &version_map_key,
+    NULL, /* end_map */
+    NULL, /* start_array */
+    NULL /* end_array */
+};
+
+/*
+ * Determines the JSON i3bar protocol version from the given buffer. In case
+ * the buffer does not contain valid JSON, or no version field is found, this
+ * function returns -1. The amount of bytes consumed by parsing the header is
+ * returned in *consumed (if non-NULL).
+ *
+ * The return type is an int32_t to avoid machines with different sizes of
+ * 'int' to allow different values here. It’s highly unlikely we ever exceed
+ * even an int8_t, but still…
+ *
+ */
+int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) {
+#if YAJL_MAJOR >= 2
+    yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
+    /* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
+     * yajl 2, we need to be explicit. */
+    yajl_config(handle, yajl_allow_trailing_garbage, 1);
+#else
+    yajl_parser_config parse_conf = { 0, 0 };
+
+    yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
+#endif
+
+    version_key = false;
+    version_number = -1;
+
+    yajl_status state = yajl_parse(handle, buffer, length);
+    if (state != yajl_status_ok) {
+        version_number = -1;
+        if (consumed != NULL)
+            *consumed = 0;
+    } else {
+        if (consumed != NULL)
+            *consumed = yajl_get_bytes_consumed(handle);
+    }
+
+    yajl_free(handle);
+
+    return version_number;
+}