mirror of
https://github.com/tmux/tmux.git
synced 2026-08-01 04:58:59 +00:00
Bugfixes and documentation updates.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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
143
json.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
7
tmux.1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user