version 1.262, 2020/09/16 18:37:55 |
version 1.263, 2020/10/06 07:36:05 |
|
|
* string. |
* string. |
*/ |
*/ |
|
|
static char *format_job_get(struct format_tree *, const char *); |
struct format_expand_state; |
static void format_job_timer(int, short, void *); |
|
|
|
static int format_replace(struct format_tree *, const char *, size_t, |
static char *format_job_get(struct format_expand_state *, const char *); |
char **, size_t *, size_t *); |
static void format_job_timer(int, short, void *); |
|
static char *format_expand1(struct format_expand_state *, const char *); |
|
static int format_replace(struct format_expand_state *, const char *, |
|
size_t, char **, size_t *, size_t *); |
static void format_defaults_session(struct format_tree *, |
static void format_defaults_session(struct format_tree *, |
struct session *); |
struct session *); |
static void format_defaults_client(struct format_tree *, struct client *); |
static void format_defaults_client(struct format_tree *, struct client *); |
static void format_defaults_winlink(struct format_tree *, struct winlink *); |
static void format_defaults_winlink(struct format_tree *, |
|
struct winlink *); |
|
|
/* Entry in format job tree. */ |
/* Entry in format job tree. */ |
struct format_job { |
struct format_job { |
|
|
/* Limit on recursion. */ |
/* Limit on recursion. */ |
#define FORMAT_LOOP_LIMIT 10 |
#define FORMAT_LOOP_LIMIT 10 |
|
|
|
/* Format expand flags. */ |
|
#define FORMAT_EXPAND_TIME 0x1 |
|
#define FORMAT_EXPAND_NOJOBS 0x2 |
|
|
/* Entry in format tree. */ |
/* Entry in format tree. */ |
struct format_entry { |
struct format_entry { |
char *key; |
char *key; |
char *value; |
char *value; |
time_t t; |
time_t time; |
format_cb cb; |
format_cb cb; |
RB_ENTRY(format_entry) entry; |
RB_ENTRY(format_entry) entry; |
}; |
}; |
|
|
struct client *client; |
struct client *client; |
int flags; |
int flags; |
u_int tag; |
u_int tag; |
time_t time; |
|
u_int loop; |
|
|
|
struct mouse_event m; |
struct mouse_event m; |
|
|
|
|
static int format_entry_cmp(struct format_entry *, struct format_entry *); |
static int format_entry_cmp(struct format_entry *, struct format_entry *); |
RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); |
RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); |
|
|
|
/* Format expand state. */ |
|
struct format_expand_state { |
|
struct format_tree *ft; |
|
u_int loop; |
|
time_t time; |
|
int flags; |
|
}; |
|
|
/* Format modifier. */ |
/* Format modifier. */ |
struct format_modifier { |
struct format_modifier { |
char modifier[3]; |
char modifier[3]; |
|
|
|
|
/* Log a message if verbose. */ |
/* Log a message if verbose. */ |
static void printflike(3, 4) |
static void printflike(3, 4) |
format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) |
format_log1(struct format_expand_state *es, const char *from, const char *fmt, |
|
...) |
{ |
{ |
|
struct format_tree *ft = es->ft; |
va_list ap; |
va_list ap; |
char *s; |
char *s; |
static const char spaces[] = " "; |
static const char spaces[] = " "; |
|
|
|
|
log_debug("%s: %s", from, s); |
log_debug("%s: %s", from, s); |
if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) |
if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) |
cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s); |
cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s); |
|
|
free(s); |
free(s); |
} |
} |
#define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) |
#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__) |
|
|
|
/* Copy expand state. */ |
|
static void |
|
format_copy_state(struct format_expand_state *to, |
|
struct format_expand_state *from, int flags) |
|
{ |
|
to->ft = from->ft; |
|
to->loop = from->loop; |
|
to->time = from->time; |
|
to->flags = from->flags|flag; |
|
} |
|
|
/* Format job update callback. */ |
/* Format job update callback. */ |
static void |
static void |
format_job_update(struct job *job) |
format_job_update(struct job *job) |
|
|
|
|
/* Find a job. */ |
/* Find a job. */ |
static char * |
static char * |
format_job_get(struct format_tree *ft, const char *cmd) |
format_job_get(struct format_expand_state *es, const char *cmd) |
{ |
{ |
struct format_job_tree *jobs; |
struct format_tree *ft = es->ft; |
struct format_job fj0, *fj; |
struct format_job_tree *jobs; |
time_t t; |
struct format_job fj0, *fj; |
char *expanded; |
time_t t; |
int force; |
char *expanded; |
|
int force; |
|
struct format_expand_state next; |
|
|
if (ft->client == NULL) |
if (ft->client == NULL) |
jobs = &format_jobs; |
jobs = &format_jobs; |
|
|
RB_INSERT(format_job_tree, jobs, fj); |
RB_INSERT(format_job_tree, jobs, fj); |
} |
} |
|
|
expanded = format_expand(ft, cmd); |
expanded = format_expand1(es, cmd); |
if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { |
if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { |
free((void *)fj->expanded); |
free((void *)fj->expanded); |
fj->expanded = xstrdup(expanded); |
fj->expanded = xstrdup(expanded); |
|
|
fj->last = t; |
fj->last = t; |
fj->updated = 0; |
fj->updated = 0; |
} |
} |
|
free(expanded); |
|
|
if (ft->flags & FORMAT_STATUS) |
if (ft->flags & FORMAT_STATUS) |
fj->status = 1; |
fj->status = 1; |
|
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); |
free(expanded); |
return (format_expand1(&next, fj->out)); |
return (format_expand(ft, fj->out)); |
|
} |
} |
|
|
/* Remove old jobs. */ |
/* Remove old jobs. */ |
|
|
|
|
ft->tag = tag; |
ft->tag = tag; |
ft->flags = flags; |
ft->flags = flags; |
ft->time = time(NULL); |
|
|
|
format_add(ft, "version", "%s", getversion()); |
format_add(ft, "version", "%s", getversion()); |
format_add_cb(ft, "host", format_cb_host); |
format_add_cb(ft, "host", format_cb_host); |
|
|
char s[64]; |
char s[64]; |
|
|
RB_FOREACH(fe, format_entry_tree, &ft->tree) { |
RB_FOREACH(fe, format_entry_tree, &ft->tree) { |
if (fe->t != 0) { |
if (fe->time != 0) { |
xsnprintf(s, sizeof s, "%lld", (long long)fe->t); |
xsnprintf(s, sizeof s, "%lld", (long long)fe->time); |
cb(fe->key, s, arg); |
cb(fe->key, s, arg); |
} else { |
} else { |
if (fe->value == NULL && fe->cb != NULL) { |
if (fe->value == NULL && fe->cb != NULL) { |
|
|
} |
} |
|
|
fe->cb = NULL; |
fe->cb = NULL; |
fe->t = 0; |
fe->time = 0; |
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
xvasprintf(&fe->value, fmt, ap); |
xvasprintf(&fe->value, fmt, ap); |
|
|
} |
} |
|
|
fe->cb = NULL; |
fe->cb = NULL; |
fe->t = tv->tv_sec; |
fe->time = tv->tv_sec; |
|
|
fe->value = NULL; |
fe->value = NULL; |
} |
} |
|
|
} |
} |
|
|
fe->cb = cb; |
fe->cb = cb; |
fe->t = 0; |
fe->time = 0; |
|
|
fe->value = NULL; |
fe->value = NULL; |
} |
} |
|
|
fe_find.key = (char *)key; |
fe_find.key = (char *)key; |
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); |
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); |
if (fe != NULL) { |
if (fe != NULL) { |
if (fe->t != 0) { |
if (fe->time != 0) { |
t = fe->t; |
t = fe->time; |
goto found; |
goto found; |
} |
} |
if (fe->value == NULL && fe->cb != NULL) { |
if (fe->value == NULL && fe->cb != NULL) { |
|
|
|
|
/* Return left and right alternatives separated by commas. */ |
/* Return left and right alternatives separated by commas. */ |
static int |
static int |
format_choose(struct format_tree *ft, const char *s, char **left, char **right, |
format_choose(struct format_expand_state *es, const char *s, char **left, |
int expand) |
char **right, int expand) |
{ |
{ |
const char *cp; |
const char *cp; |
char *left0, *right0; |
char *left0, *right0; |
|
|
right0 = xstrdup(cp + 1); |
right0 = xstrdup(cp + 1); |
|
|
if (expand) { |
if (expand) { |
*left = format_expand(ft, left0); |
*left = format_expand1(es, left0); |
free(left0); |
free(left0); |
*right = format_expand(ft, right0); |
*right = format_expand1(es, right0); |
free(right0); |
free(right0); |
} else { |
} else { |
*left = left0; |
*left = left0; |
|
|
|
|
/* Build modifier list. */ |
/* Build modifier list. */ |
static struct format_modifier * |
static struct format_modifier * |
format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) |
format_build_modifiers(struct format_expand_state *es, const char **s, |
|
u_int *count) |
{ |
{ |
const char *cp = *s, *end; |
const char *cp = *s, *end; |
struct format_modifier *list = NULL; |
struct format_modifier *list = NULL; |
|
|
|
|
argv = xcalloc(1, sizeof *argv); |
argv = xcalloc(1, sizeof *argv); |
value = xstrndup(cp + 1, end - (cp + 1)); |
value = xstrndup(cp + 1, end - (cp + 1)); |
argv[0] = format_expand(ft, value); |
argv[0] = format_expand1(es, value); |
free(value); |
free(value); |
argc = 1; |
argc = 1; |
|
|
|
|
|
|
argv = xreallocarray (argv, argc + 1, sizeof *argv); |
argv = xreallocarray (argv, argc + 1, sizeof *argv); |
value = xstrndup(cp, end - cp); |
value = xstrndup(cp, end - cp); |
argv[argc++] = format_expand(ft, value); |
argv[argc++] = format_expand1(es, value); |
free(value); |
free(value); |
|
|
cp = end; |
cp = end; |
|
|
|
|
/* Loop over sessions. */ |
/* Loop over sessions. */ |
static char * |
static char * |
format_loop_sessions(struct format_tree *ft, const char *fmt) |
format_loop_sessions(struct format_expand_state *es, const char *fmt) |
{ |
{ |
struct client *c = ft->client; |
struct format_tree *ft = es->ft; |
struct cmdq_item *item = ft->item; |
struct client *c = ft->client; |
struct format_tree *nft; |
struct cmdq_item *item = ft->item; |
char *expanded, *value; |
struct format_tree *nft; |
size_t valuelen; |
struct format_expand_state next; |
struct session *s; |
char *expanded, *value; |
|
size_t valuelen; |
|
struct session *s; |
|
|
value = xcalloc(1, 1); |
value = xcalloc(1, 1); |
valuelen = 1; |
valuelen = 1; |
|
|
RB_FOREACH(s, sessions, &sessions) { |
RB_FOREACH(s, sessions, &sessions) { |
format_log(ft, "session loop: $%u", s->id); |
format_log(es, "session loop: $%u", s->id); |
nft = format_create(c, item, FORMAT_NONE, ft->flags); |
nft = format_create(c, item, FORMAT_NONE, ft->flags); |
nft->loop = ft->loop; |
format_defaults(next.ft, ft->c, s, NULL, NULL); |
format_defaults(nft, ft->c, s, NULL, NULL); |
format_copy_state(&next, es, 0); |
expanded = format_expand(nft, fmt); |
next.ft = nft; |
format_free(nft); |
expanded = format_expand1(&next, fmt); |
|
format_free(next.ft); |
|
|
valuelen += strlen(expanded); |
valuelen += strlen(expanded); |
value = xrealloc(value, valuelen); |
value = xrealloc(value, valuelen); |
|
|
|
|
/* Loop over windows. */ |
/* Loop over windows. */ |
static char * |
static char * |
format_loop_windows(struct format_tree *ft, const char *fmt) |
format_loop_windows(struct format_expand_state *es, const char *fmt) |
{ |
{ |
struct client *c = ft->client; |
struct format_tree *ft = es->ft; |
struct cmdq_item *item = ft->item; |
struct client *c = ft->client; |
struct format_tree *nft; |
struct cmdq_item *item = ft->item; |
char *all, *active, *use, *expanded, *value; |
struct format_tree *nft; |
size_t valuelen; |
struct format_expand_state next; |
struct winlink *wl; |
char *all, *active, *use, *expanded, *value; |
struct window *w; |
size_t valuelen; |
|
struct winlink *wl; |
|
struct window *w; |
|
|
if (ft->s == NULL) { |
if (ft->s == NULL) { |
format_log(ft, "window loop but no session"); |
format_log(es, "window loop but no session"); |
return (NULL); |
return (NULL); |
} |
} |
|
|
if (format_choose(ft, fmt, &all, &active, 0) != 0) { |
if (format_choose(es, fmt, &all, &active, 0) != 0) { |
all = xstrdup(fmt); |
all = xstrdup(fmt); |
active = NULL; |
active = NULL; |
} |
} |
|
|
|
|
RB_FOREACH(wl, winlinks, &ft->s->windows) { |
RB_FOREACH(wl, winlinks, &ft->s->windows) { |
w = wl->window; |
w = wl->window; |
format_log(ft, "window loop: %u @%u", wl->idx, w->id); |
format_log(es, "window loop: %u @%u", wl->idx, w->id); |
if (active != NULL && wl == ft->s->curw) |
if (active != NULL && wl == ft->s->curw) |
use = active; |
use = active; |
else |
else |
use = all; |
use = all; |
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); |
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); |
nft->loop = ft->loop; |
|
format_defaults(nft, ft->c, ft->s, wl, NULL); |
format_defaults(nft, ft->c, ft->s, wl, NULL); |
expanded = format_expand(nft, use); |
format_copy_state(&next, es, 0); |
|
next.ft = nft; |
|
expanded = format_expand1(&next, use); |
format_free(nft); |
format_free(nft); |
|
|
valuelen += strlen(expanded); |
valuelen += strlen(expanded); |
|
|
|
|
/* Loop over panes. */ |
/* Loop over panes. */ |
static char * |
static char * |
format_loop_panes(struct format_tree *ft, const char *fmt) |
format_loop_panes(struct format_expand_state *es, const char *fmt) |
{ |
{ |
struct client *c = ft->client; |
struct format_tree *ft = es->ft; |
struct cmdq_item *item = ft->item; |
struct client *c = ft->client; |
struct format_tree *nft; |
struct cmdq_item *item = ft->item; |
char *all, *active, *use, *expanded, *value; |
struct format_tree *nft; |
size_t valuelen; |
struct format_expand_state next; |
struct window_pane *wp; |
char *all, *active, *use, *expanded, *value; |
|
size_t valuelen; |
|
struct window_pane *wp; |
|
|
if (ft->w == NULL) { |
if (ft->w == NULL) { |
format_log(ft, "pane loop but no window"); |
format_log(es, "pane loop but no window"); |
return (NULL); |
return (NULL); |
} |
} |
|
|
if (format_choose(ft, fmt, &all, &active, 0) != 0) { |
if (format_choose(es, fmt, &all, &active, 0) != 0) { |
all = xstrdup(fmt); |
all = xstrdup(fmt); |
active = NULL; |
active = NULL; |
} |
} |
|
|
valuelen = 1; |
valuelen = 1; |
|
|
TAILQ_FOREACH(wp, &ft->w->panes, entry) { |
TAILQ_FOREACH(wp, &ft->w->panes, entry) { |
format_log(ft, "pane loop: %%%u", wp->id); |
format_log(es, "pane loop: %%%u", wp->id); |
if (active != NULL && wp == ft->w->active) |
if (active != NULL && wp == ft->w->active) |
use = active; |
use = active; |
else |
else |
use = all; |
use = all; |
nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); |
nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); |
nft->loop = ft->loop; |
|
format_defaults(nft, ft->c, ft->s, ft->wl, wp); |
format_defaults(nft, ft->c, ft->s, ft->wl, wp); |
expanded = format_expand(nft, use); |
format_copy_state(&next, es, 0); |
|
next.ft = nft; |
|
expanded = format_expand1(&next, use); |
format_free(nft); |
format_free(nft); |
|
|
valuelen += strlen(expanded); |
valuelen += strlen(expanded); |
|
|
} |
} |
|
|
static char * |
static char * |
format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, |
format_replace_expression(struct format_modifier *mexp, |
const char *copy) |
struct format_expand_state *es, const char *copy) |
{ |
{ |
int argc = mexp->argc; |
int argc = mexp->argc; |
const char *errstr; |
const char *errstr; |
char *endch, *value, *left = NULL, *right = NULL; |
char *endch, *value, *left = NULL, *right = NULL; |
int use_fp = 0; |
int use_fp = 0; |
u_int prec = 0; |
u_int prec = 0; |
double mleft, mright, result; |
double mleft, mright, result; |
enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; |
enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; |
|
|
if (strcmp(mexp->argv[0], "+") == 0) |
if (strcmp(mexp->argv[0], "+") == 0) |
|
|
strcmp(mexp->argv[0], "m") == 0) |
strcmp(mexp->argv[0], "m") == 0) |
operator = MODULUS; |
operator = MODULUS; |
else { |
else { |
format_log(ft, "expression has no valid operator: '%s'", |
format_log(es, "expression has no valid operator: '%s'", |
mexp->argv[0]); |
mexp->argv[0]); |
goto fail; |
goto fail; |
} |
} |
|
|
if (argc >= 3) { |
if (argc >= 3) { |
prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); |
prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); |
if (errstr != NULL) { |
if (errstr != NULL) { |
format_log (ft, "expression precision %s: %s", errstr, |
format_log(es, "expression precision %s: %s", errstr, |
mexp->argv[2]); |
mexp->argv[2]); |
goto fail; |
goto fail; |
} |
} |
} |
} |
|
|
if (format_choose(ft, copy, &left, &right, 1) != 0) { |
if (format_choose(es, copy, &left, &right, 1) != 0) { |
format_log(ft, "expression syntax error"); |
format_log(es, "expression syntax error"); |
goto fail; |
goto fail; |
} |
} |
|
|
mleft = strtod(left, &endch); |
mleft = strtod(left, &endch); |
if (*endch != '\0') { |
if (*endch != '\0') { |
format_log(ft, "expression left side is invalid: %s", left); |
format_log(es, "expression left side is invalid: %s", left); |
goto fail; |
goto fail; |
} |
} |
|
|
mright = strtod(right, &endch); |
mright = strtod(right, &endch); |
if (*endch != '\0') { |
if (*endch != '\0') { |
format_log(ft, "expression right side is invalid: %s", right); |
format_log(es, "expression right side is invalid: %s", right); |
goto fail; |
goto fail; |
} |
} |
|
|
|
|
mleft = (long long)mleft; |
mleft = (long long)mleft; |
mright = (long long)mright; |
mright = (long long)mright; |
} |
} |
format_log(ft, "expression left side is: %.*f", prec, mleft); |
format_log(es, "expression left side is: %.*f", prec, mleft); |
format_log(ft, "expression right side is: %.*f", prec, mright); |
format_log(es, "expression right side is: %.*f", prec, mright); |
|
|
switch (operator) { |
switch (operator) { |
case ADD: |
case ADD: |
|
|
xasprintf(&value, "%.*f", prec, result); |
xasprintf(&value, "%.*f", prec, result); |
else |
else |
xasprintf(&value, "%.*f", prec, (double)(long long)result); |
xasprintf(&value, "%.*f", prec, (double)(long long)result); |
format_log(ft, "expression result is %s", value); |
format_log(es, "expression result is %s", value); |
|
|
free(right); |
free(right); |
free(left); |
free(left); |
|
|
|
|
/* Replace a key. */ |
/* Replace a key. */ |
static int |
static int |
format_replace(struct format_tree *ft, const char *key, size_t keylen, |
format_replace(struct format_expand_state *es, const char *key, size_t keylen, |
char **buf, size_t *len, size_t *off) |
char **buf, size_t *len, size_t *off) |
{ |
{ |
struct window_pane *wp = ft->wp; |
struct format_tree *ft = es->ft; |
const char *errptr, *copy, *cp, *marker = NULL; |
struct window_pane *wp = ft->wp; |
const char *time_format = NULL; |
const char *errptr, *copy, *cp, *marker = NULL; |
char *copy0, *condition, *found, *new; |
const char *time_format = NULL; |
char *value, *left, *right; |
char *copy0, *condition, *found, *new; |
size_t valuelen; |
char *value, *left, *right; |
int modifiers = 0, limit = 0, width = 0, j; |
size_t valuelen; |
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; |
int modifiers = 0, limit = 0, width = 0; |
struct format_modifier **sub = NULL, *mexp = NULL; |
int j; |
u_int i, count, nsub = 0; |
struct format_modifier *list, *cmp = NULL, *search = NULL; |
|
struct format_modifier **sub = NULL, *mexp = NULL, *fm; |
|
u_int i, count, nsub = 0; |
|
struct format_expand_state next; |
|
|
/* Make a copy of the key. */ |
/* Make a copy of the key. */ |
copy = copy0 = xstrndup(key, keylen); |
copy = copy0 = xstrndup(key, keylen); |
|
|
/* Process modifier list. */ |
/* Process modifier list. */ |
list = format_build_modifiers(ft, ©, &count); |
list = format_build_modifiers(es, ©, &count); |
for (i = 0; i < count; i++) { |
for (i = 0; i < count; i++) { |
fm = &list[i]; |
fm = &list[i]; |
if (format_logging(ft)) { |
if (format_logging(ft)) { |
format_log(ft, "modifier %u is %s", i, fm->modifier); |
format_log(es, "modifier %u is %s", i, fm->modifier); |
for (j = 0; j < fm->argc; j++) { |
for (j = 0; j < fm->argc; j++) { |
format_log(ft, "modifier %u argument %d: %s", i, |
format_log(es, "modifier %u argument %d: %s", i, |
j, fm->argv[j]); |
j, fm->argv[j]); |
} |
} |
} |
} |
|
|
|
|
/* Is this a loop, comparison or condition? */ |
/* Is this a loop, comparison or condition? */ |
if (modifiers & FORMAT_SESSIONS) { |
if (modifiers & FORMAT_SESSIONS) { |
value = format_loop_sessions(ft, copy); |
value = format_loop_sessions(es, copy); |
if (value == NULL) |
if (value == NULL) |
goto fail; |
goto fail; |
} else if (modifiers & FORMAT_WINDOWS) { |
} else if (modifiers & FORMAT_WINDOWS) { |
value = format_loop_windows(ft, copy); |
value = format_loop_windows(es, copy); |
if (value == NULL) |
if (value == NULL) |
goto fail; |
goto fail; |
} else if (modifiers & FORMAT_PANES) { |
} else if (modifiers & FORMAT_PANES) { |
value = format_loop_panes(ft, copy); |
value = format_loop_panes(es, copy); |
if (value == NULL) |
if (value == NULL) |
goto fail; |
goto fail; |
} else if (search != NULL) { |
} else if (search != NULL) { |
/* Search in pane. */ |
/* Search in pane. */ |
new = format_expand(ft, copy); |
new = format_expand1(es, copy); |
if (wp == NULL) { |
if (wp == NULL) { |
format_log(ft, "search '%s' but no pane", new); |
format_log(es, "search '%s' but no pane", new); |
value = xstrdup("0"); |
value = xstrdup("0"); |
} else { |
} else { |
format_log(ft, "search '%s' pane %%%u", new, wp->id); |
format_log(es, "search '%s' pane %%%u", new, wp->id); |
value = format_search(fm, wp, new); |
value = format_search(fm, wp, new); |
} |
} |
free(new); |
free(new); |
} else if (cmp != NULL) { |
} else if (cmp != NULL) { |
/* Comparison of left and right. */ |
/* Comparison of left and right. */ |
if (format_choose(ft, copy, &left, &right, 1) != 0) { |
if (format_choose(es, copy, &left, &right, 1) != 0) { |
format_log(ft, "compare %s syntax error: %s", |
format_log(es, "compare %s syntax error: %s", |
cmp->modifier, copy); |
cmp->modifier, copy); |
goto fail; |
goto fail; |
} |
} |
format_log(ft, "compare %s left is: %s", cmp->modifier, left); |
format_log(es, "compare %s left is: %s", cmp->modifier, left); |
format_log(ft, "compare %s right is: %s", cmp->modifier, right); |
format_log(es, "compare %s right is: %s", cmp->modifier, right); |
|
|
if (strcmp(cmp->modifier, "||") == 0) { |
if (strcmp(cmp->modifier, "||") == 0) { |
if (format_true(left) || format_true(right)) |
if (format_true(left) || format_true(right)) |
|
|
/* Conditional: check first and choose second or third. */ |
/* Conditional: check first and choose second or third. */ |
cp = format_skip(copy + 1, ","); |
cp = format_skip(copy + 1, ","); |
if (cp == NULL) { |
if (cp == NULL) { |
format_log(ft, "condition syntax error: %s", copy + 1); |
format_log(es, "condition syntax error: %s", copy + 1); |
goto fail; |
goto fail; |
} |
} |
condition = xstrndup(copy + 1, cp - (copy + 1)); |
condition = xstrndup(copy + 1, cp - (copy + 1)); |
format_log(ft, "condition is: %s", condition); |
format_log(es, "condition is: %s", condition); |
|
|
found = format_find(ft, condition, modifiers, time_format); |
found = format_find(ft, condition, modifiers, time_format); |
if (found == NULL) { |
if (found == NULL) { |
|
|
* the expansion doesn't have any effect, then assume |
* the expansion doesn't have any effect, then assume |
* false. |
* false. |
*/ |
*/ |
found = format_expand(ft, condition); |
found = format_expand1(es, condition); |
if (strcmp(found, condition) == 0) { |
if (strcmp(found, condition) == 0) { |
free(found); |
free(found); |
found = xstrdup(""); |
found = xstrdup(""); |
format_log(ft, "condition '%s' found: %s", |
format_log(es, "condition '%s' found: %s", |
condition, found); |
condition, found); |
} else { |
} else { |
format_log(ft, |
format_log(es, |
"condition '%s' not found; assuming false", |
"condition '%s' not found; assuming false", |
condition); |
condition); |
} |
} |
} else |
} else |
format_log(ft, "condition '%s' found", condition); |
format_log(es, "condition '%s' found", condition); |
|
|
if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { |
if (format_choose(es, cp + 1, &left, &right, 0) != 0) { |
format_log(ft, "condition '%s' syntax error: %s", |
format_log(es, "condition '%s' syntax error: %s", |
condition, cp + 1); |
condition, cp + 1); |
free(found); |
free(found); |
goto fail; |
goto fail; |
} |
} |
if (format_true(found)) { |
if (format_true(found)) { |
format_log(ft, "condition '%s' is true", condition); |
format_log(es, "condition '%s' is true", condition); |
value = format_expand(ft, left); |
value = format_expand1(es, left); |
} else { |
} else { |
format_log(ft, "condition '%s' is false", condition); |
format_log(es, "condition '%s' is false", condition); |
value = format_expand(ft, right); |
value = format_expand1(es, right); |
} |
} |
free(right); |
free(right); |
free(left); |
free(left); |
|
|
free(condition); |
free(condition); |
free(found); |
free(found); |
} else if (mexp != NULL) { |
} else if (mexp != NULL) { |
value = format_replace_expression(mexp, ft, copy); |
value = format_replace_expression(mexp, es, copy); |
if (value == NULL) |
if (value == NULL) |
value = xstrdup(""); |
value = xstrdup(""); |
} else { |
} else { |
if (strstr(copy, "#{") != 0) { |
if (strstr(copy, "#{") != 0) { |
format_log(ft, "expanding inner format '%s'", copy); |
format_log(es, "expanding inner format '%s'", copy); |
value = format_expand(ft, copy); |
value = format_expand1(es, copy); |
} else { |
} else { |
value = format_find(ft, copy, modifiers, time_format); |
value = format_find(ft, copy, modifiers, time_format); |
if (value == NULL) { |
if (value == NULL) { |
format_log(ft, "format '%s' not found", copy); |
format_log(es, "format '%s' not found", copy); |
value = xstrdup(""); |
value = xstrdup(""); |
} else |
} else { |
format_log(ft, "format '%s' found: %s", copy, value); |
format_log(es, "format '%s' found: %s", copy, |
|
value); |
|
} |
} |
} |
} |
} |
|
|
done: |
done: |
/* Expand again if required. */ |
/* Expand again if required. */ |
if (modifiers & FORMAT_EXPAND) { |
if (modifiers & FORMAT_EXPAND) { |
new = format_expand(ft, value); |
new = format_expand1(es, value); |
free(value); |
free(value); |
value = new; |
value = new; |
} else if (modifiers & FORMAT_EXPANDTIME) { |
} else if (modifiers & FORMAT_EXPANDTIME) { |
new = format_expand_time(ft, value); |
format_copy_state(&next, es, FORMAT_EXPAND_TIME); |
|
new = format_expand1(&next, value); |
free(value); |
free(value); |
value = new; |
value = new; |
} |
} |
|
|
/* Perform substitution if any. */ |
/* Perform substitution if any. */ |
for (i = 0; i < nsub; i++) { |
for (i = 0; i < nsub; i++) { |
left = format_expand(ft, sub[i]->argv[0]); |
left = format_expand1(es, sub[i]->argv[0]); |
right = format_expand(ft, sub[i]->argv[1]); |
right = format_expand1(es, sub[i]->argv[1]); |
new = format_sub(sub[i], value, left, right); |
new = format_sub(sub[i], value, left, right); |
format_log(ft, "substitute '%s' to '%s': %s", left, right, new); |
format_log(es, "substitute '%s' to '%s': %s", left, right, new); |
free(value); |
free(value); |
value = new; |
value = new; |
free(right); |
free(right); |
|
|
free(value); |
free(value); |
value = new; |
value = new; |
} |
} |
format_log(ft, "applied length limit %d: %s", limit, value); |
format_log(es, "applied length limit %d: %s", limit, value); |
} else if (limit < 0) { |
} else if (limit < 0) { |
new = format_trim_right(value, -limit); |
new = format_trim_right(value, -limit); |
if (marker != NULL && strcmp(new, value) != 0) { |
if (marker != NULL && strcmp(new, value) != 0) { |
|
|
free(value); |
free(value); |
value = new; |
value = new; |
} |
} |
format_log(ft, "applied length limit %d: %s", limit, value); |
format_log(es, "applied length limit %d: %s", limit, value); |
} |
} |
|
|
/* Pad the value if needed. */ |
/* Pad the value if needed. */ |
|
|
new = utf8_padcstr(value, width); |
new = utf8_padcstr(value, width); |
free(value); |
free(value); |
value = new; |
value = new; |
format_log(ft, "applied padding width %d: %s", width, value); |
format_log(es, "applied padding width %d: %s", width, value); |
} else if (width < 0) { |
} else if (width < 0) { |
new = utf8_rpadcstr(value, -width); |
new = utf8_rpadcstr(value, -width); |
free(value); |
free(value); |
value = new; |
value = new; |
format_log(ft, "applied padding width %d: %s", width, value); |
format_log(es, "applied padding width %d: %s", width, value); |
} |
} |
|
|
/* Replace with the length if needed. */ |
/* Replace with the length if needed. */ |
|
|
xasprintf(&new, "%zu", strlen(value)); |
xasprintf(&new, "%zu", strlen(value)); |
free(value); |
free(value); |
value = new; |
value = new; |
format_log(ft, "replacing with length: %s", new); |
format_log(es, "replacing with length: %s", new); |
} |
} |
|
|
/* Expand the buffer and copy in the value. */ |
/* Expand the buffer and copy in the value. */ |
|
|
memcpy(*buf + *off, value, valuelen); |
memcpy(*buf + *off, value, valuelen); |
*off += valuelen; |
*off += valuelen; |
|
|
format_log(ft, "replaced '%s' with '%s'", copy0, value); |
format_log(es, "replaced '%s' with '%s'", copy0, value); |
free(value); |
free(value); |
|
|
free(sub); |
free(sub); |
|
|
return (0); |
return (0); |
|
|
fail: |
fail: |
format_log(ft, "failed %s", copy0); |
format_log(es, "failed %s", copy0); |
|
|
free(sub); |
free(sub); |
format_free_modifiers(list, count); |
format_free_modifiers(list, count); |
|
|
|
|
/* Expand keys in a template. */ |
/* Expand keys in a template. */ |
static char * |
static char * |
format_expand1(struct format_tree *ft, const char *fmt, int time) |
format_expand1(struct format_expand_state *es, const char *fmt) |
{ |
{ |
char *buf, *out, *name; |
struct format_tree *ft = es->ft; |
const char *ptr, *s; |
char *buf, *out, *name; |
size_t off, len, n, outlen; |
const char *ptr, *s; |
int ch, brackets; |
size_t off, len, n, outlen; |
struct tm *tm; |
int ch, brackets; |
char expanded[8192]; |
struct tm *tm; |
|
char expanded[8192]; |
|
|
if (fmt == NULL || *fmt == '\0') |
if (fmt == NULL || *fmt == '\0') |
return (xstrdup("")); |
return (xstrdup("")); |
|
|
if (ft->loop == FORMAT_LOOP_LIMIT) |
if (es->loop == FORMAT_LOOP_LIMIT) |
return (xstrdup("")); |
return (xstrdup("")); |
ft->loop++; |
es->loop++; |
|
|
format_log(ft, "expanding format: %s", fmt); |
format_log(es, "expanding format: %s", fmt); |
|
|
if (time) { |
if (es->flags & FORMAT_EXPAND_TIME) { |
tm = localtime(&ft->time); |
if (es->time == 0) |
|
es->time = time(NULL); |
|
tm = localtime(&es->time); |
if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { |
if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { |
format_log(ft, "format is too long"); |
format_log(es, "format is too long"); |
return (xstrdup("")); |
return (xstrdup("")); |
} |
} |
if (format_logging(ft) && strcmp(expanded, fmt) != 0) |
if (format_logging(ft) && strcmp(expanded, fmt) != 0) |
format_log(ft, "after time expanded: %s", expanded); |
format_log(es, "after time expanded: %s", expanded); |
fmt = expanded; |
fmt = expanded; |
} |
} |
|
|
|
|
n = ptr - fmt; |
n = ptr - fmt; |
|
|
name = xstrndup(fmt, n); |
name = xstrndup(fmt, n); |
format_log(ft, "found #(): %s", name); |
format_log(es, "found #(): %s", name); |
|
|
if (ft->flags & FORMAT_NOJOBS) { |
if ((ft->flags & FORMAT_NOJOBS) || |
|
(es->flags & FORMAT_EXPAND_NOJOBS)) { |
out = xstrdup(""); |
out = xstrdup(""); |
format_log(ft, "#() is disabled"); |
format_log(es, "#() is disabled"); |
} else { |
} else { |
out = format_job_get(ft, name); |
out = format_job_get(es, name); |
format_log(ft, "#() result: %s", out); |
format_log(es, "#() result: %s", out); |
} |
} |
free(name); |
free(name); |
|
|
|
|
break; |
break; |
n = ptr - fmt; |
n = ptr - fmt; |
|
|
format_log(ft, "found #{}: %.*s", (int)n, fmt); |
format_log(es, "found #{}: %.*s", (int)n, fmt); |
if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) |
if (format_replace(es, fmt, n, &buf, &len, &off) != 0) |
break; |
break; |
fmt += n + 1; |
fmt += n + 1; |
continue; |
continue; |
case '}': |
case '}': |
case '#': |
case '#': |
case ',': |
case ',': |
format_log(ft, "found #%c", ch); |
format_log(es, "found #%c", ch); |
while (len - off < 2) { |
while (len - off < 2) { |
buf = xreallocarray(buf, 2, len); |
buf = xreallocarray(buf, 2, len); |
len *= 2; |
len *= 2; |
|
|
continue; |
continue; |
} |
} |
n = strlen(s); |
n = strlen(s); |
format_log(ft, "found #%c: %s", ch, s); |
format_log(es, "found #%c: %s", ch, s); |
if (format_replace(ft, s, n, &buf, &len, &off) != 0) |
if (format_replace(es, s, n, &buf, &len, &off) != 0) |
break; |
break; |
continue; |
continue; |
} |
} |
|
|
} |
} |
buf[off] = '\0'; |
buf[off] = '\0'; |
|
|
format_log(ft, "result is: %s", buf); |
format_log(es, "result is: %s", buf); |
ft->loop--; |
es->loop--; |
|
|
return (buf); |
return (buf); |
} |
} |
|
|
char * |
char * |
format_expand_time(struct format_tree *ft, const char *fmt) |
format_expand_time(struct format_tree *ft, const char *fmt) |
{ |
{ |
return (format_expand1(ft, fmt, 1)); |
struct format_expand_state es; |
|
|
|
memset(&es, 0, sizeof es); |
|
es.ft = ft; |
|
es.flags = FORMAT_EXPAND_TIME; |
|
return (format_expand1(&es, fmt)); |
} |
} |
|
|
/* Expand keys in a template. */ |
/* Expand keys in a template. */ |
char * |
char * |
format_expand(struct format_tree *ft, const char *fmt) |
format_expand(struct format_tree *ft, const char *fmt) |
{ |
{ |
return (format_expand1(ft, fmt, 0)); |
struct format_expand_state es; |
|
|
|
memset(&es, 0, sizeof es); |
|
es.ft = ft; |
|
es.flags = 0; |
|
return (format_expand1(&es, fmt)); |
} |
} |
|
|
/* Expand a single string. */ |
/* Expand a single string. */ |