mirror of
https://github.com/tmate-io/tmate.git
synced 2025-01-23 22:38:44 +01:00
Merge branch 'obsd-master'
This commit is contained in:
commit
beffdf6575
@ -65,10 +65,9 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq)
|
|||||||
}
|
}
|
||||||
if (tty_set_size(&c->tty, w, h))
|
if (tty_set_size(&c->tty, w, h))
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
} else if (args_has(args, 'S')) {
|
} else if (args_has(args, 'S'))
|
||||||
status_update_jobs(c);
|
|
||||||
server_status_client(c);
|
server_status_client(c);
|
||||||
} else
|
else
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
190
format.c
190
format.c
@ -36,6 +36,9 @@
|
|||||||
* string.
|
* string.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void format_job_callback(struct job *);
|
||||||
|
const char *format_job_get(struct format_tree *, const char *);
|
||||||
|
|
||||||
int format_replace(struct format_tree *, const char *, size_t, char **,
|
int format_replace(struct format_tree *, const char *, size_t, char **,
|
||||||
size_t *, size_t *);
|
size_t *, size_t *);
|
||||||
char *format_time_string(time_t);
|
char *format_time_string(time_t);
|
||||||
@ -47,6 +50,32 @@ void format_defaults_client(struct format_tree *, struct client *);
|
|||||||
void format_defaults_winlink(struct format_tree *, struct session *,
|
void format_defaults_winlink(struct format_tree *, struct session *,
|
||||||
struct winlink *);
|
struct winlink *);
|
||||||
|
|
||||||
|
/* Entry in format job tree. */
|
||||||
|
struct format_job {
|
||||||
|
const char *cmd;
|
||||||
|
|
||||||
|
time_t last;
|
||||||
|
char *out;
|
||||||
|
|
||||||
|
struct job *job;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
RB_ENTRY(format_job) entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Format job tree. */
|
||||||
|
int format_job_cmp(struct format_job *, struct format_job *);
|
||||||
|
RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
|
||||||
|
RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp);
|
||||||
|
RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp);
|
||||||
|
|
||||||
|
/* Format job tree comparison function. */
|
||||||
|
int
|
||||||
|
format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
||||||
|
{
|
||||||
|
return (strcmp(fj1->cmd, fj2->cmd));
|
||||||
|
}
|
||||||
|
|
||||||
/* Entry in format tree. */
|
/* Entry in format tree. */
|
||||||
struct format_entry {
|
struct format_entry {
|
||||||
char *key;
|
char *key;
|
||||||
@ -55,22 +84,22 @@ struct format_entry {
|
|||||||
RB_ENTRY(format_entry) entry;
|
RB_ENTRY(format_entry) entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Tree of format entries. */
|
/* Format entry tree. */
|
||||||
struct format_tree {
|
struct format_tree {
|
||||||
struct window *w;
|
struct window *w;
|
||||||
struct session *s;
|
struct session *s;
|
||||||
|
|
||||||
RB_HEAD(format_rb_tree, format_entry) tree;
|
int status;
|
||||||
|
|
||||||
|
RB_HEAD(format_entry_tree, format_entry) tree;
|
||||||
};
|
};
|
||||||
|
int format_entry_cmp(struct format_entry *, struct format_entry *);
|
||||||
|
RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp);
|
||||||
|
RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp);
|
||||||
|
|
||||||
/* Format key-value replacement entry. */
|
/* Format entry tree comparison function. */
|
||||||
int format_cmp(struct format_entry *, struct format_entry *);
|
|
||||||
RB_PROTOTYPE(format_rb_tree, format_entry, entry, format_cmp);
|
|
||||||
RB_GENERATE(format_rb_tree, format_entry, entry, format_cmp);
|
|
||||||
|
|
||||||
/* Format tree comparison function. */
|
|
||||||
int
|
int
|
||||||
format_cmp(struct format_entry *fe1, struct format_entry *fe2)
|
format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
|
||||||
{
|
{
|
||||||
return (strcmp(fe1->key, fe2->key));
|
return (strcmp(fe1->key, fe2->key));
|
||||||
}
|
}
|
||||||
@ -135,15 +164,118 @@ const char *format_lower[] = {
|
|||||||
NULL /* z */
|
NULL /* z */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Format job callback. */
|
||||||
|
void
|
||||||
|
format_job_callback(struct job *job)
|
||||||
|
{
|
||||||
|
struct format_job *fj = job->data;
|
||||||
|
char *line, *buf;
|
||||||
|
size_t len;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
|
fj->job = NULL;
|
||||||
|
free(fj->out);
|
||||||
|
|
||||||
|
if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) {
|
||||||
|
xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd,
|
||||||
|
WEXITSTATUS(job->status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WIFSIGNALED(job->status)) {
|
||||||
|
xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd,
|
||||||
|
WTERMSIG(job->status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = NULL;
|
||||||
|
if ((line = evbuffer_readline(job->event->input)) == NULL) {
|
||||||
|
len = EVBUFFER_LENGTH(job->event->input);
|
||||||
|
buf = xmalloc(len + 1);
|
||||||
|
if (len != 0)
|
||||||
|
memcpy(buf, EVBUFFER_DATA(job->event->input), len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
} else
|
||||||
|
buf = line;
|
||||||
|
fj->out = buf;
|
||||||
|
|
||||||
|
if (fj->status) {
|
||||||
|
TAILQ_FOREACH(c, &clients, entry)
|
||||||
|
server_status_client(c);
|
||||||
|
fj->status = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a job. */
|
||||||
|
const char *
|
||||||
|
format_job_get(struct format_tree *ft, const char *cmd)
|
||||||
|
{
|
||||||
|
struct format_job fj0, *fj;
|
||||||
|
|
||||||
|
fj0.cmd = cmd;
|
||||||
|
if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL)
|
||||||
|
{
|
||||||
|
fj = xcalloc(1, sizeof *fj);
|
||||||
|
fj->cmd = xstrdup(cmd);
|
||||||
|
fj->status = ft->status;
|
||||||
|
|
||||||
|
xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
|
||||||
|
|
||||||
|
RB_INSERT(format_job_tree, &format_jobs, fj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fj->job == NULL && fj->last != time(NULL)) {
|
||||||
|
fj->job = job_run(fj->cmd, NULL, -1, format_job_callback,
|
||||||
|
NULL, fj);
|
||||||
|
if (fj->job == NULL) {
|
||||||
|
free(fj->out);
|
||||||
|
xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fj->last = time(NULL);
|
||||||
|
|
||||||
|
return (fj->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove old jobs. */
|
||||||
|
void
|
||||||
|
format_clean(void)
|
||||||
|
{
|
||||||
|
struct format_job *fj, *fj1;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) {
|
||||||
|
if (fj->last > now || now - fj->last < 3600)
|
||||||
|
continue;
|
||||||
|
RB_REMOVE(format_job_tree, &format_jobs, fj);
|
||||||
|
|
||||||
|
if (fj->job != NULL)
|
||||||
|
job_free(fj->job);
|
||||||
|
|
||||||
|
free((void*)fj->cmd);
|
||||||
|
free(fj->out);
|
||||||
|
|
||||||
|
free(fj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a new tree. */
|
/* Create a new tree. */
|
||||||
struct format_tree *
|
struct format_tree *
|
||||||
format_create(void)
|
format_create(void)
|
||||||
|
{
|
||||||
|
return (format_create_status(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a new tree for the status line. */
|
||||||
|
struct format_tree *
|
||||||
|
format_create_status(int status)
|
||||||
{
|
{
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
char host[HOST_NAME_MAX + 1], *ptr;
|
char host[HOST_NAME_MAX + 1], *ptr;
|
||||||
|
|
||||||
ft = xcalloc(1, sizeof *ft);
|
ft = xcalloc(1, sizeof *ft);
|
||||||
RB_INIT(&ft->tree);
|
RB_INIT(&ft->tree);
|
||||||
|
ft->status = status;
|
||||||
|
|
||||||
if (gethostname(host, sizeof host) == 0) {
|
if (gethostname(host, sizeof host) == 0) {
|
||||||
format_add(ft, "host", "%s", host);
|
format_add(ft, "host", "%s", host);
|
||||||
@ -161,8 +293,8 @@ format_free(struct format_tree *ft)
|
|||||||
{
|
{
|
||||||
struct format_entry *fe, *fe1;
|
struct format_entry *fe, *fe1;
|
||||||
|
|
||||||
RB_FOREACH_SAFE(fe, format_rb_tree, &ft->tree, fe1) {
|
RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
|
||||||
RB_REMOVE(format_rb_tree, &ft->tree, fe);
|
RB_REMOVE(format_entry_tree, &ft->tree, fe);
|
||||||
free(fe->value);
|
free(fe->value);
|
||||||
free(fe->key);
|
free(fe->key);
|
||||||
free(fe);
|
free(fe);
|
||||||
@ -186,7 +318,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
|
|||||||
xvasprintf(&fe->value, fmt, ap);
|
xvasprintf(&fe->value, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
fe_now = RB_INSERT(format_rb_tree, &ft->tree, fe);
|
fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
|
||||||
if (fe_now != NULL) {
|
if (fe_now != NULL) {
|
||||||
free(fe_now->value);
|
free(fe_now->value);
|
||||||
fe_now->value = fe->value;
|
fe_now->value = fe->value;
|
||||||
@ -225,7 +357,7 @@ format_find(struct format_tree *ft, const char *key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fe_find.key = (char *) key;
|
fe_find.key = (char *) key;
|
||||||
fe = RB_FIND(format_rb_tree, &ft->tree, &fe_find);
|
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
|
||||||
if (fe == NULL)
|
if (fe == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
return (fe->value);
|
return (fe->value);
|
||||||
@ -359,9 +491,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t)
|
|||||||
char *
|
char *
|
||||||
format_expand(struct format_tree *ft, const char *fmt)
|
format_expand(struct format_tree *ft, const char *fmt)
|
||||||
{
|
{
|
||||||
char *buf;
|
char *buf, *tmp;
|
||||||
const char *ptr, *s;
|
const char *ptr, *s;
|
||||||
size_t off, len, n;
|
size_t off, len, n, slen;
|
||||||
int ch, brackets;
|
int ch, brackets;
|
||||||
|
|
||||||
if (fmt == NULL)
|
if (fmt == NULL)
|
||||||
@ -384,6 +516,34 @@ format_expand(struct format_tree *ft, const char *fmt)
|
|||||||
|
|
||||||
ch = (u_char) *fmt++;
|
ch = (u_char) *fmt++;
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
case '(':
|
||||||
|
brackets = 1;
|
||||||
|
for (ptr = fmt; *ptr != '\0'; ptr++) {
|
||||||
|
if (*ptr == '(')
|
||||||
|
brackets++;
|
||||||
|
if (*ptr == ')' && --brackets == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*ptr != ')' || brackets != 0)
|
||||||
|
break;
|
||||||
|
n = ptr - fmt;
|
||||||
|
|
||||||
|
tmp = xmalloc(n + 1);
|
||||||
|
memcpy(tmp, fmt, n);
|
||||||
|
tmp[n] = '\0';
|
||||||
|
|
||||||
|
s = format_job_get(ft, tmp);
|
||||||
|
slen = strlen(s);
|
||||||
|
|
||||||
|
while (len - off < slen + 1) {
|
||||||
|
buf = xreallocarray(buf, 2, len);
|
||||||
|
len *= 2;
|
||||||
|
}
|
||||||
|
memcpy(buf + off, s, slen);
|
||||||
|
off += slen;
|
||||||
|
|
||||||
|
fmt += n + 1;
|
||||||
|
continue;
|
||||||
case '{':
|
case '{':
|
||||||
brackets = 1;
|
brackets = 1;
|
||||||
for (ptr = fmt; *ptr != '\0'; ptr++) {
|
for (ptr = fmt; *ptr != '\0'; ptr++) {
|
||||||
|
@ -156,8 +156,6 @@ server_client_lost(struct client *c)
|
|||||||
if (c->stderr_data != c->stdout_data)
|
if (c->stderr_data != c->stdout_data)
|
||||||
evbuffer_free(c->stderr_data);
|
evbuffer_free(c->stderr_data);
|
||||||
|
|
||||||
status_free_jobs(&c->status_new);
|
|
||||||
status_free_jobs(&c->status_old);
|
|
||||||
screen_free(&c->status);
|
screen_free(&c->status);
|
||||||
|
|
||||||
free(c->title);
|
free(c->title);
|
||||||
@ -268,12 +266,10 @@ server_client_status_timer(void)
|
|||||||
interval = options_get_number(&s->options, "status-interval");
|
interval = options_get_number(&s->options, "status-interval");
|
||||||
|
|
||||||
difference = tv.tv_sec - c->status_timer.tv_sec;
|
difference = tv.tv_sec - c->status_timer.tv_sec;
|
||||||
if (interval != 0 && difference >= interval) {
|
if (interval != 0 && difference >= interval)
|
||||||
status_update_jobs(c);
|
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for mouse keys. */
|
/* Check for mouse keys. */
|
||||||
int
|
int
|
||||||
|
2
server.c
2
server.c
@ -486,6 +486,8 @@ server_second_callback(unused int fd, unused short events, unused void *arg)
|
|||||||
|
|
||||||
server_client_status_timer();
|
server_client_status_timer();
|
||||||
|
|
||||||
|
format_clean();
|
||||||
|
|
||||||
evtimer_del(&server_ev_second);
|
evtimer_del(&server_ev_second);
|
||||||
memset(&tv, 0, sizeof tv);
|
memset(&tv, 0, sizeof tv);
|
||||||
tv.tv_sec = 1;
|
tv.tv_sec = 1;
|
||||||
|
236
status.c
236
status.c
@ -33,13 +33,10 @@ char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *,
|
|||||||
size_t *);
|
size_t *);
|
||||||
char *status_redraw_get_right(struct client *, time_t, int,
|
char *status_redraw_get_right(struct client *, time_t, int,
|
||||||
struct grid_cell *, size_t *);
|
struct grid_cell *, size_t *);
|
||||||
char *status_find_job(struct client *, char **);
|
|
||||||
void status_job_free(void *);
|
|
||||||
void status_job_callback(struct job *);
|
|
||||||
char *status_print(struct client *, struct winlink *, time_t,
|
char *status_print(struct client *, struct winlink *, time_t,
|
||||||
struct grid_cell *);
|
struct grid_cell *);
|
||||||
char *status_replace(struct client *, struct winlink *, const char *, time_t);
|
char *status_replace(struct client *, struct winlink *, const char *, time_t);
|
||||||
void status_replace1(struct client *, char **, char **, char *, size_t);
|
void status_replace1(char **, char **, char *, size_t);
|
||||||
void status_message_callback(int, short, void *);
|
void status_message_callback(int, short, void *);
|
||||||
|
|
||||||
const char *status_prompt_up_history(u_int *);
|
const char *status_prompt_up_history(u_int *);
|
||||||
@ -368,246 +365,25 @@ out:
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace a single special sequence (prefixed by #). */
|
|
||||||
void
|
|
||||||
status_replace1(struct client *c, char **iptr, char **optr, char *out,
|
|
||||||
size_t outsize)
|
|
||||||
{
|
|
||||||
char ch, tmp[256], *ptr, *endptr;
|
|
||||||
size_t ptrlen;
|
|
||||||
long limit;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
limit = strtol(*iptr, &endptr, 10);
|
|
||||||
if ((limit == 0 && errno != EINVAL) ||
|
|
||||||
(limit == LONG_MIN && errno != ERANGE) ||
|
|
||||||
(limit == LONG_MAX && errno != ERANGE) ||
|
|
||||||
limit != 0)
|
|
||||||
*iptr = endptr;
|
|
||||||
if (limit <= 0)
|
|
||||||
limit = LONG_MAX;
|
|
||||||
|
|
||||||
switch (*(*iptr)++) {
|
|
||||||
case '(':
|
|
||||||
if ((ptr = status_find_job(c, iptr)) == NULL)
|
|
||||||
return;
|
|
||||||
goto do_replace;
|
|
||||||
case '[':
|
|
||||||
/*
|
|
||||||
* Embedded style, handled at display time. Leave present and
|
|
||||||
* skip input until ].
|
|
||||||
*/
|
|
||||||
ch = ']';
|
|
||||||
goto skip_to;
|
|
||||||
case '{':
|
|
||||||
ptr = (char *) "#{";
|
|
||||||
goto do_replace;
|
|
||||||
default:
|
|
||||||
xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1));
|
|
||||||
ptr = tmp;
|
|
||||||
goto do_replace;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
do_replace:
|
|
||||||
ptrlen = strlen(ptr);
|
|
||||||
if ((size_t) limit < ptrlen)
|
|
||||||
ptrlen = limit;
|
|
||||||
|
|
||||||
if (*optr + ptrlen >= out + outsize - 1)
|
|
||||||
return;
|
|
||||||
while (ptrlen > 0 && *ptr != '\0') {
|
|
||||||
*(*optr)++ = *ptr++;
|
|
||||||
ptrlen--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
skip_to:
|
|
||||||
*(*optr)++ = '#';
|
|
||||||
|
|
||||||
(*iptr)--; /* include ch */
|
|
||||||
while (**iptr != ch && **iptr != '\0') {
|
|
||||||
if (*optr >= out + outsize - 1)
|
|
||||||
break;
|
|
||||||
*(*optr)++ = *(*iptr)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Replace special sequences in fmt. */
|
/* Replace special sequences in fmt. */
|
||||||
char *
|
char *
|
||||||
status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
|
status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
|
||||||
{
|
{
|
||||||
static char out[BUFSIZ];
|
|
||||||
char in[BUFSIZ], ch, *iptr, *optr, *expanded;
|
|
||||||
size_t len;
|
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
|
char *expanded;
|
||||||
|
|
||||||
if (fmt == NULL)
|
if (fmt == NULL)
|
||||||
return (xstrdup(""));
|
return (xstrdup(""));
|
||||||
|
|
||||||
len = strftime(in, sizeof in, fmt, localtime(&t));
|
ft = format_create_status(1);
|
||||||
in[len] = '\0';
|
|
||||||
|
|
||||||
iptr = in;
|
|
||||||
optr = out;
|
|
||||||
|
|
||||||
while (*iptr != '\0') {
|
|
||||||
if (optr >= out + (sizeof out) - 1)
|
|
||||||
break;
|
|
||||||
ch = *iptr++;
|
|
||||||
|
|
||||||
if (ch != '#' || *iptr == '\0') {
|
|
||||||
*optr++ = ch;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
status_replace1(c, &iptr, &optr, out, sizeof out);
|
|
||||||
}
|
|
||||||
*optr = '\0';
|
|
||||||
|
|
||||||
ft = format_create();
|
|
||||||
format_defaults(ft, c, NULL, wl, NULL);
|
format_defaults(ft, c, NULL, wl, NULL);
|
||||||
expanded = format_expand(ft, out);
|
|
||||||
|
expanded = format_expand_time(ft, fmt, t);
|
||||||
|
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
return (expanded);
|
return (expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Figure out job name and get its result, starting it off if necessary. */
|
|
||||||
char *
|
|
||||||
status_find_job(struct client *c, char **iptr)
|
|
||||||
{
|
|
||||||
struct status_out *so, so_find;
|
|
||||||
char *cmd;
|
|
||||||
int lastesc;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (**iptr == '\0')
|
|
||||||
return (NULL);
|
|
||||||
if (**iptr == ')') { /* no command given */
|
|
||||||
(*iptr)++;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = xmalloc(strlen(*iptr) + 1);
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
lastesc = 0;
|
|
||||||
for (; **iptr != '\0'; (*iptr)++) {
|
|
||||||
if (!lastesc && **iptr == ')')
|
|
||||||
break; /* unescaped ) is the end */
|
|
||||||
if (!lastesc && **iptr == '\\') {
|
|
||||||
lastesc = 1;
|
|
||||||
continue; /* skip \ if not escaped */
|
|
||||||
}
|
|
||||||
lastesc = 0;
|
|
||||||
cmd[len++] = **iptr;
|
|
||||||
}
|
|
||||||
if (**iptr == '\0') /* no terminating ) */ {
|
|
||||||
free(cmd);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
(*iptr)++; /* skip final ) */
|
|
||||||
cmd[len] = '\0';
|
|
||||||
|
|
||||||
/* First try in the new tree. */
|
|
||||||
so_find.cmd = cmd;
|
|
||||||
so = RB_FIND(status_out_tree, &c->status_new, &so_find);
|
|
||||||
if (so != NULL && so->out != NULL) {
|
|
||||||
free(cmd);
|
|
||||||
return (so->out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not found at all, start the job and add to the tree. */
|
|
||||||
if (so == NULL) {
|
|
||||||
job_run(cmd, NULL, -1, status_job_callback, status_job_free, c);
|
|
||||||
c->references++;
|
|
||||||
|
|
||||||
so = xmalloc(sizeof *so);
|
|
||||||
so->cmd = xstrdup(cmd);
|
|
||||||
so->out = NULL;
|
|
||||||
RB_INSERT(status_out_tree, &c->status_new, so);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lookup in the old tree. */
|
|
||||||
so_find.cmd = cmd;
|
|
||||||
so = RB_FIND(status_out_tree, &c->status_old, &so_find);
|
|
||||||
free(cmd);
|
|
||||||
if (so != NULL)
|
|
||||||
return (so->out);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free job tree. */
|
|
||||||
void
|
|
||||||
status_free_jobs(struct status_out_tree *sotree)
|
|
||||||
{
|
|
||||||
struct status_out *so, *so_next;
|
|
||||||
|
|
||||||
so_next = RB_MIN(status_out_tree, sotree);
|
|
||||||
while (so_next != NULL) {
|
|
||||||
so = so_next;
|
|
||||||
so_next = RB_NEXT(status_out_tree, sotree, so);
|
|
||||||
|
|
||||||
RB_REMOVE(status_out_tree, sotree, so);
|
|
||||||
free(so->out);
|
|
||||||
free(so->cmd);
|
|
||||||
free(so);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update jobs on status interval. */
|
|
||||||
void
|
|
||||||
status_update_jobs(struct client *c)
|
|
||||||
{
|
|
||||||
/* Free the old tree. */
|
|
||||||
status_free_jobs(&c->status_old);
|
|
||||||
|
|
||||||
/* Move the new to old. */
|
|
||||||
memcpy(&c->status_old, &c->status_new, sizeof c->status_old);
|
|
||||||
RB_INIT(&c->status_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free status job. */
|
|
||||||
void
|
|
||||||
status_job_free(void *data)
|
|
||||||
{
|
|
||||||
struct client *c = data;
|
|
||||||
|
|
||||||
c->references--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Job has finished: save its result. */
|
|
||||||
void
|
|
||||||
status_job_callback(struct job *job)
|
|
||||||
{
|
|
||||||
struct client *c = job->data;
|
|
||||||
struct status_out *so, so_find;
|
|
||||||
char *line, *buf;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (c->flags & CLIENT_DEAD)
|
|
||||||
return;
|
|
||||||
|
|
||||||
so_find.cmd = job->cmd;
|
|
||||||
so = RB_FIND(status_out_tree, &c->status_new, &so_find);
|
|
||||||
if (so == NULL || so->out != NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
buf = NULL;
|
|
||||||
if ((line = evbuffer_readline(job->event->input)) == NULL) {
|
|
||||||
len = EVBUFFER_LENGTH(job->event->input);
|
|
||||||
buf = xmalloc(len + 1);
|
|
||||||
if (len != 0)
|
|
||||||
memcpy(buf, EVBUFFER_DATA(job->event->input), len);
|
|
||||||
buf[len] = '\0';
|
|
||||||
} else
|
|
||||||
buf = line;
|
|
||||||
|
|
||||||
so->out = buf;
|
|
||||||
server_status_client(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return winlink status line entry and adjust gc as necessary. */
|
/* Return winlink status line entry and adjust gc as necessary. */
|
||||||
char *
|
char *
|
||||||
status_print(struct client *c, struct winlink *wl, time_t t,
|
status_print(struct client *c, struct winlink *wl, time_t t,
|
||||||
|
32
tmux.1
32
tmux.1
@ -2666,25 +2666,10 @@ will be expanded.
|
|||||||
It may also contain any of the following special character sequences:
|
It may also contain any of the following special character sequences:
|
||||||
.Bl -column "Character pair" "Replaced with" -offset indent
|
.Bl -column "Character pair" "Replaced with" -offset indent
|
||||||
.It Sy "Character pair" Ta Sy "Replaced with"
|
.It Sy "Character pair" Ta Sy "Replaced with"
|
||||||
.It Li "#(shell-command)" Ta "First line of the command's output"
|
|
||||||
.It Li "#[attributes]" Ta "Colour or attribute change"
|
.It Li "#[attributes]" Ta "Colour or attribute change"
|
||||||
.It Li "##" Ta "A literal" Ql #
|
.It Li "##" Ta "A literal" Ql #
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The #(shell-command) form executes
|
|
||||||
.Ql shell-command
|
|
||||||
and inserts the first line of its output.
|
|
||||||
Note that shell commands are only executed once at the interval specified by
|
|
||||||
the
|
|
||||||
.Ic status-interval
|
|
||||||
option: if the status line is redrawn in the meantime, the previous result is
|
|
||||||
used.
|
|
||||||
Shell commands are executed with the
|
|
||||||
.Nm
|
|
||||||
global environment set (see the
|
|
||||||
.Sx ENVIRONMENT
|
|
||||||
section).
|
|
||||||
.Pp
|
|
||||||
For details on how the names and titles can be set see the
|
For details on how the names and titles can be set see the
|
||||||
.Sx "NAMES AND TITLES"
|
.Sx "NAMES AND TITLES"
|
||||||
section.
|
section.
|
||||||
@ -3249,6 +3234,23 @@ a number and a colon, so
|
|||||||
.Ql #{=10:pane_title}
|
.Ql #{=10:pane_title}
|
||||||
will include at most the first 10 characters of the pane title.
|
will include at most the first 10 characters of the pane title.
|
||||||
.Pp
|
.Pp
|
||||||
|
In addition, the first line of a shell command's output may be inserted using
|
||||||
|
.Ql #() .
|
||||||
|
For example,
|
||||||
|
.Ql #(uptime)
|
||||||
|
will insert the system's uptime.
|
||||||
|
When constructing formats,
|
||||||
|
.Nm
|
||||||
|
does not wait for
|
||||||
|
.Ql #()
|
||||||
|
commands to finish; instead, the previous result from running the same command is used,
|
||||||
|
or a placeholder if the command has not been run before.
|
||||||
|
Commands are executed with the
|
||||||
|
.Nm
|
||||||
|
global environment set (see the
|
||||||
|
.Sx ENVIRONMENT
|
||||||
|
section).
|
||||||
|
.Pp
|
||||||
The following variables are available, where appropriate:
|
The following variables are available, where appropriate:
|
||||||
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
||||||
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
||||||
|
2
tmux.h
2
tmux.h
@ -1479,7 +1479,9 @@ void cfg_show_causes(struct session *);
|
|||||||
|
|
||||||
/* format.c */
|
/* format.c */
|
||||||
struct format_tree;
|
struct format_tree;
|
||||||
|
void format_clean(void);
|
||||||
struct format_tree *format_create(void);
|
struct format_tree *format_create(void);
|
||||||
|
struct format_tree *format_create_status(int);
|
||||||
void format_free(struct format_tree *);
|
void format_free(struct format_tree *);
|
||||||
void printflike(3, 4) format_add(struct format_tree *, const char *,
|
void printflike(3, 4) format_add(struct format_tree *, const char *,
|
||||||
const char *, ...);
|
const char *, ...);
|
||||||
|
Loading…
Reference in New Issue
Block a user