Support more mouse buttons when the terminal sends them, GitHub issue
[tmux.git] / format.c
blob2fc0961e550d12d7f521f2dc74b7dc6b6f997b6c
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/wait.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fnmatch.h>
25 #include <libgen.h>
26 #include <math.h>
27 #include <regex.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
34 #include "tmux.h"
37 * Build a list of key-value pairs and use them to expand #{key} entries in a
38 * string.
41 struct format_expand_state;
43 static char *format_job_get(struct format_expand_state *, const char *);
44 static char *format_expand1(struct format_expand_state *, const char *);
45 static int format_replace(struct format_expand_state *, const char *,
46 size_t, char **, size_t *, size_t *);
47 static void format_defaults_session(struct format_tree *,
48 struct session *);
49 static void format_defaults_client(struct format_tree *, struct client *);
50 static void format_defaults_winlink(struct format_tree *,
51 struct winlink *);
53 /* Entry in format job tree. */
54 struct format_job {
55 struct client *client;
56 u_int tag;
57 const char *cmd;
58 const char *expanded;
60 time_t last;
61 char *out;
62 int updated;
64 struct job *job;
65 int status;
67 RB_ENTRY(format_job) entry;
70 /* Format job tree. */
71 static int format_job_cmp(struct format_job *, struct format_job *);
72 static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
73 RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp);
75 /* Format job tree comparison function. */
76 static int
77 format_job_cmp(struct format_job *fj1, struct format_job *fj2)
79 if (fj1->tag < fj2->tag)
80 return (-1);
81 if (fj1->tag > fj2->tag)
82 return (1);
83 return (strcmp(fj1->cmd, fj2->cmd));
86 /* Format modifiers. */
87 #define FORMAT_TIMESTRING 0x1
88 #define FORMAT_BASENAME 0x2
89 #define FORMAT_DIRNAME 0x4
90 #define FORMAT_QUOTE_SHELL 0x8
91 #define FORMAT_LITERAL 0x10
92 #define FORMAT_EXPAND 0x20
93 #define FORMAT_EXPANDTIME 0x40
94 #define FORMAT_SESSIONS 0x80
95 #define FORMAT_WINDOWS 0x100
96 #define FORMAT_PANES 0x200
97 #define FORMAT_PRETTY 0x400
98 #define FORMAT_LENGTH 0x800
99 #define FORMAT_WIDTH 0x1000
100 #define FORMAT_QUOTE_STYLE 0x2000
101 #define FORMAT_WINDOW_NAME 0x4000
102 #define FORMAT_SESSION_NAME 0x8000
103 #define FORMAT_CHARACTER 0x10000
104 #define FORMAT_COLOUR 0x20000
106 /* Limit on recursion. */
107 #define FORMAT_LOOP_LIMIT 100
109 /* Format expand flags. */
110 #define FORMAT_EXPAND_TIME 0x1
111 #define FORMAT_EXPAND_NOJOBS 0x2
113 /* Entry in format tree. */
114 struct format_entry {
115 char *key;
116 char *value;
117 time_t time;
118 format_cb cb;
119 RB_ENTRY(format_entry) entry;
122 /* Format type. */
123 enum format_type {
124 FORMAT_TYPE_UNKNOWN,
125 FORMAT_TYPE_SESSION,
126 FORMAT_TYPE_WINDOW,
127 FORMAT_TYPE_PANE
130 struct format_tree {
131 enum format_type type;
133 struct client *c;
134 struct session *s;
135 struct winlink *wl;
136 struct window *w;
137 struct window_pane *wp;
138 struct paste_buffer *pb;
140 struct cmdq_item *item;
141 struct client *client;
142 int flags;
143 u_int tag;
145 struct mouse_event m;
147 RB_HEAD(format_entry_tree, format_entry) tree;
149 static int format_entry_cmp(struct format_entry *, struct format_entry *);
150 RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
152 /* Format expand state. */
153 struct format_expand_state {
154 struct format_tree *ft;
155 u_int loop;
156 time_t time;
157 struct tm tm;
158 int flags;
161 /* Format modifier. */
162 struct format_modifier {
163 char modifier[3];
164 u_int size;
166 char **argv;
167 int argc;
170 /* Format entry tree comparison function. */
171 static int
172 format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
174 return (strcmp(fe1->key, fe2->key));
177 /* Single-character uppercase aliases. */
178 static const char *format_upper[] = {
179 NULL, /* A */
180 NULL, /* B */
181 NULL, /* C */
182 "pane_id", /* D */
183 NULL, /* E */
184 "window_flags", /* F */
185 NULL, /* G */
186 "host", /* H */
187 "window_index", /* I */
188 NULL, /* J */
189 NULL, /* K */
190 NULL, /* L */
191 NULL, /* M */
192 NULL, /* N */
193 NULL, /* O */
194 "pane_index", /* P */
195 NULL, /* Q */
196 NULL, /* R */
197 "session_name", /* S */
198 "pane_title", /* T */
199 NULL, /* U */
200 NULL, /* V */
201 "window_name", /* W */
202 NULL, /* X */
203 NULL, /* Y */
204 NULL /* Z */
207 /* Single-character lowercase aliases. */
208 static const char *format_lower[] = {
209 NULL, /* a */
210 NULL, /* b */
211 NULL, /* c */
212 NULL, /* d */
213 NULL, /* e */
214 NULL, /* f */
215 NULL, /* g */
216 "host_short", /* h */
217 NULL, /* i */
218 NULL, /* j */
219 NULL, /* k */
220 NULL, /* l */
221 NULL, /* m */
222 NULL, /* n */
223 NULL, /* o */
224 NULL, /* p */
225 NULL, /* q */
226 NULL, /* r */
227 NULL, /* s */
228 NULL, /* t */
229 NULL, /* u */
230 NULL, /* v */
231 NULL, /* w */
232 NULL, /* x */
233 NULL, /* y */
234 NULL /* z */
237 /* Is logging enabled? */
238 static inline int
239 format_logging(struct format_tree *ft)
241 return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE));
244 /* Log a message if verbose. */
245 static void printflike(3, 4)
246 format_log1(struct format_expand_state *es, const char *from, const char *fmt,
247 ...)
249 struct format_tree *ft = es->ft;
250 va_list ap;
251 char *s;
252 static const char spaces[] = " ";
254 if (!format_logging(ft))
255 return;
257 va_start(ap, fmt);
258 xvasprintf(&s, fmt, ap);
259 va_end(ap);
261 log_debug("%s: %s", from, s);
262 if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
263 cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
265 free(s);
267 #define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
269 /* Copy expand state. */
270 static void
271 format_copy_state(struct format_expand_state *to,
272 struct format_expand_state *from, int flags)
274 to->ft = from->ft;
275 to->loop = from->loop;
276 to->time = from->time;
277 memcpy(&to->tm, &from->tm, sizeof to->tm);
278 to->flags = from->flags|flags;
281 /* Format job update callback. */
282 static void
283 format_job_update(struct job *job)
285 struct format_job *fj = job_get_data(job);
286 struct evbuffer *evb = job_get_event(job)->input;
287 char *line = NULL, *next;
288 time_t t;
290 while ((next = evbuffer_readline(evb)) != NULL) {
291 free(line);
292 line = next;
294 if (line == NULL)
295 return;
296 fj->updated = 1;
298 free(fj->out);
299 fj->out = line;
301 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
303 t = time(NULL);
304 if (fj->status && fj->last != t) {
305 if (fj->client != NULL)
306 server_status_client(fj->client);
307 fj->last = t;
311 /* Format job complete callback. */
312 static void
313 format_job_complete(struct job *job)
315 struct format_job *fj = job_get_data(job);
316 struct evbuffer *evb = job_get_event(job)->input;
317 char *line, *buf;
318 size_t len;
320 fj->job = NULL;
322 buf = NULL;
323 if ((line = evbuffer_readline(evb)) == NULL) {
324 len = EVBUFFER_LENGTH(evb);
325 buf = xmalloc(len + 1);
326 if (len != 0)
327 memcpy(buf, EVBUFFER_DATA(evb), len);
328 buf[len] = '\0';
329 } else
330 buf = line;
332 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
334 if (*buf != '\0' || !fj->updated) {
335 free(fj->out);
336 fj->out = buf;
337 } else
338 free(buf);
340 if (fj->status) {
341 if (fj->client != NULL)
342 server_status_client(fj->client);
343 fj->status = 0;
347 /* Find a job. */
348 static char *
349 format_job_get(struct format_expand_state *es, const char *cmd)
351 struct format_tree *ft = es->ft;
352 struct format_job_tree *jobs;
353 struct format_job fj0, *fj;
354 time_t t;
355 char *expanded;
356 int force;
357 struct format_expand_state next;
359 if (ft->client == NULL)
360 jobs = &format_jobs;
361 else if (ft->client->jobs != NULL)
362 jobs = ft->client->jobs;
363 else {
364 jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs);
365 RB_INIT(jobs);
368 fj0.tag = ft->tag;
369 fj0.cmd = cmd;
370 if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) {
371 fj = xcalloc(1, sizeof *fj);
372 fj->client = ft->client;
373 fj->tag = ft->tag;
374 fj->cmd = xstrdup(cmd);
376 RB_INSERT(format_job_tree, jobs, fj);
379 format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
380 next.flags &= ~FORMAT_EXPAND_TIME;
382 expanded = format_expand1(&next, cmd);
383 if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
384 free((void *)fj->expanded);
385 fj->expanded = xstrdup(expanded);
386 force = 1;
387 } else
388 force = (ft->flags & FORMAT_FORCE);
390 t = time(NULL);
391 if (force && fj->job != NULL)
392 job_free(fj->job);
393 if (force || (fj->job == NULL && fj->last != t)) {
394 fj->job = job_run(expanded, 0, NULL, NULL, NULL,
395 server_client_get_cwd(ft->client, NULL), format_job_update,
396 format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1);
397 if (fj->job == NULL) {
398 free(fj->out);
399 xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
401 fj->last = t;
402 fj->updated = 0;
403 } else if (fj->job != NULL && (t - fj->last) > 1 && fj->out == NULL)
404 xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
405 free(expanded);
407 if (ft->flags & FORMAT_STATUS)
408 fj->status = 1;
409 if (fj->out == NULL)
410 return (xstrdup(""));
411 return (format_expand1(&next, fj->out));
414 /* Remove old jobs. */
415 static void
416 format_job_tidy(struct format_job_tree *jobs, int force)
418 struct format_job *fj, *fj1;
419 time_t now;
421 now = time(NULL);
422 RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) {
423 if (!force && (fj->last > now || now - fj->last < 3600))
424 continue;
425 RB_REMOVE(format_job_tree, jobs, fj);
427 log_debug("%s: %s", __func__, fj->cmd);
429 if (fj->job != NULL)
430 job_free(fj->job);
432 free((void *)fj->expanded);
433 free((void *)fj->cmd);
434 free(fj->out);
436 free(fj);
440 /* Tidy old jobs for all clients. */
441 void
442 format_tidy_jobs(void)
444 struct client *c;
446 format_job_tidy(&format_jobs, 0);
447 TAILQ_FOREACH(c, &clients, entry) {
448 if (c->jobs != NULL)
449 format_job_tidy(c->jobs, 0);
453 /* Remove old jobs for client. */
454 void
455 format_lost_client(struct client *c)
457 if (c->jobs != NULL)
458 format_job_tidy(c->jobs, 1);
459 free(c->jobs);
462 /* Wrapper for asprintf. */
463 static char * printflike(1, 2)
464 format_printf(const char *fmt, ...)
466 va_list ap;
467 char *s;
469 va_start(ap, fmt);
470 xvasprintf(&s, fmt, ap);
471 va_end(ap);
472 return (s);
475 /* Callback for host. */
476 static void *
477 format_cb_host(__unused struct format_tree *ft)
479 char host[HOST_NAME_MAX + 1];
481 if (gethostname(host, sizeof host) != 0)
482 return (xstrdup(""));
483 return (xstrdup(host));
486 /* Callback for host_short. */
487 static void *
488 format_cb_host_short(__unused struct format_tree *ft)
490 char host[HOST_NAME_MAX + 1], *cp;
492 if (gethostname(host, sizeof host) != 0)
493 return (xstrdup(""));
494 if ((cp = strchr(host, '.')) != NULL)
495 *cp = '\0';
496 return (xstrdup(host));
499 /* Callback for pid. */
500 static void *
501 format_cb_pid(__unused struct format_tree *ft)
503 char *value;
505 xasprintf(&value, "%ld", (long)getpid());
506 return (value);
509 /* Callback for session_attached_list. */
510 static void *
511 format_cb_session_attached_list(struct format_tree *ft)
513 struct session *s = ft->s;
514 struct client *loop;
515 struct evbuffer *buffer;
516 int size;
517 char *value = NULL;
519 if (s == NULL)
520 return (NULL);
522 buffer = evbuffer_new();
523 if (buffer == NULL)
524 fatalx("out of memory");
526 TAILQ_FOREACH(loop, &clients, entry) {
527 if (loop->session == s) {
528 if (EVBUFFER_LENGTH(buffer) > 0)
529 evbuffer_add(buffer, ",", 1);
530 evbuffer_add_printf(buffer, "%s", loop->name);
534 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
535 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
536 evbuffer_free(buffer);
537 return (value);
540 /* Callback for session_alerts. */
541 static void *
542 format_cb_session_alerts(struct format_tree *ft)
544 struct session *s = ft->s;
545 struct winlink *wl;
546 char alerts[1024], tmp[16];
548 if (s == NULL)
549 return (NULL);
551 *alerts = '\0';
552 RB_FOREACH(wl, winlinks, &s->windows) {
553 if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
554 continue;
555 xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
557 if (*alerts != '\0')
558 strlcat(alerts, ",", sizeof alerts);
559 strlcat(alerts, tmp, sizeof alerts);
560 if (wl->flags & WINLINK_ACTIVITY)
561 strlcat(alerts, "#", sizeof alerts);
562 if (wl->flags & WINLINK_BELL)
563 strlcat(alerts, "!", sizeof alerts);
564 if (wl->flags & WINLINK_SILENCE)
565 strlcat(alerts, "~", sizeof alerts);
567 return (xstrdup(alerts));
570 /* Callback for session_stack. */
571 static void *
572 format_cb_session_stack(struct format_tree *ft)
574 struct session *s = ft->s;
575 struct winlink *wl;
576 char result[1024], tmp[16];
578 if (s == NULL)
579 return (NULL);
581 xsnprintf(result, sizeof result, "%u", s->curw->idx);
582 TAILQ_FOREACH(wl, &s->lastw, sentry) {
583 xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
585 if (*result != '\0')
586 strlcat(result, ",", sizeof result);
587 strlcat(result, tmp, sizeof result);
589 return (xstrdup(result));
592 /* Callback for window_stack_index. */
593 static void *
594 format_cb_window_stack_index(struct format_tree *ft)
596 struct session *s;
597 struct winlink *wl;
598 u_int idx;
599 char *value = NULL;
601 if (ft->wl == NULL)
602 return (NULL);
603 s = ft->wl->session;
605 idx = 0;
606 TAILQ_FOREACH(wl, &s->lastw, sentry) {
607 idx++;
608 if (wl == ft->wl)
609 break;
611 if (wl == NULL)
612 return (xstrdup("0"));
613 xasprintf(&value, "%u", idx);
614 return (value);
617 /* Callback for window_linked_sessions_list. */
618 static void *
619 format_cb_window_linked_sessions_list(struct format_tree *ft)
621 struct window *w;
622 struct winlink *wl;
623 struct evbuffer *buffer;
624 int size;
625 char *value = NULL;
627 if (ft->wl == NULL)
628 return (NULL);
629 w = ft->wl->window;
631 buffer = evbuffer_new();
632 if (buffer == NULL)
633 fatalx("out of memory");
635 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
636 if (EVBUFFER_LENGTH(buffer) > 0)
637 evbuffer_add(buffer, ",", 1);
638 evbuffer_add_printf(buffer, "%s", wl->session->name);
641 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
642 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
643 evbuffer_free(buffer);
644 return (value);
647 /* Callback for window_active_sessions. */
648 static void *
649 format_cb_window_active_sessions(struct format_tree *ft)
651 struct window *w;
652 struct winlink *wl;
653 u_int n = 0;
654 char *value;
656 if (ft->wl == NULL)
657 return (NULL);
658 w = ft->wl->window;
660 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
661 if (wl->session->curw == wl)
662 n++;
665 xasprintf(&value, "%u", n);
666 return (value);
669 /* Callback for window_active_sessions_list. */
670 static void *
671 format_cb_window_active_sessions_list(struct format_tree *ft)
673 struct window *w;
674 struct winlink *wl;
675 struct evbuffer *buffer;
676 int size;
677 char *value = NULL;
679 if (ft->wl == NULL)
680 return (NULL);
681 w = ft->wl->window;
683 buffer = evbuffer_new();
684 if (buffer == NULL)
685 fatalx("out of memory");
687 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
688 if (wl->session->curw == wl) {
689 if (EVBUFFER_LENGTH(buffer) > 0)
690 evbuffer_add(buffer, ",", 1);
691 evbuffer_add_printf(buffer, "%s", wl->session->name);
695 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
696 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
697 evbuffer_free(buffer);
698 return (value);
701 /* Callback for window_active_clients. */
702 static void *
703 format_cb_window_active_clients(struct format_tree *ft)
705 struct window *w;
706 struct client *loop;
707 struct session *client_session;
708 u_int n = 0;
709 char *value;
711 if (ft->wl == NULL)
712 return (NULL);
713 w = ft->wl->window;
715 TAILQ_FOREACH(loop, &clients, entry) {
716 client_session = loop->session;
717 if (client_session == NULL)
718 continue;
720 if (w == client_session->curw->window)
721 n++;
724 xasprintf(&value, "%u", n);
725 return (value);
728 /* Callback for window_active_clients_list. */
729 static void *
730 format_cb_window_active_clients_list(struct format_tree *ft)
732 struct window *w;
733 struct client *loop;
734 struct session *client_session;
735 struct evbuffer *buffer;
736 int size;
737 char *value = NULL;
739 if (ft->wl == NULL)
740 return (NULL);
741 w = ft->wl->window;
743 buffer = evbuffer_new();
744 if (buffer == NULL)
745 fatalx("out of memory");
747 TAILQ_FOREACH(loop, &clients, entry) {
748 client_session = loop->session;
749 if (client_session == NULL)
750 continue;
752 if (w == client_session->curw->window) {
753 if (EVBUFFER_LENGTH(buffer) > 0)
754 evbuffer_add(buffer, ",", 1);
755 evbuffer_add_printf(buffer, "%s", loop->name);
759 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
760 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
761 evbuffer_free(buffer);
762 return (value);
765 /* Callback for window_layout. */
766 static void *
767 format_cb_window_layout(struct format_tree *ft)
769 struct window *w = ft->w;
771 if (w == NULL)
772 return (NULL);
774 if (w->saved_layout_root != NULL)
775 return (layout_dump(w->saved_layout_root));
776 return (layout_dump(w->layout_root));
779 /* Callback for window_visible_layout. */
780 static void *
781 format_cb_window_visible_layout(struct format_tree *ft)
783 struct window *w = ft->w;
785 if (w == NULL)
786 return (NULL);
788 return (layout_dump(w->layout_root));
791 /* Callback for pane_start_command. */
792 static void *
793 format_cb_start_command(struct format_tree *ft)
795 struct window_pane *wp = ft->wp;
797 if (wp == NULL)
798 return (NULL);
800 return (cmd_stringify_argv(wp->argc, wp->argv));
803 /* Callback for pane_current_command. */
804 static void *
805 format_cb_current_command(struct format_tree *ft)
807 struct window_pane *wp = ft->wp;
808 char *cmd, *value;
810 if (wp == NULL || wp->shell == NULL)
811 return (NULL);
813 cmd = osdep_get_name(wp->fd, wp->tty);
814 if (cmd == NULL || *cmd == '\0') {
815 free(cmd);
816 cmd = cmd_stringify_argv(wp->argc, wp->argv);
817 if (cmd == NULL || *cmd == '\0') {
818 free(cmd);
819 cmd = xstrdup(wp->shell);
822 value = parse_window_name(cmd);
823 free(cmd);
824 return (value);
827 /* Callback for pane_current_path. */
828 static void *
829 format_cb_current_path(struct format_tree *ft)
831 struct window_pane *wp = ft->wp;
832 char *cwd;
834 if (wp == NULL)
835 return (NULL);
837 cwd = osdep_get_cwd(wp->fd);
838 if (cwd == NULL)
839 return (NULL);
840 return (xstrdup(cwd));
843 /* Callback for history_bytes. */
844 static void *
845 format_cb_history_bytes(struct format_tree *ft)
847 struct window_pane *wp = ft->wp;
848 struct grid *gd;
849 struct grid_line *gl;
850 size_t size = 0;
851 u_int i;
852 char *value;
854 if (wp == NULL)
855 return (NULL);
856 gd = wp->base.grid;
858 for (i = 0; i < gd->hsize + gd->sy; i++) {
859 gl = grid_get_line(gd, i);
860 size += gl->cellsize * sizeof *gl->celldata;
861 size += gl->extdsize * sizeof *gl->extddata;
863 size += (gd->hsize + gd->sy) * sizeof *gl;
865 xasprintf(&value, "%zu", size);
866 return (value);
869 /* Callback for history_all_bytes. */
870 static void *
871 format_cb_history_all_bytes(struct format_tree *ft)
873 struct window_pane *wp = ft->wp;
874 struct grid *gd;
875 struct grid_line *gl;
876 u_int i, lines, cells = 0, extended_cells = 0;
877 char *value;
879 if (wp == NULL)
880 return (NULL);
881 gd = wp->base.grid;
883 lines = gd->hsize + gd->sy;
884 for (i = 0; i < lines; i++) {
885 gl = grid_get_line(gd, i);
886 cells += gl->cellsize;
887 extended_cells += gl->extdsize;
890 xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines,
891 lines * sizeof *gl, cells, cells * sizeof *gl->celldata,
892 extended_cells, extended_cells * sizeof *gl->extddata);
893 return (value);
896 /* Callback for pane_tabs. */
897 static void *
898 format_cb_pane_tabs(struct format_tree *ft)
900 struct window_pane *wp = ft->wp;
901 struct evbuffer *buffer;
902 u_int i;
903 int size;
904 char *value = NULL;
906 if (wp == NULL)
907 return (NULL);
909 buffer = evbuffer_new();
910 if (buffer == NULL)
911 fatalx("out of memory");
912 for (i = 0; i < wp->base.grid->sx; i++) {
913 if (!bit_test(wp->base.tabs, i))
914 continue;
916 if (EVBUFFER_LENGTH(buffer) > 0)
917 evbuffer_add(buffer, ",", 1);
918 evbuffer_add_printf(buffer, "%u", i);
920 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
921 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
922 evbuffer_free(buffer);
923 return (value);
926 /* Callback for pane_fg. */
927 static void *
928 format_cb_pane_fg(struct format_tree *ft)
930 struct window_pane *wp = ft->wp;
931 struct grid_cell gc;
933 if (wp == NULL)
934 return (NULL);
936 tty_default_colours(&gc, wp);
937 return (xstrdup(colour_tostring(gc.fg)));
940 /* Callback for pane_bg. */
941 static void *
942 format_cb_pane_bg(struct format_tree *ft)
944 struct window_pane *wp = ft->wp;
945 struct grid_cell gc;
947 if (wp == NULL)
948 return (NULL);
950 tty_default_colours(&gc, wp);
951 return (xstrdup(colour_tostring(gc.bg)));
954 /* Callback for session_group_list. */
955 static void *
956 format_cb_session_group_list(struct format_tree *ft)
958 struct session *s = ft->s;
959 struct session_group *sg;
960 struct session *loop;
961 struct evbuffer *buffer;
962 int size;
963 char *value = NULL;
965 if (s == NULL)
966 return (NULL);
967 sg = session_group_contains(s);
968 if (sg == NULL)
969 return (NULL);
971 buffer = evbuffer_new();
972 if (buffer == NULL)
973 fatalx("out of memory");
975 TAILQ_FOREACH(loop, &sg->sessions, gentry) {
976 if (EVBUFFER_LENGTH(buffer) > 0)
977 evbuffer_add(buffer, ",", 1);
978 evbuffer_add_printf(buffer, "%s", loop->name);
981 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
982 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
983 evbuffer_free(buffer);
984 return (value);
987 /* Callback for session_group_attached_list. */
988 static void *
989 format_cb_session_group_attached_list(struct format_tree *ft)
991 struct session *s = ft->s, *client_session, *session_loop;
992 struct session_group *sg;
993 struct client *loop;
994 struct evbuffer *buffer;
995 int size;
996 char *value = NULL;
998 if (s == NULL)
999 return (NULL);
1000 sg = session_group_contains(s);
1001 if (sg == NULL)
1002 return (NULL);
1004 buffer = evbuffer_new();
1005 if (buffer == NULL)
1006 fatalx("out of memory");
1008 TAILQ_FOREACH(loop, &clients, entry) {
1009 client_session = loop->session;
1010 if (client_session == NULL)
1011 continue;
1012 TAILQ_FOREACH(session_loop, &sg->sessions, gentry) {
1013 if (session_loop == client_session){
1014 if (EVBUFFER_LENGTH(buffer) > 0)
1015 evbuffer_add(buffer, ",", 1);
1016 evbuffer_add_printf(buffer, "%s", loop->name);
1021 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1022 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
1023 evbuffer_free(buffer);
1024 return (value);
1027 /* Callback for pane_in_mode. */
1028 static void *
1029 format_cb_pane_in_mode(struct format_tree *ft)
1031 struct window_pane *wp = ft->wp;
1032 u_int n = 0;
1033 struct window_mode_entry *wme;
1034 char *value;
1036 if (wp == NULL)
1037 return (NULL);
1039 TAILQ_FOREACH(wme, &wp->modes, entry)
1040 n++;
1041 xasprintf(&value, "%u", n);
1042 return (value);
1045 /* Callback for pane_at_top. */
1046 static void *
1047 format_cb_pane_at_top(struct format_tree *ft)
1049 struct window_pane *wp = ft->wp;
1050 struct window *w;
1051 int status, flag;
1052 char *value;
1054 if (wp == NULL)
1055 return (NULL);
1056 w = wp->window;
1058 status = options_get_number(w->options, "pane-border-status");
1059 if (status == PANE_STATUS_TOP)
1060 flag = (wp->yoff == 1);
1061 else
1062 flag = (wp->yoff == 0);
1063 xasprintf(&value, "%d", flag);
1064 return (value);
1067 /* Callback for pane_at_bottom. */
1068 static void *
1069 format_cb_pane_at_bottom(struct format_tree *ft)
1071 struct window_pane *wp = ft->wp;
1072 struct window *w;
1073 int status, flag;
1074 char *value;
1076 if (wp == NULL)
1077 return (NULL);
1078 w = wp->window;
1080 status = options_get_number(w->options, "pane-border-status");
1081 if (status == PANE_STATUS_BOTTOM)
1082 flag = (wp->yoff + wp->sy == w->sy - 1);
1083 else
1084 flag = (wp->yoff + wp->sy == w->sy);
1085 xasprintf(&value, "%d", flag);
1086 return (value);
1089 /* Callback for cursor_character. */
1090 static void *
1091 format_cb_cursor_character(struct format_tree *ft)
1093 struct window_pane *wp = ft->wp;
1094 struct grid_cell gc;
1095 char *value = NULL;
1097 if (wp == NULL)
1098 return (NULL);
1100 grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc);
1101 if (~gc.flags & GRID_FLAG_PADDING)
1102 xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data);
1103 return (value);
1106 /* Callback for mouse_word. */
1107 static void *
1108 format_cb_mouse_word(struct format_tree *ft)
1110 struct window_pane *wp;
1111 struct grid *gd;
1112 u_int x, y;
1113 char *s;
1115 if (!ft->m.valid)
1116 return (NULL);
1117 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1118 if (wp == NULL)
1119 return (NULL);
1120 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1121 return (NULL);
1123 if (!TAILQ_EMPTY(&wp->modes)) {
1124 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode ||
1125 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1126 return (s = window_copy_get_word(wp, x, y));
1127 return (NULL);
1129 gd = wp->base.grid;
1130 return (format_grid_word(gd, x, gd->hsize + y));
1133 /* Callback for mouse_line. */
1134 static void *
1135 format_cb_mouse_line(struct format_tree *ft)
1137 struct window_pane *wp;
1138 struct grid *gd;
1139 u_int x, y;
1141 if (!ft->m.valid)
1142 return (NULL);
1143 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1144 if (wp == NULL)
1145 return (NULL);
1146 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1147 return (NULL);
1149 if (!TAILQ_EMPTY(&wp->modes)) {
1150 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode ||
1151 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1152 return (window_copy_get_line(wp, y));
1153 return (NULL);
1155 gd = wp->base.grid;
1156 return (format_grid_line(gd, gd->hsize + y));
1159 /* Callback for alternate_on. */
1160 static void *
1161 format_cb_alternate_on(struct format_tree *ft)
1163 if (ft->wp != NULL) {
1164 if (ft->wp->base.saved_grid != NULL)
1165 return (xstrdup("1"));
1166 return (xstrdup("0"));
1168 return (NULL);
1171 /* Callback for alternate_saved_x. */
1172 static void *
1173 format_cb_alternate_saved_x(struct format_tree *ft)
1175 if (ft->wp != NULL)
1176 return (format_printf("%u", ft->wp->base.saved_cx));
1177 return (NULL);
1180 /* Callback for alternate_saved_y. */
1181 static void *
1182 format_cb_alternate_saved_y(struct format_tree *ft)
1184 if (ft->wp != NULL)
1185 return (format_printf("%u", ft->wp->base.saved_cy));
1186 return (NULL);
1189 /* Callback for buffer_name. */
1190 static void *
1191 format_cb_buffer_name(struct format_tree *ft)
1193 if (ft->pb != NULL)
1194 return (xstrdup(paste_buffer_name(ft->pb)));
1195 return (NULL);
1198 /* Callback for buffer_sample. */
1199 static void *
1200 format_cb_buffer_sample(struct format_tree *ft)
1202 if (ft->pb != NULL)
1203 return (paste_make_sample(ft->pb));
1204 return (NULL);
1207 /* Callback for buffer_size. */
1208 static void *
1209 format_cb_buffer_size(struct format_tree *ft)
1211 size_t size;
1213 if (ft->pb != NULL) {
1214 paste_buffer_data(ft->pb, &size);
1215 return (format_printf("%zu", size));
1217 return (NULL);
1220 /* Callback for client_cell_height. */
1221 static void *
1222 format_cb_client_cell_height(struct format_tree *ft)
1224 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1225 return (format_printf("%u", ft->c->tty.ypixel));
1226 return (NULL);
1229 /* Callback for client_cell_width. */
1230 static void *
1231 format_cb_client_cell_width(struct format_tree *ft)
1233 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1234 return (format_printf("%u", ft->c->tty.xpixel));
1235 return (NULL);
1238 /* Callback for client_control_mode. */
1239 static void *
1240 format_cb_client_control_mode(struct format_tree *ft)
1242 if (ft->c != NULL) {
1243 if (ft->c->flags & CLIENT_CONTROL)
1244 return (xstrdup("1"));
1245 return (xstrdup("0"));
1247 return (NULL);
1250 /* Callback for client_discarded. */
1251 static void *
1252 format_cb_client_discarded(struct format_tree *ft)
1254 if (ft->c != NULL)
1255 return (format_printf("%zu", ft->c->discarded));
1256 return (NULL);
1259 /* Callback for client_flags. */
1260 static void *
1261 format_cb_client_flags(struct format_tree *ft)
1263 if (ft->c != NULL)
1264 return (xstrdup(server_client_get_flags(ft->c)));
1265 return (NULL);
1268 /* Callback for client_height. */
1269 static void *
1270 format_cb_client_height(struct format_tree *ft)
1272 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1273 return (format_printf("%u", ft->c->tty.sy));
1274 return (NULL);
1277 /* Callback for client_key_table. */
1278 static void *
1279 format_cb_client_key_table(struct format_tree *ft)
1281 if (ft->c != NULL)
1282 return (xstrdup(ft->c->keytable->name));
1283 return (NULL);
1286 /* Callback for client_last_session. */
1287 static void *
1288 format_cb_client_last_session(struct format_tree *ft)
1290 if (ft->c != NULL &&
1291 ft->c->last_session != NULL &&
1292 session_alive(ft->c->last_session))
1293 return (xstrdup(ft->c->last_session->name));
1294 return (NULL);
1297 /* Callback for client_name. */
1298 static void *
1299 format_cb_client_name(struct format_tree *ft)
1301 if (ft->c != NULL)
1302 return (xstrdup(ft->c->name));
1303 return (NULL);
1306 /* Callback for client_pid. */
1307 static void *
1308 format_cb_client_pid(struct format_tree *ft)
1310 if (ft->c != NULL)
1311 return (format_printf("%ld", (long)ft->c->pid));
1312 return (NULL);
1315 /* Callback for client_prefix. */
1316 static void *
1317 format_cb_client_prefix(struct format_tree *ft)
1319 const char *name;
1321 if (ft->c != NULL) {
1322 name = server_client_get_key_table(ft->c);
1323 if (strcmp(ft->c->keytable->name, name) == 0)
1324 return (xstrdup("0"));
1325 return (xstrdup("1"));
1327 return (NULL);
1330 /* Callback for client_readonly. */
1331 static void *
1332 format_cb_client_readonly(struct format_tree *ft)
1334 if (ft->c != NULL) {
1335 if (ft->c->flags & CLIENT_READONLY)
1336 return (xstrdup("1"));
1337 return (xstrdup("0"));
1339 return (NULL);
1342 /* Callback for client_session. */
1343 static void *
1344 format_cb_client_session(struct format_tree *ft)
1346 if (ft->c != NULL && ft->c->session != NULL)
1347 return (xstrdup(ft->c->session->name));
1348 return (NULL);
1351 /* Callback for client_termfeatures. */
1352 static void *
1353 format_cb_client_termfeatures(struct format_tree *ft)
1355 if (ft->c != NULL)
1356 return (xstrdup(tty_get_features(ft->c->term_features)));
1357 return (NULL);
1360 /* Callback for client_termname. */
1361 static void *
1362 format_cb_client_termname(struct format_tree *ft)
1364 if (ft->c != NULL)
1365 return (xstrdup(ft->c->term_name));
1366 return (NULL);
1369 /* Callback for client_termtype. */
1370 static void *
1371 format_cb_client_termtype(struct format_tree *ft)
1373 if (ft->c != NULL) {
1374 if (ft->c->term_type == NULL)
1375 return (xstrdup(""));
1376 return (xstrdup(ft->c->term_type));
1378 return (NULL);
1381 /* Callback for client_tty. */
1382 static void *
1383 format_cb_client_tty(struct format_tree *ft)
1385 if (ft->c != NULL)
1386 return (xstrdup(ft->c->ttyname));
1387 return (NULL);
1390 /* Callback for client_utf8. */
1391 static void *
1392 format_cb_client_utf8(struct format_tree *ft)
1394 if (ft->c != NULL) {
1395 if (ft->c->flags & CLIENT_UTF8)
1396 return (xstrdup("1"));
1397 return (xstrdup("0"));
1399 return (NULL);
1402 /* Callback for client_width. */
1403 static void *
1404 format_cb_client_width(struct format_tree *ft)
1406 if (ft->c != NULL)
1407 return (format_printf("%u", ft->c->tty.sx));
1408 return (NULL);
1411 /* Callback for client_written. */
1412 static void *
1413 format_cb_client_written(struct format_tree *ft)
1415 if (ft->c != NULL)
1416 return (format_printf("%zu", ft->c->written));
1417 return (NULL);
1420 /* Callback for config_files. */
1421 static void *
1422 format_cb_config_files(__unused struct format_tree *ft)
1424 char *s = NULL;
1425 size_t slen = 0;
1426 u_int i;
1427 size_t n;
1429 for (i = 0; i < cfg_nfiles; i++) {
1430 n = strlen(cfg_files[i]) + 1;
1431 s = xrealloc(s, slen + n + 1);
1432 slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]);
1434 if (s == NULL)
1435 return (xstrdup(""));
1436 s[slen - 1] = '\0';
1437 return (s);
1440 /* Callback for cursor_flag. */
1441 static void *
1442 format_cb_cursor_flag(struct format_tree *ft)
1444 if (ft->wp != NULL) {
1445 if (ft->wp->base.mode & MODE_CURSOR)
1446 return (xstrdup("1"));
1447 return (xstrdup("0"));
1449 return (NULL);
1452 /* Callback for cursor_x. */
1453 static void *
1454 format_cb_cursor_x(struct format_tree *ft)
1456 if (ft->wp != NULL)
1457 return (format_printf("%u", ft->wp->base.cx));
1458 return (NULL);
1461 /* Callback for cursor_y. */
1462 static void *
1463 format_cb_cursor_y(struct format_tree *ft)
1465 if (ft->wp != NULL)
1466 return (format_printf("%u", ft->wp->base.cy));
1467 return (NULL);
1470 /* Callback for history_limit. */
1471 static void *
1472 format_cb_history_limit(struct format_tree *ft)
1474 if (ft->wp != NULL)
1475 return (format_printf("%u", ft->wp->base.grid->hlimit));
1476 return (NULL);
1479 /* Callback for history_size. */
1480 static void *
1481 format_cb_history_size(struct format_tree *ft)
1483 if (ft->wp != NULL)
1484 return (format_printf("%u", ft->wp->base.grid->hsize));
1485 return (NULL);
1488 /* Callback for insert_flag. */
1489 static void *
1490 format_cb_insert_flag(struct format_tree *ft)
1492 if (ft->wp != NULL) {
1493 if (ft->wp->base.mode & MODE_INSERT)
1494 return (xstrdup("1"));
1495 return (xstrdup("0"));
1497 return (NULL);
1500 /* Callback for keypad_cursor_flag. */
1501 static void *
1502 format_cb_keypad_cursor_flag(struct format_tree *ft)
1504 if (ft->wp != NULL) {
1505 if (ft->wp->base.mode & MODE_KCURSOR)
1506 return (xstrdup("1"));
1507 return (xstrdup("0"));
1509 return (NULL);
1512 /* Callback for keypad_flag. */
1513 static void *
1514 format_cb_keypad_flag(struct format_tree *ft)
1516 if (ft->wp != NULL) {
1517 if (ft->wp->base.mode & MODE_KKEYPAD)
1518 return (xstrdup("1"));
1519 return (xstrdup("0"));
1521 return (NULL);
1524 /* Callback for mouse_all_flag. */
1525 static void *
1526 format_cb_mouse_all_flag(struct format_tree *ft)
1528 if (ft->wp != NULL) {
1529 if (ft->wp->base.mode & MODE_MOUSE_ALL)
1530 return (xstrdup("1"));
1531 return (xstrdup("0"));
1533 return (NULL);
1536 /* Callback for mouse_any_flag. */
1537 static void *
1538 format_cb_mouse_any_flag(struct format_tree *ft)
1540 if (ft->wp != NULL) {
1541 if (ft->wp->base.mode & ALL_MOUSE_MODES)
1542 return (xstrdup("1"));
1543 return (xstrdup("0"));
1545 return (NULL);
1548 /* Callback for mouse_button_flag. */
1549 static void *
1550 format_cb_mouse_button_flag(struct format_tree *ft)
1552 if (ft->wp != NULL) {
1553 if (ft->wp->base.mode & MODE_MOUSE_BUTTON)
1554 return (xstrdup("1"));
1555 return (xstrdup("0"));
1557 return (NULL);
1560 /* Callback for mouse_pane. */
1561 static void *
1562 format_cb_mouse_pane(struct format_tree *ft)
1564 struct window_pane *wp;
1566 if (ft->m.valid) {
1567 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1568 if (wp != NULL)
1569 return (format_printf("%%%u", wp->id));
1570 return (NULL);
1572 return (NULL);
1575 /* Callback for mouse_sgr_flag. */
1576 static void *
1577 format_cb_mouse_sgr_flag(struct format_tree *ft)
1579 if (ft->wp != NULL) {
1580 if (ft->wp->base.mode & MODE_MOUSE_SGR)
1581 return (xstrdup("1"));
1582 return (xstrdup("0"));
1584 return (NULL);
1587 /* Callback for mouse_standard_flag. */
1588 static void *
1589 format_cb_mouse_standard_flag(struct format_tree *ft)
1591 if (ft->wp != NULL) {
1592 if (ft->wp->base.mode & MODE_MOUSE_STANDARD)
1593 return (xstrdup("1"));
1594 return (xstrdup("0"));
1596 return (NULL);
1599 /* Callback for mouse_utf8_flag. */
1600 static void *
1601 format_cb_mouse_utf8_flag(struct format_tree *ft)
1603 if (ft->wp != NULL) {
1604 if (ft->wp->base.mode & MODE_MOUSE_UTF8)
1605 return (xstrdup("1"));
1606 return (xstrdup("0"));
1608 return (NULL);
1611 /* Callback for mouse_x. */
1612 static void *
1613 format_cb_mouse_x(struct format_tree *ft)
1615 struct window_pane *wp;
1616 u_int x, y;
1618 if (!ft->m.valid)
1619 return (NULL);
1620 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1621 if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1622 return (format_printf("%u", x));
1623 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1624 if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1625 return (format_printf("%u", ft->m.x));
1626 if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1627 return (format_printf("%u", ft->m.x));
1629 return (NULL);
1632 /* Callback for mouse_y. */
1633 static void *
1634 format_cb_mouse_y(struct format_tree *ft)
1636 struct window_pane *wp;
1637 u_int x, y;
1639 if (!ft->m.valid)
1640 return (NULL);
1641 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1642 if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1643 return (format_printf("%u", y));
1644 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1645 if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1646 return (format_printf("%u", ft->m.y));
1647 if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1648 return (format_printf("%u", ft->m.y - ft->m.statusat));
1650 return (NULL);
1653 /* Callback for origin_flag. */
1654 static void *
1655 format_cb_origin_flag(struct format_tree *ft)
1657 if (ft->wp != NULL) {
1658 if (ft->wp->base.mode & MODE_ORIGIN)
1659 return (xstrdup("1"));
1660 return (xstrdup("0"));
1662 return (NULL);
1665 /* Callback for pane_active. */
1666 static void *
1667 format_cb_pane_active(struct format_tree *ft)
1669 if (ft->wp != NULL) {
1670 if (ft->wp == ft->wp->window->active)
1671 return (xstrdup("1"));
1672 return (xstrdup("0"));
1674 return (NULL);
1677 /* Callback for pane_at_left. */
1678 static void *
1679 format_cb_pane_at_left(struct format_tree *ft)
1681 if (ft->wp != NULL) {
1682 if (ft->wp->xoff == 0)
1683 return (xstrdup("1"));
1684 return (xstrdup("0"));
1686 return (NULL);
1689 /* Callback for pane_at_right. */
1690 static void *
1691 format_cb_pane_at_right(struct format_tree *ft)
1693 if (ft->wp != NULL) {
1694 if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx)
1695 return (xstrdup("1"));
1696 return (xstrdup("0"));
1698 return (NULL);
1701 /* Callback for pane_bottom. */
1702 static void *
1703 format_cb_pane_bottom(struct format_tree *ft)
1705 if (ft->wp != NULL)
1706 return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1));
1707 return (NULL);
1710 /* Callback for pane_dead. */
1711 static void *
1712 format_cb_pane_dead(struct format_tree *ft)
1714 if (ft->wp != NULL) {
1715 if (ft->wp->fd == -1)
1716 return (xstrdup("1"));
1717 return (xstrdup("0"));
1719 return (NULL);
1722 /* Callback for pane_dead_status. */
1723 static void *
1724 format_cb_pane_dead_status(struct format_tree *ft)
1726 struct window_pane *wp = ft->wp;
1728 if (wp != NULL) {
1729 if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status))
1730 return (format_printf("%d", WEXITSTATUS(wp->status)));
1731 return (NULL);
1733 return (NULL);
1736 /* Callback for pane_format. */
1737 static void *
1738 format_cb_pane_format(struct format_tree *ft)
1740 if (ft->type == FORMAT_TYPE_PANE)
1741 return (xstrdup("1"));
1742 return (xstrdup("0"));
1745 /* Callback for pane_height. */
1746 static void *
1747 format_cb_pane_height(struct format_tree *ft)
1749 if (ft->wp != NULL)
1750 return (format_printf("%u", ft->wp->sy));
1751 return (NULL);
1754 /* Callback for pane_id. */
1755 static void *
1756 format_cb_pane_id(struct format_tree *ft)
1758 if (ft->wp != NULL)
1759 return (format_printf("%%%u", ft->wp->id));
1760 return (NULL);
1763 /* Callback for pane_index. */
1764 static void *
1765 format_cb_pane_index(struct format_tree *ft)
1767 u_int idx;
1769 if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0)
1770 return (format_printf("%u", idx));
1771 return (NULL);
1774 /* Callback for pane_input_off. */
1775 static void *
1776 format_cb_pane_input_off(struct format_tree *ft)
1778 if (ft->wp != NULL) {
1779 if (ft->wp->flags & PANE_INPUTOFF)
1780 return (xstrdup("1"));
1781 return (xstrdup("0"));
1783 return (NULL);
1786 /* Callback for pane_last. */
1787 static void *
1788 format_cb_pane_last(struct format_tree *ft)
1790 if (ft->wp != NULL) {
1791 if (ft->wp == ft->wp->window->last)
1792 return (xstrdup("1"));
1793 return (xstrdup("0"));
1795 return (NULL);
1798 /* Callback for pane_left. */
1799 static void *
1800 format_cb_pane_left(struct format_tree *ft)
1802 if (ft->wp != NULL)
1803 return (format_printf("%u", ft->wp->xoff));
1804 return (NULL);
1807 /* Callback for pane_marked. */
1808 static void *
1809 format_cb_pane_marked(struct format_tree *ft)
1811 if (ft->wp != NULL) {
1812 if (server_check_marked() && marked_pane.wp == ft->wp)
1813 return (xstrdup("1"));
1814 return (xstrdup("0"));
1816 return (NULL);
1819 /* Callback for pane_marked_set. */
1820 static void *
1821 format_cb_pane_marked_set(struct format_tree *ft)
1823 if (ft->wp != NULL) {
1824 if (server_check_marked())
1825 return (xstrdup("1"));
1826 return (xstrdup("0"));
1828 return (NULL);
1831 /* Callback for pane_mode. */
1832 static void *
1833 format_cb_pane_mode(struct format_tree *ft)
1835 struct window_mode_entry *wme;
1837 if (ft->wp != NULL) {
1838 wme = TAILQ_FIRST(&ft->wp->modes);
1839 if (wme != NULL)
1840 return (xstrdup(wme->mode->name));
1841 return (NULL);
1843 return (NULL);
1846 /* Callback for pane_path. */
1847 static void *
1848 format_cb_pane_path(struct format_tree *ft)
1850 if (ft->wp != NULL) {
1851 if (ft->wp->base.path == NULL)
1852 return (xstrdup(""));
1853 return (xstrdup(ft->wp->base.path));
1855 return (NULL);
1858 /* Callback for pane_pid. */
1859 static void *
1860 format_cb_pane_pid(struct format_tree *ft)
1862 if (ft->wp != NULL)
1863 return (format_printf("%ld", (long)ft->wp->pid));
1864 return (NULL);
1867 /* Callback for pane_pipe. */
1868 static void *
1869 format_cb_pane_pipe(struct format_tree *ft)
1871 if (ft->wp != NULL) {
1872 if (ft->wp->pipe_fd != -1)
1873 return (xstrdup("1"));
1874 return (xstrdup("0"));
1876 return (NULL);
1879 /* Callback for pane_right. */
1880 static void *
1881 format_cb_pane_right(struct format_tree *ft)
1883 if (ft->wp != NULL)
1884 return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1));
1885 return (NULL);
1888 /* Callback for pane_search_string. */
1889 static void *
1890 format_cb_pane_search_string(struct format_tree *ft)
1892 if (ft->wp != NULL) {
1893 if (ft->wp->searchstr == NULL)
1894 return (xstrdup(""));
1895 return (xstrdup(ft->wp->searchstr));
1897 return (NULL);
1900 /* Callback for pane_synchronized. */
1901 static void *
1902 format_cb_pane_synchronized(struct format_tree *ft)
1904 if (ft->wp != NULL) {
1905 if (options_get_number(ft->wp->options, "synchronize-panes"))
1906 return (xstrdup("1"));
1907 return (xstrdup("0"));
1909 return (NULL);
1912 /* Callback for pane_title. */
1913 static void *
1914 format_cb_pane_title(struct format_tree *ft)
1916 if (ft->wp != NULL)
1917 return (xstrdup(ft->wp->base.title));
1918 return (NULL);
1921 /* Callback for pane_top. */
1922 static void *
1923 format_cb_pane_top(struct format_tree *ft)
1925 if (ft->wp != NULL)
1926 return (format_printf("%u", ft->wp->yoff));
1927 return (NULL);
1930 /* Callback for pane_tty. */
1931 static void *
1932 format_cb_pane_tty(struct format_tree *ft)
1934 if (ft->wp != NULL)
1935 return (xstrdup(ft->wp->tty));
1936 return (NULL);
1939 /* Callback for pane_width. */
1940 static void *
1941 format_cb_pane_width(struct format_tree *ft)
1943 if (ft->wp != NULL)
1944 return (format_printf("%u", ft->wp->sx));
1945 return (NULL);
1948 /* Callback for scroll_region_lower. */
1949 static void *
1950 format_cb_scroll_region_lower(struct format_tree *ft)
1952 if (ft->wp != NULL)
1953 return (format_printf("%u", ft->wp->base.rlower));
1954 return (NULL);
1957 /* Callback for scroll_region_upper. */
1958 static void *
1959 format_cb_scroll_region_upper(struct format_tree *ft)
1961 if (ft->wp != NULL)
1962 return (format_printf("%u", ft->wp->base.rupper));
1963 return (NULL);
1966 /* Callback for session_attached. */
1967 static void *
1968 format_cb_session_attached(struct format_tree *ft)
1970 if (ft->s != NULL)
1971 return (format_printf("%u", ft->s->attached));
1972 return (NULL);
1975 /* Callback for session_format. */
1976 static void *
1977 format_cb_session_format(struct format_tree *ft)
1979 if (ft->type == FORMAT_TYPE_SESSION)
1980 return (xstrdup("1"));
1981 return (xstrdup("0"));
1984 /* Callback for session_group. */
1985 static void *
1986 format_cb_session_group(struct format_tree *ft)
1988 struct session_group *sg;
1990 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
1991 return (xstrdup(sg->name));
1992 return (NULL);
1995 /* Callback for session_group_attached. */
1996 static void *
1997 format_cb_session_group_attached(struct format_tree *ft)
1999 struct session_group *sg;
2001 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2002 return (format_printf("%u", session_group_attached_count (sg)));
2003 return (NULL);
2006 /* Callback for session_group_many_attached. */
2007 static void *
2008 format_cb_session_group_many_attached(struct format_tree *ft)
2010 struct session_group *sg;
2012 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) {
2013 if (session_group_attached_count (sg) > 1)
2014 return (xstrdup("1"));
2015 return (xstrdup("0"));
2017 return (NULL);
2020 /* Callback for session_group_size. */
2021 static void *
2022 format_cb_session_group_size(struct format_tree *ft)
2024 struct session_group *sg;
2026 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2027 return (format_printf("%u", session_group_count (sg)));
2028 return (NULL);
2031 /* Callback for session_grouped. */
2032 static void *
2033 format_cb_session_grouped(struct format_tree *ft)
2035 if (ft->s != NULL) {
2036 if (session_group_contains(ft->s) != NULL)
2037 return (xstrdup("1"));
2038 return (xstrdup("0"));
2040 return (NULL);
2043 /* Callback for session_id. */
2044 static void *
2045 format_cb_session_id(struct format_tree *ft)
2047 if (ft->s != NULL)
2048 return (format_printf("$%u", ft->s->id));
2049 return (NULL);
2052 /* Callback for session_many_attached. */
2053 static void *
2054 format_cb_session_many_attached(struct format_tree *ft)
2056 if (ft->s != NULL) {
2057 if (ft->s->attached > 1)
2058 return (xstrdup("1"));
2059 return (xstrdup("0"));
2061 return (NULL);
2064 /* Callback for session_marked. */
2065 static void *
2066 format_cb_session_marked(struct format_tree *ft)
2068 if (ft->s != NULL) {
2069 if (server_check_marked() && marked_pane.s == ft->s)
2070 return (xstrdup("1"));
2071 return (xstrdup("0"));
2073 return (NULL);
2076 /* Callback for session_name. */
2077 static void *
2078 format_cb_session_name(struct format_tree *ft)
2080 if (ft->s != NULL)
2081 return (xstrdup(ft->s->name));
2082 return (NULL);
2085 /* Callback for session_path. */
2086 static void *
2087 format_cb_session_path(struct format_tree *ft)
2089 if (ft->s != NULL)
2090 return (xstrdup(ft->s->cwd));
2091 return (NULL);
2094 /* Callback for session_windows. */
2095 static void *
2096 format_cb_session_windows(struct format_tree *ft)
2098 if (ft->s != NULL)
2099 return (format_printf("%u", winlink_count(&ft->s->windows)));
2100 return (NULL);
2103 /* Callback for socket_path. */
2104 static void *
2105 format_cb_socket_path(__unused struct format_tree *ft)
2107 return (xstrdup(socket_path));
2110 /* Callback for version. */
2111 static void *
2112 format_cb_version(__unused struct format_tree *ft)
2114 return (xstrdup(getversion()));
2117 /* Callback for active_window_index. */
2118 static void *
2119 format_cb_active_window_index(struct format_tree *ft)
2121 if (ft->s != NULL)
2122 return (format_printf("%u", ft->s->curw->idx));
2123 return (NULL);
2126 /* Callback for last_window_index. */
2127 static void *
2128 format_cb_last_window_index(struct format_tree *ft)
2130 struct winlink *wl;
2132 if (ft->s != NULL) {
2133 wl = RB_MAX(winlinks, &ft->s->windows);
2134 return (format_printf("%u", wl->idx));
2136 return (NULL);
2139 /* Callback for window_active. */
2140 static void *
2141 format_cb_window_active(struct format_tree *ft)
2143 if (ft->wl != NULL) {
2144 if (ft->wl == ft->wl->session->curw)
2145 return (xstrdup("1"));
2146 return (xstrdup("0"));
2148 return (NULL);
2151 /* Callback for window_activity_flag. */
2152 static void *
2153 format_cb_window_activity_flag(struct format_tree *ft)
2155 if (ft->wl != NULL) {
2156 if (ft->wl->flags & WINLINK_ACTIVITY)
2157 return (xstrdup("1"));
2158 return (xstrdup("0"));
2160 return (NULL);
2163 /* Callback for window_bell_flag. */
2164 static void *
2165 format_cb_window_bell_flag(struct format_tree *ft)
2167 if (ft->wl != NULL) {
2168 if (ft->wl->flags & WINLINK_BELL)
2169 return (xstrdup("1"));
2170 return (xstrdup("0"));
2172 return (NULL);
2175 /* Callback for window_bigger. */
2176 static void *
2177 format_cb_window_bigger(struct format_tree *ft)
2179 u_int ox, oy, sx, sy;
2181 if (ft->c != NULL) {
2182 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2183 return (xstrdup("1"));
2184 return (xstrdup("0"));
2186 return (NULL);
2189 /* Callback for window_cell_height. */
2190 static void *
2191 format_cb_window_cell_height(struct format_tree *ft)
2193 if (ft->w != NULL)
2194 return (format_printf("%u", ft->w->ypixel));
2195 return (NULL);
2198 /* Callback for window_cell_width. */
2199 static void *
2200 format_cb_window_cell_width(struct format_tree *ft)
2202 if (ft->w != NULL)
2203 return (format_printf("%u", ft->w->xpixel));
2204 return (NULL);
2207 /* Callback for window_end_flag. */
2208 static void *
2209 format_cb_window_end_flag(struct format_tree *ft)
2211 if (ft->wl != NULL) {
2212 if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows))
2213 return (xstrdup("1"));
2214 return (xstrdup("0"));
2216 return (NULL);
2219 /* Callback for window_flags. */
2220 static void *
2221 format_cb_window_flags(struct format_tree *ft)
2223 if (ft->wl != NULL)
2224 return (xstrdup(window_printable_flags(ft->wl, 1)));
2225 return (NULL);
2228 /* Callback for window_format. */
2229 static void *
2230 format_cb_window_format(struct format_tree *ft)
2232 if (ft->type == FORMAT_TYPE_WINDOW)
2233 return (xstrdup("1"));
2234 return (xstrdup("0"));
2237 /* Callback for window_height. */
2238 static void *
2239 format_cb_window_height(struct format_tree *ft)
2241 if (ft->w != NULL)
2242 return (format_printf("%u", ft->w->sy));
2243 return (NULL);
2246 /* Callback for window_id. */
2247 static void *
2248 format_cb_window_id(struct format_tree *ft)
2250 if (ft->w != NULL)
2251 return (format_printf("@%u", ft->w->id));
2252 return (NULL);
2255 /* Callback for window_index. */
2256 static void *
2257 format_cb_window_index(struct format_tree *ft)
2259 if (ft->wl != NULL)
2260 return (format_printf("%d", ft->wl->idx));
2261 return (NULL);
2264 /* Callback for window_last_flag. */
2265 static void *
2266 format_cb_window_last_flag(struct format_tree *ft)
2268 if (ft->wl != NULL) {
2269 if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw))
2270 return (xstrdup("1"));
2271 return (xstrdup("0"));
2273 return (NULL);
2276 /* Callback for window_linked. */
2277 static void *
2278 format_cb_window_linked(struct format_tree *ft)
2280 if (ft->wl != NULL) {
2281 if (session_is_linked(ft->wl->session, ft->wl->window))
2282 return (xstrdup("1"));
2283 return (xstrdup("0"));
2285 return (NULL);
2288 /* Callback for window_linked_sessions. */
2289 static void *
2290 format_cb_window_linked_sessions(struct format_tree *ft)
2292 if (ft->wl != NULL)
2293 return (format_printf("%u", ft->wl->window->references));
2294 return (NULL);
2297 /* Callback for window_marked_flag. */
2298 static void *
2299 format_cb_window_marked_flag(struct format_tree *ft)
2301 if (ft->wl != NULL) {
2302 if (server_check_marked() && marked_pane.wl == ft->wl)
2303 return (xstrdup("1"));
2304 return (xstrdup("0"));
2306 return (NULL);
2309 /* Callback for window_name. */
2310 static void *
2311 format_cb_window_name(struct format_tree *ft)
2313 if (ft->w != NULL)
2314 return (format_printf("%s", ft->w->name));
2315 return (NULL);
2318 /* Callback for window_offset_x. */
2319 static void *
2320 format_cb_window_offset_x(struct format_tree *ft)
2322 u_int ox, oy, sx, sy;
2324 if (ft->c != NULL) {
2325 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2326 return (format_printf("%u", ox));
2327 return (NULL);
2329 return (NULL);
2332 /* Callback for window_offset_y. */
2333 static void *
2334 format_cb_window_offset_y(struct format_tree *ft)
2336 u_int ox, oy, sx, sy;
2338 if (ft->c != NULL) {
2339 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2340 return (format_printf("%u", oy));
2341 return (NULL);
2343 return (NULL);
2346 /* Callback for window_panes. */
2347 static void *
2348 format_cb_window_panes(struct format_tree *ft)
2350 if (ft->w != NULL)
2351 return (format_printf("%u", window_count_panes(ft->w)));
2352 return (NULL);
2355 /* Callback for window_raw_flags. */
2356 static void *
2357 format_cb_window_raw_flags(struct format_tree *ft)
2359 if (ft->wl != NULL)
2360 return (xstrdup(window_printable_flags(ft->wl, 0)));
2361 return (NULL);
2364 /* Callback for window_silence_flag. */
2365 static void *
2366 format_cb_window_silence_flag(struct format_tree *ft)
2368 if (ft->wl != NULL) {
2369 if (ft->wl->flags & WINLINK_SILENCE)
2370 return (xstrdup("1"));
2371 return (xstrdup("0"));
2373 return (NULL);
2376 /* Callback for window_start_flag. */
2377 static void *
2378 format_cb_window_start_flag(struct format_tree *ft)
2380 if (ft->wl != NULL) {
2381 if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows))
2382 return (xstrdup("1"));
2383 return (xstrdup("0"));
2385 return (NULL);
2388 /* Callback for window_width. */
2389 static void *
2390 format_cb_window_width(struct format_tree *ft)
2392 if (ft->w != NULL)
2393 return (format_printf("%u", ft->w->sx));
2394 return (NULL);
2397 /* Callback for window_zoomed_flag. */
2398 static void *
2399 format_cb_window_zoomed_flag(struct format_tree *ft)
2401 if (ft->w != NULL) {
2402 if (ft->w->flags & WINDOW_ZOOMED)
2403 return (xstrdup("1"));
2404 return (xstrdup("0"));
2406 return (NULL);
2409 /* Callback for wrap_flag. */
2410 static void *
2411 format_cb_wrap_flag(struct format_tree *ft)
2413 if (ft->wp != NULL) {
2414 if (ft->wp->base.mode & MODE_WRAP)
2415 return (xstrdup("1"));
2416 return (xstrdup("0"));
2418 return (NULL);
2421 /* Callback for buffer_created. */
2422 static void *
2423 format_cb_buffer_created(struct format_tree *ft)
2425 static struct timeval tv;
2427 if (ft->pb != NULL) {
2428 timerclear(&tv);
2429 tv.tv_sec = paste_buffer_created(ft->pb);
2430 return (&tv);
2432 return (NULL);
2435 /* Callback for client_activity. */
2436 static void *
2437 format_cb_client_activity(struct format_tree *ft)
2439 if (ft->c != NULL)
2440 return (&ft->c->activity_time);
2441 return (NULL);
2444 /* Callback for client_created. */
2445 static void *
2446 format_cb_client_created(struct format_tree *ft)
2448 if (ft->c != NULL)
2449 return (&ft->c->creation_time);
2450 return (NULL);
2453 /* Callback for session_activity. */
2454 static void *
2455 format_cb_session_activity(struct format_tree *ft)
2457 if (ft->s != NULL)
2458 return (&ft->s->activity_time);
2459 return (NULL);
2462 /* Callback for session_created. */
2463 static void *
2464 format_cb_session_created(struct format_tree *ft)
2466 if (ft->s != NULL)
2467 return (&ft->s->creation_time);
2468 return (NULL);
2471 /* Callback for session_last_attached. */
2472 static void *
2473 format_cb_session_last_attached(struct format_tree *ft)
2475 if (ft->s != NULL)
2476 return (&ft->s->last_attached_time);
2477 return (NULL);
2480 /* Callback for start_time. */
2481 static void *
2482 format_cb_start_time(__unused struct format_tree *ft)
2484 return (&start_time);
2487 /* Callback for window_activity. */
2488 static void *
2489 format_cb_window_activity(struct format_tree *ft)
2491 if (ft->w != NULL)
2492 return (&ft->w->activity_time);
2493 return (NULL);
2496 /* Callback for buffer_mode_format, */
2497 static void *
2498 format_cb_buffer_mode_format(__unused struct format_tree *ft)
2500 return (xstrdup(window_buffer_mode.default_format));
2503 /* Callback for client_mode_format, */
2504 static void *
2505 format_cb_client_mode_format(__unused struct format_tree *ft)
2507 return (xstrdup(window_client_mode.default_format));
2510 /* Callback for tree_mode_format, */
2511 static void *
2512 format_cb_tree_mode_format(__unused struct format_tree *ft)
2514 return (xstrdup(window_tree_mode.default_format));
2517 /* Format table type. */
2518 enum format_table_type {
2519 FORMAT_TABLE_STRING,
2520 FORMAT_TABLE_TIME
2523 /* Format table entry. */
2524 struct format_table_entry {
2525 const char *key;
2526 enum format_table_type type;
2527 format_cb cb;
2531 * Format table. Default format variables (that are almost always in the tree
2532 * and where the value is expanded by a callback in this file) are listed here.
2533 * Only variables which are added by the caller go into the tree.
2535 static const struct format_table_entry format_table[] = {
2536 { "active_window_index", FORMAT_TABLE_STRING,
2537 format_cb_active_window_index
2539 { "alternate_on", FORMAT_TABLE_STRING,
2540 format_cb_alternate_on
2542 { "alternate_saved_x", FORMAT_TABLE_STRING,
2543 format_cb_alternate_saved_x
2545 { "alternate_saved_y", FORMAT_TABLE_STRING,
2546 format_cb_alternate_saved_y
2548 { "buffer_created", FORMAT_TABLE_TIME,
2549 format_cb_buffer_created
2551 { "buffer_mode_format", FORMAT_TABLE_STRING,
2552 format_cb_buffer_mode_format
2554 { "buffer_name", FORMAT_TABLE_STRING,
2555 format_cb_buffer_name
2557 { "buffer_sample", FORMAT_TABLE_STRING,
2558 format_cb_buffer_sample
2560 { "buffer_size", FORMAT_TABLE_STRING,
2561 format_cb_buffer_size
2563 { "client_activity", FORMAT_TABLE_TIME,
2564 format_cb_client_activity
2566 { "client_cell_height", FORMAT_TABLE_STRING,
2567 format_cb_client_cell_height
2569 { "client_cell_width", FORMAT_TABLE_STRING,
2570 format_cb_client_cell_width
2572 { "client_control_mode", FORMAT_TABLE_STRING,
2573 format_cb_client_control_mode
2575 { "client_created", FORMAT_TABLE_TIME,
2576 format_cb_client_created
2578 { "client_discarded", FORMAT_TABLE_STRING,
2579 format_cb_client_discarded
2581 { "client_flags", FORMAT_TABLE_STRING,
2582 format_cb_client_flags
2584 { "client_height", FORMAT_TABLE_STRING,
2585 format_cb_client_height
2587 { "client_key_table", FORMAT_TABLE_STRING,
2588 format_cb_client_key_table
2590 { "client_last_session", FORMAT_TABLE_STRING,
2591 format_cb_client_last_session
2593 { "client_mode_format", FORMAT_TABLE_STRING,
2594 format_cb_client_mode_format
2596 { "client_name", FORMAT_TABLE_STRING,
2597 format_cb_client_name
2599 { "client_pid", FORMAT_TABLE_STRING,
2600 format_cb_client_pid
2602 { "client_prefix", FORMAT_TABLE_STRING,
2603 format_cb_client_prefix
2605 { "client_readonly", FORMAT_TABLE_STRING,
2606 format_cb_client_readonly
2608 { "client_session", FORMAT_TABLE_STRING,
2609 format_cb_client_session
2611 { "client_termfeatures", FORMAT_TABLE_STRING,
2612 format_cb_client_termfeatures
2614 { "client_termname", FORMAT_TABLE_STRING,
2615 format_cb_client_termname
2617 { "client_termtype", FORMAT_TABLE_STRING,
2618 format_cb_client_termtype
2620 { "client_tty", FORMAT_TABLE_STRING,
2621 format_cb_client_tty
2623 { "client_utf8", FORMAT_TABLE_STRING,
2624 format_cb_client_utf8
2626 { "client_width", FORMAT_TABLE_STRING,
2627 format_cb_client_width
2629 { "client_written", FORMAT_TABLE_STRING,
2630 format_cb_client_written
2632 { "config_files", FORMAT_TABLE_STRING,
2633 format_cb_config_files
2635 { "cursor_character", FORMAT_TABLE_STRING,
2636 format_cb_cursor_character
2638 { "cursor_flag", FORMAT_TABLE_STRING,
2639 format_cb_cursor_flag
2641 { "cursor_x", FORMAT_TABLE_STRING,
2642 format_cb_cursor_x
2644 { "cursor_y", FORMAT_TABLE_STRING,
2645 format_cb_cursor_y
2647 { "history_all_bytes", FORMAT_TABLE_STRING,
2648 format_cb_history_all_bytes
2650 { "history_bytes", FORMAT_TABLE_STRING,
2651 format_cb_history_bytes
2653 { "history_limit", FORMAT_TABLE_STRING,
2654 format_cb_history_limit
2656 { "history_size", FORMAT_TABLE_STRING,
2657 format_cb_history_size
2659 { "host", FORMAT_TABLE_STRING,
2660 format_cb_host
2662 { "host_short", FORMAT_TABLE_STRING,
2663 format_cb_host_short
2665 { "insert_flag", FORMAT_TABLE_STRING,
2666 format_cb_insert_flag
2668 { "keypad_cursor_flag", FORMAT_TABLE_STRING,
2669 format_cb_keypad_cursor_flag
2671 { "keypad_flag", FORMAT_TABLE_STRING,
2672 format_cb_keypad_flag
2674 { "last_window_index", FORMAT_TABLE_STRING,
2675 format_cb_last_window_index
2677 { "mouse_all_flag", FORMAT_TABLE_STRING,
2678 format_cb_mouse_all_flag
2680 { "mouse_any_flag", FORMAT_TABLE_STRING,
2681 format_cb_mouse_any_flag
2683 { "mouse_button_flag", FORMAT_TABLE_STRING,
2684 format_cb_mouse_button_flag
2686 { "mouse_line", FORMAT_TABLE_STRING,
2687 format_cb_mouse_line
2689 { "mouse_pane", FORMAT_TABLE_STRING,
2690 format_cb_mouse_pane
2692 { "mouse_sgr_flag", FORMAT_TABLE_STRING,
2693 format_cb_mouse_sgr_flag
2695 { "mouse_standard_flag", FORMAT_TABLE_STRING,
2696 format_cb_mouse_standard_flag
2698 { "mouse_utf8_flag", FORMAT_TABLE_STRING,
2699 format_cb_mouse_utf8_flag
2701 { "mouse_word", FORMAT_TABLE_STRING,
2702 format_cb_mouse_word
2704 { "mouse_x", FORMAT_TABLE_STRING,
2705 format_cb_mouse_x
2707 { "mouse_y", FORMAT_TABLE_STRING,
2708 format_cb_mouse_y
2710 { "origin_flag", FORMAT_TABLE_STRING,
2711 format_cb_origin_flag
2713 { "pane_active", FORMAT_TABLE_STRING,
2714 format_cb_pane_active
2716 { "pane_at_bottom", FORMAT_TABLE_STRING,
2717 format_cb_pane_at_bottom
2719 { "pane_at_left", FORMAT_TABLE_STRING,
2720 format_cb_pane_at_left
2722 { "pane_at_right", FORMAT_TABLE_STRING,
2723 format_cb_pane_at_right
2725 { "pane_at_top", FORMAT_TABLE_STRING,
2726 format_cb_pane_at_top
2728 { "pane_bg", FORMAT_TABLE_STRING,
2729 format_cb_pane_bg
2731 { "pane_bottom", FORMAT_TABLE_STRING,
2732 format_cb_pane_bottom
2734 { "pane_current_command", FORMAT_TABLE_STRING,
2735 format_cb_current_command
2737 { "pane_current_path", FORMAT_TABLE_STRING,
2738 format_cb_current_path
2740 { "pane_dead", FORMAT_TABLE_STRING,
2741 format_cb_pane_dead
2743 { "pane_dead_status", FORMAT_TABLE_STRING,
2744 format_cb_pane_dead_status
2746 { "pane_fg", FORMAT_TABLE_STRING,
2747 format_cb_pane_fg
2749 { "pane_format", FORMAT_TABLE_STRING,
2750 format_cb_pane_format
2752 { "pane_height", FORMAT_TABLE_STRING,
2753 format_cb_pane_height
2755 { "pane_id", FORMAT_TABLE_STRING,
2756 format_cb_pane_id
2758 { "pane_in_mode", FORMAT_TABLE_STRING,
2759 format_cb_pane_in_mode
2761 { "pane_index", FORMAT_TABLE_STRING,
2762 format_cb_pane_index
2764 { "pane_input_off", FORMAT_TABLE_STRING,
2765 format_cb_pane_input_off
2767 { "pane_last", FORMAT_TABLE_STRING,
2768 format_cb_pane_last
2770 { "pane_left", FORMAT_TABLE_STRING,
2771 format_cb_pane_left
2773 { "pane_marked", FORMAT_TABLE_STRING,
2774 format_cb_pane_marked
2776 { "pane_marked_set", FORMAT_TABLE_STRING,
2777 format_cb_pane_marked_set
2779 { "pane_mode", FORMAT_TABLE_STRING,
2780 format_cb_pane_mode
2782 { "pane_path", FORMAT_TABLE_STRING,
2783 format_cb_pane_path
2785 { "pane_pid", FORMAT_TABLE_STRING,
2786 format_cb_pane_pid
2788 { "pane_pipe", FORMAT_TABLE_STRING,
2789 format_cb_pane_pipe
2791 { "pane_right", FORMAT_TABLE_STRING,
2792 format_cb_pane_right
2794 { "pane_search_string", FORMAT_TABLE_STRING,
2795 format_cb_pane_search_string
2797 { "pane_start_command", FORMAT_TABLE_STRING,
2798 format_cb_start_command
2800 { "pane_synchronized", FORMAT_TABLE_STRING,
2801 format_cb_pane_synchronized
2803 { "pane_tabs", FORMAT_TABLE_STRING,
2804 format_cb_pane_tabs
2806 { "pane_title", FORMAT_TABLE_STRING,
2807 format_cb_pane_title
2809 { "pane_top", FORMAT_TABLE_STRING,
2810 format_cb_pane_top
2812 { "pane_tty", FORMAT_TABLE_STRING,
2813 format_cb_pane_tty
2815 { "pane_width", FORMAT_TABLE_STRING,
2816 format_cb_pane_width
2818 { "pid", FORMAT_TABLE_STRING,
2819 format_cb_pid
2821 { "scroll_region_lower", FORMAT_TABLE_STRING,
2822 format_cb_scroll_region_lower
2824 { "scroll_region_upper", FORMAT_TABLE_STRING,
2825 format_cb_scroll_region_upper
2827 { "session_activity", FORMAT_TABLE_TIME,
2828 format_cb_session_activity
2830 { "session_alerts", FORMAT_TABLE_STRING,
2831 format_cb_session_alerts
2833 { "session_attached", FORMAT_TABLE_STRING,
2834 format_cb_session_attached
2836 { "session_attached_list", FORMAT_TABLE_STRING,
2837 format_cb_session_attached_list
2839 { "session_created", FORMAT_TABLE_TIME,
2840 format_cb_session_created
2842 { "session_format", FORMAT_TABLE_STRING,
2843 format_cb_session_format
2845 { "session_group", FORMAT_TABLE_STRING,
2846 format_cb_session_group
2848 { "session_group_attached", FORMAT_TABLE_STRING,
2849 format_cb_session_group_attached
2851 { "session_group_attached_list", FORMAT_TABLE_STRING,
2852 format_cb_session_group_attached_list
2854 { "session_group_list", FORMAT_TABLE_STRING,
2855 format_cb_session_group_list
2857 { "session_group_many_attached", FORMAT_TABLE_STRING,
2858 format_cb_session_group_many_attached
2860 { "session_group_size", FORMAT_TABLE_STRING,
2861 format_cb_session_group_size
2863 { "session_grouped", FORMAT_TABLE_STRING,
2864 format_cb_session_grouped
2866 { "session_id", FORMAT_TABLE_STRING,
2867 format_cb_session_id
2869 { "session_last_attached", FORMAT_TABLE_TIME,
2870 format_cb_session_last_attached
2872 { "session_many_attached", FORMAT_TABLE_STRING,
2873 format_cb_session_many_attached
2875 { "session_marked", FORMAT_TABLE_STRING,
2876 format_cb_session_marked,
2878 { "session_name", FORMAT_TABLE_STRING,
2879 format_cb_session_name
2881 { "session_path", FORMAT_TABLE_STRING,
2882 format_cb_session_path
2884 { "session_stack", FORMAT_TABLE_STRING,
2885 format_cb_session_stack
2887 { "session_windows", FORMAT_TABLE_STRING,
2888 format_cb_session_windows
2890 { "socket_path", FORMAT_TABLE_STRING,
2891 format_cb_socket_path
2893 { "start_time", FORMAT_TABLE_TIME,
2894 format_cb_start_time
2896 { "tree_mode_format", FORMAT_TABLE_STRING,
2897 format_cb_tree_mode_format
2899 { "version", FORMAT_TABLE_STRING,
2900 format_cb_version
2902 { "window_active", FORMAT_TABLE_STRING,
2903 format_cb_window_active
2905 { "window_active_clients", FORMAT_TABLE_STRING,
2906 format_cb_window_active_clients
2908 { "window_active_clients_list", FORMAT_TABLE_STRING,
2909 format_cb_window_active_clients_list
2911 { "window_active_sessions", FORMAT_TABLE_STRING,
2912 format_cb_window_active_sessions
2914 { "window_active_sessions_list", FORMAT_TABLE_STRING,
2915 format_cb_window_active_sessions_list
2917 { "window_activity", FORMAT_TABLE_TIME,
2918 format_cb_window_activity
2920 { "window_activity_flag", FORMAT_TABLE_STRING,
2921 format_cb_window_activity_flag
2923 { "window_bell_flag", FORMAT_TABLE_STRING,
2924 format_cb_window_bell_flag
2926 { "window_bigger", FORMAT_TABLE_STRING,
2927 format_cb_window_bigger
2929 { "window_cell_height", FORMAT_TABLE_STRING,
2930 format_cb_window_cell_height
2932 { "window_cell_width", FORMAT_TABLE_STRING,
2933 format_cb_window_cell_width
2935 { "window_end_flag", FORMAT_TABLE_STRING,
2936 format_cb_window_end_flag
2938 { "window_flags", FORMAT_TABLE_STRING,
2939 format_cb_window_flags
2941 { "window_format", FORMAT_TABLE_STRING,
2942 format_cb_window_format
2944 { "window_height", FORMAT_TABLE_STRING,
2945 format_cb_window_height
2947 { "window_id", FORMAT_TABLE_STRING,
2948 format_cb_window_id
2950 { "window_index", FORMAT_TABLE_STRING,
2951 format_cb_window_index
2953 { "window_last_flag", FORMAT_TABLE_STRING,
2954 format_cb_window_last_flag
2956 { "window_layout", FORMAT_TABLE_STRING,
2957 format_cb_window_layout
2959 { "window_linked", FORMAT_TABLE_STRING,
2960 format_cb_window_linked
2962 { "window_linked_sessions", FORMAT_TABLE_STRING,
2963 format_cb_window_linked_sessions
2965 { "window_linked_sessions_list", FORMAT_TABLE_STRING,
2966 format_cb_window_linked_sessions_list
2968 { "window_marked_flag", FORMAT_TABLE_STRING,
2969 format_cb_window_marked_flag
2971 { "window_name", FORMAT_TABLE_STRING,
2972 format_cb_window_name
2974 { "window_offset_x", FORMAT_TABLE_STRING,
2975 format_cb_window_offset_x
2977 { "window_offset_y", FORMAT_TABLE_STRING,
2978 format_cb_window_offset_y
2980 { "window_panes", FORMAT_TABLE_STRING,
2981 format_cb_window_panes
2983 { "window_raw_flags", FORMAT_TABLE_STRING,
2984 format_cb_window_raw_flags
2986 { "window_silence_flag", FORMAT_TABLE_STRING,
2987 format_cb_window_silence_flag
2989 { "window_stack_index", FORMAT_TABLE_STRING,
2990 format_cb_window_stack_index
2992 { "window_start_flag", FORMAT_TABLE_STRING,
2993 format_cb_window_start_flag
2995 { "window_visible_layout", FORMAT_TABLE_STRING,
2996 format_cb_window_visible_layout
2998 { "window_width", FORMAT_TABLE_STRING,
2999 format_cb_window_width
3001 { "window_zoomed_flag", FORMAT_TABLE_STRING,
3002 format_cb_window_zoomed_flag
3004 { "wrap_flag", FORMAT_TABLE_STRING,
3005 format_cb_wrap_flag
3009 /* Compare format table entries. */
3010 static int
3011 format_table_compare(const void *key0, const void *entry0)
3013 const char *key = key0;
3014 const struct format_table_entry *entry = entry0;
3016 return (strcmp(key, entry->key));
3019 /* Get a format callback. */
3020 static struct format_table_entry *
3021 format_table_get(const char *key)
3023 return (bsearch(key, format_table, nitems(format_table),
3024 sizeof *format_table, format_table_compare));
3027 /* Merge one format tree into another. */
3028 void
3029 format_merge(struct format_tree *ft, struct format_tree *from)
3031 struct format_entry *fe;
3033 RB_FOREACH(fe, format_entry_tree, &from->tree) {
3034 if (fe->value != NULL)
3035 format_add(ft, fe->key, "%s", fe->value);
3039 /* Get format pane. */
3040 struct window_pane *
3041 format_get_pane(struct format_tree *ft)
3043 return (ft->wp);
3046 /* Add item bits to tree. */
3047 static void
3048 format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
3050 struct key_event *event = cmdq_get_event(item);
3051 struct mouse_event *m = &event->m;
3053 cmdq_merge_formats(item, ft);
3054 memcpy(&ft->m, m, sizeof ft->m);
3057 /* Create a new tree. */
3058 struct format_tree *
3059 format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
3061 struct format_tree *ft;
3063 ft = xcalloc(1, sizeof *ft);
3064 RB_INIT(&ft->tree);
3066 if (c != NULL) {
3067 ft->client = c;
3068 ft->client->references++;
3070 ft->item = item;
3072 ft->tag = tag;
3073 ft->flags = flags;
3075 if (item != NULL)
3076 format_create_add_item(ft, item);
3078 return (ft);
3081 /* Free a tree. */
3082 void
3083 format_free(struct format_tree *ft)
3085 struct format_entry *fe, *fe1;
3087 RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
3088 RB_REMOVE(format_entry_tree, &ft->tree, fe);
3089 free(fe->value);
3090 free(fe->key);
3091 free(fe);
3094 if (ft->client != NULL)
3095 server_client_unref(ft->client);
3096 free(ft);
3099 /* Log each format. */
3100 static void
3101 format_log_debug_cb(const char *key, const char *value, void *arg)
3103 const char *prefix = arg;
3105 log_debug("%s: %s=%s", prefix, key, value);
3108 /* Log a format tree. */
3109 void
3110 format_log_debug(struct format_tree *ft, const char *prefix)
3112 format_each(ft, format_log_debug_cb, (void *)prefix);
3115 /* Walk each format. */
3116 void
3117 format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
3118 void *), void *arg)
3120 const struct format_table_entry *fte;
3121 struct format_entry *fe;
3122 u_int i;
3123 char s[64];
3124 void *value;
3125 struct timeval *tv;
3127 for (i = 0; i < nitems(format_table); i++) {
3128 fte = &format_table[i];
3130 value = fte->cb(ft);
3131 if (value == NULL)
3132 continue;
3133 if (fte->type == FORMAT_TABLE_TIME) {
3134 tv = value;
3135 xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec);
3136 cb(fte->key, s, arg);
3137 } else {
3138 cb(fte->key, value, arg);
3139 free(value);
3142 RB_FOREACH(fe, format_entry_tree, &ft->tree) {
3143 if (fe->time != 0) {
3144 xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
3145 cb(fe->key, s, arg);
3146 } else {
3147 if (fe->value == NULL && fe->cb != NULL) {
3148 fe->value = fe->cb(ft);
3149 if (fe->value == NULL)
3150 fe->value = xstrdup("");
3152 cb(fe->key, fe->value, arg);
3157 /* Add a key-value pair. */
3158 void
3159 format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
3161 struct format_entry *fe;
3162 struct format_entry *fe_now;
3163 va_list ap;
3165 fe = xmalloc(sizeof *fe);
3166 fe->key = xstrdup(key);
3168 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3169 if (fe_now != NULL) {
3170 free(fe->key);
3171 free(fe);
3172 free(fe_now->value);
3173 fe = fe_now;
3176 fe->cb = NULL;
3177 fe->time = 0;
3179 va_start(ap, fmt);
3180 xvasprintf(&fe->value, fmt, ap);
3181 va_end(ap);
3184 /* Add a key and time. */
3185 void
3186 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
3188 struct format_entry *fe, *fe_now;
3190 fe = xmalloc(sizeof *fe);
3191 fe->key = xstrdup(key);
3193 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3194 if (fe_now != NULL) {
3195 free(fe->key);
3196 free(fe);
3197 free(fe_now->value);
3198 fe = fe_now;
3201 fe->cb = NULL;
3202 fe->time = tv->tv_sec;
3204 fe->value = NULL;
3207 /* Add a key and function. */
3208 void
3209 format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
3211 struct format_entry *fe;
3212 struct format_entry *fe_now;
3214 fe = xmalloc(sizeof *fe);
3215 fe->key = xstrdup(key);
3217 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3218 if (fe_now != NULL) {
3219 free(fe->key);
3220 free(fe);
3221 free(fe_now->value);
3222 fe = fe_now;
3225 fe->cb = cb;
3226 fe->time = 0;
3228 fe->value = NULL;
3231 /* Quote shell special characters in string. */
3232 static char *
3233 format_quote_shell(const char *s)
3235 const char *cp;
3236 char *out, *at;
3238 at = out = xmalloc(strlen(s) * 2 + 1);
3239 for (cp = s; *cp != '\0'; cp++) {
3240 if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL)
3241 *at++ = '\\';
3242 *at++ = *cp;
3244 *at = '\0';
3245 return (out);
3248 /* Quote #s in string. */
3249 static char *
3250 format_quote_style(const char *s)
3252 const char *cp;
3253 char *out, *at;
3255 at = out = xmalloc(strlen(s) * 2 + 1);
3256 for (cp = s; *cp != '\0'; cp++) {
3257 if (*cp == '#')
3258 *at++ = '#';
3259 *at++ = *cp;
3261 *at = '\0';
3262 return (out);
3265 /* Make a prettier time. */
3266 static char *
3267 format_pretty_time(time_t t)
3269 struct tm now_tm, tm;
3270 time_t now, age;
3271 char s[6];
3273 time(&now);
3274 if (now < t)
3275 now = t;
3276 age = now - t;
3278 localtime_r(&now, &now_tm);
3279 localtime_r(&t, &tm);
3281 /* Last 24 hours. */
3282 if (age < 24 * 3600) {
3283 strftime(s, sizeof s, "%H:%M", &tm);
3284 return (xstrdup(s));
3287 /* This month or last 28 days. */
3288 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) ||
3289 age < 28 * 24 * 3600) {
3290 strftime(s, sizeof s, "%a%d", &tm);
3291 return (xstrdup(s));
3294 /* Last 12 months. */
3295 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) ||
3296 (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) {
3297 strftime(s, sizeof s, "%d%b", &tm);
3298 return (xstrdup(s));
3301 /* Older than that. */
3302 strftime(s, sizeof s, "%h%y", &tm);
3303 return (xstrdup(s));
3306 /* Find a format entry. */
3307 static char *
3308 format_find(struct format_tree *ft, const char *key, int modifiers,
3309 const char *time_format)
3311 struct format_table_entry *fte;
3312 void *value;
3313 struct format_entry *fe, fe_find;
3314 struct environ_entry *envent;
3315 struct options_entry *o;
3316 int idx;
3317 char *found = NULL, *saved, s[512];
3318 const char *errstr;
3319 time_t t = 0;
3320 struct tm tm;
3322 o = options_parse_get(global_options, key, &idx, 0);
3323 if (o == NULL && ft->wp != NULL)
3324 o = options_parse_get(ft->wp->options, key, &idx, 0);
3325 if (o == NULL && ft->w != NULL)
3326 o = options_parse_get(ft->w->options, key, &idx, 0);
3327 if (o == NULL)
3328 o = options_parse_get(global_w_options, key, &idx, 0);
3329 if (o == NULL && ft->s != NULL)
3330 o = options_parse_get(ft->s->options, key, &idx, 0);
3331 if (o == NULL)
3332 o = options_parse_get(global_s_options, key, &idx, 0);
3333 if (o != NULL) {
3334 found = options_to_string(o, idx, 1);
3335 goto found;
3338 fte = format_table_get(key);
3339 if (fte != NULL) {
3340 value = fte->cb(ft);
3341 if (fte->type == FORMAT_TABLE_TIME && value != NULL)
3342 t = ((struct timeval *)value)->tv_sec;
3343 else
3344 found = value;
3345 goto found;
3347 fe_find.key = (char *)key;
3348 fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
3349 if (fe != NULL) {
3350 if (fe->time != 0) {
3351 t = fe->time;
3352 goto found;
3354 if (fe->value == NULL && fe->cb != NULL) {
3355 fe->value = fe->cb(ft);
3356 if (fe->value == NULL)
3357 fe->value = xstrdup("");
3359 found = xstrdup(fe->value);
3360 goto found;
3363 if (~modifiers & FORMAT_TIMESTRING) {
3364 envent = NULL;
3365 if (ft->s != NULL)
3366 envent = environ_find(ft->s->environ, key);
3367 if (envent == NULL)
3368 envent = environ_find(global_environ, key);
3369 if (envent != NULL && envent->value != NULL) {
3370 found = xstrdup(envent->value);
3371 goto found;
3375 return (NULL);
3377 found:
3378 if (modifiers & FORMAT_TIMESTRING) {
3379 if (t == 0 && found != NULL) {
3380 t = strtonum(found, 0, INT64_MAX, &errstr);
3381 if (errstr != NULL)
3382 t = 0;
3383 free(found);
3385 if (t == 0)
3386 return (NULL);
3387 if (modifiers & FORMAT_PRETTY)
3388 found = format_pretty_time(t);
3389 else {
3390 if (time_format != NULL) {
3391 localtime_r(&t, &tm);
3392 strftime(s, sizeof s, time_format, &tm);
3393 } else {
3394 ctime_r(&t, s);
3395 s[strcspn(s, "\n")] = '\0';
3397 found = xstrdup(s);
3399 return (found);
3402 if (t != 0)
3403 xasprintf(&found, "%lld", (long long)t);
3404 else if (found == NULL)
3405 return (NULL);
3406 if (modifiers & FORMAT_BASENAME) {
3407 saved = found;
3408 found = xstrdup(basename(saved));
3409 free(saved);
3411 if (modifiers & FORMAT_DIRNAME) {
3412 saved = found;
3413 found = xstrdup(dirname(saved));
3414 free(saved);
3416 if (modifiers & FORMAT_QUOTE_SHELL) {
3417 saved = found;
3418 found = xstrdup(format_quote_shell(saved));
3419 free(saved);
3421 if (modifiers & FORMAT_QUOTE_STYLE) {
3422 saved = found;
3423 found = xstrdup(format_quote_style(saved));
3424 free(saved);
3426 return (found);
3429 /* Remove escaped characters from string. */
3430 static char *
3431 format_strip(const char *s)
3433 char *out, *cp;
3434 int brackets = 0;
3436 cp = out = xmalloc(strlen(s) + 1);
3437 for (; *s != '\0'; s++) {
3438 if (*s == '#' && s[1] == '{')
3439 brackets++;
3440 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
3441 if (brackets != 0)
3442 *cp++ = *s;
3443 continue;
3445 if (*s == '}')
3446 brackets--;
3447 *cp++ = *s;
3449 *cp = '\0';
3450 return (out);
3453 /* Skip until end. */
3454 const char *
3455 format_skip(const char *s, const char *end)
3457 int brackets = 0;
3459 for (; *s != '\0'; s++) {
3460 if (*s == '#' && s[1] == '{')
3461 brackets++;
3462 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
3463 s++;
3464 continue;
3466 if (*s == '}')
3467 brackets--;
3468 if (strchr(end, *s) != NULL && brackets == 0)
3469 break;
3471 if (*s == '\0')
3472 return (NULL);
3473 return (s);
3476 /* Return left and right alternatives separated by commas. */
3477 static int
3478 format_choose(struct format_expand_state *es, const char *s, char **left,
3479 char **right, int expand)
3481 const char *cp;
3482 char *left0, *right0;
3484 cp = format_skip(s, ",");
3485 if (cp == NULL)
3486 return (-1);
3487 left0 = xstrndup(s, cp - s);
3488 right0 = xstrdup(cp + 1);
3490 if (expand) {
3491 *left = format_expand1(es, left0);
3492 free(left0);
3493 *right = format_expand1(es, right0);
3494 free(right0);
3495 } else {
3496 *left = left0;
3497 *right = right0;
3499 return (0);
3502 /* Is this true? */
3504 format_true(const char *s)
3506 if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
3507 return (1);
3508 return (0);
3511 /* Check if modifier end. */
3512 static int
3513 format_is_end(char c)
3515 return (c == ';' || c == ':');
3518 /* Add to modifier list. */
3519 static void
3520 format_add_modifier(struct format_modifier **list, u_int *count,
3521 const char *c, size_t n, char **argv, int argc)
3523 struct format_modifier *fm;
3525 *list = xreallocarray(*list, (*count) + 1, sizeof **list);
3526 fm = &(*list)[(*count)++];
3528 memcpy(fm->modifier, c, n);
3529 fm->modifier[n] = '\0';
3530 fm->size = n;
3532 fm->argv = argv;
3533 fm->argc = argc;
3536 /* Free modifier list. */
3537 static void
3538 format_free_modifiers(struct format_modifier *list, u_int count)
3540 u_int i;
3542 for (i = 0; i < count; i++)
3543 cmd_free_argv(list[i].argc, list[i].argv);
3544 free(list);
3547 /* Build modifier list. */
3548 static struct format_modifier *
3549 format_build_modifiers(struct format_expand_state *es, const char **s,
3550 u_int *count)
3552 const char *cp = *s, *end;
3553 struct format_modifier *list = NULL;
3554 char c, last[] = "X;:", **argv, *value;
3555 int argc;
3558 * Modifiers are a ; separated list of the forms:
3559 * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,>
3560 * =a
3561 * =/a
3562 * =/a/
3563 * s/a/b/
3564 * s/a/b
3565 * ||,&&,!=,==,<=,>=
3568 *count = 0;
3570 while (*cp != '\0' && *cp != ':') {
3571 /* Skip any separator character. */
3572 if (*cp == ';')
3573 cp++;
3575 /* Check single character modifiers with no arguments. */
3576 if (strchr("labcdnwETSWP<>", cp[0]) != NULL &&
3577 format_is_end(cp[1])) {
3578 format_add_modifier(&list, count, cp, 1, NULL, 0);
3579 cp++;
3580 continue;
3583 /* Then try double character with no arguments. */
3584 if ((memcmp("||", cp, 2) == 0 ||
3585 memcmp("&&", cp, 2) == 0 ||
3586 memcmp("!=", cp, 2) == 0 ||
3587 memcmp("==", cp, 2) == 0 ||
3588 memcmp("<=", cp, 2) == 0 ||
3589 memcmp(">=", cp, 2) == 0) &&
3590 format_is_end(cp[2])) {
3591 format_add_modifier(&list, count, cp, 2, NULL, 0);
3592 cp += 2;
3593 continue;
3596 /* Now try single character with arguments. */
3597 if (strchr("mCNst=peq", cp[0]) == NULL)
3598 break;
3599 c = cp[0];
3601 /* No arguments provided. */
3602 if (format_is_end(cp[1])) {
3603 format_add_modifier(&list, count, cp, 1, NULL, 0);
3604 cp++;
3605 continue;
3607 argv = NULL;
3608 argc = 0;
3610 /* Single argument with no wrapper character. */
3611 if (!ispunct(cp[1]) || cp[1] == '-') {
3612 end = format_skip(cp + 1, ":;");
3613 if (end == NULL)
3614 break;
3616 argv = xcalloc(1, sizeof *argv);
3617 value = xstrndup(cp + 1, end - (cp + 1));
3618 argv[0] = format_expand1(es, value);
3619 free(value);
3620 argc = 1;
3622 format_add_modifier(&list, count, &c, 1, argv, argc);
3623 cp = end;
3624 continue;
3627 /* Multiple arguments with a wrapper character. */
3628 last[0] = cp[1];
3629 cp++;
3630 do {
3631 if (cp[0] == last[0] && format_is_end(cp[1])) {
3632 cp++;
3633 break;
3635 end = format_skip(cp + 1, last);
3636 if (end == NULL)
3637 break;
3638 cp++;
3640 argv = xreallocarray(argv, argc + 1, sizeof *argv);
3641 value = xstrndup(cp, end - cp);
3642 argv[argc++] = format_expand1(es, value);
3643 free(value);
3645 cp = end;
3646 } while (!format_is_end(cp[0]));
3647 format_add_modifier(&list, count, &c, 1, argv, argc);
3649 if (*cp != ':') {
3650 format_free_modifiers(list, *count);
3651 *count = 0;
3652 return (NULL);
3654 *s = cp + 1;
3655 return (list);
3658 /* Match against an fnmatch(3) pattern or regular expression. */
3659 static char *
3660 format_match(struct format_modifier *fm, const char *pattern, const char *text)
3662 const char *s = "";
3663 regex_t r;
3664 int flags = 0;
3666 if (fm->argc >= 1)
3667 s = fm->argv[0];
3668 if (strchr(s, 'r') == NULL) {
3669 if (strchr(s, 'i') != NULL)
3670 flags |= FNM_CASEFOLD;
3671 if (fnmatch(pattern, text, flags) != 0)
3672 return (xstrdup("0"));
3673 } else {
3674 flags = REG_EXTENDED|REG_NOSUB;
3675 if (strchr(s, 'i') != NULL)
3676 flags |= REG_ICASE;
3677 if (regcomp(&r, pattern, flags) != 0)
3678 return (xstrdup("0"));
3679 if (regexec(&r, text, 0, NULL, 0) != 0) {
3680 regfree(&r);
3681 return (xstrdup("0"));
3683 regfree(&r);
3685 return (xstrdup("1"));
3688 /* Perform substitution in string. */
3689 static char *
3690 format_sub(struct format_modifier *fm, const char *text, const char *pattern,
3691 const char *with)
3693 char *value;
3694 int flags = REG_EXTENDED;
3696 if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
3697 flags |= REG_ICASE;
3698 value = regsub(pattern, with, text, flags);
3699 if (value == NULL)
3700 return (xstrdup(text));
3701 return (value);
3704 /* Search inside pane. */
3705 static char *
3706 format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
3708 int ignore = 0, regex = 0;
3709 char *value;
3711 if (fm->argc >= 1) {
3712 if (strchr(fm->argv[0], 'i') != NULL)
3713 ignore = 1;
3714 if (strchr(fm->argv[0], 'r') != NULL)
3715 regex = 1;
3717 xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
3718 return (value);
3721 /* Does session name exist? */
3722 static char *
3723 format_session_name(struct format_expand_state *es, const char *fmt)
3725 char *name;
3726 struct session *s;
3728 name = format_expand1(es, fmt);
3729 RB_FOREACH(s, sessions, &sessions) {
3730 if (strcmp(s->name, name) == 0) {
3731 free(name);
3732 return (xstrdup("1"));
3735 free(name);
3736 return (xstrdup("0"));
3739 /* Loop over sessions. */
3740 static char *
3741 format_loop_sessions(struct format_expand_state *es, const char *fmt)
3743 struct format_tree *ft = es->ft;
3744 struct client *c = ft->client;
3745 struct cmdq_item *item = ft->item;
3746 struct format_tree *nft;
3747 struct format_expand_state next;
3748 char *expanded, *value;
3749 size_t valuelen;
3750 struct session *s;
3752 value = xcalloc(1, 1);
3753 valuelen = 1;
3755 RB_FOREACH(s, sessions, &sessions) {
3756 format_log(es, "session loop: $%u", s->id);
3757 nft = format_create(c, item, FORMAT_NONE, ft->flags);
3758 format_defaults(nft, ft->c, s, NULL, NULL);
3759 format_copy_state(&next, es, 0);
3760 next.ft = nft;
3761 expanded = format_expand1(&next, fmt);
3762 format_free(next.ft);
3764 valuelen += strlen(expanded);
3765 value = xrealloc(value, valuelen);
3767 strlcat(value, expanded, valuelen);
3768 free(expanded);
3771 return (value);
3774 /* Does window name exist? */
3775 static char *
3776 format_window_name(struct format_expand_state *es, const char *fmt)
3778 struct format_tree *ft = es->ft;
3779 char *name;
3780 struct winlink *wl;
3782 if (ft->s == NULL) {
3783 format_log(es, "window name but no session");
3784 return (NULL);
3787 name = format_expand1(es, fmt);
3788 RB_FOREACH(wl, winlinks, &ft->s->windows) {
3789 if (strcmp(wl->window->name, name) == 0) {
3790 free(name);
3791 return (xstrdup("1"));
3794 free(name);
3795 return (xstrdup("0"));
3798 /* Loop over windows. */
3799 static char *
3800 format_loop_windows(struct format_expand_state *es, const char *fmt)
3802 struct format_tree *ft = es->ft;
3803 struct client *c = ft->client;
3804 struct cmdq_item *item = ft->item;
3805 struct format_tree *nft;
3806 struct format_expand_state next;
3807 char *all, *active, *use, *expanded, *value;
3808 size_t valuelen;
3809 struct winlink *wl;
3810 struct window *w;
3812 if (ft->s == NULL) {
3813 format_log(es, "window loop but no session");
3814 return (NULL);
3817 if (format_choose(es, fmt, &all, &active, 0) != 0) {
3818 all = xstrdup(fmt);
3819 active = NULL;
3822 value = xcalloc(1, 1);
3823 valuelen = 1;
3825 RB_FOREACH(wl, winlinks, &ft->s->windows) {
3826 w = wl->window;
3827 format_log(es, "window loop: %u @%u", wl->idx, w->id);
3828 if (active != NULL && wl == ft->s->curw)
3829 use = active;
3830 else
3831 use = all;
3832 nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
3833 format_defaults(nft, ft->c, ft->s, wl, NULL);
3834 format_copy_state(&next, es, 0);
3835 next.ft = nft;
3836 expanded = format_expand1(&next, use);
3837 format_free(nft);
3839 valuelen += strlen(expanded);
3840 value = xrealloc(value, valuelen);
3842 strlcat(value, expanded, valuelen);
3843 free(expanded);
3846 free(active);
3847 free(all);
3849 return (value);
3852 /* Loop over panes. */
3853 static char *
3854 format_loop_panes(struct format_expand_state *es, const char *fmt)
3856 struct format_tree *ft = es->ft;
3857 struct client *c = ft->client;
3858 struct cmdq_item *item = ft->item;
3859 struct format_tree *nft;
3860 struct format_expand_state next;
3861 char *all, *active, *use, *expanded, *value;
3862 size_t valuelen;
3863 struct window_pane *wp;
3865 if (ft->w == NULL) {
3866 format_log(es, "pane loop but no window");
3867 return (NULL);
3870 if (format_choose(es, fmt, &all, &active, 0) != 0) {
3871 all = xstrdup(fmt);
3872 active = NULL;
3875 value = xcalloc(1, 1);
3876 valuelen = 1;
3878 TAILQ_FOREACH(wp, &ft->w->panes, entry) {
3879 format_log(es, "pane loop: %%%u", wp->id);
3880 if (active != NULL && wp == ft->w->active)
3881 use = active;
3882 else
3883 use = all;
3884 nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
3885 format_defaults(nft, ft->c, ft->s, ft->wl, wp);
3886 format_copy_state(&next, es, 0);
3887 next.ft = nft;
3888 expanded = format_expand1(&next, use);
3889 format_free(nft);
3891 valuelen += strlen(expanded);
3892 value = xrealloc(value, valuelen);
3894 strlcat(value, expanded, valuelen);
3895 free(expanded);
3898 free(active);
3899 free(all);
3901 return (value);
3904 static char *
3905 format_replace_expression(struct format_modifier *mexp,
3906 struct format_expand_state *es, const char *copy)
3908 int argc = mexp->argc;
3909 const char *errstr;
3910 char *endch, *value, *left = NULL, *right = NULL;
3911 int use_fp = 0;
3912 u_int prec = 0;
3913 double mleft, mright, result;
3914 enum { ADD,
3915 SUBTRACT,
3916 MULTIPLY,
3917 DIVIDE,
3918 MODULUS,
3919 EQUAL,
3920 NOT_EQUAL,
3921 GREATER_THAN,
3922 GREATER_THAN_EQUAL,
3923 LESS_THAN,
3924 LESS_THAN_EQUAL } operator;
3926 if (strcmp(mexp->argv[0], "+") == 0)
3927 operator = ADD;
3928 else if (strcmp(mexp->argv[0], "-") == 0)
3929 operator = SUBTRACT;
3930 else if (strcmp(mexp->argv[0], "*") == 0)
3931 operator = MULTIPLY;
3932 else if (strcmp(mexp->argv[0], "/") == 0)
3933 operator = DIVIDE;
3934 else if (strcmp(mexp->argv[0], "%") == 0 ||
3935 strcmp(mexp->argv[0], "m") == 0)
3936 operator = MODULUS;
3937 else if (strcmp(mexp->argv[0], "==") == 0)
3938 operator = EQUAL;
3939 else if (strcmp(mexp->argv[0], "!=") == 0)
3940 operator = NOT_EQUAL;
3941 else if (strcmp(mexp->argv[0], ">") == 0)
3942 operator = GREATER_THAN;
3943 else if (strcmp(mexp->argv[0], "<") == 0)
3944 operator = LESS_THAN;
3945 else if (strcmp(mexp->argv[0], ">=") == 0)
3946 operator = GREATER_THAN_EQUAL;
3947 else if (strcmp(mexp->argv[0], "<=") == 0)
3948 operator = LESS_THAN_EQUAL;
3949 else {
3950 format_log(es, "expression has no valid operator: '%s'",
3951 mexp->argv[0]);
3952 goto fail;
3955 /* The second argument may be flags. */
3956 if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) {
3957 use_fp = 1;
3958 prec = 2;
3961 /* The third argument may be precision. */
3962 if (argc >= 3) {
3963 prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr);
3964 if (errstr != NULL) {
3965 format_log(es, "expression precision %s: %s", errstr,
3966 mexp->argv[2]);
3967 goto fail;
3971 if (format_choose(es, copy, &left, &right, 1) != 0) {
3972 format_log(es, "expression syntax error");
3973 goto fail;
3976 mleft = strtod(left, &endch);
3977 if (*endch != '\0') {
3978 format_log(es, "expression left side is invalid: %s", left);
3979 goto fail;
3982 mright = strtod(right, &endch);
3983 if (*endch != '\0') {
3984 format_log(es, "expression right side is invalid: %s", right);
3985 goto fail;
3988 if (!use_fp) {
3989 mleft = (long long)mleft;
3990 mright = (long long)mright;
3992 format_log(es, "expression left side is: %.*f", prec, mleft);
3993 format_log(es, "expression right side is: %.*f", prec, mright);
3995 switch (operator) {
3996 case ADD:
3997 result = mleft + mright;
3998 break;
3999 case SUBTRACT:
4000 result = mleft - mright;
4001 break;
4002 case MULTIPLY:
4003 result = mleft * mright;
4004 break;
4005 case DIVIDE:
4006 result = mleft / mright;
4007 break;
4008 case MODULUS:
4009 result = fmod(mleft, mright);
4010 break;
4011 case EQUAL:
4012 result = fabs(mleft - mright) < 1e-9;
4013 break;
4014 case NOT_EQUAL:
4015 result = fabs(mleft - mright) > 1e-9;
4016 break;
4017 case GREATER_THAN:
4018 result = (mleft > mright);
4019 break;
4020 case GREATER_THAN_EQUAL:
4021 result = (mleft >= mright);
4022 break;
4023 case LESS_THAN:
4024 result = (mleft < mright);
4025 break;
4026 case LESS_THAN_EQUAL:
4027 result = (mleft <= mright);
4028 break;
4030 if (use_fp)
4031 xasprintf(&value, "%.*f", prec, result);
4032 else
4033 xasprintf(&value, "%.*f", prec, (double)(long long)result);
4034 format_log(es, "expression result is %s", value);
4036 free(right);
4037 free(left);
4038 return (value);
4040 fail:
4041 free(right);
4042 free(left);
4043 return (NULL);
4046 /* Replace a key. */
4047 static int
4048 format_replace(struct format_expand_state *es, const char *key, size_t keylen,
4049 char **buf, size_t *len, size_t *off)
4051 struct format_tree *ft = es->ft;
4052 struct window_pane *wp = ft->wp;
4053 const char *errstr, *copy, *cp, *marker = NULL;
4054 const char *time_format = NULL;
4055 char *copy0, *condition, *found, *new;
4056 char *value, *left, *right;
4057 size_t valuelen;
4058 int modifiers = 0, limit = 0, width = 0;
4059 int j, c;
4060 struct format_modifier *list, *cmp = NULL, *search = NULL;
4061 struct format_modifier **sub = NULL, *mexp = NULL, *fm;
4062 u_int i, count, nsub = 0;
4063 struct format_expand_state next;
4065 /* Make a copy of the key. */
4066 copy = copy0 = xstrndup(key, keylen);
4068 /* Process modifier list. */
4069 list = format_build_modifiers(es, &copy, &count);
4070 for (i = 0; i < count; i++) {
4071 fm = &list[i];
4072 if (format_logging(ft)) {
4073 format_log(es, "modifier %u is %s", i, fm->modifier);
4074 for (j = 0; j < fm->argc; j++) {
4075 format_log(es, "modifier %u argument %d: %s", i,
4076 j, fm->argv[j]);
4079 if (fm->size == 1) {
4080 switch (fm->modifier[0]) {
4081 case 'm':
4082 case '<':
4083 case '>':
4084 cmp = fm;
4085 break;
4086 case 'C':
4087 search = fm;
4088 break;
4089 case 's':
4090 if (fm->argc < 2)
4091 break;
4092 sub = xreallocarray(sub, nsub + 1, sizeof *sub);
4093 sub[nsub++] = fm;
4094 break;
4095 case '=':
4096 if (fm->argc < 1)
4097 break;
4098 limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4099 &errstr);
4100 if (errstr != NULL)
4101 limit = 0;
4102 if (fm->argc >= 2 && fm->argv[1] != NULL)
4103 marker = fm->argv[1];
4104 break;
4105 case 'p':
4106 if (fm->argc < 1)
4107 break;
4108 width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4109 &errstr);
4110 if (errstr != NULL)
4111 width = 0;
4112 break;
4113 case 'w':
4114 modifiers |= FORMAT_WIDTH;
4115 break;
4116 case 'e':
4117 if (fm->argc < 1 || fm->argc > 3)
4118 break;
4119 mexp = fm;
4120 break;
4121 case 'l':
4122 modifiers |= FORMAT_LITERAL;
4123 break;
4124 case 'a':
4125 modifiers |= FORMAT_CHARACTER;
4126 break;
4127 case 'b':
4128 modifiers |= FORMAT_BASENAME;
4129 break;
4130 case 'c':
4131 modifiers |= FORMAT_COLOUR;
4132 break;
4133 case 'd':
4134 modifiers |= FORMAT_DIRNAME;
4135 break;
4136 case 'n':
4137 modifiers |= FORMAT_LENGTH;
4138 break;
4139 case 't':
4140 modifiers |= FORMAT_TIMESTRING;
4141 if (fm->argc < 1)
4142 break;
4143 if (strchr(fm->argv[0], 'p') != NULL)
4144 modifiers |= FORMAT_PRETTY;
4145 else if (fm->argc >= 2 &&
4146 strchr(fm->argv[0], 'f') != NULL)
4147 time_format = format_strip(fm->argv[1]);
4148 break;
4149 case 'q':
4150 if (fm->argc < 1)
4151 modifiers |= FORMAT_QUOTE_SHELL;
4152 else if (strchr(fm->argv[0], 'e') != NULL ||
4153 strchr(fm->argv[0], 'h') != NULL)
4154 modifiers |= FORMAT_QUOTE_STYLE;
4155 break;
4156 case 'E':
4157 modifiers |= FORMAT_EXPAND;
4158 break;
4159 case 'T':
4160 modifiers |= FORMAT_EXPANDTIME;
4161 break;
4162 case 'N':
4163 if (fm->argc < 1 ||
4164 strchr(fm->argv[0], 'w') != NULL)
4165 modifiers |= FORMAT_WINDOW_NAME;
4166 else if (strchr(fm->argv[0], 's') != NULL)
4167 modifiers |= FORMAT_SESSION_NAME;
4168 break;
4169 case 'S':
4170 modifiers |= FORMAT_SESSIONS;
4171 break;
4172 case 'W':
4173 modifiers |= FORMAT_WINDOWS;
4174 break;
4175 case 'P':
4176 modifiers |= FORMAT_PANES;
4177 break;
4179 } else if (fm->size == 2) {
4180 if (strcmp(fm->modifier, "||") == 0 ||
4181 strcmp(fm->modifier, "&&") == 0 ||
4182 strcmp(fm->modifier, "==") == 0 ||
4183 strcmp(fm->modifier, "!=") == 0 ||
4184 strcmp(fm->modifier, ">=") == 0 ||
4185 strcmp(fm->modifier, "<=") == 0)
4186 cmp = fm;
4190 /* Is this a literal string? */
4191 if (modifiers & FORMAT_LITERAL) {
4192 value = xstrdup(copy);
4193 goto done;
4196 /* Is this a character? */
4197 if (modifiers & FORMAT_CHARACTER) {
4198 new = format_expand1(es, copy);
4199 c = strtonum(new, 32, 126, &errstr);
4200 if (errstr != NULL)
4201 value = xstrdup("");
4202 else
4203 xasprintf(&value, "%c", c);
4204 free(new);
4205 goto done;
4208 /* Is this a colour? */
4209 if (modifiers & FORMAT_COLOUR) {
4210 new = format_expand1(es, copy);
4211 c = colour_fromstring(new);
4212 if (c == -1 || (c = colour_force_rgb(c)) == -1)
4213 value = xstrdup("");
4214 else
4215 xasprintf(&value, "%06x", c & 0xffffff);
4216 free(new);
4217 goto done;
4220 /* Is this a loop, comparison or condition? */
4221 if (modifiers & FORMAT_SESSIONS) {
4222 value = format_loop_sessions(es, copy);
4223 if (value == NULL)
4224 goto fail;
4225 } else if (modifiers & FORMAT_WINDOWS) {
4226 value = format_loop_windows(es, copy);
4227 if (value == NULL)
4228 goto fail;
4229 } else if (modifiers & FORMAT_PANES) {
4230 value = format_loop_panes(es, copy);
4231 if (value == NULL)
4232 goto fail;
4233 } else if (modifiers & FORMAT_WINDOW_NAME) {
4234 value = format_window_name(es, copy);
4235 if (value == NULL)
4236 goto fail;
4237 } else if (modifiers & FORMAT_SESSION_NAME) {
4238 value = format_session_name(es, copy);
4239 if (value == NULL)
4240 goto fail;
4241 } else if (search != NULL) {
4242 /* Search in pane. */
4243 new = format_expand1(es, copy);
4244 if (wp == NULL) {
4245 format_log(es, "search '%s' but no pane", new);
4246 value = xstrdup("0");
4247 } else {
4248 format_log(es, "search '%s' pane %%%u", new, wp->id);
4249 value = format_search(search, wp, new);
4251 free(new);
4252 } else if (cmp != NULL) {
4253 /* Comparison of left and right. */
4254 if (format_choose(es, copy, &left, &right, 1) != 0) {
4255 format_log(es, "compare %s syntax error: %s",
4256 cmp->modifier, copy);
4257 goto fail;
4259 format_log(es, "compare %s left is: %s", cmp->modifier, left);
4260 format_log(es, "compare %s right is: %s", cmp->modifier, right);
4262 if (strcmp(cmp->modifier, "||") == 0) {
4263 if (format_true(left) || format_true(right))
4264 value = xstrdup("1");
4265 else
4266 value = xstrdup("0");
4267 } else if (strcmp(cmp->modifier, "&&") == 0) {
4268 if (format_true(left) && format_true(right))
4269 value = xstrdup("1");
4270 else
4271 value = xstrdup("0");
4272 } else if (strcmp(cmp->modifier, "==") == 0) {
4273 if (strcmp(left, right) == 0)
4274 value = xstrdup("1");
4275 else
4276 value = xstrdup("0");
4277 } else if (strcmp(cmp->modifier, "!=") == 0) {
4278 if (strcmp(left, right) != 0)
4279 value = xstrdup("1");
4280 else
4281 value = xstrdup("0");
4282 } else if (strcmp(cmp->modifier, "<") == 0) {
4283 if (strcmp(left, right) < 0)
4284 value = xstrdup("1");
4285 else
4286 value = xstrdup("0");
4287 } else if (strcmp(cmp->modifier, ">") == 0) {
4288 if (strcmp(left, right) > 0)
4289 value = xstrdup("1");
4290 else
4291 value = xstrdup("0");
4292 } else if (strcmp(cmp->modifier, "<=") == 0) {
4293 if (strcmp(left, right) <= 0)
4294 value = xstrdup("1");
4295 else
4296 value = xstrdup("0");
4297 } else if (strcmp(cmp->modifier, ">=") == 0) {
4298 if (strcmp(left, right) >= 0)
4299 value = xstrdup("1");
4300 else
4301 value = xstrdup("0");
4302 } else if (strcmp(cmp->modifier, "m") == 0)
4303 value = format_match(cmp, left, right);
4305 free(right);
4306 free(left);
4307 } else if (*copy == '?') {
4308 /* Conditional: check first and choose second or third. */
4309 cp = format_skip(copy + 1, ",");
4310 if (cp == NULL) {
4311 format_log(es, "condition syntax error: %s", copy + 1);
4312 goto fail;
4314 condition = xstrndup(copy + 1, cp - (copy + 1));
4315 format_log(es, "condition is: %s", condition);
4317 found = format_find(ft, condition, modifiers, time_format);
4318 if (found == NULL) {
4320 * If the condition not found, try to expand it. If
4321 * the expansion doesn't have any effect, then assume
4322 * false.
4324 found = format_expand1(es, condition);
4325 if (strcmp(found, condition) == 0) {
4326 free(found);
4327 found = xstrdup("");
4328 format_log(es,
4329 "condition '%s' not found; assuming false",
4330 condition);
4332 } else {
4333 format_log(es, "condition '%s' found: %s", condition,
4334 found);
4337 if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
4338 format_log(es, "condition '%s' syntax error: %s",
4339 condition, cp + 1);
4340 free(found);
4341 goto fail;
4343 if (format_true(found)) {
4344 format_log(es, "condition '%s' is true", condition);
4345 value = format_expand1(es, left);
4346 } else {
4347 format_log(es, "condition '%s' is false", condition);
4348 value = format_expand1(es, right);
4350 free(right);
4351 free(left);
4353 free(condition);
4354 free(found);
4355 } else if (mexp != NULL) {
4356 value = format_replace_expression(mexp, es, copy);
4357 if (value == NULL)
4358 value = xstrdup("");
4359 } else {
4360 if (strstr(copy, "#{") != 0) {
4361 format_log(es, "expanding inner format '%s'", copy);
4362 value = format_expand1(es, copy);
4363 } else {
4364 value = format_find(ft, copy, modifiers, time_format);
4365 if (value == NULL) {
4366 format_log(es, "format '%s' not found", copy);
4367 value = xstrdup("");
4368 } else {
4369 format_log(es, "format '%s' found: %s", copy,
4370 value);
4375 done:
4376 /* Expand again if required. */
4377 if (modifiers & FORMAT_EXPAND) {
4378 new = format_expand1(es, value);
4379 free(value);
4380 value = new;
4381 } else if (modifiers & FORMAT_EXPANDTIME) {
4382 format_copy_state(&next, es, FORMAT_EXPAND_TIME);
4383 new = format_expand1(&next, value);
4384 free(value);
4385 value = new;
4388 /* Perform substitution if any. */
4389 for (i = 0; i < nsub; i++) {
4390 left = format_expand1(es, sub[i]->argv[0]);
4391 right = format_expand1(es, sub[i]->argv[1]);
4392 new = format_sub(sub[i], value, left, right);
4393 format_log(es, "substitute '%s' to '%s': %s", left, right, new);
4394 free(value);
4395 value = new;
4396 free(right);
4397 free(left);
4400 /* Truncate the value if needed. */
4401 if (limit > 0) {
4402 new = format_trim_left(value, limit);
4403 if (marker != NULL && strcmp(new, value) != 0) {
4404 free(value);
4405 xasprintf(&value, "%s%s", new, marker);
4406 } else {
4407 free(value);
4408 value = new;
4410 format_log(es, "applied length limit %d: %s", limit, value);
4411 } else if (limit < 0) {
4412 new = format_trim_right(value, -limit);
4413 if (marker != NULL && strcmp(new, value) != 0) {
4414 free(value);
4415 xasprintf(&value, "%s%s", marker, new);
4416 } else {
4417 free(value);
4418 value = new;
4420 format_log(es, "applied length limit %d: %s", limit, value);
4423 /* Pad the value if needed. */
4424 if (width > 0) {
4425 new = utf8_padcstr(value, width);
4426 free(value);
4427 value = new;
4428 format_log(es, "applied padding width %d: %s", width, value);
4429 } else if (width < 0) {
4430 new = utf8_rpadcstr(value, -width);
4431 free(value);
4432 value = new;
4433 format_log(es, "applied padding width %d: %s", width, value);
4436 /* Replace with the length or width if needed. */
4437 if (modifiers & FORMAT_LENGTH) {
4438 xasprintf(&new, "%zu", strlen(value));
4439 free(value);
4440 value = new;
4441 format_log(es, "replacing with length: %s", new);
4443 if (modifiers & FORMAT_WIDTH) {
4444 xasprintf(&new, "%u", format_width(value));
4445 free(value);
4446 value = new;
4447 format_log(es, "replacing with width: %s", new);
4450 /* Expand the buffer and copy in the value. */
4451 valuelen = strlen(value);
4452 while (*len - *off < valuelen + 1) {
4453 *buf = xreallocarray(*buf, 2, *len);
4454 *len *= 2;
4456 memcpy(*buf + *off, value, valuelen);
4457 *off += valuelen;
4459 format_log(es, "replaced '%s' with '%s'", copy0, value);
4460 free(value);
4462 free(sub);
4463 format_free_modifiers(list, count);
4464 free(copy0);
4465 return (0);
4467 fail:
4468 format_log(es, "failed %s", copy0);
4470 free(sub);
4471 format_free_modifiers(list, count);
4472 free(copy0);
4473 return (-1);
4476 /* Expand keys in a template. */
4477 static char *
4478 format_expand1(struct format_expand_state *es, const char *fmt)
4480 struct format_tree *ft = es->ft;
4481 char *buf, *out, *name;
4482 const char *ptr, *s;
4483 size_t off, len, n, outlen;
4484 int ch, brackets;
4485 char expanded[8192];
4487 if (fmt == NULL || *fmt == '\0')
4488 return (xstrdup(""));
4490 if (es->loop == FORMAT_LOOP_LIMIT) {
4491 format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT);
4492 return (xstrdup(""));
4494 es->loop++;
4496 format_log(es, "expanding format: %s", fmt);
4498 if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) {
4499 if (es->time == 0) {
4500 es->time = time(NULL);
4501 localtime_r(&es->time, &es->tm);
4503 if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) {
4504 format_log(es, "format is too long");
4505 return (xstrdup(""));
4507 if (format_logging(ft) && strcmp(expanded, fmt) != 0)
4508 format_log(es, "after time expanded: %s", expanded);
4509 fmt = expanded;
4512 len = 64;
4513 buf = xmalloc(len);
4514 off = 0;
4516 while (*fmt != '\0') {
4517 if (*fmt != '#') {
4518 while (len - off < 2) {
4519 buf = xreallocarray(buf, 2, len);
4520 len *= 2;
4522 buf[off++] = *fmt++;
4523 continue;
4525 fmt++;
4527 ch = (u_char)*fmt++;
4528 switch (ch) {
4529 case '(':
4530 brackets = 1;
4531 for (ptr = fmt; *ptr != '\0'; ptr++) {
4532 if (*ptr == '(')
4533 brackets++;
4534 if (*ptr == ')' && --brackets == 0)
4535 break;
4537 if (*ptr != ')' || brackets != 0)
4538 break;
4539 n = ptr - fmt;
4541 name = xstrndup(fmt, n);
4542 format_log(es, "found #(): %s", name);
4544 if ((ft->flags & FORMAT_NOJOBS) ||
4545 (es->flags & FORMAT_EXPAND_NOJOBS)) {
4546 out = xstrdup("");
4547 format_log(es, "#() is disabled");
4548 } else {
4549 out = format_job_get(es, name);
4550 format_log(es, "#() result: %s", out);
4552 free(name);
4554 outlen = strlen(out);
4555 while (len - off < outlen + 1) {
4556 buf = xreallocarray(buf, 2, len);
4557 len *= 2;
4559 memcpy(buf + off, out, outlen);
4560 off += outlen;
4562 free(out);
4564 fmt += n + 1;
4565 continue;
4566 case '{':
4567 ptr = format_skip((char *)fmt - 2, "}");
4568 if (ptr == NULL)
4569 break;
4570 n = ptr - fmt;
4572 format_log(es, "found #{}: %.*s", (int)n, fmt);
4573 if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
4574 break;
4575 fmt += n + 1;
4576 continue;
4577 case '#':
4579 * If ##[ (with two or more #s), then it is a style and
4580 * can be left for format_draw to handle.
4582 ptr = fmt;
4583 n = 2;
4584 while (*ptr == '#') {
4585 ptr++;
4586 n++;
4588 if (*ptr == '[') {
4589 format_log(es, "found #*%zu[", n);
4590 while (len - off < n + 2) {
4591 buf = xreallocarray(buf, 2, len);
4592 len *= 2;
4594 memcpy(buf + off, fmt - 2, n + 1);
4595 off += n + 1;
4596 fmt = ptr + 1;
4597 continue;
4599 /* FALLTHROUGH */
4600 case '}':
4601 case ',':
4602 format_log(es, "found #%c", ch);
4603 while (len - off < 2) {
4604 buf = xreallocarray(buf, 2, len);
4605 len *= 2;
4607 buf[off++] = ch;
4608 continue;
4609 default:
4610 s = NULL;
4611 if (ch >= 'A' && ch <= 'Z')
4612 s = format_upper[ch - 'A'];
4613 else if (ch >= 'a' && ch <= 'z')
4614 s = format_lower[ch - 'a'];
4615 if (s == NULL) {
4616 while (len - off < 3) {
4617 buf = xreallocarray(buf, 2, len);
4618 len *= 2;
4620 buf[off++] = '#';
4621 buf[off++] = ch;
4622 continue;
4624 n = strlen(s);
4625 format_log(es, "found #%c: %s", ch, s);
4626 if (format_replace(es, s, n, &buf, &len, &off) != 0)
4627 break;
4628 continue;
4631 break;
4633 buf[off] = '\0';
4635 format_log(es, "result is: %s", buf);
4636 es->loop--;
4638 return (buf);
4641 /* Expand keys in a template, passing through strftime first. */
4642 char *
4643 format_expand_time(struct format_tree *ft, const char *fmt)
4645 struct format_expand_state es;
4647 memset(&es, 0, sizeof es);
4648 es.ft = ft;
4649 es.flags = FORMAT_EXPAND_TIME;
4650 return (format_expand1(&es, fmt));
4653 /* Expand keys in a template. */
4654 char *
4655 format_expand(struct format_tree *ft, const char *fmt)
4657 struct format_expand_state es;
4659 memset(&es, 0, sizeof es);
4660 es.ft = ft;
4661 es.flags = 0;
4662 return (format_expand1(&es, fmt));
4665 /* Expand a single string. */
4666 char *
4667 format_single(struct cmdq_item *item, const char *fmt, struct client *c,
4668 struct session *s, struct winlink *wl, struct window_pane *wp)
4670 struct format_tree *ft;
4671 char *expanded;
4673 ft = format_create_defaults(item, c, s, wl, wp);
4674 expanded = format_expand(ft, fmt);
4675 format_free(ft);
4676 return (expanded);
4679 /* Expand a single string using state. */
4680 char *
4681 format_single_from_state(struct cmdq_item *item, const char *fmt,
4682 struct client *c, struct cmd_find_state *fs)
4684 return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp));
4687 /* Expand a single string using target. */
4688 char *
4689 format_single_from_target(struct cmdq_item *item, const char *fmt)
4691 struct client *tc = cmdq_get_target_client(item);
4693 return (format_single_from_state(item, fmt, tc, cmdq_get_target(item)));
4696 /* Create and add defaults. */
4697 struct format_tree *
4698 format_create_defaults(struct cmdq_item *item, struct client *c,
4699 struct session *s, struct winlink *wl, struct window_pane *wp)
4701 struct format_tree *ft;
4703 if (item != NULL)
4704 ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
4705 else
4706 ft = format_create(NULL, item, FORMAT_NONE, 0);
4707 format_defaults(ft, c, s, wl, wp);
4708 return (ft);
4711 /* Create and add defaults using state. */
4712 struct format_tree *
4713 format_create_from_state(struct cmdq_item *item, struct client *c,
4714 struct cmd_find_state *fs)
4716 return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp));
4719 /* Create and add defaults using target. */
4720 struct format_tree *
4721 format_create_from_target(struct cmdq_item *item)
4723 struct client *tc = cmdq_get_target_client(item);
4725 return (format_create_from_state(item, tc, cmdq_get_target(item)));
4728 /* Set defaults for any of arguments that are not NULL. */
4729 void
4730 format_defaults(struct format_tree *ft, struct client *c, struct session *s,
4731 struct winlink *wl, struct window_pane *wp)
4733 struct paste_buffer *pb;
4735 if (c != NULL && c->name != NULL)
4736 log_debug("%s: c=%s", __func__, c->name);
4737 else
4738 log_debug("%s: c=none", __func__);
4739 if (s != NULL)
4740 log_debug("%s: s=$%u", __func__, s->id);
4741 else
4742 log_debug("%s: s=none", __func__);
4743 if (wl != NULL)
4744 log_debug("%s: wl=%u", __func__, wl->idx);
4745 else
4746 log_debug("%s: wl=none", __func__);
4747 if (wp != NULL)
4748 log_debug("%s: wp=%%%u", __func__, wp->id);
4749 else
4750 log_debug("%s: wp=none", __func__);
4752 if (c != NULL && s != NULL && c->session != s)
4753 log_debug("%s: session does not match", __func__);
4755 if (wp != NULL)
4756 ft->type = FORMAT_TYPE_PANE;
4757 else if (wl != NULL)
4758 ft->type = FORMAT_TYPE_WINDOW;
4759 else if (s != NULL)
4760 ft->type = FORMAT_TYPE_SESSION;
4761 else
4762 ft->type = FORMAT_TYPE_UNKNOWN;
4764 if (s == NULL && c != NULL)
4765 s = c->session;
4766 if (wl == NULL && s != NULL)
4767 wl = s->curw;
4768 if (wp == NULL && wl != NULL)
4769 wp = wl->window->active;
4771 if (c != NULL)
4772 format_defaults_client(ft, c);
4773 if (s != NULL)
4774 format_defaults_session(ft, s);
4775 if (wl != NULL)
4776 format_defaults_winlink(ft, wl);
4777 if (wp != NULL)
4778 format_defaults_pane(ft, wp);
4780 pb = paste_get_top(NULL);
4781 if (pb != NULL)
4782 format_defaults_paste_buffer(ft, pb);
4785 /* Set default format keys for a session. */
4786 static void
4787 format_defaults_session(struct format_tree *ft, struct session *s)
4789 ft->s = s;
4792 /* Set default format keys for a client. */
4793 static void
4794 format_defaults_client(struct format_tree *ft, struct client *c)
4796 if (ft->s == NULL)
4797 ft->s = c->session;
4798 ft->c = c;
4801 /* Set default format keys for a window. */
4802 void
4803 format_defaults_window(struct format_tree *ft, struct window *w)
4805 ft->w = w;
4808 /* Set default format keys for a winlink. */
4809 static void
4810 format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
4812 if (ft->w == NULL)
4813 format_defaults_window(ft, wl->window);
4814 ft->wl = wl;
4817 /* Set default format keys for a window pane. */
4818 void
4819 format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
4821 struct window_mode_entry *wme;
4823 if (ft->w == NULL)
4824 format_defaults_window(ft, wp->window);
4825 ft->wp = wp;
4827 wme = TAILQ_FIRST(&wp->modes);
4828 if (wme != NULL && wme->mode->formats != NULL)
4829 wme->mode->formats(wme, ft);
4832 /* Set default format keys for paste buffer. */
4833 void
4834 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
4836 ft->pb = pb;
4839 /* Return word at given coordinates. Caller frees. */
4840 char *
4841 format_grid_word(struct grid *gd, u_int x, u_int y)
4843 const struct grid_line *gl;
4844 struct grid_cell gc;
4845 const char *ws;
4846 struct utf8_data *ud = NULL;
4847 u_int end;
4848 size_t size = 0;
4849 int found = 0;
4850 char *s = NULL;
4852 ws = options_get_string(global_s_options, "word-separators");
4854 for (;;) {
4855 grid_get_cell(gd, x, y, &gc);
4856 if (gc.flags & GRID_FLAG_PADDING)
4857 break;
4858 if (utf8_cstrhas(ws, &gc.data) ||
4859 (gc.data.size == 1 && *gc.data.data == ' ')) {
4860 found = 1;
4861 break;
4864 if (x == 0) {
4865 if (y == 0)
4866 break;
4867 gl = grid_peek_line(gd, y - 1);
4868 if (~gl->flags & GRID_LINE_WRAPPED)
4869 break;
4870 y--;
4871 x = grid_line_length(gd, y);
4872 if (x == 0)
4873 break;
4875 x--;
4877 for (;;) {
4878 if (found) {
4879 end = grid_line_length(gd, y);
4880 if (end == 0 || x == end - 1) {
4881 if (y == gd->hsize + gd->sy - 1)
4882 break;
4883 gl = grid_peek_line(gd, y);
4884 if (~gl->flags & GRID_LINE_WRAPPED)
4885 break;
4886 y++;
4887 x = 0;
4888 } else
4889 x++;
4891 found = 1;
4893 grid_get_cell(gd, x, y, &gc);
4894 if (gc.flags & GRID_FLAG_PADDING)
4895 break;
4896 if (utf8_cstrhas(ws, &gc.data) ||
4897 (gc.data.size == 1 && *gc.data.data == ' '))
4898 break;
4900 ud = xreallocarray(ud, size + 2, sizeof *ud);
4901 memcpy(&ud[size++], &gc.data, sizeof *ud);
4903 if (size != 0) {
4904 ud[size].size = 0;
4905 s = utf8_tocstr(ud);
4906 free(ud);
4908 return (s);
4911 /* Return line at given coordinates. Caller frees. */
4912 char *
4913 format_grid_line(struct grid *gd, u_int y)
4915 struct grid_cell gc;
4916 struct utf8_data *ud = NULL;
4917 u_int x;
4918 size_t size = 0;
4919 char *s = NULL;
4921 for (x = 0; x < grid_line_length(gd, y); x++) {
4922 grid_get_cell(gd, x, y, &gc);
4923 if (gc.flags & GRID_FLAG_PADDING)
4924 break;
4926 ud = xreallocarray(ud, size + 2, sizeof *ud);
4927 memcpy(&ud[size++], &gc.data, sizeof *ud);
4929 if (size != 0) {
4930 ud[size].size = 0;
4931 s = utf8_tocstr(ud);
4932 free(ud);
4934 return (s);