mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 01:34:18 +00:00 
			
		
		
		
	Merge branch 'obsd-master'
This commit is contained in:
		@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										192
									
								
								format.c
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								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,10 +266,8 @@ 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;
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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 *, ...);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user