Add unit (milliseconds) to escape-time, show unset colours as "none"
[tmux-openbsd.git] / format.c
blob981161b38269cdc31eebfd0231d8dba527ab480b
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 <pwd.h>
28 #include <regex.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <unistd.h>
35 #include "tmux.h"
38 * Build a list of key-value pairs and use them to expand #{key} entries in a
39 * string.
42 struct format_expand_state;
44 static char *format_job_get(struct format_expand_state *, const char *);
45 static char *format_expand1(struct format_expand_state *, const char *);
46 static int format_replace(struct format_expand_state *, const char *,
47 size_t, char **, size_t *, size_t *);
48 static void format_defaults_session(struct format_tree *,
49 struct session *);
50 static void format_defaults_client(struct format_tree *, struct client *);
51 static void format_defaults_winlink(struct format_tree *,
52 struct winlink *);
54 /* Entry in format job tree. */
55 struct format_job {
56 struct client *client;
57 u_int tag;
58 const char *cmd;
59 const char *expanded;
61 time_t last;
62 char *out;
63 int updated;
65 struct job *job;
66 int status;
68 RB_ENTRY(format_job) entry;
71 /* Format job tree. */
72 static int format_job_cmp(struct format_job *, struct format_job *);
73 static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
74 RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp);
76 /* Format job tree comparison function. */
77 static int
78 format_job_cmp(struct format_job *fj1, struct format_job *fj2)
80 if (fj1->tag < fj2->tag)
81 return (-1);
82 if (fj1->tag > fj2->tag)
83 return (1);
84 return (strcmp(fj1->cmd, fj2->cmd));
87 /* Format modifiers. */
88 #define FORMAT_TIMESTRING 0x1
89 #define FORMAT_BASENAME 0x2
90 #define FORMAT_DIRNAME 0x4
91 #define FORMAT_QUOTE_SHELL 0x8
92 #define FORMAT_LITERAL 0x10
93 #define FORMAT_EXPAND 0x20
94 #define FORMAT_EXPANDTIME 0x40
95 #define FORMAT_SESSIONS 0x80
96 #define FORMAT_WINDOWS 0x100
97 #define FORMAT_PANES 0x200
98 #define FORMAT_PRETTY 0x400
99 #define FORMAT_LENGTH 0x800
100 #define FORMAT_WIDTH 0x1000
101 #define FORMAT_QUOTE_STYLE 0x2000
102 #define FORMAT_WINDOW_NAME 0x4000
103 #define FORMAT_SESSION_NAME 0x8000
104 #define FORMAT_CHARACTER 0x10000
105 #define FORMAT_COLOUR 0x20000
107 /* Limit on recursion. */
108 #define FORMAT_LOOP_LIMIT 100
110 /* Format expand flags. */
111 #define FORMAT_EXPAND_TIME 0x1
112 #define FORMAT_EXPAND_NOJOBS 0x2
114 /* Entry in format tree. */
115 struct format_entry {
116 char *key;
117 char *value;
118 time_t time;
119 format_cb cb;
120 RB_ENTRY(format_entry) entry;
123 /* Format type. */
124 enum format_type {
125 FORMAT_TYPE_UNKNOWN,
126 FORMAT_TYPE_SESSION,
127 FORMAT_TYPE_WINDOW,
128 FORMAT_TYPE_PANE
131 struct format_tree {
132 enum format_type type;
134 struct client *c;
135 struct session *s;
136 struct winlink *wl;
137 struct window *w;
138 struct window_pane *wp;
139 struct paste_buffer *pb;
141 struct cmdq_item *item;
142 struct client *client;
143 int flags;
144 u_int tag;
146 struct mouse_event m;
148 RB_HEAD(format_entry_tree, format_entry) tree;
150 static int format_entry_cmp(struct format_entry *, struct format_entry *);
151 RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
153 /* Format expand state. */
154 struct format_expand_state {
155 struct format_tree *ft;
156 u_int loop;
157 time_t time;
158 struct tm tm;
159 int flags;
162 /* Format modifier. */
163 struct format_modifier {
164 char modifier[3];
165 u_int size;
167 char **argv;
168 int argc;
171 /* Format entry tree comparison function. */
172 static int
173 format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
175 return (strcmp(fe1->key, fe2->key));
178 /* Single-character uppercase aliases. */
179 static const char *format_upper[] = {
180 NULL, /* A */
181 NULL, /* B */
182 NULL, /* C */
183 "pane_id", /* D */
184 NULL, /* E */
185 "window_flags", /* F */
186 NULL, /* G */
187 "host", /* H */
188 "window_index", /* I */
189 NULL, /* J */
190 NULL, /* K */
191 NULL, /* L */
192 NULL, /* M */
193 NULL, /* N */
194 NULL, /* O */
195 "pane_index", /* P */
196 NULL, /* Q */
197 NULL, /* R */
198 "session_name", /* S */
199 "pane_title", /* T */
200 NULL, /* U */
201 NULL, /* V */
202 "window_name", /* W */
203 NULL, /* X */
204 NULL, /* Y */
205 NULL /* Z */
208 /* Single-character lowercase aliases. */
209 static const char *format_lower[] = {
210 NULL, /* a */
211 NULL, /* b */
212 NULL, /* c */
213 NULL, /* d */
214 NULL, /* e */
215 NULL, /* f */
216 NULL, /* g */
217 "host_short", /* h */
218 NULL, /* i */
219 NULL, /* j */
220 NULL, /* k */
221 NULL, /* l */
222 NULL, /* m */
223 NULL, /* n */
224 NULL, /* o */
225 NULL, /* p */
226 NULL, /* q */
227 NULL, /* r */
228 NULL, /* s */
229 NULL, /* t */
230 NULL, /* u */
231 NULL, /* v */
232 NULL, /* w */
233 NULL, /* x */
234 NULL, /* y */
235 NULL /* z */
238 /* Is logging enabled? */
239 static inline int
240 format_logging(struct format_tree *ft)
242 return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE));
245 /* Log a message if verbose. */
246 static void printflike(3, 4)
247 format_log1(struct format_expand_state *es, const char *from, const char *fmt,
248 ...)
250 struct format_tree *ft = es->ft;
251 va_list ap;
252 char *s;
253 static const char spaces[] = " ";
255 if (!format_logging(ft))
256 return;
258 va_start(ap, fmt);
259 xvasprintf(&s, fmt, ap);
260 va_end(ap);
262 log_debug("%s: %s", from, s);
263 if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
264 cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
266 free(s);
268 #define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
270 /* Copy expand state. */
271 static void
272 format_copy_state(struct format_expand_state *to,
273 struct format_expand_state *from, int flags)
275 to->ft = from->ft;
276 to->loop = from->loop;
277 to->time = from->time;
278 memcpy(&to->tm, &from->tm, sizeof to->tm);
279 to->flags = from->flags|flags;
282 /* Format job update callback. */
283 static void
284 format_job_update(struct job *job)
286 struct format_job *fj = job_get_data(job);
287 struct evbuffer *evb = job_get_event(job)->input;
288 char *line = NULL, *next;
289 time_t t;
291 while ((next = evbuffer_readline(evb)) != NULL) {
292 free(line);
293 line = next;
295 if (line == NULL)
296 return;
297 fj->updated = 1;
299 free(fj->out);
300 fj->out = line;
302 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
304 t = time(NULL);
305 if (fj->status && fj->last != t) {
306 if (fj->client != NULL)
307 server_status_client(fj->client);
308 fj->last = t;
312 /* Format job complete callback. */
313 static void
314 format_job_complete(struct job *job)
316 struct format_job *fj = job_get_data(job);
317 struct evbuffer *evb = job_get_event(job)->input;
318 char *line, *buf;
319 size_t len;
321 fj->job = NULL;
323 buf = NULL;
324 if ((line = evbuffer_readline(evb)) == NULL) {
325 len = EVBUFFER_LENGTH(evb);
326 buf = xmalloc(len + 1);
327 if (len != 0)
328 memcpy(buf, EVBUFFER_DATA(evb), len);
329 buf[len] = '\0';
330 } else
331 buf = line;
333 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
335 if (*buf != '\0' || !fj->updated) {
336 free(fj->out);
337 fj->out = buf;
338 } else
339 free(buf);
341 if (fj->status) {
342 if (fj->client != NULL)
343 server_status_client(fj->client);
344 fj->status = 0;
348 /* Find a job. */
349 static char *
350 format_job_get(struct format_expand_state *es, const char *cmd)
352 struct format_tree *ft = es->ft;
353 struct format_job_tree *jobs;
354 struct format_job fj0, *fj;
355 time_t t;
356 char *expanded;
357 int force;
358 struct format_expand_state next;
360 if (ft->client == NULL)
361 jobs = &format_jobs;
362 else if (ft->client->jobs != NULL)
363 jobs = ft->client->jobs;
364 else {
365 jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs);
366 RB_INIT(jobs);
369 fj0.tag = ft->tag;
370 fj0.cmd = cmd;
371 if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) {
372 fj = xcalloc(1, sizeof *fj);
373 fj->client = ft->client;
374 fj->tag = ft->tag;
375 fj->cmd = xstrdup(cmd);
377 RB_INSERT(format_job_tree, jobs, fj);
380 format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
381 next.flags &= ~FORMAT_EXPAND_TIME;
383 expanded = format_expand1(&next, cmd);
384 if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
385 free((void *)fj->expanded);
386 fj->expanded = xstrdup(expanded);
387 force = 1;
388 } else
389 force = (ft->flags & FORMAT_FORCE);
391 t = time(NULL);
392 if (force && fj->job != NULL)
393 job_free(fj->job);
394 if (force || (fj->job == NULL && fj->last != t)) {
395 fj->job = job_run(expanded, 0, NULL, NULL, NULL,
396 server_client_get_cwd(ft->client, NULL), format_job_update,
397 format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1);
398 if (fj->job == NULL) {
399 free(fj->out);
400 xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
402 fj->last = t;
403 fj->updated = 0;
404 } else if (fj->job != NULL && (t - fj->last) > 1 && fj->out == NULL)
405 xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
406 free(expanded);
408 if (ft->flags & FORMAT_STATUS)
409 fj->status = 1;
410 if (fj->out == NULL)
411 return (xstrdup(""));
412 return (format_expand1(&next, fj->out));
415 /* Remove old jobs. */
416 static void
417 format_job_tidy(struct format_job_tree *jobs, int force)
419 struct format_job *fj, *fj1;
420 time_t now;
422 now = time(NULL);
423 RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) {
424 if (!force && (fj->last > now || now - fj->last < 3600))
425 continue;
426 RB_REMOVE(format_job_tree, jobs, fj);
428 log_debug("%s: %s", __func__, fj->cmd);
430 if (fj->job != NULL)
431 job_free(fj->job);
433 free((void *)fj->expanded);
434 free((void *)fj->cmd);
435 free(fj->out);
437 free(fj);
441 /* Tidy old jobs for all clients. */
442 void
443 format_tidy_jobs(void)
445 struct client *c;
447 format_job_tidy(&format_jobs, 0);
448 TAILQ_FOREACH(c, &clients, entry) {
449 if (c->jobs != NULL)
450 format_job_tidy(c->jobs, 0);
454 /* Remove old jobs for client. */
455 void
456 format_lost_client(struct client *c)
458 if (c->jobs != NULL)
459 format_job_tidy(c->jobs, 1);
460 free(c->jobs);
463 /* Wrapper for asprintf. */
464 static char * printflike(1, 2)
465 format_printf(const char *fmt, ...)
467 va_list ap;
468 char *s;
470 va_start(ap, fmt);
471 xvasprintf(&s, fmt, ap);
472 va_end(ap);
473 return (s);
476 /* Callback for host. */
477 static void *
478 format_cb_host(__unused struct format_tree *ft)
480 char host[HOST_NAME_MAX + 1];
482 if (gethostname(host, sizeof host) != 0)
483 return (xstrdup(""));
484 return (xstrdup(host));
487 /* Callback for host_short. */
488 static void *
489 format_cb_host_short(__unused struct format_tree *ft)
491 char host[HOST_NAME_MAX + 1], *cp;
493 if (gethostname(host, sizeof host) != 0)
494 return (xstrdup(""));
495 if ((cp = strchr(host, '.')) != NULL)
496 *cp = '\0';
497 return (xstrdup(host));
500 /* Callback for pid. */
501 static void *
502 format_cb_pid(__unused struct format_tree *ft)
504 char *value;
506 xasprintf(&value, "%ld", (long)getpid());
507 return (value);
510 /* Callback for session_attached_list. */
511 static void *
512 format_cb_session_attached_list(struct format_tree *ft)
514 struct session *s = ft->s;
515 struct client *loop;
516 struct evbuffer *buffer;
517 int size;
518 char *value = NULL;
520 if (s == NULL)
521 return (NULL);
523 buffer = evbuffer_new();
524 if (buffer == NULL)
525 fatalx("out of memory");
527 TAILQ_FOREACH(loop, &clients, entry) {
528 if (loop->session == s) {
529 if (EVBUFFER_LENGTH(buffer) > 0)
530 evbuffer_add(buffer, ",", 1);
531 evbuffer_add_printf(buffer, "%s", loop->name);
535 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
536 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
537 evbuffer_free(buffer);
538 return (value);
541 /* Callback for session_alerts. */
542 static void *
543 format_cb_session_alerts(struct format_tree *ft)
545 struct session *s = ft->s;
546 struct winlink *wl;
547 char alerts[1024], tmp[16];
549 if (s == NULL)
550 return (NULL);
552 *alerts = '\0';
553 RB_FOREACH(wl, winlinks, &s->windows) {
554 if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
555 continue;
556 xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
558 if (*alerts != '\0')
559 strlcat(alerts, ",", sizeof alerts);
560 strlcat(alerts, tmp, sizeof alerts);
561 if (wl->flags & WINLINK_ACTIVITY)
562 strlcat(alerts, "#", sizeof alerts);
563 if (wl->flags & WINLINK_BELL)
564 strlcat(alerts, "!", sizeof alerts);
565 if (wl->flags & WINLINK_SILENCE)
566 strlcat(alerts, "~", sizeof alerts);
568 return (xstrdup(alerts));
571 /* Callback for session_stack. */
572 static void *
573 format_cb_session_stack(struct format_tree *ft)
575 struct session *s = ft->s;
576 struct winlink *wl;
577 char result[1024], tmp[16];
579 if (s == NULL)
580 return (NULL);
582 xsnprintf(result, sizeof result, "%u", s->curw->idx);
583 TAILQ_FOREACH(wl, &s->lastw, sentry) {
584 xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
586 if (*result != '\0')
587 strlcat(result, ",", sizeof result);
588 strlcat(result, tmp, sizeof result);
590 return (xstrdup(result));
593 /* Callback for window_stack_index. */
594 static void *
595 format_cb_window_stack_index(struct format_tree *ft)
597 struct session *s;
598 struct winlink *wl;
599 u_int idx;
600 char *value = NULL;
602 if (ft->wl == NULL)
603 return (NULL);
604 s = ft->wl->session;
606 idx = 0;
607 TAILQ_FOREACH(wl, &s->lastw, sentry) {
608 idx++;
609 if (wl == ft->wl)
610 break;
612 if (wl == NULL)
613 return (xstrdup("0"));
614 xasprintf(&value, "%u", idx);
615 return (value);
618 /* Callback for window_linked_sessions_list. */
619 static void *
620 format_cb_window_linked_sessions_list(struct format_tree *ft)
622 struct window *w;
623 struct winlink *wl;
624 struct evbuffer *buffer;
625 int size;
626 char *value = NULL;
628 if (ft->wl == NULL)
629 return (NULL);
630 w = ft->wl->window;
632 buffer = evbuffer_new();
633 if (buffer == NULL)
634 fatalx("out of memory");
636 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
637 if (EVBUFFER_LENGTH(buffer) > 0)
638 evbuffer_add(buffer, ",", 1);
639 evbuffer_add_printf(buffer, "%s", wl->session->name);
642 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
643 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
644 evbuffer_free(buffer);
645 return (value);
648 /* Callback for window_active_sessions. */
649 static void *
650 format_cb_window_active_sessions(struct format_tree *ft)
652 struct window *w;
653 struct winlink *wl;
654 u_int n = 0;
655 char *value;
657 if (ft->wl == NULL)
658 return (NULL);
659 w = ft->wl->window;
661 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
662 if (wl->session->curw == wl)
663 n++;
666 xasprintf(&value, "%u", n);
667 return (value);
670 /* Callback for window_active_sessions_list. */
671 static void *
672 format_cb_window_active_sessions_list(struct format_tree *ft)
674 struct window *w;
675 struct winlink *wl;
676 struct evbuffer *buffer;
677 int size;
678 char *value = NULL;
680 if (ft->wl == NULL)
681 return (NULL);
682 w = ft->wl->window;
684 buffer = evbuffer_new();
685 if (buffer == NULL)
686 fatalx("out of memory");
688 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
689 if (wl->session->curw == wl) {
690 if (EVBUFFER_LENGTH(buffer) > 0)
691 evbuffer_add(buffer, ",", 1);
692 evbuffer_add_printf(buffer, "%s", wl->session->name);
696 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
697 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
698 evbuffer_free(buffer);
699 return (value);
702 /* Callback for window_active_clients. */
703 static void *
704 format_cb_window_active_clients(struct format_tree *ft)
706 struct window *w;
707 struct client *loop;
708 struct session *client_session;
709 u_int n = 0;
710 char *value;
712 if (ft->wl == NULL)
713 return (NULL);
714 w = ft->wl->window;
716 TAILQ_FOREACH(loop, &clients, entry) {
717 client_session = loop->session;
718 if (client_session == NULL)
719 continue;
721 if (w == client_session->curw->window)
722 n++;
725 xasprintf(&value, "%u", n);
726 return (value);
729 /* Callback for window_active_clients_list. */
730 static void *
731 format_cb_window_active_clients_list(struct format_tree *ft)
733 struct window *w;
734 struct client *loop;
735 struct session *client_session;
736 struct evbuffer *buffer;
737 int size;
738 char *value = NULL;
740 if (ft->wl == NULL)
741 return (NULL);
742 w = ft->wl->window;
744 buffer = evbuffer_new();
745 if (buffer == NULL)
746 fatalx("out of memory");
748 TAILQ_FOREACH(loop, &clients, entry) {
749 client_session = loop->session;
750 if (client_session == NULL)
751 continue;
753 if (w == client_session->curw->window) {
754 if (EVBUFFER_LENGTH(buffer) > 0)
755 evbuffer_add(buffer, ",", 1);
756 evbuffer_add_printf(buffer, "%s", loop->name);
760 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
761 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
762 evbuffer_free(buffer);
763 return (value);
766 /* Callback for window_layout. */
767 static void *
768 format_cb_window_layout(struct format_tree *ft)
770 struct window *w = ft->w;
772 if (w == NULL)
773 return (NULL);
775 if (w->saved_layout_root != NULL)
776 return (layout_dump(w->saved_layout_root));
777 return (layout_dump(w->layout_root));
780 /* Callback for window_visible_layout. */
781 static void *
782 format_cb_window_visible_layout(struct format_tree *ft)
784 struct window *w = ft->w;
786 if (w == NULL)
787 return (NULL);
789 return (layout_dump(w->layout_root));
792 /* Callback for pane_start_command. */
793 static void *
794 format_cb_start_command(struct format_tree *ft)
796 struct window_pane *wp = ft->wp;
798 if (wp == NULL)
799 return (NULL);
801 return (cmd_stringify_argv(wp->argc, wp->argv));
804 /* Callback for pane_current_command. */
805 static void *
806 format_cb_current_command(struct format_tree *ft)
808 struct window_pane *wp = ft->wp;
809 char *cmd, *value;
811 if (wp == NULL || wp->shell == NULL)
812 return (NULL);
814 cmd = get_proc_name(wp->fd, wp->tty);
815 if (cmd == NULL || *cmd == '\0') {
816 free(cmd);
817 cmd = cmd_stringify_argv(wp->argc, wp->argv);
818 if (cmd == NULL || *cmd == '\0') {
819 free(cmd);
820 cmd = xstrdup(wp->shell);
823 value = parse_window_name(cmd);
824 free(cmd);
825 return (value);
828 /* Callback for pane_current_path. */
829 static void *
830 format_cb_current_path(struct format_tree *ft)
832 struct window_pane *wp = ft->wp;
833 char *cwd;
835 if (wp == NULL)
836 return (NULL);
838 cwd = get_proc_cwd(wp->fd);
839 if (cwd == NULL)
840 return (NULL);
841 return (xstrdup(cwd));
844 /* Callback for history_bytes. */
845 static void *
846 format_cb_history_bytes(struct format_tree *ft)
848 struct window_pane *wp = ft->wp;
849 struct grid *gd;
850 struct grid_line *gl;
851 size_t size = 0;
852 u_int i;
853 char *value;
855 if (wp == NULL)
856 return (NULL);
857 gd = wp->base.grid;
859 for (i = 0; i < gd->hsize + gd->sy; i++) {
860 gl = grid_get_line(gd, i);
861 size += gl->cellsize * sizeof *gl->celldata;
862 size += gl->extdsize * sizeof *gl->extddata;
864 size += (gd->hsize + gd->sy) * sizeof *gl;
866 xasprintf(&value, "%zu", size);
867 return (value);
870 /* Callback for history_all_bytes. */
871 static void *
872 format_cb_history_all_bytes(struct format_tree *ft)
874 struct window_pane *wp = ft->wp;
875 struct grid *gd;
876 struct grid_line *gl;
877 u_int i, lines, cells = 0, extended_cells = 0;
878 char *value;
880 if (wp == NULL)
881 return (NULL);
882 gd = wp->base.grid;
884 lines = gd->hsize + gd->sy;
885 for (i = 0; i < lines; i++) {
886 gl = grid_get_line(gd, i);
887 cells += gl->cellsize;
888 extended_cells += gl->extdsize;
891 xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines,
892 lines * sizeof *gl, cells, cells * sizeof *gl->celldata,
893 extended_cells, extended_cells * sizeof *gl->extddata);
894 return (value);
897 /* Callback for pane_tabs. */
898 static void *
899 format_cb_pane_tabs(struct format_tree *ft)
901 struct window_pane *wp = ft->wp;
902 struct evbuffer *buffer;
903 u_int i;
904 int size;
905 char *value = NULL;
907 if (wp == NULL)
908 return (NULL);
910 buffer = evbuffer_new();
911 if (buffer == NULL)
912 fatalx("out of memory");
913 for (i = 0; i < wp->base.grid->sx; i++) {
914 if (!bit_test(wp->base.tabs, i))
915 continue;
917 if (EVBUFFER_LENGTH(buffer) > 0)
918 evbuffer_add(buffer, ",", 1);
919 evbuffer_add_printf(buffer, "%u", i);
921 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
922 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
923 evbuffer_free(buffer);
924 return (value);
927 /* Callback for pane_fg. */
928 static void *
929 format_cb_pane_fg(struct format_tree *ft)
931 struct window_pane *wp = ft->wp;
932 struct grid_cell gc;
934 if (wp == NULL)
935 return (NULL);
937 tty_default_colours(&gc, wp);
938 return (xstrdup(colour_tostring(gc.fg)));
941 /* Callback for pane_bg. */
942 static void *
943 format_cb_pane_bg(struct format_tree *ft)
945 struct window_pane *wp = ft->wp;
946 struct grid_cell gc;
948 if (wp == NULL)
949 return (NULL);
951 tty_default_colours(&gc, wp);
952 return (xstrdup(colour_tostring(gc.bg)));
955 /* Callback for session_group_list. */
956 static void *
957 format_cb_session_group_list(struct format_tree *ft)
959 struct session *s = ft->s;
960 struct session_group *sg;
961 struct session *loop;
962 struct evbuffer *buffer;
963 int size;
964 char *value = NULL;
966 if (s == NULL)
967 return (NULL);
968 sg = session_group_contains(s);
969 if (sg == NULL)
970 return (NULL);
972 buffer = evbuffer_new();
973 if (buffer == NULL)
974 fatalx("out of memory");
976 TAILQ_FOREACH(loop, &sg->sessions, gentry) {
977 if (EVBUFFER_LENGTH(buffer) > 0)
978 evbuffer_add(buffer, ",", 1);
979 evbuffer_add_printf(buffer, "%s", loop->name);
982 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
983 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
984 evbuffer_free(buffer);
985 return (value);
988 /* Callback for session_group_attached_list. */
989 static void *
990 format_cb_session_group_attached_list(struct format_tree *ft)
992 struct session *s = ft->s, *client_session, *session_loop;
993 struct session_group *sg;
994 struct client *loop;
995 struct evbuffer *buffer;
996 int size;
997 char *value = NULL;
999 if (s == NULL)
1000 return (NULL);
1001 sg = session_group_contains(s);
1002 if (sg == NULL)
1003 return (NULL);
1005 buffer = evbuffer_new();
1006 if (buffer == NULL)
1007 fatalx("out of memory");
1009 TAILQ_FOREACH(loop, &clients, entry) {
1010 client_session = loop->session;
1011 if (client_session == NULL)
1012 continue;
1013 TAILQ_FOREACH(session_loop, &sg->sessions, gentry) {
1014 if (session_loop == client_session){
1015 if (EVBUFFER_LENGTH(buffer) > 0)
1016 evbuffer_add(buffer, ",", 1);
1017 evbuffer_add_printf(buffer, "%s", loop->name);
1022 if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1023 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
1024 evbuffer_free(buffer);
1025 return (value);
1028 /* Callback for pane_in_mode. */
1029 static void *
1030 format_cb_pane_in_mode(struct format_tree *ft)
1032 struct window_pane *wp = ft->wp;
1033 u_int n = 0;
1034 struct window_mode_entry *wme;
1035 char *value;
1037 if (wp == NULL)
1038 return (NULL);
1040 TAILQ_FOREACH(wme, &wp->modes, entry)
1041 n++;
1042 xasprintf(&value, "%u", n);
1043 return (value);
1046 /* Callback for pane_at_top. */
1047 static void *
1048 format_cb_pane_at_top(struct format_tree *ft)
1050 struct window_pane *wp = ft->wp;
1051 struct window *w;
1052 int status, flag;
1053 char *value;
1055 if (wp == NULL)
1056 return (NULL);
1057 w = wp->window;
1059 status = options_get_number(w->options, "pane-border-status");
1060 if (status == PANE_STATUS_TOP)
1061 flag = (wp->yoff == 1);
1062 else
1063 flag = (wp->yoff == 0);
1064 xasprintf(&value, "%d", flag);
1065 return (value);
1068 /* Callback for pane_at_bottom. */
1069 static void *
1070 format_cb_pane_at_bottom(struct format_tree *ft)
1072 struct window_pane *wp = ft->wp;
1073 struct window *w;
1074 int status, flag;
1075 char *value;
1077 if (wp == NULL)
1078 return (NULL);
1079 w = wp->window;
1081 status = options_get_number(w->options, "pane-border-status");
1082 if (status == PANE_STATUS_BOTTOM)
1083 flag = (wp->yoff + wp->sy == w->sy - 1);
1084 else
1085 flag = (wp->yoff + wp->sy == w->sy);
1086 xasprintf(&value, "%d", flag);
1087 return (value);
1090 /* Callback for cursor_character. */
1091 static void *
1092 format_cb_cursor_character(struct format_tree *ft)
1094 struct window_pane *wp = ft->wp;
1095 struct grid_cell gc;
1096 char *value = NULL;
1098 if (wp == NULL)
1099 return (NULL);
1101 grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc);
1102 if (~gc.flags & GRID_FLAG_PADDING)
1103 xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data);
1104 return (value);
1107 /* Callback for mouse_word. */
1108 static void *
1109 format_cb_mouse_word(struct format_tree *ft)
1111 struct window_pane *wp;
1112 struct grid *gd;
1113 u_int x, y;
1114 char *s;
1116 if (!ft->m.valid)
1117 return (NULL);
1118 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1119 if (wp == NULL)
1120 return (NULL);
1121 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1122 return (NULL);
1124 if (!TAILQ_EMPTY(&wp->modes)) {
1125 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode ||
1126 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1127 return (s = window_copy_get_word(wp, x, y));
1128 return (NULL);
1130 gd = wp->base.grid;
1131 return (format_grid_word(gd, x, gd->hsize + y));
1134 /* Callback for mouse_line. */
1135 static void *
1136 format_cb_mouse_line(struct format_tree *ft)
1138 struct window_pane *wp;
1139 struct grid *gd;
1140 u_int x, y;
1142 if (!ft->m.valid)
1143 return (NULL);
1144 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1145 if (wp == NULL)
1146 return (NULL);
1147 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1148 return (NULL);
1150 if (!TAILQ_EMPTY(&wp->modes)) {
1151 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode ||
1152 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1153 return (window_copy_get_line(wp, y));
1154 return (NULL);
1156 gd = wp->base.grid;
1157 return (format_grid_line(gd, gd->hsize + y));
1160 /* Callback for alternate_on. */
1161 static void *
1162 format_cb_alternate_on(struct format_tree *ft)
1164 if (ft->wp != NULL) {
1165 if (ft->wp->base.saved_grid != NULL)
1166 return (xstrdup("1"));
1167 return (xstrdup("0"));
1169 return (NULL);
1172 /* Callback for alternate_saved_x. */
1173 static void *
1174 format_cb_alternate_saved_x(struct format_tree *ft)
1176 if (ft->wp != NULL)
1177 return (format_printf("%u", ft->wp->base.saved_cx));
1178 return (NULL);
1181 /* Callback for alternate_saved_y. */
1182 static void *
1183 format_cb_alternate_saved_y(struct format_tree *ft)
1185 if (ft->wp != NULL)
1186 return (format_printf("%u", ft->wp->base.saved_cy));
1187 return (NULL);
1190 /* Callback for buffer_name. */
1191 static void *
1192 format_cb_buffer_name(struct format_tree *ft)
1194 if (ft->pb != NULL)
1195 return (xstrdup(paste_buffer_name(ft->pb)));
1196 return (NULL);
1199 /* Callback for buffer_sample. */
1200 static void *
1201 format_cb_buffer_sample(struct format_tree *ft)
1203 if (ft->pb != NULL)
1204 return (paste_make_sample(ft->pb));
1205 return (NULL);
1208 /* Callback for buffer_size. */
1209 static void *
1210 format_cb_buffer_size(struct format_tree *ft)
1212 size_t size;
1214 if (ft->pb != NULL) {
1215 paste_buffer_data(ft->pb, &size);
1216 return (format_printf("%zu", size));
1218 return (NULL);
1221 /* Callback for client_cell_height. */
1222 static void *
1223 format_cb_client_cell_height(struct format_tree *ft)
1225 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1226 return (format_printf("%u", ft->c->tty.ypixel));
1227 return (NULL);
1230 /* Callback for client_cell_width. */
1231 static void *
1232 format_cb_client_cell_width(struct format_tree *ft)
1234 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1235 return (format_printf("%u", ft->c->tty.xpixel));
1236 return (NULL);
1239 /* Callback for client_control_mode. */
1240 static void *
1241 format_cb_client_control_mode(struct format_tree *ft)
1243 if (ft->c != NULL) {
1244 if (ft->c->flags & CLIENT_CONTROL)
1245 return (xstrdup("1"));
1246 return (xstrdup("0"));
1248 return (NULL);
1251 /* Callback for client_discarded. */
1252 static void *
1253 format_cb_client_discarded(struct format_tree *ft)
1255 if (ft->c != NULL)
1256 return (format_printf("%zu", ft->c->discarded));
1257 return (NULL);
1260 /* Callback for client_flags. */
1261 static void *
1262 format_cb_client_flags(struct format_tree *ft)
1264 if (ft->c != NULL)
1265 return (xstrdup(server_client_get_flags(ft->c)));
1266 return (NULL);
1269 /* Callback for client_height. */
1270 static void *
1271 format_cb_client_height(struct format_tree *ft)
1273 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1274 return (format_printf("%u", ft->c->tty.sy));
1275 return (NULL);
1278 /* Callback for client_key_table. */
1279 static void *
1280 format_cb_client_key_table(struct format_tree *ft)
1282 if (ft->c != NULL)
1283 return (xstrdup(ft->c->keytable->name));
1284 return (NULL);
1287 /* Callback for client_last_session. */
1288 static void *
1289 format_cb_client_last_session(struct format_tree *ft)
1291 if (ft->c != NULL &&
1292 ft->c->last_session != NULL &&
1293 session_alive(ft->c->last_session))
1294 return (xstrdup(ft->c->last_session->name));
1295 return (NULL);
1298 /* Callback for client_name. */
1299 static void *
1300 format_cb_client_name(struct format_tree *ft)
1302 if (ft->c != NULL)
1303 return (xstrdup(ft->c->name));
1304 return (NULL);
1307 /* Callback for client_pid. */
1308 static void *
1309 format_cb_client_pid(struct format_tree *ft)
1311 if (ft->c != NULL)
1312 return (format_printf("%ld", (long)ft->c->pid));
1313 return (NULL);
1316 /* Callback for client_prefix. */
1317 static void *
1318 format_cb_client_prefix(struct format_tree *ft)
1320 const char *name;
1322 if (ft->c != NULL) {
1323 name = server_client_get_key_table(ft->c);
1324 if (strcmp(ft->c->keytable->name, name) == 0)
1325 return (xstrdup("0"));
1326 return (xstrdup("1"));
1328 return (NULL);
1331 /* Callback for client_readonly. */
1332 static void *
1333 format_cb_client_readonly(struct format_tree *ft)
1335 if (ft->c != NULL) {
1336 if (ft->c->flags & CLIENT_READONLY)
1337 return (xstrdup("1"));
1338 return (xstrdup("0"));
1340 return (NULL);
1343 /* Callback for client_session. */
1344 static void *
1345 format_cb_client_session(struct format_tree *ft)
1347 if (ft->c != NULL && ft->c->session != NULL)
1348 return (xstrdup(ft->c->session->name));
1349 return (NULL);
1352 /* Callback for client_termfeatures. */
1353 static void *
1354 format_cb_client_termfeatures(struct format_tree *ft)
1356 if (ft->c != NULL)
1357 return (xstrdup(tty_get_features(ft->c->term_features)));
1358 return (NULL);
1361 /* Callback for client_termname. */
1362 static void *
1363 format_cb_client_termname(struct format_tree *ft)
1365 if (ft->c != NULL)
1366 return (xstrdup(ft->c->term_name));
1367 return (NULL);
1370 /* Callback for client_termtype. */
1371 static void *
1372 format_cb_client_termtype(struct format_tree *ft)
1374 if (ft->c != NULL) {
1375 if (ft->c->term_type == NULL)
1376 return (xstrdup(""));
1377 return (xstrdup(ft->c->term_type));
1379 return (NULL);
1382 /* Callback for client_tty. */
1383 static void *
1384 format_cb_client_tty(struct format_tree *ft)
1386 if (ft->c != NULL)
1387 return (xstrdup(ft->c->ttyname));
1388 return (NULL);
1391 /* Callback for client_uid. */
1392 static void *
1393 format_cb_client_uid(struct format_tree *ft)
1395 uid_t uid;
1397 if (ft->c != NULL) {
1398 uid = proc_get_peer_uid(ft->c->peer);
1399 if (uid != (uid_t)-1)
1400 return (format_printf("%ld", (long)uid));
1402 return (NULL);
1405 /* Callback for client_user. */
1406 static void *
1407 format_cb_client_user(struct format_tree *ft)
1409 uid_t uid;
1410 struct passwd *pw;
1412 if (ft->c != NULL) {
1413 uid = proc_get_peer_uid(ft->c->peer);
1414 if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL)
1415 return (xstrdup(pw->pw_name));
1417 return (NULL);
1420 /* Callback for client_utf8. */
1421 static void *
1422 format_cb_client_utf8(struct format_tree *ft)
1424 if (ft->c != NULL) {
1425 if (ft->c->flags & CLIENT_UTF8)
1426 return (xstrdup("1"));
1427 return (xstrdup("0"));
1429 return (NULL);
1432 /* Callback for client_width. */
1433 static void *
1434 format_cb_client_width(struct format_tree *ft)
1436 if (ft->c != NULL)
1437 return (format_printf("%u", ft->c->tty.sx));
1438 return (NULL);
1441 /* Callback for client_written. */
1442 static void *
1443 format_cb_client_written(struct format_tree *ft)
1445 if (ft->c != NULL)
1446 return (format_printf("%zu", ft->c->written));
1447 return (NULL);
1450 /* Callback for config_files. */
1451 static void *
1452 format_cb_config_files(__unused struct format_tree *ft)
1454 char *s = NULL;
1455 size_t slen = 0;
1456 u_int i;
1457 size_t n;
1459 for (i = 0; i < cfg_nfiles; i++) {
1460 n = strlen(cfg_files[i]) + 1;
1461 s = xrealloc(s, slen + n + 1);
1462 slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]);
1464 if (s == NULL)
1465 return (xstrdup(""));
1466 s[slen - 1] = '\0';
1467 return (s);
1470 /* Callback for cursor_flag. */
1471 static void *
1472 format_cb_cursor_flag(struct format_tree *ft)
1474 if (ft->wp != NULL) {
1475 if (ft->wp->base.mode & MODE_CURSOR)
1476 return (xstrdup("1"));
1477 return (xstrdup("0"));
1479 return (NULL);
1482 /* Callback for cursor_x. */
1483 static void *
1484 format_cb_cursor_x(struct format_tree *ft)
1486 if (ft->wp != NULL)
1487 return (format_printf("%u", ft->wp->base.cx));
1488 return (NULL);
1491 /* Callback for cursor_y. */
1492 static void *
1493 format_cb_cursor_y(struct format_tree *ft)
1495 if (ft->wp != NULL)
1496 return (format_printf("%u", ft->wp->base.cy));
1497 return (NULL);
1500 /* Callback for history_limit. */
1501 static void *
1502 format_cb_history_limit(struct format_tree *ft)
1504 if (ft->wp != NULL)
1505 return (format_printf("%u", ft->wp->base.grid->hlimit));
1506 return (NULL);
1509 /* Callback for history_size. */
1510 static void *
1511 format_cb_history_size(struct format_tree *ft)
1513 if (ft->wp != NULL)
1514 return (format_printf("%u", ft->wp->base.grid->hsize));
1515 return (NULL);
1518 /* Callback for insert_flag. */
1519 static void *
1520 format_cb_insert_flag(struct format_tree *ft)
1522 if (ft->wp != NULL) {
1523 if (ft->wp->base.mode & MODE_INSERT)
1524 return (xstrdup("1"));
1525 return (xstrdup("0"));
1527 return (NULL);
1530 /* Callback for keypad_cursor_flag. */
1531 static void *
1532 format_cb_keypad_cursor_flag(struct format_tree *ft)
1534 if (ft->wp != NULL) {
1535 if (ft->wp->base.mode & MODE_KCURSOR)
1536 return (xstrdup("1"));
1537 return (xstrdup("0"));
1539 return (NULL);
1542 /* Callback for keypad_flag. */
1543 static void *
1544 format_cb_keypad_flag(struct format_tree *ft)
1546 if (ft->wp != NULL) {
1547 if (ft->wp->base.mode & MODE_KKEYPAD)
1548 return (xstrdup("1"));
1549 return (xstrdup("0"));
1551 return (NULL);
1554 /* Callback for mouse_all_flag. */
1555 static void *
1556 format_cb_mouse_all_flag(struct format_tree *ft)
1558 if (ft->wp != NULL) {
1559 if (ft->wp->base.mode & MODE_MOUSE_ALL)
1560 return (xstrdup("1"));
1561 return (xstrdup("0"));
1563 return (NULL);
1566 /* Callback for mouse_any_flag. */
1567 static void *
1568 format_cb_mouse_any_flag(struct format_tree *ft)
1570 if (ft->wp != NULL) {
1571 if (ft->wp->base.mode & ALL_MOUSE_MODES)
1572 return (xstrdup("1"));
1573 return (xstrdup("0"));
1575 return (NULL);
1578 /* Callback for mouse_button_flag. */
1579 static void *
1580 format_cb_mouse_button_flag(struct format_tree *ft)
1582 if (ft->wp != NULL) {
1583 if (ft->wp->base.mode & MODE_MOUSE_BUTTON)
1584 return (xstrdup("1"));
1585 return (xstrdup("0"));
1587 return (NULL);
1590 /* Callback for mouse_pane. */
1591 static void *
1592 format_cb_mouse_pane(struct format_tree *ft)
1594 struct window_pane *wp;
1596 if (ft->m.valid) {
1597 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1598 if (wp != NULL)
1599 return (format_printf("%%%u", wp->id));
1600 return (NULL);
1602 return (NULL);
1605 /* Callback for mouse_sgr_flag. */
1606 static void *
1607 format_cb_mouse_sgr_flag(struct format_tree *ft)
1609 if (ft->wp != NULL) {
1610 if (ft->wp->base.mode & MODE_MOUSE_SGR)
1611 return (xstrdup("1"));
1612 return (xstrdup("0"));
1614 return (NULL);
1617 /* Callback for mouse_standard_flag. */
1618 static void *
1619 format_cb_mouse_standard_flag(struct format_tree *ft)
1621 if (ft->wp != NULL) {
1622 if (ft->wp->base.mode & MODE_MOUSE_STANDARD)
1623 return (xstrdup("1"));
1624 return (xstrdup("0"));
1626 return (NULL);
1629 /* Callback for mouse_utf8_flag. */
1630 static void *
1631 format_cb_mouse_utf8_flag(struct format_tree *ft)
1633 if (ft->wp != NULL) {
1634 if (ft->wp->base.mode & MODE_MOUSE_UTF8)
1635 return (xstrdup("1"));
1636 return (xstrdup("0"));
1638 return (NULL);
1641 /* Callback for mouse_x. */
1642 static void *
1643 format_cb_mouse_x(struct format_tree *ft)
1645 struct window_pane *wp;
1646 u_int x, y;
1648 if (!ft->m.valid)
1649 return (NULL);
1650 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1651 if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1652 return (format_printf("%u", x));
1653 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1654 if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1655 return (format_printf("%u", ft->m.x));
1656 if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1657 return (format_printf("%u", ft->m.x));
1659 return (NULL);
1662 /* Callback for mouse_y. */
1663 static void *
1664 format_cb_mouse_y(struct format_tree *ft)
1666 struct window_pane *wp;
1667 u_int x, y;
1669 if (!ft->m.valid)
1670 return (NULL);
1671 wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1672 if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1673 return (format_printf("%u", y));
1674 if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1675 if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1676 return (format_printf("%u", ft->m.y));
1677 if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1678 return (format_printf("%u", ft->m.y - ft->m.statusat));
1680 return (NULL);
1683 /* Callback for next_session_id. */
1684 static void *
1685 format_cb_next_session_id(__unused struct format_tree *ft)
1687 return (format_printf("$%u", next_session_id));
1690 /* Callback for origin_flag. */
1691 static void *
1692 format_cb_origin_flag(struct format_tree *ft)
1694 if (ft->wp != NULL) {
1695 if (ft->wp->base.mode & MODE_ORIGIN)
1696 return (xstrdup("1"));
1697 return (xstrdup("0"));
1699 return (NULL);
1702 /* Callback for pane_active. */
1703 static void *
1704 format_cb_pane_active(struct format_tree *ft)
1706 if (ft->wp != NULL) {
1707 if (ft->wp == ft->wp->window->active)
1708 return (xstrdup("1"));
1709 return (xstrdup("0"));
1711 return (NULL);
1714 /* Callback for pane_at_left. */
1715 static void *
1716 format_cb_pane_at_left(struct format_tree *ft)
1718 if (ft->wp != NULL) {
1719 if (ft->wp->xoff == 0)
1720 return (xstrdup("1"));
1721 return (xstrdup("0"));
1723 return (NULL);
1726 /* Callback for pane_at_right. */
1727 static void *
1728 format_cb_pane_at_right(struct format_tree *ft)
1730 if (ft->wp != NULL) {
1731 if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx)
1732 return (xstrdup("1"));
1733 return (xstrdup("0"));
1735 return (NULL);
1738 /* Callback for pane_bottom. */
1739 static void *
1740 format_cb_pane_bottom(struct format_tree *ft)
1742 if (ft->wp != NULL)
1743 return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1));
1744 return (NULL);
1747 /* Callback for pane_dead. */
1748 static void *
1749 format_cb_pane_dead(struct format_tree *ft)
1751 if (ft->wp != NULL) {
1752 if (ft->wp->fd == -1)
1753 return (xstrdup("1"));
1754 return (xstrdup("0"));
1756 return (NULL);
1759 /* Callback for pane_dead_signal. */
1760 static void *
1761 format_cb_pane_dead_signal(struct format_tree *ft)
1763 struct window_pane *wp = ft->wp;
1764 const char *name;
1766 if (wp != NULL) {
1767 if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) {
1768 name = sig2name(WTERMSIG(wp->status));
1769 return (format_printf("%s", name));
1771 return (NULL);
1773 return (NULL);
1776 /* Callback for pane_dead_status. */
1777 static void *
1778 format_cb_pane_dead_status(struct format_tree *ft)
1780 struct window_pane *wp = ft->wp;
1782 if (wp != NULL) {
1783 if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status))
1784 return (format_printf("%d", WEXITSTATUS(wp->status)));
1785 return (NULL);
1787 return (NULL);
1790 /* Callback for pane_dead_time. */
1791 static void *
1792 format_cb_pane_dead_time(struct format_tree *ft)
1794 struct window_pane *wp = ft->wp;
1796 if (wp != NULL) {
1797 if (wp->flags & PANE_STATUSDRAWN)
1798 return (&wp->dead_time);
1799 return (NULL);
1801 return (NULL);
1804 /* Callback for pane_format. */
1805 static void *
1806 format_cb_pane_format(struct format_tree *ft)
1808 if (ft->type == FORMAT_TYPE_PANE)
1809 return (xstrdup("1"));
1810 return (xstrdup("0"));
1813 /* Callback for pane_height. */
1814 static void *
1815 format_cb_pane_height(struct format_tree *ft)
1817 if (ft->wp != NULL)
1818 return (format_printf("%u", ft->wp->sy));
1819 return (NULL);
1822 /* Callback for pane_id. */
1823 static void *
1824 format_cb_pane_id(struct format_tree *ft)
1826 if (ft->wp != NULL)
1827 return (format_printf("%%%u", ft->wp->id));
1828 return (NULL);
1831 /* Callback for pane_index. */
1832 static void *
1833 format_cb_pane_index(struct format_tree *ft)
1835 u_int idx;
1837 if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0)
1838 return (format_printf("%u", idx));
1839 return (NULL);
1842 /* Callback for pane_input_off. */
1843 static void *
1844 format_cb_pane_input_off(struct format_tree *ft)
1846 if (ft->wp != NULL) {
1847 if (ft->wp->flags & PANE_INPUTOFF)
1848 return (xstrdup("1"));
1849 return (xstrdup("0"));
1851 return (NULL);
1854 /* Callback for pane_last. */
1855 static void *
1856 format_cb_pane_last(struct format_tree *ft)
1858 if (ft->wp != NULL) {
1859 if (ft->wp == ft->wp->window->last)
1860 return (xstrdup("1"));
1861 return (xstrdup("0"));
1863 return (NULL);
1866 /* Callback for pane_left. */
1867 static void *
1868 format_cb_pane_left(struct format_tree *ft)
1870 if (ft->wp != NULL)
1871 return (format_printf("%u", ft->wp->xoff));
1872 return (NULL);
1875 /* Callback for pane_marked. */
1876 static void *
1877 format_cb_pane_marked(struct format_tree *ft)
1879 if (ft->wp != NULL) {
1880 if (server_check_marked() && marked_pane.wp == ft->wp)
1881 return (xstrdup("1"));
1882 return (xstrdup("0"));
1884 return (NULL);
1887 /* Callback for pane_marked_set. */
1888 static void *
1889 format_cb_pane_marked_set(struct format_tree *ft)
1891 if (ft->wp != NULL) {
1892 if (server_check_marked())
1893 return (xstrdup("1"));
1894 return (xstrdup("0"));
1896 return (NULL);
1899 /* Callback for pane_mode. */
1900 static void *
1901 format_cb_pane_mode(struct format_tree *ft)
1903 struct window_mode_entry *wme;
1905 if (ft->wp != NULL) {
1906 wme = TAILQ_FIRST(&ft->wp->modes);
1907 if (wme != NULL)
1908 return (xstrdup(wme->mode->name));
1909 return (NULL);
1911 return (NULL);
1914 /* Callback for pane_path. */
1915 static void *
1916 format_cb_pane_path(struct format_tree *ft)
1918 if (ft->wp != NULL) {
1919 if (ft->wp->base.path == NULL)
1920 return (xstrdup(""));
1921 return (xstrdup(ft->wp->base.path));
1923 return (NULL);
1926 /* Callback for pane_pid. */
1927 static void *
1928 format_cb_pane_pid(struct format_tree *ft)
1930 if (ft->wp != NULL)
1931 return (format_printf("%ld", (long)ft->wp->pid));
1932 return (NULL);
1935 /* Callback for pane_pipe. */
1936 static void *
1937 format_cb_pane_pipe(struct format_tree *ft)
1939 if (ft->wp != NULL) {
1940 if (ft->wp->pipe_fd != -1)
1941 return (xstrdup("1"));
1942 return (xstrdup("0"));
1944 return (NULL);
1947 /* Callback for pane_right. */
1948 static void *
1949 format_cb_pane_right(struct format_tree *ft)
1951 if (ft->wp != NULL)
1952 return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1));
1953 return (NULL);
1956 /* Callback for pane_search_string. */
1957 static void *
1958 format_cb_pane_search_string(struct format_tree *ft)
1960 if (ft->wp != NULL) {
1961 if (ft->wp->searchstr == NULL)
1962 return (xstrdup(""));
1963 return (xstrdup(ft->wp->searchstr));
1965 return (NULL);
1968 /* Callback for pane_synchronized. */
1969 static void *
1970 format_cb_pane_synchronized(struct format_tree *ft)
1972 if (ft->wp != NULL) {
1973 if (options_get_number(ft->wp->options, "synchronize-panes"))
1974 return (xstrdup("1"));
1975 return (xstrdup("0"));
1977 return (NULL);
1980 /* Callback for pane_title. */
1981 static void *
1982 format_cb_pane_title(struct format_tree *ft)
1984 if (ft->wp != NULL)
1985 return (xstrdup(ft->wp->base.title));
1986 return (NULL);
1989 /* Callback for pane_top. */
1990 static void *
1991 format_cb_pane_top(struct format_tree *ft)
1993 if (ft->wp != NULL)
1994 return (format_printf("%u", ft->wp->yoff));
1995 return (NULL);
1998 /* Callback for pane_tty. */
1999 static void *
2000 format_cb_pane_tty(struct format_tree *ft)
2002 if (ft->wp != NULL)
2003 return (xstrdup(ft->wp->tty));
2004 return (NULL);
2007 /* Callback for pane_width. */
2008 static void *
2009 format_cb_pane_width(struct format_tree *ft)
2011 if (ft->wp != NULL)
2012 return (format_printf("%u", ft->wp->sx));
2013 return (NULL);
2016 /* Callback for scroll_region_lower. */
2017 static void *
2018 format_cb_scroll_region_lower(struct format_tree *ft)
2020 if (ft->wp != NULL)
2021 return (format_printf("%u", ft->wp->base.rlower));
2022 return (NULL);
2025 /* Callback for scroll_region_upper. */
2026 static void *
2027 format_cb_scroll_region_upper(struct format_tree *ft)
2029 if (ft->wp != NULL)
2030 return (format_printf("%u", ft->wp->base.rupper));
2031 return (NULL);
2034 /* Callback for session_attached. */
2035 static void *
2036 format_cb_session_attached(struct format_tree *ft)
2038 if (ft->s != NULL)
2039 return (format_printf("%u", ft->s->attached));
2040 return (NULL);
2043 /* Callback for session_format. */
2044 static void *
2045 format_cb_session_format(struct format_tree *ft)
2047 if (ft->type == FORMAT_TYPE_SESSION)
2048 return (xstrdup("1"));
2049 return (xstrdup("0"));
2052 /* Callback for session_group. */
2053 static void *
2054 format_cb_session_group(struct format_tree *ft)
2056 struct session_group *sg;
2058 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2059 return (xstrdup(sg->name));
2060 return (NULL);
2063 /* Callback for session_group_attached. */
2064 static void *
2065 format_cb_session_group_attached(struct format_tree *ft)
2067 struct session_group *sg;
2069 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2070 return (format_printf("%u", session_group_attached_count (sg)));
2071 return (NULL);
2074 /* Callback for session_group_many_attached. */
2075 static void *
2076 format_cb_session_group_many_attached(struct format_tree *ft)
2078 struct session_group *sg;
2080 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) {
2081 if (session_group_attached_count (sg) > 1)
2082 return (xstrdup("1"));
2083 return (xstrdup("0"));
2085 return (NULL);
2088 /* Callback for session_group_size. */
2089 static void *
2090 format_cb_session_group_size(struct format_tree *ft)
2092 struct session_group *sg;
2094 if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2095 return (format_printf("%u", session_group_count (sg)));
2096 return (NULL);
2099 /* Callback for session_grouped. */
2100 static void *
2101 format_cb_session_grouped(struct format_tree *ft)
2103 if (ft->s != NULL) {
2104 if (session_group_contains(ft->s) != NULL)
2105 return (xstrdup("1"));
2106 return (xstrdup("0"));
2108 return (NULL);
2111 /* Callback for session_id. */
2112 static void *
2113 format_cb_session_id(struct format_tree *ft)
2115 if (ft->s != NULL)
2116 return (format_printf("$%u", ft->s->id));
2117 return (NULL);
2120 /* Callback for session_many_attached. */
2121 static void *
2122 format_cb_session_many_attached(struct format_tree *ft)
2124 if (ft->s != NULL) {
2125 if (ft->s->attached > 1)
2126 return (xstrdup("1"));
2127 return (xstrdup("0"));
2129 return (NULL);
2132 /* Callback for session_marked. */
2133 static void *
2134 format_cb_session_marked(struct format_tree *ft)
2136 if (ft->s != NULL) {
2137 if (server_check_marked() && marked_pane.s == ft->s)
2138 return (xstrdup("1"));
2139 return (xstrdup("0"));
2141 return (NULL);
2144 /* Callback for session_name. */
2145 static void *
2146 format_cb_session_name(struct format_tree *ft)
2148 if (ft->s != NULL)
2149 return (xstrdup(ft->s->name));
2150 return (NULL);
2153 /* Callback for session_path. */
2154 static void *
2155 format_cb_session_path(struct format_tree *ft)
2157 if (ft->s != NULL)
2158 return (xstrdup(ft->s->cwd));
2159 return (NULL);
2162 /* Callback for session_windows. */
2163 static void *
2164 format_cb_session_windows(struct format_tree *ft)
2166 if (ft->s != NULL)
2167 return (format_printf("%u", winlink_count(&ft->s->windows)));
2168 return (NULL);
2171 /* Callback for socket_path. */
2172 static void *
2173 format_cb_socket_path(__unused struct format_tree *ft)
2175 return (xstrdup(socket_path));
2178 /* Callback for version. */
2179 static void *
2180 format_cb_version(__unused struct format_tree *ft)
2182 return (xstrdup(getversion()));
2185 /* Callback for active_window_index. */
2186 static void *
2187 format_cb_active_window_index(struct format_tree *ft)
2189 if (ft->s != NULL)
2190 return (format_printf("%u", ft->s->curw->idx));
2191 return (NULL);
2194 /* Callback for last_window_index. */
2195 static void *
2196 format_cb_last_window_index(struct format_tree *ft)
2198 struct winlink *wl;
2200 if (ft->s != NULL) {
2201 wl = RB_MAX(winlinks, &ft->s->windows);
2202 return (format_printf("%u", wl->idx));
2204 return (NULL);
2207 /* Callback for window_active. */
2208 static void *
2209 format_cb_window_active(struct format_tree *ft)
2211 if (ft->wl != NULL) {
2212 if (ft->wl == ft->wl->session->curw)
2213 return (xstrdup("1"));
2214 return (xstrdup("0"));
2216 return (NULL);
2219 /* Callback for window_activity_flag. */
2220 static void *
2221 format_cb_window_activity_flag(struct format_tree *ft)
2223 if (ft->wl != NULL) {
2224 if (ft->wl->flags & WINLINK_ACTIVITY)
2225 return (xstrdup("1"));
2226 return (xstrdup("0"));
2228 return (NULL);
2231 /* Callback for window_bell_flag. */
2232 static void *
2233 format_cb_window_bell_flag(struct format_tree *ft)
2235 if (ft->wl != NULL) {
2236 if (ft->wl->flags & WINLINK_BELL)
2237 return (xstrdup("1"));
2238 return (xstrdup("0"));
2240 return (NULL);
2243 /* Callback for window_bigger. */
2244 static void *
2245 format_cb_window_bigger(struct format_tree *ft)
2247 u_int ox, oy, sx, sy;
2249 if (ft->c != NULL) {
2250 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2251 return (xstrdup("1"));
2252 return (xstrdup("0"));
2254 return (NULL);
2257 /* Callback for window_cell_height. */
2258 static void *
2259 format_cb_window_cell_height(struct format_tree *ft)
2261 if (ft->w != NULL)
2262 return (format_printf("%u", ft->w->ypixel));
2263 return (NULL);
2266 /* Callback for window_cell_width. */
2267 static void *
2268 format_cb_window_cell_width(struct format_tree *ft)
2270 if (ft->w != NULL)
2271 return (format_printf("%u", ft->w->xpixel));
2272 return (NULL);
2275 /* Callback for window_end_flag. */
2276 static void *
2277 format_cb_window_end_flag(struct format_tree *ft)
2279 if (ft->wl != NULL) {
2280 if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows))
2281 return (xstrdup("1"));
2282 return (xstrdup("0"));
2284 return (NULL);
2287 /* Callback for window_flags. */
2288 static void *
2289 format_cb_window_flags(struct format_tree *ft)
2291 if (ft->wl != NULL)
2292 return (xstrdup(window_printable_flags(ft->wl, 1)));
2293 return (NULL);
2296 /* Callback for window_format. */
2297 static void *
2298 format_cb_window_format(struct format_tree *ft)
2300 if (ft->type == FORMAT_TYPE_WINDOW)
2301 return (xstrdup("1"));
2302 return (xstrdup("0"));
2305 /* Callback for window_height. */
2306 static void *
2307 format_cb_window_height(struct format_tree *ft)
2309 if (ft->w != NULL)
2310 return (format_printf("%u", ft->w->sy));
2311 return (NULL);
2314 /* Callback for window_id. */
2315 static void *
2316 format_cb_window_id(struct format_tree *ft)
2318 if (ft->w != NULL)
2319 return (format_printf("@%u", ft->w->id));
2320 return (NULL);
2323 /* Callback for window_index. */
2324 static void *
2325 format_cb_window_index(struct format_tree *ft)
2327 if (ft->wl != NULL)
2328 return (format_printf("%d", ft->wl->idx));
2329 return (NULL);
2332 /* Callback for window_last_flag. */
2333 static void *
2334 format_cb_window_last_flag(struct format_tree *ft)
2336 if (ft->wl != NULL) {
2337 if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw))
2338 return (xstrdup("1"));
2339 return (xstrdup("0"));
2341 return (NULL);
2344 /* Callback for window_linked. */
2345 static void *
2346 format_cb_window_linked(struct format_tree *ft)
2348 if (ft->wl != NULL) {
2349 if (session_is_linked(ft->wl->session, ft->wl->window))
2350 return (xstrdup("1"));
2351 return (xstrdup("0"));
2353 return (NULL);
2356 /* Callback for window_linked_sessions. */
2357 static void *
2358 format_cb_window_linked_sessions(struct format_tree *ft)
2360 if (ft->wl != NULL)
2361 return (format_printf("%u", ft->wl->window->references));
2362 return (NULL);
2365 /* Callback for window_marked_flag. */
2366 static void *
2367 format_cb_window_marked_flag(struct format_tree *ft)
2369 if (ft->wl != NULL) {
2370 if (server_check_marked() && marked_pane.wl == ft->wl)
2371 return (xstrdup("1"));
2372 return (xstrdup("0"));
2374 return (NULL);
2377 /* Callback for window_name. */
2378 static void *
2379 format_cb_window_name(struct format_tree *ft)
2381 if (ft->w != NULL)
2382 return (format_printf("%s", ft->w->name));
2383 return (NULL);
2386 /* Callback for window_offset_x. */
2387 static void *
2388 format_cb_window_offset_x(struct format_tree *ft)
2390 u_int ox, oy, sx, sy;
2392 if (ft->c != NULL) {
2393 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2394 return (format_printf("%u", ox));
2395 return (NULL);
2397 return (NULL);
2400 /* Callback for window_offset_y. */
2401 static void *
2402 format_cb_window_offset_y(struct format_tree *ft)
2404 u_int ox, oy, sx, sy;
2406 if (ft->c != NULL) {
2407 if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2408 return (format_printf("%u", oy));
2409 return (NULL);
2411 return (NULL);
2414 /* Callback for window_panes. */
2415 static void *
2416 format_cb_window_panes(struct format_tree *ft)
2418 if (ft->w != NULL)
2419 return (format_printf("%u", window_count_panes(ft->w)));
2420 return (NULL);
2423 /* Callback for window_raw_flags. */
2424 static void *
2425 format_cb_window_raw_flags(struct format_tree *ft)
2427 if (ft->wl != NULL)
2428 return (xstrdup(window_printable_flags(ft->wl, 0)));
2429 return (NULL);
2432 /* Callback for window_silence_flag. */
2433 static void *
2434 format_cb_window_silence_flag(struct format_tree *ft)
2436 if (ft->wl != NULL) {
2437 if (ft->wl->flags & WINLINK_SILENCE)
2438 return (xstrdup("1"));
2439 return (xstrdup("0"));
2441 return (NULL);
2444 /* Callback for window_start_flag. */
2445 static void *
2446 format_cb_window_start_flag(struct format_tree *ft)
2448 if (ft->wl != NULL) {
2449 if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows))
2450 return (xstrdup("1"));
2451 return (xstrdup("0"));
2453 return (NULL);
2456 /* Callback for window_width. */
2457 static void *
2458 format_cb_window_width(struct format_tree *ft)
2460 if (ft->w != NULL)
2461 return (format_printf("%u", ft->w->sx));
2462 return (NULL);
2465 /* Callback for window_zoomed_flag. */
2466 static void *
2467 format_cb_window_zoomed_flag(struct format_tree *ft)
2469 if (ft->w != NULL) {
2470 if (ft->w->flags & WINDOW_ZOOMED)
2471 return (xstrdup("1"));
2472 return (xstrdup("0"));
2474 return (NULL);
2477 /* Callback for wrap_flag. */
2478 static void *
2479 format_cb_wrap_flag(struct format_tree *ft)
2481 if (ft->wp != NULL) {
2482 if (ft->wp->base.mode & MODE_WRAP)
2483 return (xstrdup("1"));
2484 return (xstrdup("0"));
2486 return (NULL);
2489 /* Callback for buffer_created. */
2490 static void *
2491 format_cb_buffer_created(struct format_tree *ft)
2493 static struct timeval tv;
2495 if (ft->pb != NULL) {
2496 timerclear(&tv);
2497 tv.tv_sec = paste_buffer_created(ft->pb);
2498 return (&tv);
2500 return (NULL);
2503 /* Callback for client_activity. */
2504 static void *
2505 format_cb_client_activity(struct format_tree *ft)
2507 if (ft->c != NULL)
2508 return (&ft->c->activity_time);
2509 return (NULL);
2512 /* Callback for client_created. */
2513 static void *
2514 format_cb_client_created(struct format_tree *ft)
2516 if (ft->c != NULL)
2517 return (&ft->c->creation_time);
2518 return (NULL);
2521 /* Callback for session_activity. */
2522 static void *
2523 format_cb_session_activity(struct format_tree *ft)
2525 if (ft->s != NULL)
2526 return (&ft->s->activity_time);
2527 return (NULL);
2530 /* Callback for session_created. */
2531 static void *
2532 format_cb_session_created(struct format_tree *ft)
2534 if (ft->s != NULL)
2535 return (&ft->s->creation_time);
2536 return (NULL);
2539 /* Callback for session_last_attached. */
2540 static void *
2541 format_cb_session_last_attached(struct format_tree *ft)
2543 if (ft->s != NULL)
2544 return (&ft->s->last_attached_time);
2545 return (NULL);
2548 /* Callback for start_time. */
2549 static void *
2550 format_cb_start_time(__unused struct format_tree *ft)
2552 return (&start_time);
2555 /* Callback for window_activity. */
2556 static void *
2557 format_cb_window_activity(struct format_tree *ft)
2559 if (ft->w != NULL)
2560 return (&ft->w->activity_time);
2561 return (NULL);
2564 /* Callback for buffer_mode_format, */
2565 static void *
2566 format_cb_buffer_mode_format(__unused struct format_tree *ft)
2568 return (xstrdup(window_buffer_mode.default_format));
2571 /* Callback for client_mode_format, */
2572 static void *
2573 format_cb_client_mode_format(__unused struct format_tree *ft)
2575 return (xstrdup(window_client_mode.default_format));
2578 /* Callback for tree_mode_format, */
2579 static void *
2580 format_cb_tree_mode_format(__unused struct format_tree *ft)
2582 return (xstrdup(window_tree_mode.default_format));
2585 /* Callback for uid. */
2586 static void *
2587 format_cb_uid(__unused struct format_tree *ft)
2589 return (format_printf("%ld", (long)getuid()));
2592 /* Callback for user. */
2593 static void *
2594 format_cb_user(__unused struct format_tree *ft)
2596 struct passwd *pw;
2598 if ((pw = getpwuid(getuid())) != NULL)
2599 return (xstrdup(pw->pw_name));
2600 return NULL;
2603 /* Format table type. */
2604 enum format_table_type {
2605 FORMAT_TABLE_STRING,
2606 FORMAT_TABLE_TIME
2609 /* Format table entry. */
2610 struct format_table_entry {
2611 const char *key;
2612 enum format_table_type type;
2613 format_cb cb;
2617 * Format table. Default format variables (that are almost always in the tree
2618 * and where the value is expanded by a callback in this file) are listed here.
2619 * Only variables which are added by the caller go into the tree.
2621 static const struct format_table_entry format_table[] = {
2622 { "active_window_index", FORMAT_TABLE_STRING,
2623 format_cb_active_window_index
2625 { "alternate_on", FORMAT_TABLE_STRING,
2626 format_cb_alternate_on
2628 { "alternate_saved_x", FORMAT_TABLE_STRING,
2629 format_cb_alternate_saved_x
2631 { "alternate_saved_y", FORMAT_TABLE_STRING,
2632 format_cb_alternate_saved_y
2634 { "buffer_created", FORMAT_TABLE_TIME,
2635 format_cb_buffer_created
2637 { "buffer_mode_format", FORMAT_TABLE_STRING,
2638 format_cb_buffer_mode_format
2640 { "buffer_name", FORMAT_TABLE_STRING,
2641 format_cb_buffer_name
2643 { "buffer_sample", FORMAT_TABLE_STRING,
2644 format_cb_buffer_sample
2646 { "buffer_size", FORMAT_TABLE_STRING,
2647 format_cb_buffer_size
2649 { "client_activity", FORMAT_TABLE_TIME,
2650 format_cb_client_activity
2652 { "client_cell_height", FORMAT_TABLE_STRING,
2653 format_cb_client_cell_height
2655 { "client_cell_width", FORMAT_TABLE_STRING,
2656 format_cb_client_cell_width
2658 { "client_control_mode", FORMAT_TABLE_STRING,
2659 format_cb_client_control_mode
2661 { "client_created", FORMAT_TABLE_TIME,
2662 format_cb_client_created
2664 { "client_discarded", FORMAT_TABLE_STRING,
2665 format_cb_client_discarded
2667 { "client_flags", FORMAT_TABLE_STRING,
2668 format_cb_client_flags
2670 { "client_height", FORMAT_TABLE_STRING,
2671 format_cb_client_height
2673 { "client_key_table", FORMAT_TABLE_STRING,
2674 format_cb_client_key_table
2676 { "client_last_session", FORMAT_TABLE_STRING,
2677 format_cb_client_last_session
2679 { "client_mode_format", FORMAT_TABLE_STRING,
2680 format_cb_client_mode_format
2682 { "client_name", FORMAT_TABLE_STRING,
2683 format_cb_client_name
2685 { "client_pid", FORMAT_TABLE_STRING,
2686 format_cb_client_pid
2688 { "client_prefix", FORMAT_TABLE_STRING,
2689 format_cb_client_prefix
2691 { "client_readonly", FORMAT_TABLE_STRING,
2692 format_cb_client_readonly
2694 { "client_session", FORMAT_TABLE_STRING,
2695 format_cb_client_session
2697 { "client_termfeatures", FORMAT_TABLE_STRING,
2698 format_cb_client_termfeatures
2700 { "client_termname", FORMAT_TABLE_STRING,
2701 format_cb_client_termname
2703 { "client_termtype", FORMAT_TABLE_STRING,
2704 format_cb_client_termtype
2706 { "client_tty", FORMAT_TABLE_STRING,
2707 format_cb_client_tty
2709 { "client_uid", FORMAT_TABLE_STRING,
2710 format_cb_client_uid
2712 { "client_user", FORMAT_TABLE_STRING,
2713 format_cb_client_user
2715 { "client_utf8", FORMAT_TABLE_STRING,
2716 format_cb_client_utf8
2718 { "client_width", FORMAT_TABLE_STRING,
2719 format_cb_client_width
2721 { "client_written", FORMAT_TABLE_STRING,
2722 format_cb_client_written
2724 { "config_files", FORMAT_TABLE_STRING,
2725 format_cb_config_files
2727 { "cursor_character", FORMAT_TABLE_STRING,
2728 format_cb_cursor_character
2730 { "cursor_flag", FORMAT_TABLE_STRING,
2731 format_cb_cursor_flag
2733 { "cursor_x", FORMAT_TABLE_STRING,
2734 format_cb_cursor_x
2736 { "cursor_y", FORMAT_TABLE_STRING,
2737 format_cb_cursor_y
2739 { "history_all_bytes", FORMAT_TABLE_STRING,
2740 format_cb_history_all_bytes
2742 { "history_bytes", FORMAT_TABLE_STRING,
2743 format_cb_history_bytes
2745 { "history_limit", FORMAT_TABLE_STRING,
2746 format_cb_history_limit
2748 { "history_size", FORMAT_TABLE_STRING,
2749 format_cb_history_size
2751 { "host", FORMAT_TABLE_STRING,
2752 format_cb_host
2754 { "host_short", FORMAT_TABLE_STRING,
2755 format_cb_host_short
2757 { "insert_flag", FORMAT_TABLE_STRING,
2758 format_cb_insert_flag
2760 { "keypad_cursor_flag", FORMAT_TABLE_STRING,
2761 format_cb_keypad_cursor_flag
2763 { "keypad_flag", FORMAT_TABLE_STRING,
2764 format_cb_keypad_flag
2766 { "last_window_index", FORMAT_TABLE_STRING,
2767 format_cb_last_window_index
2769 { "mouse_all_flag", FORMAT_TABLE_STRING,
2770 format_cb_mouse_all_flag
2772 { "mouse_any_flag", FORMAT_TABLE_STRING,
2773 format_cb_mouse_any_flag
2775 { "mouse_button_flag", FORMAT_TABLE_STRING,
2776 format_cb_mouse_button_flag
2778 { "mouse_line", FORMAT_TABLE_STRING,
2779 format_cb_mouse_line
2781 { "mouse_pane", FORMAT_TABLE_STRING,
2782 format_cb_mouse_pane
2784 { "mouse_sgr_flag", FORMAT_TABLE_STRING,
2785 format_cb_mouse_sgr_flag
2787 { "mouse_standard_flag", FORMAT_TABLE_STRING,
2788 format_cb_mouse_standard_flag
2790 { "mouse_utf8_flag", FORMAT_TABLE_STRING,
2791 format_cb_mouse_utf8_flag
2793 { "mouse_word", FORMAT_TABLE_STRING,
2794 format_cb_mouse_word
2796 { "mouse_x", FORMAT_TABLE_STRING,
2797 format_cb_mouse_x
2799 { "mouse_y", FORMAT_TABLE_STRING,
2800 format_cb_mouse_y
2802 { "next_session_id", FORMAT_TABLE_STRING,
2803 format_cb_next_session_id
2805 { "origin_flag", FORMAT_TABLE_STRING,
2806 format_cb_origin_flag
2808 { "pane_active", FORMAT_TABLE_STRING,
2809 format_cb_pane_active
2811 { "pane_at_bottom", FORMAT_TABLE_STRING,
2812 format_cb_pane_at_bottom
2814 { "pane_at_left", FORMAT_TABLE_STRING,
2815 format_cb_pane_at_left
2817 { "pane_at_right", FORMAT_TABLE_STRING,
2818 format_cb_pane_at_right
2820 { "pane_at_top", FORMAT_TABLE_STRING,
2821 format_cb_pane_at_top
2823 { "pane_bg", FORMAT_TABLE_STRING,
2824 format_cb_pane_bg
2826 { "pane_bottom", FORMAT_TABLE_STRING,
2827 format_cb_pane_bottom
2829 { "pane_current_command", FORMAT_TABLE_STRING,
2830 format_cb_current_command
2832 { "pane_current_path", FORMAT_TABLE_STRING,
2833 format_cb_current_path
2835 { "pane_dead", FORMAT_TABLE_STRING,
2836 format_cb_pane_dead
2838 { "pane_dead_signal", FORMAT_TABLE_STRING,
2839 format_cb_pane_dead_signal
2841 { "pane_dead_status", FORMAT_TABLE_STRING,
2842 format_cb_pane_dead_status
2844 { "pane_dead_time", FORMAT_TABLE_TIME,
2845 format_cb_pane_dead_time
2847 { "pane_fg", FORMAT_TABLE_STRING,
2848 format_cb_pane_fg
2850 { "pane_format", FORMAT_TABLE_STRING,
2851 format_cb_pane_format
2853 { "pane_height", FORMAT_TABLE_STRING,
2854 format_cb_pane_height
2856 { "pane_id", FORMAT_TABLE_STRING,
2857 format_cb_pane_id
2859 { "pane_in_mode", FORMAT_TABLE_STRING,
2860 format_cb_pane_in_mode
2862 { "pane_index", FORMAT_TABLE_STRING,
2863 format_cb_pane_index
2865 { "pane_input_off", FORMAT_TABLE_STRING,
2866 format_cb_pane_input_off
2868 { "pane_last", FORMAT_TABLE_STRING,
2869 format_cb_pane_last
2871 { "pane_left", FORMAT_TABLE_STRING,
2872 format_cb_pane_left
2874 { "pane_marked", FORMAT_TABLE_STRING,
2875 format_cb_pane_marked
2877 { "pane_marked_set", FORMAT_TABLE_STRING,
2878 format_cb_pane_marked_set
2880 { "pane_mode", FORMAT_TABLE_STRING,
2881 format_cb_pane_mode
2883 { "pane_path", FORMAT_TABLE_STRING,
2884 format_cb_pane_path
2886 { "pane_pid", FORMAT_TABLE_STRING,
2887 format_cb_pane_pid
2889 { "pane_pipe", FORMAT_TABLE_STRING,
2890 format_cb_pane_pipe
2892 { "pane_right", FORMAT_TABLE_STRING,
2893 format_cb_pane_right
2895 { "pane_search_string", FORMAT_TABLE_STRING,
2896 format_cb_pane_search_string
2898 { "pane_start_command", FORMAT_TABLE_STRING,
2899 format_cb_start_command
2901 { "pane_synchronized", FORMAT_TABLE_STRING,
2902 format_cb_pane_synchronized
2904 { "pane_tabs", FORMAT_TABLE_STRING,
2905 format_cb_pane_tabs
2907 { "pane_title", FORMAT_TABLE_STRING,
2908 format_cb_pane_title
2910 { "pane_top", FORMAT_TABLE_STRING,
2911 format_cb_pane_top
2913 { "pane_tty", FORMAT_TABLE_STRING,
2914 format_cb_pane_tty
2916 { "pane_width", FORMAT_TABLE_STRING,
2917 format_cb_pane_width
2919 { "pid", FORMAT_TABLE_STRING,
2920 format_cb_pid
2922 { "scroll_region_lower", FORMAT_TABLE_STRING,
2923 format_cb_scroll_region_lower
2925 { "scroll_region_upper", FORMAT_TABLE_STRING,
2926 format_cb_scroll_region_upper
2928 { "session_activity", FORMAT_TABLE_TIME,
2929 format_cb_session_activity
2931 { "session_alerts", FORMAT_TABLE_STRING,
2932 format_cb_session_alerts
2934 { "session_attached", FORMAT_TABLE_STRING,
2935 format_cb_session_attached
2937 { "session_attached_list", FORMAT_TABLE_STRING,
2938 format_cb_session_attached_list
2940 { "session_created", FORMAT_TABLE_TIME,
2941 format_cb_session_created
2943 { "session_format", FORMAT_TABLE_STRING,
2944 format_cb_session_format
2946 { "session_group", FORMAT_TABLE_STRING,
2947 format_cb_session_group
2949 { "session_group_attached", FORMAT_TABLE_STRING,
2950 format_cb_session_group_attached
2952 { "session_group_attached_list", FORMAT_TABLE_STRING,
2953 format_cb_session_group_attached_list
2955 { "session_group_list", FORMAT_TABLE_STRING,
2956 format_cb_session_group_list
2958 { "session_group_many_attached", FORMAT_TABLE_STRING,
2959 format_cb_session_group_many_attached
2961 { "session_group_size", FORMAT_TABLE_STRING,
2962 format_cb_session_group_size
2964 { "session_grouped", FORMAT_TABLE_STRING,
2965 format_cb_session_grouped
2967 { "session_id", FORMAT_TABLE_STRING,
2968 format_cb_session_id
2970 { "session_last_attached", FORMAT_TABLE_TIME,
2971 format_cb_session_last_attached
2973 { "session_many_attached", FORMAT_TABLE_STRING,
2974 format_cb_session_many_attached
2976 { "session_marked", FORMAT_TABLE_STRING,
2977 format_cb_session_marked,
2979 { "session_name", FORMAT_TABLE_STRING,
2980 format_cb_session_name
2982 { "session_path", FORMAT_TABLE_STRING,
2983 format_cb_session_path
2985 { "session_stack", FORMAT_TABLE_STRING,
2986 format_cb_session_stack
2988 { "session_windows", FORMAT_TABLE_STRING,
2989 format_cb_session_windows
2991 { "socket_path", FORMAT_TABLE_STRING,
2992 format_cb_socket_path
2994 { "start_time", FORMAT_TABLE_TIME,
2995 format_cb_start_time
2997 { "tree_mode_format", FORMAT_TABLE_STRING,
2998 format_cb_tree_mode_format
3000 { "uid", FORMAT_TABLE_STRING,
3001 format_cb_uid
3003 { "user", FORMAT_TABLE_STRING,
3004 format_cb_user
3006 { "version", FORMAT_TABLE_STRING,
3007 format_cb_version
3009 { "window_active", FORMAT_TABLE_STRING,
3010 format_cb_window_active
3012 { "window_active_clients", FORMAT_TABLE_STRING,
3013 format_cb_window_active_clients
3015 { "window_active_clients_list", FORMAT_TABLE_STRING,
3016 format_cb_window_active_clients_list
3018 { "window_active_sessions", FORMAT_TABLE_STRING,
3019 format_cb_window_active_sessions
3021 { "window_active_sessions_list", FORMAT_TABLE_STRING,
3022 format_cb_window_active_sessions_list
3024 { "window_activity", FORMAT_TABLE_TIME,
3025 format_cb_window_activity
3027 { "window_activity_flag", FORMAT_TABLE_STRING,
3028 format_cb_window_activity_flag
3030 { "window_bell_flag", FORMAT_TABLE_STRING,
3031 format_cb_window_bell_flag
3033 { "window_bigger", FORMAT_TABLE_STRING,
3034 format_cb_window_bigger
3036 { "window_cell_height", FORMAT_TABLE_STRING,
3037 format_cb_window_cell_height
3039 { "window_cell_width", FORMAT_TABLE_STRING,
3040 format_cb_window_cell_width
3042 { "window_end_flag", FORMAT_TABLE_STRING,
3043 format_cb_window_end_flag
3045 { "window_flags", FORMAT_TABLE_STRING,
3046 format_cb_window_flags
3048 { "window_format", FORMAT_TABLE_STRING,
3049 format_cb_window_format
3051 { "window_height", FORMAT_TABLE_STRING,
3052 format_cb_window_height
3054 { "window_id", FORMAT_TABLE_STRING,
3055 format_cb_window_id
3057 { "window_index", FORMAT_TABLE_STRING,
3058 format_cb_window_index
3060 { "window_last_flag", FORMAT_TABLE_STRING,
3061 format_cb_window_last_flag
3063 { "window_layout", FORMAT_TABLE_STRING,
3064 format_cb_window_layout
3066 { "window_linked", FORMAT_TABLE_STRING,
3067 format_cb_window_linked
3069 { "window_linked_sessions", FORMAT_TABLE_STRING,
3070 format_cb_window_linked_sessions
3072 { "window_linked_sessions_list", FORMAT_TABLE_STRING,
3073 format_cb_window_linked_sessions_list
3075 { "window_marked_flag", FORMAT_TABLE_STRING,
3076 format_cb_window_marked_flag
3078 { "window_name", FORMAT_TABLE_STRING,
3079 format_cb_window_name
3081 { "window_offset_x", FORMAT_TABLE_STRING,
3082 format_cb_window_offset_x
3084 { "window_offset_y", FORMAT_TABLE_STRING,
3085 format_cb_window_offset_y
3087 { "window_panes", FORMAT_TABLE_STRING,
3088 format_cb_window_panes
3090 { "window_raw_flags", FORMAT_TABLE_STRING,
3091 format_cb_window_raw_flags
3093 { "window_silence_flag", FORMAT_TABLE_STRING,
3094 format_cb_window_silence_flag
3096 { "window_stack_index", FORMAT_TABLE_STRING,
3097 format_cb_window_stack_index
3099 { "window_start_flag", FORMAT_TABLE_STRING,
3100 format_cb_window_start_flag
3102 { "window_visible_layout", FORMAT_TABLE_STRING,
3103 format_cb_window_visible_layout
3105 { "window_width", FORMAT_TABLE_STRING,
3106 format_cb_window_width
3108 { "window_zoomed_flag", FORMAT_TABLE_STRING,
3109 format_cb_window_zoomed_flag
3111 { "wrap_flag", FORMAT_TABLE_STRING,
3112 format_cb_wrap_flag
3116 /* Compare format table entries. */
3117 static int
3118 format_table_compare(const void *key0, const void *entry0)
3120 const char *key = key0;
3121 const struct format_table_entry *entry = entry0;
3123 return (strcmp(key, entry->key));
3126 /* Get a format callback. */
3127 static struct format_table_entry *
3128 format_table_get(const char *key)
3130 return (bsearch(key, format_table, nitems(format_table),
3131 sizeof *format_table, format_table_compare));
3134 /* Merge one format tree into another. */
3135 void
3136 format_merge(struct format_tree *ft, struct format_tree *from)
3138 struct format_entry *fe;
3140 RB_FOREACH(fe, format_entry_tree, &from->tree) {
3141 if (fe->value != NULL)
3142 format_add(ft, fe->key, "%s", fe->value);
3146 /* Get format pane. */
3147 struct window_pane *
3148 format_get_pane(struct format_tree *ft)
3150 return (ft->wp);
3153 /* Add item bits to tree. */
3154 static void
3155 format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
3157 struct key_event *event = cmdq_get_event(item);
3158 struct mouse_event *m = &event->m;
3160 cmdq_merge_formats(item, ft);
3161 memcpy(&ft->m, m, sizeof ft->m);
3164 /* Create a new tree. */
3165 struct format_tree *
3166 format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
3168 struct format_tree *ft;
3170 ft = xcalloc(1, sizeof *ft);
3171 RB_INIT(&ft->tree);
3173 if (c != NULL) {
3174 ft->client = c;
3175 ft->client->references++;
3177 ft->item = item;
3179 ft->tag = tag;
3180 ft->flags = flags;
3182 if (item != NULL)
3183 format_create_add_item(ft, item);
3185 return (ft);
3188 /* Free a tree. */
3189 void
3190 format_free(struct format_tree *ft)
3192 struct format_entry *fe, *fe1;
3194 RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
3195 RB_REMOVE(format_entry_tree, &ft->tree, fe);
3196 free(fe->value);
3197 free(fe->key);
3198 free(fe);
3201 if (ft->client != NULL)
3202 server_client_unref(ft->client);
3203 free(ft);
3206 /* Log each format. */
3207 static void
3208 format_log_debug_cb(const char *key, const char *value, void *arg)
3210 const char *prefix = arg;
3212 log_debug("%s: %s=%s", prefix, key, value);
3215 /* Log a format tree. */
3216 void
3217 format_log_debug(struct format_tree *ft, const char *prefix)
3219 format_each(ft, format_log_debug_cb, (void *)prefix);
3222 /* Walk each format. */
3223 void
3224 format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
3225 void *), void *arg)
3227 const struct format_table_entry *fte;
3228 struct format_entry *fe;
3229 u_int i;
3230 char s[64];
3231 void *value;
3232 struct timeval *tv;
3234 for (i = 0; i < nitems(format_table); i++) {
3235 fte = &format_table[i];
3237 value = fte->cb(ft);
3238 if (value == NULL)
3239 continue;
3240 if (fte->type == FORMAT_TABLE_TIME) {
3241 tv = value;
3242 xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec);
3243 cb(fte->key, s, arg);
3244 } else {
3245 cb(fte->key, value, arg);
3246 free(value);
3249 RB_FOREACH(fe, format_entry_tree, &ft->tree) {
3250 if (fe->time != 0) {
3251 xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
3252 cb(fe->key, s, arg);
3253 } else {
3254 if (fe->value == NULL && fe->cb != NULL) {
3255 fe->value = fe->cb(ft);
3256 if (fe->value == NULL)
3257 fe->value = xstrdup("");
3259 cb(fe->key, fe->value, arg);
3264 /* Add a key-value pair. */
3265 void
3266 format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
3268 struct format_entry *fe;
3269 struct format_entry *fe_now;
3270 va_list ap;
3272 fe = xmalloc(sizeof *fe);
3273 fe->key = xstrdup(key);
3275 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3276 if (fe_now != NULL) {
3277 free(fe->key);
3278 free(fe);
3279 free(fe_now->value);
3280 fe = fe_now;
3283 fe->cb = NULL;
3284 fe->time = 0;
3286 va_start(ap, fmt);
3287 xvasprintf(&fe->value, fmt, ap);
3288 va_end(ap);
3291 /* Add a key and time. */
3292 void
3293 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
3295 struct format_entry *fe, *fe_now;
3297 fe = xmalloc(sizeof *fe);
3298 fe->key = xstrdup(key);
3300 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3301 if (fe_now != NULL) {
3302 free(fe->key);
3303 free(fe);
3304 free(fe_now->value);
3305 fe = fe_now;
3308 fe->cb = NULL;
3309 fe->time = tv->tv_sec;
3311 fe->value = NULL;
3314 /* Add a key and function. */
3315 void
3316 format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
3318 struct format_entry *fe;
3319 struct format_entry *fe_now;
3321 fe = xmalloc(sizeof *fe);
3322 fe->key = xstrdup(key);
3324 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3325 if (fe_now != NULL) {
3326 free(fe->key);
3327 free(fe);
3328 free(fe_now->value);
3329 fe = fe_now;
3332 fe->cb = cb;
3333 fe->time = 0;
3335 fe->value = NULL;
3338 /* Quote shell special characters in string. */
3339 static char *
3340 format_quote_shell(const char *s)
3342 const char *cp;
3343 char *out, *at;
3345 at = out = xmalloc(strlen(s) * 2 + 1);
3346 for (cp = s; *cp != '\0'; cp++) {
3347 if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL)
3348 *at++ = '\\';
3349 *at++ = *cp;
3351 *at = '\0';
3352 return (out);
3355 /* Quote #s in string. */
3356 static char *
3357 format_quote_style(const char *s)
3359 const char *cp;
3360 char *out, *at;
3362 at = out = xmalloc(strlen(s) * 2 + 1);
3363 for (cp = s; *cp != '\0'; cp++) {
3364 if (*cp == '#')
3365 *at++ = '#';
3366 *at++ = *cp;
3368 *at = '\0';
3369 return (out);
3372 /* Make a prettier time. */
3373 static char *
3374 format_pretty_time(time_t t)
3376 struct tm now_tm, tm;
3377 time_t now, age;
3378 char s[6];
3380 time(&now);
3381 if (now < t)
3382 now = t;
3383 age = now - t;
3385 localtime_r(&now, &now_tm);
3386 localtime_r(&t, &tm);
3388 /* Last 24 hours. */
3389 if (age < 24 * 3600) {
3390 strftime(s, sizeof s, "%H:%M", &tm);
3391 return (xstrdup(s));
3394 /* This month or last 28 days. */
3395 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) ||
3396 age < 28 * 24 * 3600) {
3397 strftime(s, sizeof s, "%a%d", &tm);
3398 return (xstrdup(s));
3401 /* Last 12 months. */
3402 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) ||
3403 (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) {
3404 strftime(s, sizeof s, "%d%b", &tm);
3405 return (xstrdup(s));
3408 /* Older than that. */
3409 strftime(s, sizeof s, "%h%y", &tm);
3410 return (xstrdup(s));
3413 /* Find a format entry. */
3414 static char *
3415 format_find(struct format_tree *ft, const char *key, int modifiers,
3416 const char *time_format)
3418 struct format_table_entry *fte;
3419 void *value;
3420 struct format_entry *fe, fe_find;
3421 struct environ_entry *envent;
3422 struct options_entry *o;
3423 int idx;
3424 char *found = NULL, *saved, s[512];
3425 const char *errstr;
3426 time_t t = 0;
3427 struct tm tm;
3429 o = options_parse_get(global_options, key, &idx, 0);
3430 if (o == NULL && ft->wp != NULL)
3431 o = options_parse_get(ft->wp->options, key, &idx, 0);
3432 if (o == NULL && ft->w != NULL)
3433 o = options_parse_get(ft->w->options, key, &idx, 0);
3434 if (o == NULL)
3435 o = options_parse_get(global_w_options, key, &idx, 0);
3436 if (o == NULL && ft->s != NULL)
3437 o = options_parse_get(ft->s->options, key, &idx, 0);
3438 if (o == NULL)
3439 o = options_parse_get(global_s_options, key, &idx, 0);
3440 if (o != NULL) {
3441 found = options_to_string(o, idx, 1);
3442 goto found;
3445 fte = format_table_get(key);
3446 if (fte != NULL) {
3447 value = fte->cb(ft);
3448 if (fte->type == FORMAT_TABLE_TIME && value != NULL)
3449 t = ((struct timeval *)value)->tv_sec;
3450 else
3451 found = value;
3452 goto found;
3454 fe_find.key = (char *)key;
3455 fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
3456 if (fe != NULL) {
3457 if (fe->time != 0) {
3458 t = fe->time;
3459 goto found;
3461 if (fe->value == NULL && fe->cb != NULL) {
3462 fe->value = fe->cb(ft);
3463 if (fe->value == NULL)
3464 fe->value = xstrdup("");
3466 found = xstrdup(fe->value);
3467 goto found;
3470 if (~modifiers & FORMAT_TIMESTRING) {
3471 envent = NULL;
3472 if (ft->s != NULL)
3473 envent = environ_find(ft->s->environ, key);
3474 if (envent == NULL)
3475 envent = environ_find(global_environ, key);
3476 if (envent != NULL && envent->value != NULL) {
3477 found = xstrdup(envent->value);
3478 goto found;
3482 return (NULL);
3484 found:
3485 if (modifiers & FORMAT_TIMESTRING) {
3486 if (t == 0 && found != NULL) {
3487 t = strtonum(found, 0, INT64_MAX, &errstr);
3488 if (errstr != NULL)
3489 t = 0;
3490 free(found);
3492 if (t == 0)
3493 return (NULL);
3494 if (modifiers & FORMAT_PRETTY)
3495 found = format_pretty_time(t);
3496 else {
3497 if (time_format != NULL) {
3498 localtime_r(&t, &tm);
3499 strftime(s, sizeof s, time_format, &tm);
3500 } else {
3501 ctime_r(&t, s);
3502 s[strcspn(s, "\n")] = '\0';
3504 found = xstrdup(s);
3506 return (found);
3509 if (t != 0)
3510 xasprintf(&found, "%lld", (long long)t);
3511 else if (found == NULL)
3512 return (NULL);
3513 if (modifiers & FORMAT_BASENAME) {
3514 saved = found;
3515 found = xstrdup(basename(saved));
3516 free(saved);
3518 if (modifiers & FORMAT_DIRNAME) {
3519 saved = found;
3520 found = xstrdup(dirname(saved));
3521 free(saved);
3523 if (modifiers & FORMAT_QUOTE_SHELL) {
3524 saved = found;
3525 found = xstrdup(format_quote_shell(saved));
3526 free(saved);
3528 if (modifiers & FORMAT_QUOTE_STYLE) {
3529 saved = found;
3530 found = xstrdup(format_quote_style(saved));
3531 free(saved);
3533 return (found);
3536 /* Remove escaped characters from string. */
3537 static char *
3538 format_strip(const char *s)
3540 char *out, *cp;
3541 int brackets = 0;
3543 cp = out = xmalloc(strlen(s) + 1);
3544 for (; *s != '\0'; s++) {
3545 if (*s == '#' && s[1] == '{')
3546 brackets++;
3547 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
3548 if (brackets != 0)
3549 *cp++ = *s;
3550 continue;
3552 if (*s == '}')
3553 brackets--;
3554 *cp++ = *s;
3556 *cp = '\0';
3557 return (out);
3560 /* Skip until end. */
3561 const char *
3562 format_skip(const char *s, const char *end)
3564 int brackets = 0;
3566 for (; *s != '\0'; s++) {
3567 if (*s == '#' && s[1] == '{')
3568 brackets++;
3569 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
3570 s++;
3571 continue;
3573 if (*s == '}')
3574 brackets--;
3575 if (strchr(end, *s) != NULL && brackets == 0)
3576 break;
3578 if (*s == '\0')
3579 return (NULL);
3580 return (s);
3583 /* Return left and right alternatives separated by commas. */
3584 static int
3585 format_choose(struct format_expand_state *es, const char *s, char **left,
3586 char **right, int expand)
3588 const char *cp;
3589 char *left0, *right0;
3591 cp = format_skip(s, ",");
3592 if (cp == NULL)
3593 return (-1);
3594 left0 = xstrndup(s, cp - s);
3595 right0 = xstrdup(cp + 1);
3597 if (expand) {
3598 *left = format_expand1(es, left0);
3599 free(left0);
3600 *right = format_expand1(es, right0);
3601 free(right0);
3602 } else {
3603 *left = left0;
3604 *right = right0;
3606 return (0);
3609 /* Is this true? */
3611 format_true(const char *s)
3613 if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
3614 return (1);
3615 return (0);
3618 /* Check if modifier end. */
3619 static int
3620 format_is_end(char c)
3622 return (c == ';' || c == ':');
3625 /* Add to modifier list. */
3626 static void
3627 format_add_modifier(struct format_modifier **list, u_int *count,
3628 const char *c, size_t n, char **argv, int argc)
3630 struct format_modifier *fm;
3632 *list = xreallocarray(*list, (*count) + 1, sizeof **list);
3633 fm = &(*list)[(*count)++];
3635 memcpy(fm->modifier, c, n);
3636 fm->modifier[n] = '\0';
3637 fm->size = n;
3639 fm->argv = argv;
3640 fm->argc = argc;
3643 /* Free modifier list. */
3644 static void
3645 format_free_modifiers(struct format_modifier *list, u_int count)
3647 u_int i;
3649 for (i = 0; i < count; i++)
3650 cmd_free_argv(list[i].argc, list[i].argv);
3651 free(list);
3654 /* Build modifier list. */
3655 static struct format_modifier *
3656 format_build_modifiers(struct format_expand_state *es, const char **s,
3657 u_int *count)
3659 const char *cp = *s, *end;
3660 struct format_modifier *list = NULL;
3661 char c, last[] = "X;:", **argv, *value;
3662 int argc;
3665 * Modifiers are a ; separated list of the forms:
3666 * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,>
3667 * =a
3668 * =/a
3669 * =/a/
3670 * s/a/b/
3671 * s/a/b
3672 * ||,&&,!=,==,<=,>=
3675 *count = 0;
3677 while (*cp != '\0' && *cp != ':') {
3678 /* Skip any separator character. */
3679 if (*cp == ';')
3680 cp++;
3682 /* Check single character modifiers with no arguments. */
3683 if (strchr("labcdnwETSWP<>", cp[0]) != NULL &&
3684 format_is_end(cp[1])) {
3685 format_add_modifier(&list, count, cp, 1, NULL, 0);
3686 cp++;
3687 continue;
3690 /* Then try double character with no arguments. */
3691 if ((memcmp("||", cp, 2) == 0 ||
3692 memcmp("&&", cp, 2) == 0 ||
3693 memcmp("!=", cp, 2) == 0 ||
3694 memcmp("==", cp, 2) == 0 ||
3695 memcmp("<=", cp, 2) == 0 ||
3696 memcmp(">=", cp, 2) == 0) &&
3697 format_is_end(cp[2])) {
3698 format_add_modifier(&list, count, cp, 2, NULL, 0);
3699 cp += 2;
3700 continue;
3703 /* Now try single character with arguments. */
3704 if (strchr("mCNst=peq", cp[0]) == NULL)
3705 break;
3706 c = cp[0];
3708 /* No arguments provided. */
3709 if (format_is_end(cp[1])) {
3710 format_add_modifier(&list, count, cp, 1, NULL, 0);
3711 cp++;
3712 continue;
3714 argv = NULL;
3715 argc = 0;
3717 /* Single argument with no wrapper character. */
3718 if (!ispunct(cp[1]) || cp[1] == '-') {
3719 end = format_skip(cp + 1, ":;");
3720 if (end == NULL)
3721 break;
3723 argv = xcalloc(1, sizeof *argv);
3724 value = xstrndup(cp + 1, end - (cp + 1));
3725 argv[0] = format_expand1(es, value);
3726 free(value);
3727 argc = 1;
3729 format_add_modifier(&list, count, &c, 1, argv, argc);
3730 cp = end;
3731 continue;
3734 /* Multiple arguments with a wrapper character. */
3735 last[0] = cp[1];
3736 cp++;
3737 do {
3738 if (cp[0] == last[0] && format_is_end(cp[1])) {
3739 cp++;
3740 break;
3742 end = format_skip(cp + 1, last);
3743 if (end == NULL)
3744 break;
3745 cp++;
3747 argv = xreallocarray(argv, argc + 1, sizeof *argv);
3748 value = xstrndup(cp, end - cp);
3749 argv[argc++] = format_expand1(es, value);
3750 free(value);
3752 cp = end;
3753 } while (!format_is_end(cp[0]));
3754 format_add_modifier(&list, count, &c, 1, argv, argc);
3756 if (*cp != ':') {
3757 format_free_modifiers(list, *count);
3758 *count = 0;
3759 return (NULL);
3761 *s = cp + 1;
3762 return (list);
3765 /* Match against an fnmatch(3) pattern or regular expression. */
3766 static char *
3767 format_match(struct format_modifier *fm, const char *pattern, const char *text)
3769 const char *s = "";
3770 regex_t r;
3771 int flags = 0;
3773 if (fm->argc >= 1)
3774 s = fm->argv[0];
3775 if (strchr(s, 'r') == NULL) {
3776 if (strchr(s, 'i') != NULL)
3777 flags |= FNM_CASEFOLD;
3778 if (fnmatch(pattern, text, flags) != 0)
3779 return (xstrdup("0"));
3780 } else {
3781 flags = REG_EXTENDED|REG_NOSUB;
3782 if (strchr(s, 'i') != NULL)
3783 flags |= REG_ICASE;
3784 if (regcomp(&r, pattern, flags) != 0)
3785 return (xstrdup("0"));
3786 if (regexec(&r, text, 0, NULL, 0) != 0) {
3787 regfree(&r);
3788 return (xstrdup("0"));
3790 regfree(&r);
3792 return (xstrdup("1"));
3795 /* Perform substitution in string. */
3796 static char *
3797 format_sub(struct format_modifier *fm, const char *text, const char *pattern,
3798 const char *with)
3800 char *value;
3801 int flags = REG_EXTENDED;
3803 if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
3804 flags |= REG_ICASE;
3805 value = regsub(pattern, with, text, flags);
3806 if (value == NULL)
3807 return (xstrdup(text));
3808 return (value);
3811 /* Search inside pane. */
3812 static char *
3813 format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
3815 int ignore = 0, regex = 0;
3816 char *value;
3818 if (fm->argc >= 1) {
3819 if (strchr(fm->argv[0], 'i') != NULL)
3820 ignore = 1;
3821 if (strchr(fm->argv[0], 'r') != NULL)
3822 regex = 1;
3824 xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
3825 return (value);
3828 /* Does session name exist? */
3829 static char *
3830 format_session_name(struct format_expand_state *es, const char *fmt)
3832 char *name;
3833 struct session *s;
3835 name = format_expand1(es, fmt);
3836 RB_FOREACH(s, sessions, &sessions) {
3837 if (strcmp(s->name, name) == 0) {
3838 free(name);
3839 return (xstrdup("1"));
3842 free(name);
3843 return (xstrdup("0"));
3846 /* Loop over sessions. */
3847 static char *
3848 format_loop_sessions(struct format_expand_state *es, const char *fmt)
3850 struct format_tree *ft = es->ft;
3851 struct client *c = ft->client;
3852 struct cmdq_item *item = ft->item;
3853 struct format_tree *nft;
3854 struct format_expand_state next;
3855 char *expanded, *value;
3856 size_t valuelen;
3857 struct session *s;
3859 value = xcalloc(1, 1);
3860 valuelen = 1;
3862 RB_FOREACH(s, sessions, &sessions) {
3863 format_log(es, "session loop: $%u", s->id);
3864 nft = format_create(c, item, FORMAT_NONE, ft->flags);
3865 format_defaults(nft, ft->c, s, NULL, NULL);
3866 format_copy_state(&next, es, 0);
3867 next.ft = nft;
3868 expanded = format_expand1(&next, fmt);
3869 format_free(next.ft);
3871 valuelen += strlen(expanded);
3872 value = xrealloc(value, valuelen);
3874 strlcat(value, expanded, valuelen);
3875 free(expanded);
3878 return (value);
3881 /* Does window name exist? */
3882 static char *
3883 format_window_name(struct format_expand_state *es, const char *fmt)
3885 struct format_tree *ft = es->ft;
3886 char *name;
3887 struct winlink *wl;
3889 if (ft->s == NULL) {
3890 format_log(es, "window name but no session");
3891 return (NULL);
3894 name = format_expand1(es, fmt);
3895 RB_FOREACH(wl, winlinks, &ft->s->windows) {
3896 if (strcmp(wl->window->name, name) == 0) {
3897 free(name);
3898 return (xstrdup("1"));
3901 free(name);
3902 return (xstrdup("0"));
3905 /* Loop over windows. */
3906 static char *
3907 format_loop_windows(struct format_expand_state *es, const char *fmt)
3909 struct format_tree *ft = es->ft;
3910 struct client *c = ft->client;
3911 struct cmdq_item *item = ft->item;
3912 struct format_tree *nft;
3913 struct format_expand_state next;
3914 char *all, *active, *use, *expanded, *value;
3915 size_t valuelen;
3916 struct winlink *wl;
3917 struct window *w;
3919 if (ft->s == NULL) {
3920 format_log(es, "window loop but no session");
3921 return (NULL);
3924 if (format_choose(es, fmt, &all, &active, 0) != 0) {
3925 all = xstrdup(fmt);
3926 active = NULL;
3929 value = xcalloc(1, 1);
3930 valuelen = 1;
3932 RB_FOREACH(wl, winlinks, &ft->s->windows) {
3933 w = wl->window;
3934 format_log(es, "window loop: %u @%u", wl->idx, w->id);
3935 if (active != NULL && wl == ft->s->curw)
3936 use = active;
3937 else
3938 use = all;
3939 nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
3940 format_defaults(nft, ft->c, ft->s, wl, NULL);
3941 format_copy_state(&next, es, 0);
3942 next.ft = nft;
3943 expanded = format_expand1(&next, use);
3944 format_free(nft);
3946 valuelen += strlen(expanded);
3947 value = xrealloc(value, valuelen);
3949 strlcat(value, expanded, valuelen);
3950 free(expanded);
3953 free(active);
3954 free(all);
3956 return (value);
3959 /* Loop over panes. */
3960 static char *
3961 format_loop_panes(struct format_expand_state *es, const char *fmt)
3963 struct format_tree *ft = es->ft;
3964 struct client *c = ft->client;
3965 struct cmdq_item *item = ft->item;
3966 struct format_tree *nft;
3967 struct format_expand_state next;
3968 char *all, *active, *use, *expanded, *value;
3969 size_t valuelen;
3970 struct window_pane *wp;
3972 if (ft->w == NULL) {
3973 format_log(es, "pane loop but no window");
3974 return (NULL);
3977 if (format_choose(es, fmt, &all, &active, 0) != 0) {
3978 all = xstrdup(fmt);
3979 active = NULL;
3982 value = xcalloc(1, 1);
3983 valuelen = 1;
3985 TAILQ_FOREACH(wp, &ft->w->panes, entry) {
3986 format_log(es, "pane loop: %%%u", wp->id);
3987 if (active != NULL && wp == ft->w->active)
3988 use = active;
3989 else
3990 use = all;
3991 nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
3992 format_defaults(nft, ft->c, ft->s, ft->wl, wp);
3993 format_copy_state(&next, es, 0);
3994 next.ft = nft;
3995 expanded = format_expand1(&next, use);
3996 format_free(nft);
3998 valuelen += strlen(expanded);
3999 value = xrealloc(value, valuelen);
4001 strlcat(value, expanded, valuelen);
4002 free(expanded);
4005 free(active);
4006 free(all);
4008 return (value);
4011 static char *
4012 format_replace_expression(struct format_modifier *mexp,
4013 struct format_expand_state *es, const char *copy)
4015 int argc = mexp->argc;
4016 const char *errstr;
4017 char *endch, *value, *left = NULL, *right = NULL;
4018 int use_fp = 0;
4019 u_int prec = 0;
4020 double mleft, mright, result;
4021 enum { ADD,
4022 SUBTRACT,
4023 MULTIPLY,
4024 DIVIDE,
4025 MODULUS,
4026 EQUAL,
4027 NOT_EQUAL,
4028 GREATER_THAN,
4029 GREATER_THAN_EQUAL,
4030 LESS_THAN,
4031 LESS_THAN_EQUAL } operator;
4033 if (strcmp(mexp->argv[0], "+") == 0)
4034 operator = ADD;
4035 else if (strcmp(mexp->argv[0], "-") == 0)
4036 operator = SUBTRACT;
4037 else if (strcmp(mexp->argv[0], "*") == 0)
4038 operator = MULTIPLY;
4039 else if (strcmp(mexp->argv[0], "/") == 0)
4040 operator = DIVIDE;
4041 else if (strcmp(mexp->argv[0], "%") == 0 ||
4042 strcmp(mexp->argv[0], "m") == 0)
4043 operator = MODULUS;
4044 else if (strcmp(mexp->argv[0], "==") == 0)
4045 operator = EQUAL;
4046 else if (strcmp(mexp->argv[0], "!=") == 0)
4047 operator = NOT_EQUAL;
4048 else if (strcmp(mexp->argv[0], ">") == 0)
4049 operator = GREATER_THAN;
4050 else if (strcmp(mexp->argv[0], "<") == 0)
4051 operator = LESS_THAN;
4052 else if (strcmp(mexp->argv[0], ">=") == 0)
4053 operator = GREATER_THAN_EQUAL;
4054 else if (strcmp(mexp->argv[0], "<=") == 0)
4055 operator = LESS_THAN_EQUAL;
4056 else {
4057 format_log(es, "expression has no valid operator: '%s'",
4058 mexp->argv[0]);
4059 goto fail;
4062 /* The second argument may be flags. */
4063 if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) {
4064 use_fp = 1;
4065 prec = 2;
4068 /* The third argument may be precision. */
4069 if (argc >= 3) {
4070 prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr);
4071 if (errstr != NULL) {
4072 format_log(es, "expression precision %s: %s", errstr,
4073 mexp->argv[2]);
4074 goto fail;
4078 if (format_choose(es, copy, &left, &right, 1) != 0) {
4079 format_log(es, "expression syntax error");
4080 goto fail;
4083 mleft = strtod(left, &endch);
4084 if (*endch != '\0') {
4085 format_log(es, "expression left side is invalid: %s", left);
4086 goto fail;
4089 mright = strtod(right, &endch);
4090 if (*endch != '\0') {
4091 format_log(es, "expression right side is invalid: %s", right);
4092 goto fail;
4095 if (!use_fp) {
4096 mleft = (long long)mleft;
4097 mright = (long long)mright;
4099 format_log(es, "expression left side is: %.*f", prec, mleft);
4100 format_log(es, "expression right side is: %.*f", prec, mright);
4102 switch (operator) {
4103 case ADD:
4104 result = mleft + mright;
4105 break;
4106 case SUBTRACT:
4107 result = mleft - mright;
4108 break;
4109 case MULTIPLY:
4110 result = mleft * mright;
4111 break;
4112 case DIVIDE:
4113 result = mleft / mright;
4114 break;
4115 case MODULUS:
4116 result = fmod(mleft, mright);
4117 break;
4118 case EQUAL:
4119 result = fabs(mleft - mright) < 1e-9;
4120 break;
4121 case NOT_EQUAL:
4122 result = fabs(mleft - mright) > 1e-9;
4123 break;
4124 case GREATER_THAN:
4125 result = (mleft > mright);
4126 break;
4127 case GREATER_THAN_EQUAL:
4128 result = (mleft >= mright);
4129 break;
4130 case LESS_THAN:
4131 result = (mleft < mright);
4132 break;
4133 case LESS_THAN_EQUAL:
4134 result = (mleft <= mright);
4135 break;
4137 if (use_fp)
4138 xasprintf(&value, "%.*f", prec, result);
4139 else
4140 xasprintf(&value, "%.*f", prec, (double)(long long)result);
4141 format_log(es, "expression result is %s", value);
4143 free(right);
4144 free(left);
4145 return (value);
4147 fail:
4148 free(right);
4149 free(left);
4150 return (NULL);
4153 /* Replace a key. */
4154 static int
4155 format_replace(struct format_expand_state *es, const char *key, size_t keylen,
4156 char **buf, size_t *len, size_t *off)
4158 struct format_tree *ft = es->ft;
4159 struct window_pane *wp = ft->wp;
4160 const char *errstr, *copy, *cp, *marker = NULL;
4161 const char *time_format = NULL;
4162 char *copy0, *condition, *found, *new;
4163 char *value, *left, *right;
4164 size_t valuelen;
4165 int modifiers = 0, limit = 0, width = 0;
4166 int j, c;
4167 struct format_modifier *list, *cmp = NULL, *search = NULL;
4168 struct format_modifier **sub = NULL, *mexp = NULL, *fm;
4169 u_int i, count, nsub = 0;
4170 struct format_expand_state next;
4172 /* Make a copy of the key. */
4173 copy = copy0 = xstrndup(key, keylen);
4175 /* Process modifier list. */
4176 list = format_build_modifiers(es, &copy, &count);
4177 for (i = 0; i < count; i++) {
4178 fm = &list[i];
4179 if (format_logging(ft)) {
4180 format_log(es, "modifier %u is %s", i, fm->modifier);
4181 for (j = 0; j < fm->argc; j++) {
4182 format_log(es, "modifier %u argument %d: %s", i,
4183 j, fm->argv[j]);
4186 if (fm->size == 1) {
4187 switch (fm->modifier[0]) {
4188 case 'm':
4189 case '<':
4190 case '>':
4191 cmp = fm;
4192 break;
4193 case 'C':
4194 search = fm;
4195 break;
4196 case 's':
4197 if (fm->argc < 2)
4198 break;
4199 sub = xreallocarray(sub, nsub + 1, sizeof *sub);
4200 sub[nsub++] = fm;
4201 break;
4202 case '=':
4203 if (fm->argc < 1)
4204 break;
4205 limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4206 &errstr);
4207 if (errstr != NULL)
4208 limit = 0;
4209 if (fm->argc >= 2 && fm->argv[1] != NULL)
4210 marker = fm->argv[1];
4211 break;
4212 case 'p':
4213 if (fm->argc < 1)
4214 break;
4215 width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4216 &errstr);
4217 if (errstr != NULL)
4218 width = 0;
4219 break;
4220 case 'w':
4221 modifiers |= FORMAT_WIDTH;
4222 break;
4223 case 'e':
4224 if (fm->argc < 1 || fm->argc > 3)
4225 break;
4226 mexp = fm;
4227 break;
4228 case 'l':
4229 modifiers |= FORMAT_LITERAL;
4230 break;
4231 case 'a':
4232 modifiers |= FORMAT_CHARACTER;
4233 break;
4234 case 'b':
4235 modifiers |= FORMAT_BASENAME;
4236 break;
4237 case 'c':
4238 modifiers |= FORMAT_COLOUR;
4239 break;
4240 case 'd':
4241 modifiers |= FORMAT_DIRNAME;
4242 break;
4243 case 'n':
4244 modifiers |= FORMAT_LENGTH;
4245 break;
4246 case 't':
4247 modifiers |= FORMAT_TIMESTRING;
4248 if (fm->argc < 1)
4249 break;
4250 if (strchr(fm->argv[0], 'p') != NULL)
4251 modifiers |= FORMAT_PRETTY;
4252 else if (fm->argc >= 2 &&
4253 strchr(fm->argv[0], 'f') != NULL)
4254 time_format = format_strip(fm->argv[1]);
4255 break;
4256 case 'q':
4257 if (fm->argc < 1)
4258 modifiers |= FORMAT_QUOTE_SHELL;
4259 else if (strchr(fm->argv[0], 'e') != NULL ||
4260 strchr(fm->argv[0], 'h') != NULL)
4261 modifiers |= FORMAT_QUOTE_STYLE;
4262 break;
4263 case 'E':
4264 modifiers |= FORMAT_EXPAND;
4265 break;
4266 case 'T':
4267 modifiers |= FORMAT_EXPANDTIME;
4268 break;
4269 case 'N':
4270 if (fm->argc < 1 ||
4271 strchr(fm->argv[0], 'w') != NULL)
4272 modifiers |= FORMAT_WINDOW_NAME;
4273 else if (strchr(fm->argv[0], 's') != NULL)
4274 modifiers |= FORMAT_SESSION_NAME;
4275 break;
4276 case 'S':
4277 modifiers |= FORMAT_SESSIONS;
4278 break;
4279 case 'W':
4280 modifiers |= FORMAT_WINDOWS;
4281 break;
4282 case 'P':
4283 modifiers |= FORMAT_PANES;
4284 break;
4286 } else if (fm->size == 2) {
4287 if (strcmp(fm->modifier, "||") == 0 ||
4288 strcmp(fm->modifier, "&&") == 0 ||
4289 strcmp(fm->modifier, "==") == 0 ||
4290 strcmp(fm->modifier, "!=") == 0 ||
4291 strcmp(fm->modifier, ">=") == 0 ||
4292 strcmp(fm->modifier, "<=") == 0)
4293 cmp = fm;
4297 /* Is this a literal string? */
4298 if (modifiers & FORMAT_LITERAL) {
4299 value = xstrdup(copy);
4300 goto done;
4303 /* Is this a character? */
4304 if (modifiers & FORMAT_CHARACTER) {
4305 new = format_expand1(es, copy);
4306 c = strtonum(new, 32, 126, &errstr);
4307 if (errstr != NULL)
4308 value = xstrdup("");
4309 else
4310 xasprintf(&value, "%c", c);
4311 free(new);
4312 goto done;
4315 /* Is this a colour? */
4316 if (modifiers & FORMAT_COLOUR) {
4317 new = format_expand1(es, copy);
4318 c = colour_fromstring(new);
4319 if (c == -1 || (c = colour_force_rgb(c)) == -1)
4320 value = xstrdup("");
4321 else
4322 xasprintf(&value, "%06x", c & 0xffffff);
4323 free(new);
4324 goto done;
4327 /* Is this a loop, comparison or condition? */
4328 if (modifiers & FORMAT_SESSIONS) {
4329 value = format_loop_sessions(es, copy);
4330 if (value == NULL)
4331 goto fail;
4332 } else if (modifiers & FORMAT_WINDOWS) {
4333 value = format_loop_windows(es, copy);
4334 if (value == NULL)
4335 goto fail;
4336 } else if (modifiers & FORMAT_PANES) {
4337 value = format_loop_panes(es, copy);
4338 if (value == NULL)
4339 goto fail;
4340 } else if (modifiers & FORMAT_WINDOW_NAME) {
4341 value = format_window_name(es, copy);
4342 if (value == NULL)
4343 goto fail;
4344 } else if (modifiers & FORMAT_SESSION_NAME) {
4345 value = format_session_name(es, copy);
4346 if (value == NULL)
4347 goto fail;
4348 } else if (search != NULL) {
4349 /* Search in pane. */
4350 new = format_expand1(es, copy);
4351 if (wp == NULL) {
4352 format_log(es, "search '%s' but no pane", new);
4353 value = xstrdup("0");
4354 } else {
4355 format_log(es, "search '%s' pane %%%u", new, wp->id);
4356 value = format_search(search, wp, new);
4358 free(new);
4359 } else if (cmp != NULL) {
4360 /* Comparison of left and right. */
4361 if (format_choose(es, copy, &left, &right, 1) != 0) {
4362 format_log(es, "compare %s syntax error: %s",
4363 cmp->modifier, copy);
4364 goto fail;
4366 format_log(es, "compare %s left is: %s", cmp->modifier, left);
4367 format_log(es, "compare %s right is: %s", cmp->modifier, right);
4369 if (strcmp(cmp->modifier, "||") == 0) {
4370 if (format_true(left) || format_true(right))
4371 value = xstrdup("1");
4372 else
4373 value = xstrdup("0");
4374 } else if (strcmp(cmp->modifier, "&&") == 0) {
4375 if (format_true(left) && format_true(right))
4376 value = xstrdup("1");
4377 else
4378 value = xstrdup("0");
4379 } else if (strcmp(cmp->modifier, "==") == 0) {
4380 if (strcmp(left, right) == 0)
4381 value = xstrdup("1");
4382 else
4383 value = xstrdup("0");
4384 } else if (strcmp(cmp->modifier, "!=") == 0) {
4385 if (strcmp(left, right) != 0)
4386 value = xstrdup("1");
4387 else
4388 value = xstrdup("0");
4389 } else if (strcmp(cmp->modifier, "<") == 0) {
4390 if (strcmp(left, right) < 0)
4391 value = xstrdup("1");
4392 else
4393 value = xstrdup("0");
4394 } else if (strcmp(cmp->modifier, ">") == 0) {
4395 if (strcmp(left, right) > 0)
4396 value = xstrdup("1");
4397 else
4398 value = xstrdup("0");
4399 } else if (strcmp(cmp->modifier, "<=") == 0) {
4400 if (strcmp(left, right) <= 0)
4401 value = xstrdup("1");
4402 else
4403 value = xstrdup("0");
4404 } else if (strcmp(cmp->modifier, ">=") == 0) {
4405 if (strcmp(left, right) >= 0)
4406 value = xstrdup("1");
4407 else
4408 value = xstrdup("0");
4409 } else if (strcmp(cmp->modifier, "m") == 0)
4410 value = format_match(cmp, left, right);
4412 free(right);
4413 free(left);
4414 } else if (*copy == '?') {
4415 /* Conditional: check first and choose second or third. */
4416 cp = format_skip(copy + 1, ",");
4417 if (cp == NULL) {
4418 format_log(es, "condition syntax error: %s", copy + 1);
4419 goto fail;
4421 condition = xstrndup(copy + 1, cp - (copy + 1));
4422 format_log(es, "condition is: %s", condition);
4424 found = format_find(ft, condition, modifiers, time_format);
4425 if (found == NULL) {
4427 * If the condition not found, try to expand it. If
4428 * the expansion doesn't have any effect, then assume
4429 * false.
4431 found = format_expand1(es, condition);
4432 if (strcmp(found, condition) == 0) {
4433 free(found);
4434 found = xstrdup("");
4435 format_log(es,
4436 "condition '%s' not found; assuming false",
4437 condition);
4439 } else {
4440 format_log(es, "condition '%s' found: %s", condition,
4441 found);
4444 if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
4445 format_log(es, "condition '%s' syntax error: %s",
4446 condition, cp + 1);
4447 free(found);
4448 goto fail;
4450 if (format_true(found)) {
4451 format_log(es, "condition '%s' is true", condition);
4452 value = format_expand1(es, left);
4453 } else {
4454 format_log(es, "condition '%s' is false", condition);
4455 value = format_expand1(es, right);
4457 free(right);
4458 free(left);
4460 free(condition);
4461 free(found);
4462 } else if (mexp != NULL) {
4463 value = format_replace_expression(mexp, es, copy);
4464 if (value == NULL)
4465 value = xstrdup("");
4466 } else {
4467 if (strstr(copy, "#{") != 0) {
4468 format_log(es, "expanding inner format '%s'", copy);
4469 value = format_expand1(es, copy);
4470 } else {
4471 value = format_find(ft, copy, modifiers, time_format);
4472 if (value == NULL) {
4473 format_log(es, "format '%s' not found", copy);
4474 value = xstrdup("");
4475 } else {
4476 format_log(es, "format '%s' found: %s", copy,
4477 value);
4482 done:
4483 /* Expand again if required. */
4484 if (modifiers & FORMAT_EXPAND) {
4485 new = format_expand1(es, value);
4486 free(value);
4487 value = new;
4488 } else if (modifiers & FORMAT_EXPANDTIME) {
4489 format_copy_state(&next, es, FORMAT_EXPAND_TIME);
4490 new = format_expand1(&next, value);
4491 free(value);
4492 value = new;
4495 /* Perform substitution if any. */
4496 for (i = 0; i < nsub; i++) {
4497 left = format_expand1(es, sub[i]->argv[0]);
4498 right = format_expand1(es, sub[i]->argv[1]);
4499 new = format_sub(sub[i], value, left, right);
4500 format_log(es, "substitute '%s' to '%s': %s", left, right, new);
4501 free(value);
4502 value = new;
4503 free(right);
4504 free(left);
4507 /* Truncate the value if needed. */
4508 if (limit > 0) {
4509 new = format_trim_left(value, limit);
4510 if (marker != NULL && strcmp(new, value) != 0) {
4511 free(value);
4512 xasprintf(&value, "%s%s", new, marker);
4513 } else {
4514 free(value);
4515 value = new;
4517 format_log(es, "applied length limit %d: %s", limit, value);
4518 } else if (limit < 0) {
4519 new = format_trim_right(value, -limit);
4520 if (marker != NULL && strcmp(new, value) != 0) {
4521 free(value);
4522 xasprintf(&value, "%s%s", marker, new);
4523 } else {
4524 free(value);
4525 value = new;
4527 format_log(es, "applied length limit %d: %s", limit, value);
4530 /* Pad the value if needed. */
4531 if (width > 0) {
4532 new = utf8_padcstr(value, width);
4533 free(value);
4534 value = new;
4535 format_log(es, "applied padding width %d: %s", width, value);
4536 } else if (width < 0) {
4537 new = utf8_rpadcstr(value, -width);
4538 free(value);
4539 value = new;
4540 format_log(es, "applied padding width %d: %s", width, value);
4543 /* Replace with the length or width if needed. */
4544 if (modifiers & FORMAT_LENGTH) {
4545 xasprintf(&new, "%zu", strlen(value));
4546 free(value);
4547 value = new;
4548 format_log(es, "replacing with length: %s", new);
4550 if (modifiers & FORMAT_WIDTH) {
4551 xasprintf(&new, "%u", format_width(value));
4552 free(value);
4553 value = new;
4554 format_log(es, "replacing with width: %s", new);
4557 /* Expand the buffer and copy in the value. */
4558 valuelen = strlen(value);
4559 while (*len - *off < valuelen + 1) {
4560 *buf = xreallocarray(*buf, 2, *len);
4561 *len *= 2;
4563 memcpy(*buf + *off, value, valuelen);
4564 *off += valuelen;
4566 format_log(es, "replaced '%s' with '%s'", copy0, value);
4567 free(value);
4569 free(sub);
4570 format_free_modifiers(list, count);
4571 free(copy0);
4572 return (0);
4574 fail:
4575 format_log(es, "failed %s", copy0);
4577 free(sub);
4578 format_free_modifiers(list, count);
4579 free(copy0);
4580 return (-1);
4583 /* Expand keys in a template. */
4584 static char *
4585 format_expand1(struct format_expand_state *es, const char *fmt)
4587 struct format_tree *ft = es->ft;
4588 char *buf, *out, *name;
4589 const char *ptr, *s;
4590 size_t off, len, n, outlen;
4591 int ch, brackets;
4592 char expanded[8192];
4594 if (fmt == NULL || *fmt == '\0')
4595 return (xstrdup(""));
4597 if (es->loop == FORMAT_LOOP_LIMIT) {
4598 format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT);
4599 return (xstrdup(""));
4601 es->loop++;
4603 format_log(es, "expanding format: %s", fmt);
4605 if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) {
4606 if (es->time == 0) {
4607 es->time = time(NULL);
4608 localtime_r(&es->time, &es->tm);
4610 if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) {
4611 format_log(es, "format is too long");
4612 return (xstrdup(""));
4614 if (format_logging(ft) && strcmp(expanded, fmt) != 0)
4615 format_log(es, "after time expanded: %s", expanded);
4616 fmt = expanded;
4619 len = 64;
4620 buf = xmalloc(len);
4621 off = 0;
4623 while (*fmt != '\0') {
4624 if (*fmt != '#') {
4625 while (len - off < 2) {
4626 buf = xreallocarray(buf, 2, len);
4627 len *= 2;
4629 buf[off++] = *fmt++;
4630 continue;
4632 fmt++;
4634 ch = (u_char)*fmt++;
4635 switch (ch) {
4636 case '(':
4637 brackets = 1;
4638 for (ptr = fmt; *ptr != '\0'; ptr++) {
4639 if (*ptr == '(')
4640 brackets++;
4641 if (*ptr == ')' && --brackets == 0)
4642 break;
4644 if (*ptr != ')' || brackets != 0)
4645 break;
4646 n = ptr - fmt;
4648 name = xstrndup(fmt, n);
4649 format_log(es, "found #(): %s", name);
4651 if ((ft->flags & FORMAT_NOJOBS) ||
4652 (es->flags & FORMAT_EXPAND_NOJOBS)) {
4653 out = xstrdup("");
4654 format_log(es, "#() is disabled");
4655 } else {
4656 out = format_job_get(es, name);
4657 format_log(es, "#() result: %s", out);
4659 free(name);
4661 outlen = strlen(out);
4662 while (len - off < outlen + 1) {
4663 buf = xreallocarray(buf, 2, len);
4664 len *= 2;
4666 memcpy(buf + off, out, outlen);
4667 off += outlen;
4669 free(out);
4671 fmt += n + 1;
4672 continue;
4673 case '{':
4674 ptr = format_skip((char *)fmt - 2, "}");
4675 if (ptr == NULL)
4676 break;
4677 n = ptr - fmt;
4679 format_log(es, "found #{}: %.*s", (int)n, fmt);
4680 if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
4681 break;
4682 fmt += n + 1;
4683 continue;
4684 case '#':
4686 * If ##[ (with two or more #s), then it is a style and
4687 * can be left for format_draw to handle.
4689 ptr = fmt;
4690 n = 2;
4691 while (*ptr == '#') {
4692 ptr++;
4693 n++;
4695 if (*ptr == '[') {
4696 format_log(es, "found #*%zu[", n);
4697 while (len - off < n + 2) {
4698 buf = xreallocarray(buf, 2, len);
4699 len *= 2;
4701 memcpy(buf + off, fmt - 2, n + 1);
4702 off += n + 1;
4703 fmt = ptr + 1;
4704 continue;
4706 /* FALLTHROUGH */
4707 case '}':
4708 case ',':
4709 format_log(es, "found #%c", ch);
4710 while (len - off < 2) {
4711 buf = xreallocarray(buf, 2, len);
4712 len *= 2;
4714 buf[off++] = ch;
4715 continue;
4716 default:
4717 s = NULL;
4718 if (ch >= 'A' && ch <= 'Z')
4719 s = format_upper[ch - 'A'];
4720 else if (ch >= 'a' && ch <= 'z')
4721 s = format_lower[ch - 'a'];
4722 if (s == NULL) {
4723 while (len - off < 3) {
4724 buf = xreallocarray(buf, 2, len);
4725 len *= 2;
4727 buf[off++] = '#';
4728 buf[off++] = ch;
4729 continue;
4731 n = strlen(s);
4732 format_log(es, "found #%c: %s", ch, s);
4733 if (format_replace(es, s, n, &buf, &len, &off) != 0)
4734 break;
4735 continue;
4738 break;
4740 buf[off] = '\0';
4742 format_log(es, "result is: %s", buf);
4743 es->loop--;
4745 return (buf);
4748 /* Expand keys in a template, passing through strftime first. */
4749 char *
4750 format_expand_time(struct format_tree *ft, const char *fmt)
4752 struct format_expand_state es;
4754 memset(&es, 0, sizeof es);
4755 es.ft = ft;
4756 es.flags = FORMAT_EXPAND_TIME;
4757 return (format_expand1(&es, fmt));
4760 /* Expand keys in a template. */
4761 char *
4762 format_expand(struct format_tree *ft, const char *fmt)
4764 struct format_expand_state es;
4766 memset(&es, 0, sizeof es);
4767 es.ft = ft;
4768 es.flags = 0;
4769 return (format_expand1(&es, fmt));
4772 /* Expand a single string. */
4773 char *
4774 format_single(struct cmdq_item *item, const char *fmt, struct client *c,
4775 struct session *s, struct winlink *wl, struct window_pane *wp)
4777 struct format_tree *ft;
4778 char *expanded;
4780 ft = format_create_defaults(item, c, s, wl, wp);
4781 expanded = format_expand(ft, fmt);
4782 format_free(ft);
4783 return (expanded);
4786 /* Expand a single string using state. */
4787 char *
4788 format_single_from_state(struct cmdq_item *item, const char *fmt,
4789 struct client *c, struct cmd_find_state *fs)
4791 return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp));
4794 /* Expand a single string using target. */
4795 char *
4796 format_single_from_target(struct cmdq_item *item, const char *fmt)
4798 struct client *tc = cmdq_get_target_client(item);
4800 return (format_single_from_state(item, fmt, tc, cmdq_get_target(item)));
4803 /* Create and add defaults. */
4804 struct format_tree *
4805 format_create_defaults(struct cmdq_item *item, struct client *c,
4806 struct session *s, struct winlink *wl, struct window_pane *wp)
4808 struct format_tree *ft;
4810 if (item != NULL)
4811 ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
4812 else
4813 ft = format_create(NULL, item, FORMAT_NONE, 0);
4814 format_defaults(ft, c, s, wl, wp);
4815 return (ft);
4818 /* Create and add defaults using state. */
4819 struct format_tree *
4820 format_create_from_state(struct cmdq_item *item, struct client *c,
4821 struct cmd_find_state *fs)
4823 return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp));
4826 /* Create and add defaults using target. */
4827 struct format_tree *
4828 format_create_from_target(struct cmdq_item *item)
4830 struct client *tc = cmdq_get_target_client(item);
4832 return (format_create_from_state(item, tc, cmdq_get_target(item)));
4835 /* Set defaults for any of arguments that are not NULL. */
4836 void
4837 format_defaults(struct format_tree *ft, struct client *c, struct session *s,
4838 struct winlink *wl, struct window_pane *wp)
4840 struct paste_buffer *pb;
4842 if (c != NULL && c->name != NULL)
4843 log_debug("%s: c=%s", __func__, c->name);
4844 else
4845 log_debug("%s: c=none", __func__);
4846 if (s != NULL)
4847 log_debug("%s: s=$%u", __func__, s->id);
4848 else
4849 log_debug("%s: s=none", __func__);
4850 if (wl != NULL)
4851 log_debug("%s: wl=%u", __func__, wl->idx);
4852 else
4853 log_debug("%s: wl=none", __func__);
4854 if (wp != NULL)
4855 log_debug("%s: wp=%%%u", __func__, wp->id);
4856 else
4857 log_debug("%s: wp=none", __func__);
4859 if (c != NULL && s != NULL && c->session != s)
4860 log_debug("%s: session does not match", __func__);
4862 if (wp != NULL)
4863 ft->type = FORMAT_TYPE_PANE;
4864 else if (wl != NULL)
4865 ft->type = FORMAT_TYPE_WINDOW;
4866 else if (s != NULL)
4867 ft->type = FORMAT_TYPE_SESSION;
4868 else
4869 ft->type = FORMAT_TYPE_UNKNOWN;
4871 if (s == NULL && c != NULL)
4872 s = c->session;
4873 if (wl == NULL && s != NULL)
4874 wl = s->curw;
4875 if (wp == NULL && wl != NULL)
4876 wp = wl->window->active;
4878 if (c != NULL)
4879 format_defaults_client(ft, c);
4880 if (s != NULL)
4881 format_defaults_session(ft, s);
4882 if (wl != NULL)
4883 format_defaults_winlink(ft, wl);
4884 if (wp != NULL)
4885 format_defaults_pane(ft, wp);
4887 pb = paste_get_top(NULL);
4888 if (pb != NULL)
4889 format_defaults_paste_buffer(ft, pb);
4892 /* Set default format keys for a session. */
4893 static void
4894 format_defaults_session(struct format_tree *ft, struct session *s)
4896 ft->s = s;
4899 /* Set default format keys for a client. */
4900 static void
4901 format_defaults_client(struct format_tree *ft, struct client *c)
4903 if (ft->s == NULL)
4904 ft->s = c->session;
4905 ft->c = c;
4908 /* Set default format keys for a window. */
4909 void
4910 format_defaults_window(struct format_tree *ft, struct window *w)
4912 ft->w = w;
4915 /* Set default format keys for a winlink. */
4916 static void
4917 format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
4919 if (ft->w == NULL)
4920 format_defaults_window(ft, wl->window);
4921 ft->wl = wl;
4924 /* Set default format keys for a window pane. */
4925 void
4926 format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
4928 struct window_mode_entry *wme;
4930 if (ft->w == NULL)
4931 format_defaults_window(ft, wp->window);
4932 ft->wp = wp;
4934 wme = TAILQ_FIRST(&wp->modes);
4935 if (wme != NULL && wme->mode->formats != NULL)
4936 wme->mode->formats(wme, ft);
4939 /* Set default format keys for paste buffer. */
4940 void
4941 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
4943 ft->pb = pb;
4946 /* Return word at given coordinates. Caller frees. */
4947 char *
4948 format_grid_word(struct grid *gd, u_int x, u_int y)
4950 const struct grid_line *gl;
4951 struct grid_cell gc;
4952 const char *ws;
4953 struct utf8_data *ud = NULL;
4954 u_int end;
4955 size_t size = 0;
4956 int found = 0;
4957 char *s = NULL;
4959 ws = options_get_string(global_s_options, "word-separators");
4961 for (;;) {
4962 grid_get_cell(gd, x, y, &gc);
4963 if (gc.flags & GRID_FLAG_PADDING)
4964 break;
4965 if (utf8_cstrhas(ws, &gc.data) ||
4966 (gc.data.size == 1 && *gc.data.data == ' ')) {
4967 found = 1;
4968 break;
4971 if (x == 0) {
4972 if (y == 0)
4973 break;
4974 gl = grid_peek_line(gd, y - 1);
4975 if (~gl->flags & GRID_LINE_WRAPPED)
4976 break;
4977 y--;
4978 x = grid_line_length(gd, y);
4979 if (x == 0)
4980 break;
4982 x--;
4984 for (;;) {
4985 if (found) {
4986 end = grid_line_length(gd, y);
4987 if (end == 0 || x == end - 1) {
4988 if (y == gd->hsize + gd->sy - 1)
4989 break;
4990 gl = grid_peek_line(gd, y);
4991 if (~gl->flags & GRID_LINE_WRAPPED)
4992 break;
4993 y++;
4994 x = 0;
4995 } else
4996 x++;
4998 found = 1;
5000 grid_get_cell(gd, x, y, &gc);
5001 if (gc.flags & GRID_FLAG_PADDING)
5002 break;
5003 if (utf8_cstrhas(ws, &gc.data) ||
5004 (gc.data.size == 1 && *gc.data.data == ' '))
5005 break;
5007 ud = xreallocarray(ud, size + 2, sizeof *ud);
5008 memcpy(&ud[size++], &gc.data, sizeof *ud);
5010 if (size != 0) {
5011 ud[size].size = 0;
5012 s = utf8_tocstr(ud);
5013 free(ud);
5015 return (s);
5018 /* Return line at given coordinates. Caller frees. */
5019 char *
5020 format_grid_line(struct grid *gd, u_int y)
5022 struct grid_cell gc;
5023 struct utf8_data *ud = NULL;
5024 u_int x;
5025 size_t size = 0;
5026 char *s = NULL;
5028 for (x = 0; x < grid_line_length(gd, y); x++) {
5029 grid_get_cell(gd, x, y, &gc);
5030 if (gc.flags & GRID_FLAG_PADDING)
5031 break;
5033 ud = xreallocarray(ud, size + 2, sizeof *ud);
5034 memcpy(&ud[size++], &gc.data, sizeof *ud);
5036 if (size != 0) {
5037 ud[size].size = 0;
5038 s = utf8_tocstr(ud);
5039 free(ud);
5041 return (s);