]> git.sur5r.net Git - i3/i3/blob - i3bar/src/determine_json_version.c
docs/i3bar-protocol: Document stop/cont_signal
[i3/i3] / i3bar / src / determine_json_version.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3bar - an xcb-based status- and ws-bar for i3
5  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
6  *
7  * determine_json_version.c: Determines the JSON protocol version based on the
8  *                           first line of input from a child program.
9  *
10  */
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <err.h>
21 #include <ev.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <yajl/yajl_common.h>
25 #include <yajl/yajl_parse.h>
26 #include <yajl/yajl_version.h>
27
28 static bool version_key;
29 static int32_t version_number;
30
31 #if YAJL_MAJOR >= 2
32 static int version_integer(void *ctx, long long val) {
33 #else
34 static int version_integer(void *ctx, long val) {
35 #endif
36     if (version_key)
37         version_number = (uint32_t)val;
38     return 1;
39 }
40
41 #if YAJL_MAJOR >= 2
42 static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
43 #else
44 static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
45 #endif
46     version_key = (stringlen == strlen("version") &&
47                    strncmp((const char*)stringval, "version", strlen("version")) == 0);
48     return 1;
49 }
50
51 static yajl_callbacks version_callbacks = {
52     NULL, /* null */
53     NULL, /* boolean */
54     &version_integer,
55     NULL, /* double */
56     NULL, /* number */
57     NULL, /* string */
58     NULL, /* start_map */
59     &version_map_key,
60     NULL, /* end_map */
61     NULL, /* start_array */
62     NULL /* end_array */
63 };
64
65 /*
66  * Determines the JSON i3bar protocol version from the given buffer. In case
67  * the buffer does not contain valid JSON, or no version field is found, this
68  * function returns -1. The amount of bytes consumed by parsing the header is
69  * returned in *consumed (if non-NULL).
70  *
71  * The return type is an int32_t to avoid machines with different sizes of
72  * 'int' to allow different values here. It’s highly unlikely we ever exceed
73  * even an int8_t, but still…
74  *
75  */
76 int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) {
77 #if YAJL_MAJOR >= 2
78     yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
79     /* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
80      * yajl 2, we need to be explicit. */
81     yajl_config(handle, yajl_allow_trailing_garbage, 1);
82 #else
83     yajl_parser_config parse_conf = { 0, 0 };
84
85     yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
86 #endif
87
88     version_key = false;
89     version_number = -1;
90
91     yajl_status state = yajl_parse(handle, buffer, length);
92     if (state != yajl_status_ok) {
93         version_number = -1;
94         if (consumed != NULL)
95             *consumed = 0;
96     } else {
97         if (consumed != NULL)
98             *consumed = yajl_get_bytes_consumed(handle);
99     }
100
101     yajl_free(handle);
102
103     return version_number;
104 }