Files
i3/i3bar/src/parse_json_header.c
Quentin Glidic 8210c6be79 i3bar: Allow child to specify signals to use
We now wait for the child process to send the first line before stopping
it to use the signal which might be specified in the i3bar protocol
header
Since clients might use the same signal for both stop and cont, we also
save the stopped state of the child to avoid stopping it while hidden!
2012-09-22 15:13:21 +02:00

134 lines
3.5 KiB
C

/*
* 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)
*
* 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>
#include "common.h"
static enum {
KEY_VERSION,
KEY_STOP_SIGNAL,
KEY_CONT_SIGNAL,
NO_KEY
} current_key;
#if YAJL_MAJOR >= 2
static int header_integer(void *ctx, long long val) {
#else
static int header_integer(void *ctx, long val) {
#endif
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;
default:
break;
}
return 1;
}
#define CHECK_KEY(name) (stringlen == strlen(name) && \
STARTS_WITH((const char*)stringval, stringlen, name))
#if YAJL_MAJOR >= 2
static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
#else
static int header_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
#endif
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;
}
return 1;
}
static yajl_callbacks version_callbacks = {
NULL, /* null */
NULL, /* boolean */
&header_integer,
NULL, /* double */
NULL, /* number */
NULL, /* string */
NULL, /* start_map */
&header_map_key,
NULL, /* end_map */
NULL, /* start_array */
NULL /* end_array */
};
static void child_init(i3bar_child *child) {
child->version = 0;
child->stop_signal = SIGSTOP;
child->cont_signal = SIGCONT;
}
/*
* 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).
*
*/
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
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 };
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) {
child_init(child);
if (consumed != NULL)
*consumed = 0;
} else {
if (consumed != NULL)
*consumed = yajl_get_bytes_consumed(handle);
}
yajl_free(handle);
}