i3/i3bar/src/parse_json_header.c

152 lines
3.8 KiB
C
Raw Normal View History

/*
* 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)
*
2012-09-03 04:43:29 -04:00
* parse_json_header.c: Parse the JSON protocol header to determine
* protocol version and features.
*
*/
#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>
2012-09-03 04:43:29 -04:00
#include "common.h"
static enum {
KEY_VERSION,
KEY_STOP_SIGNAL,
KEY_CONT_SIGNAL,
KEY_CLICK_EVENTS,
2012-09-03 04:43:29 -04:00
NO_KEY
} current_key;
#if YAJL_MAJOR >= 2
2012-09-03 04:43:29 -04:00
static int header_integer(void *ctx, long long val) {
#else
2012-09-03 04:43:29 -04:00
static int header_integer(void *ctx, long val) {
#endif
2012-09-03 04:43:29 -04:00
i3bar_child *child = ctx;
switch (current_key) {
case KEY_VERSION:
child->version = val;
break;
case KEY_STOP_SIGNAL:
child->stop_signal = val;
break;
case KEY_CONT_SIGNAL:
child->cont_signal = val;
break;
2012-09-03 04:43:29 -04:00
default:
break;
}
return 1;
}
static int header_boolean(void *ctx, int val) {
i3bar_child *child = ctx;
switch (current_key) {
case KEY_CLICK_EVENTS:
child->click_events = val;
break;
default:
break;
}
return 1;
}
2012-09-03 04:43:29 -04:00
#define CHECK_KEY(name) (stringlen == strlen(name) && \
STARTS_WITH((const char*)stringval, stringlen, name))
#if YAJL_MAJOR >= 2
2012-09-03 04:43:29 -04:00
static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
#else
2012-09-03 04:43:29 -04:00
static int header_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
#endif
2012-09-03 04:43:29 -04:00
if (CHECK_KEY("version")) {
current_key = KEY_VERSION;
} else if (CHECK_KEY("stop_signal")) {
current_key = KEY_STOP_SIGNAL;
} else if (CHECK_KEY("cont_signal")) {
current_key = KEY_CONT_SIGNAL;
} else if (CHECK_KEY("click_events")) {
current_key = KEY_CLICK_EVENTS;
2012-09-03 04:43:29 -04:00
}
return 1;
}
static yajl_callbacks version_callbacks = {
NULL, /* null */
&header_boolean, /* boolean */
2012-09-03 04:43:29 -04:00
&header_integer,
NULL, /* double */
NULL, /* number */
NULL, /* string */
NULL, /* start_map */
2012-09-03 04:43:29 -04:00
&header_map_key,
NULL, /* end_map */
NULL, /* start_array */
NULL /* end_array */
};
2012-09-03 04:43:29 -04:00
static void child_init(i3bar_child *child) {
child->version = 0;
child->stop_signal = SIGSTOP;
child->cont_signal = SIGCONT;
2012-09-03 04:43:29 -04:00
}
/*
2012-09-03 04:43:29 -04:00
* Parse the JSON protocol header to determine protocol version and features.
* In case the buffer does not contain a valid header (invalid JSON, or no
* version field found), the 'correct' field of the returned header is set to
* false. The amount of bytes consumed by parsing the header is returned in
* *consumed (if non-NULL).
*
*/
2012-09-03 04:43:29 -04:00
void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed) {
child_init(child);
current_key = NO_KEY;
#if YAJL_MAJOR >= 2
2012-09-03 04:43:29 -04:00
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, child);
/* 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 };
2012-09-03 04:43:29 -04:00
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, child);
#endif
yajl_status state = yajl_parse(handle, buffer, length);
if (state != yajl_status_ok) {
2012-09-03 04:43:29 -04:00
child_init(child);
if (consumed != NULL)
*consumed = 0;
} else {
if (consumed != NULL)
*consumed = yajl_get_bytes_consumed(handle);
}
yajl_free(handle);
}