Bugfixes and documentation updates.

This commit is contained in:
Dane Jensen
2026-07-24 22:35:22 -07:00
parent 60f9170804
commit a7843a2a7e
5 changed files with 107 additions and 75 deletions

View File

@@ -78,7 +78,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct window_pane *wp = target->wp;
const char *layoutname;
char *oldlayout, *cause;
char *oldlayout, *cause = NULL;
int next, previous, layout;
int flags = 0;

View File

@@ -68,6 +68,7 @@ control_window_layout_changed_cb(__unused const char *name,
struct session *s;
struct winlink *wl;
struct window *w = event_payload_get_window(ep, "window");
struct format_tree *ft;
const char *template;
char *cp;
@@ -90,11 +91,16 @@ control_window_layout_changed_cb(__unused const char *name,
continue;
s = c->session;
wl = winlink_find_by_window_id(&s->windows, w->id);
if (wl != NULL) {
cp = format_single(NULL, template, c, s, wl, NULL);
control_write(c, "%s", cp);
free(cp);
}
if (wl == NULL)
continue;
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, NULL);
cp = format_expand(ft, template);
format_free(ft);
control_write(c, "%s", cp);
free(cp);
}
}

143
json.c
View File

@@ -18,15 +18,14 @@
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
#define TOKENS_MAX 4096
#define ERROR_CTX_LEN 8
/* JSON Token types. */
enum json_token_type {
@@ -67,7 +66,7 @@ static struct json_node *json_create_node(struct json_node *,
const void *);
static void json_assign_value(struct json_node *, const void *);
static struct json_node *json_parse_tokens(struct json_tokens **, char **);
static int layout_parse_key(struct json_token **,
static int json_parse_key(struct json_token **,
struct json_string *, char **);
static struct json_node *json_parse_object(struct json_token **,
struct json_node *, struct json_string *, char **);
@@ -80,10 +79,26 @@ static struct json_node *json_parse_number(struct json_token **,
static struct json_node *json_parse_boolean(struct json_token **,
struct json_node *, struct json_string *, char **);
static int
/* Fill an error cause. */
static void
json_error(char **cause, const char *reason, const char *input)
{
return (xasprintf(cause, "%s: %.*s...", reason, 8, input));
const char *ellipsis = "...";
int i;
if (input == NULL || *input == '\0') {
xasprintf(cause, "%s", reason);
return;
}
for (i = 0; i < ERROR_CTX_LEN; i++) {
if (input[i] == '\0') {
ellipsis = "";
break;
}
}
xasprintf(cause, "%s: %.*s%s", reason, ERROR_CTX_LEN, input, ellipsis);
}
/* Wrapper for string views with strcmp semantics. */
@@ -107,12 +122,16 @@ json_tokenize_input(const char *input, char **cause)
{
struct json_tokens *tokens = json_create_tokens();
enum json_token_type type;
struct json_string jstr = { 0 };
struct json_string jstr;
int scan;
while (*input != '\0') {
if (tokens->size >= TOKENS_MAX)
goto fail;
jstr.ptr = input;
jstr.len = 1;
switch (*input) {
case ' ':
case '\t':
@@ -151,8 +170,6 @@ json_tokenize_input(const char *input, char **cause)
if (json_add_token(tokens, type, &jstr) != 0)
goto fail;
jstr.ptr = NULL;
jstr.len = 0;
input++;
}
if (json_add_token(tokens, TOK_EOF, NULL) != 0)
@@ -167,7 +184,7 @@ fail:
/*
* Tokenize a value from the input string. Strings are terminated by a '"', and
* numbers/booleans are terminated by a ',', ']', or '}'.
* numbers/booleans are terminated by a ',', ']', '}', or whitespace.
*/
static int
json_tokenize_value(struct json_tokens *tokens, const char *input,
@@ -187,14 +204,14 @@ json_tokenize_value(struct json_tokens *tokens, const char *input,
else if (escaping)
escaping = 0;
scan++;
} while (input[scan] != '"' && !escaping);
} while (input[scan] != '"' || escaping);
} else if (prev->type == TOK_COLON) {
do {
if (input[scan] == '\0')
return (-1);
scan++;
} while (input[scan] != ']' && input[scan] != '}' &&
input[scan] != ',' && !isspace(input[scan]));
input[scan] != ',' && !isspace((u_char) input[scan]));
} else
return (-1);
@@ -241,6 +258,9 @@ json_add_token(struct json_tokens *tokens, enum json_token_type type,
if (jstr != NULL) {
tok->val.ptr = jstr->ptr;
tok->val.len = jstr->len;
} else {
tok->val.ptr = NULL;
tok->val.len = 0;
}
return (0);
@@ -332,6 +352,7 @@ json_destroy_node(struct json_node *node)
free(node);
}
/* Parse an input string into JSON. */
struct json_node *
json_parse(const char *input, char **cause)
{
@@ -348,27 +369,29 @@ static struct json_node *
json_parse_tokens(struct json_tokens **tokens, char **cause)
{
struct json_token *toks = (*tokens)->toks;
struct json_node *layout;
struct json_node *json = NULL;
if (toks->type == TOK_OPENOBJECT)
layout = json_parse_object(&toks, NULL, NULL, cause);
json = json_parse_object(&toks, NULL, NULL, cause);
else {
json_error(cause, "must start with object", toks->val.ptr);
json_error(cause, "expected object", toks->val.ptr);
goto fail;
}
if (layout == NULL)
if (json == NULL)
goto fail;
if (toks->type != TOK_EOF) {
json_destroy_node(layout);
layout = NULL;
json_error(cause, "unexpected trailing data", toks->val.ptr);
goto fail;
}
json_free_tokens(*tokens);
*tokens = NULL;
return (layout);
return (json);
fail:
if (json != NULL)
json_destroy_node(json);
json_free_tokens(*tokens);
*tokens = NULL;
return (NULL);
@@ -376,7 +399,7 @@ fail:
/* Parse and return a key string, and advance the token pointer. */
static int
layout_parse_key(struct json_token **toks, struct json_string *jstr,
json_parse_key(struct json_token **toks, struct json_string *jstr,
char **cause)
{
const char *start = (*toks)->val.ptr;
@@ -398,7 +421,7 @@ layout_parse_key(struct json_token **toks, struct json_string *jstr,
return (0);
fail:
json_error(cause, "invalid key string", start);
json_error(cause, "invalid key", start);
return (-1);
}
@@ -408,7 +431,8 @@ json_parse_object(struct json_token **toks, struct json_node *parent,
struct json_string *key, char **cause)
{
struct json_node *object, *field;
struct json_string fkey;
struct json_string fkey, *val;
u_char *valstr;
if ((*toks)->type != TOK_OPENOBJECT)
return (NULL);
@@ -416,7 +440,7 @@ json_parse_object(struct json_token **toks, struct json_node *parent,
object = json_create_node(parent, NODE_OBJECT, key, NULL);
while ((*toks)->type != TOK_CLOSEOBJECT) {
if (layout_parse_key(toks, &fkey, cause) != 0)
if (json_parse_key(toks, &fkey, cause) != 0)
goto fail;
if ((*toks)->type != TOK_COLON) {
json_error(cause, "missing colon", (*toks)->val.ptr);
@@ -429,13 +453,19 @@ json_parse_object(struct json_token **toks, struct json_node *parent,
field = json_parse_string(toks, object, &fkey, cause);
break;
case TOK_VALUE:
if (jstrcmp(&(*toks)->val, "true") == 0 ||
jstrcmp(&(*toks)->val, "false") == 0) {
field = json_parse_boolean(toks, object,
&fkey, cause);
val = &(*toks)->val;
valstr = (u_char *)val->ptr;
if (jstrcmp(val, "true") == 0 ||
jstrcmp(val, "false") == 0) {
field = json_parse_boolean(toks, object, &fkey,
cause);
} else if ((*valstr == '-' && isdigit(valstr[1])) ||
isdigit(*valstr)) {
field = json_parse_number(toks, object, &fkey,
cause);
} else {
field = json_parse_number(toks, object,
&fkey, cause);
json_error(cause, "invalid value", val->ptr);
goto fail;
}
break;
case TOK_OPENOBJECT:
@@ -445,27 +475,23 @@ json_parse_object(struct json_token **toks, struct json_node *parent,
field = json_parse_array(toks, object, &fkey, cause);
break;
default:
json_error(cause, "unsupported token for object",
json_error(cause, "unsupported object token",
(*toks)->val.ptr);
goto fail;
}
if (field == NULL) {
json_error(cause, "could not parse object value",
(*toks)->val.ptr);
if (field == NULL)
goto fail;
}
json_assign_value(object, field);
json_assign_value(object, field);
if ((*toks)->type == TOK_COMMA) {
if ((*toks)[1].type == TOK_CLOSEOBJECT) {
json_error(cause, "invalid json object",
json_error(cause, "invalid object",
(*toks)->val.ptr);
goto fail;
}
(*toks)++;
} else if ((*toks)->type != TOK_CLOSEOBJECT) {
json_error(cause, "invalid json object",
(*toks)->val.ptr);
json_error(cause, "invalid object", (*toks)->val.ptr);
goto fail;
}
}
@@ -494,28 +520,25 @@ json_parse_array(struct json_token **toks, struct json_node *parent,
case TOK_OPENOBJECT:
member = json_parse_object(toks, array, NULL, cause);
break;
case TOK_COMMA: /* only objects */
if ((*toks)[1].type != TOK_OPENOBJECT) {
json_error(cause, "only objects in arrays",
default:
json_error(cause, "invalid array member",
(*toks)->val.ptr);
goto fail;
}
if (member == NULL)
goto fail;
json_assign_value(array, member);
if ((*toks)->type == TOK_COMMA) {
if ((*toks)[1].type == TOK_CLOSEARRAY) {
json_error(cause, "invalid array",
(*toks)->val.ptr);
goto fail;
}
(*toks)++;
continue;
default:
goto fail;
}
if (member == NULL) {
json_error(cause, "invalid json array",
(*toks)->val.ptr);
goto fail;
}
json_assign_value(array, member);
if ((*toks)->type != TOK_COMMA &&
(*toks)->type != TOK_CLOSEARRAY) {
json_error(cause, "invalid json array",
(*toks)->val.ptr);
} else if ((*toks)->type != TOK_CLOSEARRAY) {
json_error(cause, "invalid array", (*toks)->val.ptr);
goto fail;
}
}
@@ -549,7 +572,7 @@ json_parse_string(struct json_token **toks, struct json_node *parent,
return (json_create_node(parent, NODE_STRING, key, val));
fail:
json_error(cause, "invalid json string", start);
json_error(cause, "invalid string", start);
return (NULL);
}
@@ -570,7 +593,7 @@ json_parse_number(struct json_token **toks, struct json_node *parent,
return (json_create_node(parent, NODE_NUMBER, key, &val));
fail:
json_error(cause, "invalid json number", numstr);
json_error(cause, "invalid number", numstr);
return (NULL);
}
@@ -593,7 +616,7 @@ json_parse_boolean(struct json_token **toks, struct json_node *parent,
return (json_create_node(parent, NODE_BOOLEAN, key, &val));
fail:
json_error(cause, "invalid json boolean", start);
json_error(cause, "invalid boolean", start);
return (NULL);
}
@@ -617,7 +640,7 @@ json_val_is_eq(const struct json_node *field, const void *val)
return (field->val.boolean == *(int *)val);
case NODE_OBJECT:
case NODE_ARRAY:
fatalx("unknown value comparison");
fatalx("cannot compare object or array");
}
return (0);
}

View File

@@ -21,10 +21,9 @@
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -700,7 +699,8 @@ fail:
static int
layout_parse_json(struct json_node *json, struct layout_parse_ctx *pctx)
{
struct json_node *field;
struct json_node *field;
char **cause = pctx->cause;
TAILQ_FOREACH(field, &json->val.fields, entry) {
switch (field->type) {
@@ -710,8 +710,10 @@ layout_parse_json(struct json_node *json, struct layout_parse_ctx *pctx)
break;
case NODE_OBJECT:
if (json_key_is_eq(field, "L")) {
if (pctx->root != NULL)
if (pctx->root != NULL) {
*cause = xstrdup("duplicate layout");
goto fail;
}
pctx->root = layout_parse_json_layout(field,
NULL, pctx);
if (pctx->root== NULL)
@@ -733,8 +735,8 @@ fail:
layout_free_cell(pctx->root, 0);
return (-1);
}
/* Evaluate nodes into layout cells. */
// TODO: add causes for failures.
/* 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)

7
tmux.1
View File

@@ -2695,8 +2695,8 @@ For example:
.Bd -literal -offset indent
$ tmux list\-windows
0: ksh [159x48]
layout: {"v":2,"L":{"t":"h","w":159,"h":48,"x":0,"y":0,"c":[{"t":"p","w":79,"h":48,"x":0,"y":0,"l":0,"i":"%0"},{"t":"p","w":79,"h":48,"x":80,"y":0,"a":true,"i":"%2"}]}}
$ tmux select\-layout \[aq]{"v":2,"L":{"t":"h","w":159,"h":48,"x":0,"y":0,"c":[{"t":"p","w":79,"h":48,"x":0,"y":0,"l":0,"i":"%0"},{"t":"p","w":79,"h":48,"x":80,"y":0,"a":true,"i":"%2"}]}}\[aq]
layout: {"V":2,"L":{"t":"h","w":159,"h":48,"x":0,"y":0,"c":[{"t":"p","w":79,"h":48,"x":0,"y":0,"l":0,"i":0,"I":"%0"},{"t":"p","w":79,"h":48,"x":80,"y":0,"a":true,"i":1,"I":"%2"}]}}
$ tmux select\-layout \[aq]{"V":2,"L":{"t":"h","w":159,"h":48,"x":0,"y":0,"c":[{"t":"p","w":79,"h":48,"x":0,"y":0,"l":0,"i":0,"I":"%0"},{"t":"p","w":79,"h":48,"x":80,"y":0,"a":true,"i":1","I":"%2"}]}}\[aq]
.Ed
.Pp
.Nm
@@ -9007,7 +9007,8 @@ and flags (currently not used).
For example:
.Bd -literal -offset indent
%begin 1363006971 2 1
0: ksh* (1 panes) [80x24] [layout {"V":2,"L":{"t":"p","w":80,"h":24,"x":0,"y":0,"a":true,"i":"%2"}}] @2 (active)
0: ksh* (1 panes) [80x24] [layout
{"V":2,"L":{"t":"p","w":80,"h":24,"x":0,"y":0,"a":true,"i":0,"I":"%2"}}] @2 (active)
%end 1363006971 2 1
.Ed
.Pp