]> git.sur5r.net Git - i3/i3/blob - i3bar/src/parse_json_header.c
Merge branch 'master' into next
[i3/i3] / i3bar / src / parse_json_header.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  * parse_json_header.c: Parse the JSON protocol header to determine
8  *                      protocol version and features.
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 #include "common.h"
29
30 static enum {
31     KEY_VERSION,
32     KEY_STOP_SIGNAL,
33     KEY_CONT_SIGNAL,
34     KEY_CLICK_EVENTS,
35     NO_KEY
36 } current_key;
37
38 #if YAJL_MAJOR >= 2
39 static int header_integer(void *ctx, long long val) {
40 #else
41 static int header_integer(void *ctx, long val) {
42 #endif
43     i3bar_child *child = ctx;
44
45     switch (current_key) {
46         case KEY_VERSION:
47             child->version = val;
48             break;
49         case KEY_STOP_SIGNAL:
50             child->stop_signal = val;
51             break;
52         case KEY_CONT_SIGNAL:
53             child->cont_signal = val;
54             break;
55         default:
56             break;
57     }
58
59     return 1;
60 }
61
62 static int header_boolean(void *ctx, int val) {
63     i3bar_child *child = ctx;
64
65     switch (current_key) {
66         case KEY_CLICK_EVENTS:
67             child->click_events = val;
68             break;
69         default:
70             break;
71     }
72
73     return 1;
74 }
75
76 #define CHECK_KEY(name) (stringlen == strlen(name) && \
77                          STARTS_WITH((const char*)stringval, stringlen, name))
78
79 #if YAJL_MAJOR >= 2
80 static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
81 #else
82 static int header_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
83 #endif
84     if (CHECK_KEY("version")) {
85         current_key = KEY_VERSION;
86     } else if (CHECK_KEY("stop_signal")) {
87         current_key = KEY_STOP_SIGNAL;
88     } else if (CHECK_KEY("cont_signal")) {
89         current_key = KEY_CONT_SIGNAL;
90     } else if (CHECK_KEY("click_events")) {
91         current_key = KEY_CLICK_EVENTS;
92     }
93     return 1;
94 }
95
96 static yajl_callbacks version_callbacks = {
97     NULL, /* null */
98     &header_boolean, /* boolean */
99     &header_integer,
100     NULL, /* double */
101     NULL, /* number */
102     NULL, /* string */
103     NULL, /* start_map */
104     &header_map_key,
105     NULL, /* end_map */
106     NULL, /* start_array */
107     NULL /* end_array */
108 };
109
110 static void child_init(i3bar_child *child) {
111     child->version = 0;
112     child->stop_signal = SIGSTOP;
113     child->cont_signal = SIGCONT;
114 }
115
116 /*
117  * Parse the JSON protocol header to determine protocol version and features.
118  * In case the buffer does not contain a valid header (invalid JSON, or no
119  * version field found), the 'correct' field of the returned header is set to
120  * false. The amount of bytes consumed by parsing the header is returned in
121  * *consumed (if non-NULL).
122  *
123  */
124 void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed) {
125     child_init(child);
126
127     current_key = NO_KEY;
128
129 #if YAJL_MAJOR >= 2
130     yajl_handle handle = yajl_alloc(&version_callbacks, NULL, child);
131     /* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
132      * yajl 2, we need to be explicit. */
133     yajl_config(handle, yajl_allow_trailing_garbage, 1);
134 #else
135     yajl_parser_config parse_conf = { 0, 0 };
136
137     yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, child);
138 #endif
139
140     yajl_status state = yajl_parse(handle, buffer, length);
141     if (state != yajl_status_ok) {
142         child_init(child);
143         if (consumed != NULL)
144             *consumed = 0;
145     } else {
146         if (consumed != NULL)
147             *consumed = yajl_get_bytes_consumed(handle);
148     }
149
150     yajl_free(handle);
151 }