Added read-only helper functions to the json api which handle errors.

This commit is contained in:
Dane Jensen
2026-08-15 10:50:48 -07:00
parent 924d4aa274
commit b7aa734066
3 changed files with 191 additions and 65 deletions

134
json.c
View File

@@ -26,7 +26,8 @@
#include "tmux.h"
#define TOKENS_MAX 4096
#define INPUT_MAX 8192
#define TOKENS_MAX (INPUT_MAX)
#define ERROR_CTX_LEN 8
/* JSON Token types. */
@@ -115,6 +116,7 @@ static struct json_node *json_parse_boolean(struct json_token **, const char *,
static int json_node_cmp(struct json_node *, struct json_node *);
RB_GENERATE_STATIC(json_fields, json_node, oentry, json_node_cmp);
/* Comparator for the json_fields tree. */
static int
json_node_cmp(struct json_node *a, struct json_node *b)
{
@@ -133,6 +135,7 @@ json_parse(const char *input, char **cause)
return (json_parse_tokens(&tokens, cause));
}
/* Returns a field node from an object node. */
struct json_node *
json_find(struct json_node *jn, const char *key)
{
@@ -145,8 +148,9 @@ json_find(struct json_node *jn, const char *key)
return (RB_FIND(json_fields, &jn->fields, &tmp));
}
/* Returns the first member of an array node. */
struct json_node *
json_array_first(struct json_node *jn)
json_array_first(const struct json_node *jn)
{
if (jn->type != NODE_ARRAY)
return (NULL);
@@ -154,8 +158,9 @@ json_array_first(struct json_node *jn)
return (TAILQ_FIRST(&jn->members));
}
/* Returns the next member of an array's member node. */
struct json_node *
json_array_next(struct json_node *jn)
json_array_next(const struct json_node *jn)
{
if (jn->parent->type != NODE_ARRAY)
return (NULL);
@@ -163,6 +168,7 @@ json_array_next(struct json_node *jn)
return (TAILQ_NEXT(jn, aentry));
}
/* Returns the string value from a node. */
int
json_get_string(struct json_node *jn, const char **s)
{
@@ -173,6 +179,7 @@ json_get_string(struct json_node *jn, const char **s)
return (0);
}
/* Returns the number value from a node. */
int
json_get_number(struct json_node *jn, int64_t *i)
{
@@ -183,6 +190,7 @@ json_get_number(struct json_node *jn, int64_t *i)
return (0);
}
/* Returns the boolean value from a node. */
int
json_get_boolean(struct json_node *jn, int *b)
{
@@ -193,6 +201,7 @@ json_get_boolean(struct json_node *jn, int *b)
return (0);
}
/* Returns the object value from a node. */
int
json_get_object(struct json_node *jn, struct json_node **o)
{
@@ -202,6 +211,8 @@ json_get_object(struct json_node *jn, struct json_node **o)
*o = jn;
return (0);
}
/* Returns the array value from a node. */
int
json_get_array(struct json_node *jn, struct json_node **a)
{
@@ -212,6 +223,123 @@ json_get_array(struct json_node *jn, struct json_node **a)
return (0);
}
/* Returns the string value from a given key in an object node. */
const char *
json_find_string(const struct json_node *jn, const char *key, char **cause)
{
struct json_node *field;
static char ret[INPUT_MAX];
if ((field = json_find((struct json_node *)jn, key)) == NULL) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" not found", key);
return (NULL);
}
if (field->type != NODE_STRING) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" expected STRING value",
key);
return (NULL);
}
if (xsnprintf(ret, sizeof ret, "%s", field->str) >= (int)sizeof ret) {
if (cause != NULL)
xasprintf(cause, "string overflow for key \"%s\"", key);
return (NULL);
}
return (ret);
}
/* Returns the number value from a given key in an object node. */
const int64_t *
json_find_number(const struct json_node *jn, const char *key, char **cause)
{
struct json_node *field;
static int64_t ret;
if ((field = json_find((struct json_node *)jn, key)) == NULL) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" not found", key);
return (NULL);
}
if (field->type != NODE_NUMBER) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" expected NUMBER value",
key);
return (NULL);
}
ret = field->num;
return (&ret);
}
/* Returns the boolean value from a given key in an object node. */
const int *
json_find_boolean(const struct json_node *jn, const char *key, char **cause)
{
struct json_node *field;
static int ret;
if ((field = json_find((struct json_node *)jn, key)) == NULL) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" not found", key);
return (NULL);
}
if (field->type != NODE_BOOLEAN) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" expected BOOLEAN value",
key);
return (NULL);
}
ret = field->boolean;
return (&ret);
}
/* Returns the object value from a given key in an object node. */
const struct json_node *
json_find_object(const struct json_node *jn, const char *key, char **cause)
{
struct json_node *field;
if ((field = json_find((struct json_node *)jn, key)) == NULL) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" not found", key);
return (NULL);
}
if (field->type != NODE_OBJECT) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" expected OBJECT value",
key);
return (NULL);
}
return (field);
}
/* Returns the array value from a given key in an object node. */
const struct json_node *
json_find_array(const struct json_node *jn, const char *key, char **cause)
{
struct json_node *field;
if ((field = json_find((struct json_node *)jn, key)) == NULL) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" not found", key);
return (NULL);
}
if (field->type != NODE_ARRAY) {
if (cause != NULL)
xasprintf(cause, "key \"%s\" expected ARRAY value",
key);
return (NULL);
}
return (field);
}
/* Fill an error cause. */
static void
json_error(char **cause, const char *reason, const char *input)

View File

@@ -89,7 +89,8 @@ static void layout_assign(struct window *,
struct layout_parse_ctx *);
static void layout_parse_apply_ctx(struct window *,
struct layout_parse_ctx *);
static struct layout_cell *layout_parse_json_layout(struct json_node *,
static struct layout_cell *layout_parse_json_layout(
const struct json_node *,
struct layout_cell *,
struct layout_parse_ctx *);
@@ -702,27 +703,22 @@ fail:
static int
layout_parse_json(struct json_node *jnroot, struct layout_parse_ctx *pctx)
{
struct json_node *jn, *field, *object;
struct json_node *jn;
const struct json_node *object;
const int64_t *num;
char **cause = pctx->cause;
if (json_get_object(jnroot, &jn) != 0) {
*cause = xstrdup("invalid json");
*cause = xstrdup("invalid layout json");
goto fail;
}
if ((field = json_find(jn, "V")) == NULL) {
*cause = xstrdup("missing version");
if ((num = json_find_number(jn, "V", cause)) == NULL) {
goto fail;
}
if (json_get_number(field, &pctx->version) != 0) {
*cause = xstrdup("invalid version type");
goto fail;
}
if ((field = json_find(jn, "L")) == NULL) {
*cause = xstrdup("missing layout");
goto fail;
}
if (json_get_object(field, &object) != 0) {
*cause = xstrdup("invalid version type");
pctx->version = *num;
if ((object = json_find_object(jn, "L", cause)) == NULL) {
goto fail;
}
pctx->root = layout_parse_json_layout(object, NULL, pctx);
@@ -743,20 +739,19 @@ fail:
/* Parse nodes into layout cells. */
static struct layout_cell *
layout_parse_json_layout(struct json_node *node, struct layout_cell *lcparent,
struct layout_parse_ctx *pctx)
layout_parse_json_layout(const struct json_node *node,
struct layout_cell *lcparent, struct layout_parse_ctx *pctx)
{
struct json_node *field, *array, *member;
struct json_node *member;
struct layout_cell *lc = layout_create_cell(lcparent), *lcchild;
const struct json_node *array;
const char *str;
char *endptr;
int64_t num;
const int64_t *num;
const int *boolean;
char *endptr, **cause = pctx->cause;
u_int id, index, zindex, active = -1, last = -1;
int boolean;
if ((field = json_find(node, "t")) == NULL)
goto fail;
if (json_get_string(field, &str) != 0)
if ((str = json_find_string(node, "t", cause)) == NULL)
goto fail;
if (strcmp(str, "p") == 0)
lc->type = LAYOUT_WINDOWPANE;
@@ -767,63 +762,56 @@ layout_parse_json_layout(struct json_node *node, struct layout_cell *lcparent,
else
goto fail;
if ((field = json_find(node, "w")) == NULL)
if ((num = json_find_number(node, "w", cause)) == NULL)
goto fail;
if (json_get_number(field, &num) != 0)
goto fail;
lc->g.sx = num;
lc->g.sx = *num;
if ((field = json_find(node, "h")) == NULL)
if ((num = json_find_number(node, "h", cause)) == NULL)
goto fail;
if (json_get_number(field, &num) != 0)
goto fail;
lc->g.sy = num;
lc->g.sy = *num;
if ((field = json_find(node, "x")) == NULL)
if ((num = json_find_number(node, "x", cause)) == NULL)
goto fail;
if (json_get_number(field, &num) != 0)
goto fail;
lc->g.xoff = num;
lc->g.xoff = *num;
if ((field = json_find(node, "y")) == NULL)
if ((num = json_find_number(node, "y", cause)) == NULL)
goto fail;
if (json_get_number(field, &num) != 0)
goto fail;
lc->g.yoff = num;
lc->g.yoff = *num;
if (lc->type == LAYOUT_WINDOWPANE) {
if ((field = json_find(node, "I")) == NULL)
goto fail;
if (json_get_string(field, &str) != 0)
if ((str = json_find_string(node, "I", cause)) == NULL)
goto fail;
errno = 0;
if (*str != '%') {
*cause = xstrdup("pane id must begin with '%'");
goto fail;
}
id = strtol(str + 1, &endptr, 10);
if (errno != 0 || endptr != str + strlen(str)) {
*cause = xstrdup("invalid number string '%s'");
goto fail;
}
if ((field = json_find(node, "i")) == NULL)
if ((num = json_find_number(node, "i", cause)) == NULL)
goto fail;
if (json_get_number(field, &num) != 0)
goto fail;
index = num;
index = *num;
if ((field = json_find(node, "a")) != NULL) {
if (json_get_boolean(field, &boolean) != 0)
if (json_find((struct json_node *)node, "a") != NULL) {
boolean = json_find_boolean(node, "a", cause);
if (boolean == NULL)
goto fail;
active = boolean;
} else if ((field = json_find(node, "l")) != NULL) {
if (json_get_number(field, &num) != 0)
active = *boolean;
} else if (json_find((struct json_node *)node, "l") != NULL) {
num = json_find_number(node, "l", cause);
if (num == NULL)
goto fail;
last = num;
last = *num;
}
if ((field = json_find(node, "z")) != NULL) {
if (json_get_number(field, &num) != 0)
if (json_find((struct json_node *)node, "z") != NULL) {
num = json_find_number(node, "z", cause);
if (num == NULL)
goto fail;
zindex = num;
zindex = *num;
lc->flags |= LAYOUT_CELL_FLOATING;
} else
zindex = INT_MAX;
@@ -831,12 +819,12 @@ layout_parse_json_layout(struct json_node *node, struct layout_cell *lcparent,
layout_parse_add_cctx(pctx, lc, active, last, id, index,
zindex);
} else {
if ((field = json_find(node, "c")) == NULL)
if ((array = json_find_array(node, "c", cause)) == NULL)
goto fail;
if (json_get_array(field, &array) != 0)
goto fail;
if ((member = json_array_first(array)) == NULL)
if ((member = json_array_first(array)) == NULL) {
*cause = xstrdup("nodes must have children");
goto fail;
}
while (member != NULL) {
lcchild = layout_parse_json_layout(member, lc,
pctx);

14
tmux.h
View File

@@ -4278,8 +4278,8 @@ void hyperlinks_free(struct hyperlinks *);
struct json_node *json_parse(const char *, char **);
void json_destroy_node(struct json_node *);
struct json_node *json_find(struct json_node *, const char *);
struct json_node *json_array_first(struct json_node *);
struct json_node *json_array_next(struct json_node *);
struct json_node *json_array_first(const struct json_node *);
struct json_node *json_array_next(const struct json_node *);
int json_get_string(struct json_node *, const char **);
int json_get_number(struct json_node *, int64_t *);
int json_get_boolean(struct json_node *, int *);
@@ -4287,5 +4287,15 @@ int json_get_object(struct json_node *,
struct json_node **);
int json_get_array(struct json_node *,
struct json_node **);
const char *json_find_string(const struct json_node *,
const char *, char **);
const int64_t *json_find_number(const struct json_node *,
const char *, char **);
const int *json_find_boolean(const struct json_node *,
const char *, char **);
const struct json_node *json_find_object(const struct json_node *,
const char *, char **);
const struct json_node *json_find_array(const struct json_node *,
const char *, char **);
#endif /* TMUX_H */