1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
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.
9  *
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.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 
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>
34 
35 #include "tmux.h"
36 
37 /*
38  * Build a list of key-value pairs and use them to expand #{key} entries in a
39  * string.
40  */
41 
42 struct format_expand_state;
43 
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 *);
53 
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;
60 
61           time_t                         last;
62           char                          *out;
63           int                            updated;
64 
65           struct job                    *job;
66           int                            status;
67 
68           RB_ENTRY(format_job)           entry;
69 };
70 
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);
75 
76 /* Format job tree comparison function. */
77 static int
format_job_cmp(struct format_job * fj1,struct format_job * fj2)78 format_job_cmp(struct format_job *fj1, struct format_job *fj2)
79 {
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));
85 }
86 
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
106 #define FORMAT_CLIENTS 0x40000
107 
108 /* Limit on recursion. */
109 #define FORMAT_LOOP_LIMIT 100
110 
111 /* Format expand flags. */
112 #define FORMAT_EXPAND_TIME 0x1
113 #define FORMAT_EXPAND_NOJOBS 0x2
114 
115 /* Entry in format tree. */
116 struct format_entry {
117           char                          *key;
118           char                          *value;
119           time_t                         time;
120           format_cb            cb;
121           RB_ENTRY(format_entry)         entry;
122 };
123 
124 /* Format type. */
125 enum format_type {
126           FORMAT_TYPE_UNKNOWN,
127           FORMAT_TYPE_SESSION,
128           FORMAT_TYPE_WINDOW,
129           FORMAT_TYPE_PANE
130 };
131 
132 struct format_tree {
133           enum format_type     type;
134 
135           struct client                 *c;
136           struct session                *s;
137           struct winlink                *wl;
138           struct window                 *w;
139           struct window_pane  *wp;
140           struct paste_buffer *pb;
141 
142           struct cmdq_item    *item;
143           struct client                 *client;
144           int                            flags;
145           u_int                          tag;
146 
147           struct mouse_event   m;
148 
149           RB_HEAD(format_entry_tree, format_entry) tree;
150 };
151 static int format_entry_cmp(struct format_entry *, struct format_entry *);
152 RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
153 
154 /* Format expand state. */
155 struct format_expand_state {
156           struct format_tree  *ft;
157           u_int                          loop;
158           time_t                         time;
159           struct tm            tm;
160           int                            flags;
161 };
162 
163 /* Format modifier. */
164 struct format_modifier {
165           char        modifier[3];
166           u_int       size;
167 
168           char      **argv;
169           int         argc;
170 };
171 
172 /* Format entry tree comparison function. */
173 static int
format_entry_cmp(struct format_entry * fe1,struct format_entry * fe2)174 format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
175 {
176           return (strcmp(fe1->key, fe2->key));
177 }
178 
179 /* Single-character uppercase aliases. */
180 static const char *format_upper[] = {
181           NULL,               /* A */
182           NULL,               /* B */
183           NULL,               /* C */
184           "pane_id",          /* D */
185           NULL,               /* E */
186           "window_flags",     /* F */
187           NULL,               /* G */
188           "host",             /* H */
189           "window_index",     /* I */
190           NULL,               /* J */
191           NULL,               /* K */
192           NULL,               /* L */
193           NULL,               /* M */
194           NULL,               /* N */
195           NULL,               /* O */
196           "pane_index",       /* P */
197           NULL,               /* Q */
198           NULL,               /* R */
199           "session_name",     /* S */
200           "pane_title",       /* T */
201           NULL,               /* U */
202           NULL,               /* V */
203           "window_name",      /* W */
204           NULL,               /* X */
205           NULL,               /* Y */
206           NULL                /* Z */
207 };
208 
209 /* Single-character lowercase aliases. */
210 static const char *format_lower[] = {
211           NULL,               /* a */
212           NULL,               /* b */
213           NULL,               /* c */
214           NULL,               /* d */
215           NULL,               /* e */
216           NULL,               /* f */
217           NULL,               /* g */
218           "host_short",       /* h */
219           NULL,               /* i */
220           NULL,               /* j */
221           NULL,               /* k */
222           NULL,               /* l */
223           NULL,               /* m */
224           NULL,               /* n */
225           NULL,               /* o */
226           NULL,               /* p */
227           NULL,               /* q */
228           NULL,               /* r */
229           NULL,               /* s */
230           NULL,               /* t */
231           NULL,               /* u */
232           NULL,               /* v */
233           NULL,               /* w */
234           NULL,               /* x */
235           NULL,               /* y */
236           NULL                /* z */
237 };
238 
239 /* Is logging enabled? */
240 static inline int
format_logging(struct format_tree * ft)241 format_logging(struct format_tree *ft)
242 {
243           return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE));
244 }
245 
246 /* Log a message if verbose. */
247 static void printflike(3, 4)
format_log1(struct format_expand_state * es,const char * from,const char * fmt,...)248 format_log1(struct format_expand_state *es, const char *from, const char *fmt,
249     ...)
250 {
251           struct format_tree  *ft = es->ft;
252           va_list                        ap;
253           char                          *s;
254           static const char    spaces[] = "          ";
255 
256           if (!format_logging(ft))
257                     return;
258 
259           va_start(ap, fmt);
260           xvasprintf(&s, fmt, ap);
261           va_end(ap);
262 
263           log_debug("%s: %s", from, s);
264           if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
265                     cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
266 
267           free(s);
268 }
269 #define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
270 
271 /* Copy expand state. */
272 static void
format_copy_state(struct format_expand_state * to,struct format_expand_state * from,int flags)273 format_copy_state(struct format_expand_state *to,
274     struct format_expand_state *from, int flags)
275 {
276           to->ft = from->ft;
277           to->loop = from->loop;
278           to->time = from->time;
279           memcpy(&to->tm, &from->tm, sizeof to->tm);
280           to->flags = from->flags|flags;
281 }
282 
283 /* Format job update callback. */
284 static void
format_job_update(struct job * job)285 format_job_update(struct job *job)
286 {
287           struct format_job   *fj = job_get_data(job);
288           struct evbuffer               *evb = job_get_event(job)->input;
289           char                          *line = NULL, *next;
290           time_t                         t;
291 
292           while ((next = evbuffer_readline(evb)) != NULL) {
293                     free(line);
294                     line = next;
295           }
296           if (line == NULL)
297                     return;
298           fj->updated = 1;
299 
300           free(fj->out);
301           fj->out = line;
302 
303           log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
304 
305           t = time(NULL);
306           if (fj->status && fj->last != t) {
307                     if (fj->client != NULL)
308                               server_status_client(fj->client);
309                     fj->last = t;
310           }
311 }
312 
313 /* Format job complete callback. */
314 static void
format_job_complete(struct job * job)315 format_job_complete(struct job *job)
316 {
317           struct format_job   *fj = job_get_data(job);
318           struct evbuffer               *evb = job_get_event(job)->input;
319           char                          *line, *buf;
320           size_t                         len;
321 
322           fj->job = NULL;
323 
324           buf = NULL;
325           if ((line = evbuffer_readline(evb)) == NULL) {
326                     len = EVBUFFER_LENGTH(evb);
327                     buf = xmalloc(len + 1);
328                     if (len != 0)
329                               memcpy(buf, EVBUFFER_DATA(evb), len);
330                     buf[len] = '\0';
331           } else
332                     buf = line;
333 
334           log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
335 
336           if (*buf != '\0' || !fj->updated) {
337                     free(fj->out);
338                     fj->out = buf;
339           } else
340                     free(buf);
341 
342           if (fj->status) {
343                     if (fj->client != NULL)
344                               server_status_client(fj->client);
345                     fj->status = 0;
346           }
347 }
348 
349 /* Find a job. */
350 static char *
format_job_get(struct format_expand_state * es,const char * cmd)351 format_job_get(struct format_expand_state *es, const char *cmd)
352 {
353           struct format_tree            *ft = es->ft;
354           struct format_job_tree                  *jobs;
355           struct format_job              fj0, *fj;
356           time_t                                   t;
357           char                                    *expanded;
358           int                                      force;
359           struct format_expand_state     next;
360 
361           if (ft->client == NULL)
362                     jobs = &format_jobs;
363           else if (ft->client->jobs != NULL)
364                     jobs = ft->client->jobs;
365           else {
366                     jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs);
367                     RB_INIT(jobs);
368           }
369 
370           fj0.tag = ft->tag;
371           fj0.cmd = cmd;
372           if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) {
373                     fj = xcalloc(1, sizeof *fj);
374                     fj->client = ft->client;
375                     fj->tag = ft->tag;
376                     fj->cmd = xstrdup(cmd);
377 
378                     RB_INSERT(format_job_tree, jobs, fj);
379           }
380 
381           format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
382           next.flags &= ~FORMAT_EXPAND_TIME;
383 
384           expanded = format_expand1(&next, cmd);
385           if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
386                     free(__UNCONST(fj->expanded));
387                     fj->expanded = xstrdup(expanded);
388                     force = 1;
389           } else
390                     force = (ft->flags & FORMAT_FORCE);
391 
392           t = time(NULL);
393           if (force && fj->job != NULL)
394                  job_free(fj->job);
395           if (force || (fj->job == NULL && fj->last != t)) {
396                     fj->job = job_run(expanded, 0, NULL, NULL, NULL,
397                         server_client_get_cwd(ft->client, NULL), format_job_update,
398                         format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1);
399                     if (fj->job == NULL) {
400                               free(fj->out);
401                               xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
402                     }
403                     fj->last = t;
404                     fj->updated = 0;
405           } else if (fj->job != NULL && (t - fj->last) > 1 && fj->out == NULL)
406                     xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
407           free(expanded);
408 
409           if (ft->flags & FORMAT_STATUS)
410                     fj->status = 1;
411           if (fj->out == NULL)
412                     return (xstrdup(""));
413           return (format_expand1(&next, fj->out));
414 }
415 
416 /* Remove old jobs. */
417 static void
format_job_tidy(struct format_job_tree * jobs,int force)418 format_job_tidy(struct format_job_tree *jobs, int force)
419 {
420           struct format_job   *fj, *fj1;
421           time_t                         now;
422 
423           now = time(NULL);
424           RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) {
425                     if (!force && (fj->last > now || now - fj->last < 3600))
426                               continue;
427                     RB_REMOVE(format_job_tree, jobs, fj);
428 
429                     log_debug("%s: %s", __func__, fj->cmd);
430 
431                     if (fj->job != NULL)
432                               job_free(fj->job);
433 
434                     free(__UNCONST(fj->expanded));
435                     free(__UNCONST(fj->cmd));
436                     free(fj->out);
437 
438                     free(fj);
439           }
440 }
441 
442 /* Tidy old jobs for all clients. */
443 void
format_tidy_jobs(void)444 format_tidy_jobs(void)
445 {
446           struct client       *c;
447 
448           format_job_tidy(&format_jobs, 0);
449           TAILQ_FOREACH(c, &clients, entry) {
450                     if (c->jobs != NULL)
451                               format_job_tidy(c->jobs, 0);
452           }
453 }
454 
455 /* Remove old jobs for client. */
456 void
format_lost_client(struct client * c)457 format_lost_client(struct client *c)
458 {
459           if (c->jobs != NULL)
460                     format_job_tidy(c->jobs, 1);
461           free(c->jobs);
462 }
463 
464 /* Wrapper for asprintf. */
465 static char * printflike(1, 2)
format_printf(const char * fmt,...)466 format_printf(const char *fmt, ...)
467 {
468           va_list    ap;
469           char      *s;
470 
471           va_start(ap, fmt);
472           xvasprintf(&s, fmt, ap);
473           va_end(ap);
474           return (s);
475 }
476 
477 /* Callback for host. */
478 static void *
format_cb_host(__unused struct format_tree * ft)479 format_cb_host(__unused struct format_tree *ft)
480 {
481           char host[HOST_NAME_MAX + 1];
482 
483           if (gethostname(host, sizeof host) != 0)
484                     return (xstrdup(""));
485           return (xstrdup(host));
486 }
487 
488 /* Callback for host_short. */
489 static void *
format_cb_host_short(__unused struct format_tree * ft)490 format_cb_host_short(__unused struct format_tree *ft)
491 {
492           char host[HOST_NAME_MAX + 1], *cp;
493 
494           if (gethostname(host, sizeof host) != 0)
495                     return (xstrdup(""));
496           if ((cp = strchr(host, '.')) != NULL)
497                     *cp = '\0';
498           return (xstrdup(host));
499 }
500 
501 /* Callback for pid. */
502 static void *
format_cb_pid(__unused struct format_tree * ft)503 format_cb_pid(__unused struct format_tree *ft)
504 {
505           char      *value;
506 
507           xasprintf(&value, "%ld", (long)getpid());
508           return (value);
509 }
510 
511 /* Callback for session_attached_list. */
512 static void *
format_cb_session_attached_list(struct format_tree * ft)513 format_cb_session_attached_list(struct format_tree *ft)
514 {
515           struct session      *s = ft->s;
516           struct client       *loop;
517           struct evbuffer     *buffer;
518           int                  size;
519           char                *value = NULL;
520 
521           if (s == NULL)
522                     return (NULL);
523 
524           buffer = evbuffer_new();
525           if (buffer == NULL)
526                     fatalx("out of memory");
527 
528           TAILQ_FOREACH(loop, &clients, entry) {
529                     if (loop->session == s) {
530                               if (EVBUFFER_LENGTH(buffer) > 0)
531                                         evbuffer_add(buffer, ",", 1);
532                               evbuffer_add_printf(buffer, "%s", loop->name);
533                     }
534           }
535 
536           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
537                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
538           evbuffer_free(buffer);
539           return (value);
540 }
541 
542 /* Callback for session_alerts. */
543 static void *
format_cb_session_alerts(struct format_tree * ft)544 format_cb_session_alerts(struct format_tree *ft)
545 {
546           struct session      *s = ft->s;
547           struct winlink      *wl;
548           char                 alerts[1024], tmp[16];
549 
550           if (s == NULL)
551                     return (NULL);
552 
553           *alerts = '\0';
554           RB_FOREACH(wl, winlinks, &s->windows) {
555                     if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
556                               continue;
557                     xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
558 
559                     if (*alerts != '\0')
560                               strlcat(alerts, ",", sizeof alerts);
561                     strlcat(alerts, tmp, sizeof alerts);
562                     if (wl->flags & WINLINK_ACTIVITY)
563                               strlcat(alerts, "#", sizeof alerts);
564                     if (wl->flags & WINLINK_BELL)
565                               strlcat(alerts, "!", sizeof alerts);
566                     if (wl->flags & WINLINK_SILENCE)
567                               strlcat(alerts, "~", sizeof alerts);
568           }
569           return (xstrdup(alerts));
570 }
571 
572 /* Callback for session_stack. */
573 static void *
format_cb_session_stack(struct format_tree * ft)574 format_cb_session_stack(struct format_tree *ft)
575 {
576           struct session      *s = ft->s;
577           struct winlink      *wl;
578           char                 result[1024], tmp[16];
579 
580           if (s == NULL)
581                     return (NULL);
582 
583           xsnprintf(result, sizeof result, "%u", s->curw->idx);
584           TAILQ_FOREACH(wl, &s->lastw, sentry) {
585                     xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
586 
587                     if (*result != '\0')
588                               strlcat(result, ",", sizeof result);
589                     strlcat(result, tmp, sizeof result);
590           }
591           return (xstrdup(result));
592 }
593 
594 /* Callback for window_stack_index. */
595 static void *
format_cb_window_stack_index(struct format_tree * ft)596 format_cb_window_stack_index(struct format_tree *ft)
597 {
598           struct session      *s;
599           struct winlink      *wl;
600           u_int                idx;
601           char                *value = NULL;
602 
603           if (ft->wl == NULL)
604                     return (NULL);
605           s = ft->wl->session;
606 
607           idx = 0;
608           TAILQ_FOREACH(wl, &s->lastw, sentry) {
609                     idx++;
610                     if (wl == ft->wl)
611                               break;
612           }
613           if (wl == NULL)
614                     return (xstrdup("0"));
615           xasprintf(&value, "%u", idx);
616           return (value);
617 }
618 
619 /* Callback for window_linked_sessions_list. */
620 static void *
format_cb_window_linked_sessions_list(struct format_tree * ft)621 format_cb_window_linked_sessions_list(struct format_tree *ft)
622 {
623           struct window       *w;
624           struct winlink      *wl;
625           struct evbuffer     *buffer;
626           int                  size;
627           char                *value = NULL;
628 
629           if (ft->wl == NULL)
630                     return (NULL);
631           w = ft->wl->window;
632 
633           buffer = evbuffer_new();
634           if (buffer == NULL)
635                     fatalx("out of memory");
636 
637           TAILQ_FOREACH(wl, &w->winlinks, wentry) {
638                     if (EVBUFFER_LENGTH(buffer) > 0)
639                               evbuffer_add(buffer, ",", 1);
640                     evbuffer_add_printf(buffer, "%s", wl->session->name);
641           }
642 
643           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
644                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
645           evbuffer_free(buffer);
646           return (value);
647 }
648 
649 /* Callback for window_active_sessions. */
650 static void *
format_cb_window_active_sessions(struct format_tree * ft)651 format_cb_window_active_sessions(struct format_tree *ft)
652 {
653           struct window       *w;
654           struct winlink      *wl;
655           u_int                n = 0;
656           char                *value;
657 
658           if (ft->wl == NULL)
659                     return (NULL);
660           w = ft->wl->window;
661 
662           TAILQ_FOREACH(wl, &w->winlinks, wentry) {
663                     if (wl->session->curw == wl)
664                               n++;
665           }
666 
667           xasprintf(&value, "%u", n);
668           return (value);
669 }
670 
671 /* Callback for window_active_sessions_list. */
672 static void *
format_cb_window_active_sessions_list(struct format_tree * ft)673 format_cb_window_active_sessions_list(struct format_tree *ft)
674 {
675           struct window       *w;
676           struct winlink      *wl;
677           struct evbuffer     *buffer;
678           int                  size;
679           char                *value = NULL;
680 
681           if (ft->wl == NULL)
682                     return (NULL);
683           w = ft->wl->window;
684 
685           buffer = evbuffer_new();
686           if (buffer == NULL)
687                     fatalx("out of memory");
688 
689           TAILQ_FOREACH(wl, &w->winlinks, wentry) {
690                     if (wl->session->curw == wl) {
691                               if (EVBUFFER_LENGTH(buffer) > 0)
692                                         evbuffer_add(buffer, ",", 1);
693                               evbuffer_add_printf(buffer, "%s", wl->session->name);
694                     }
695           }
696 
697           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
698                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
699           evbuffer_free(buffer);
700           return (value);
701 }
702 
703 /* Callback for window_active_clients. */
704 static void *
format_cb_window_active_clients(struct format_tree * ft)705 format_cb_window_active_clients(struct format_tree *ft)
706 {
707           struct window       *w;
708           struct client       *loop;
709           struct session      *client_session;
710           u_int                n = 0;
711           char                *value;
712 
713           if (ft->wl == NULL)
714                     return (NULL);
715           w = ft->wl->window;
716 
717           TAILQ_FOREACH(loop, &clients, entry) {
718                     client_session = loop->session;
719                     if (client_session == NULL)
720                               continue;
721 
722                     if (w == client_session->curw->window)
723                               n++;
724           }
725 
726           xasprintf(&value, "%u", n);
727           return (value);
728 }
729 
730 /* Callback for window_active_clients_list. */
731 static void *
format_cb_window_active_clients_list(struct format_tree * ft)732 format_cb_window_active_clients_list(struct format_tree *ft)
733 {
734           struct window       *w;
735           struct client       *loop;
736           struct session      *client_session;
737           struct evbuffer     *buffer;
738           int                  size;
739           char                *value = NULL;
740 
741           if (ft->wl == NULL)
742                     return (NULL);
743           w = ft->wl->window;
744 
745           buffer = evbuffer_new();
746           if (buffer == NULL)
747                     fatalx("out of memory");
748 
749           TAILQ_FOREACH(loop, &clients, entry) {
750                     client_session = loop->session;
751                     if (client_session == NULL)
752                               continue;
753 
754                     if (w == client_session->curw->window) {
755                               if (EVBUFFER_LENGTH(buffer) > 0)
756                                         evbuffer_add(buffer, ",", 1);
757                               evbuffer_add_printf(buffer, "%s", loop->name);
758                     }
759           }
760 
761           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
762                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
763           evbuffer_free(buffer);
764           return (value);
765 }
766 
767 /* Callback for window_layout. */
768 static void *
format_cb_window_layout(struct format_tree * ft)769 format_cb_window_layout(struct format_tree *ft)
770 {
771           struct window       *w = ft->w;
772 
773           if (w == NULL)
774                     return (NULL);
775 
776           if (w->saved_layout_root != NULL)
777                     return (layout_dump(w->saved_layout_root));
778           return (layout_dump(w->layout_root));
779 }
780 
781 /* Callback for window_visible_layout. */
782 static void *
format_cb_window_visible_layout(struct format_tree * ft)783 format_cb_window_visible_layout(struct format_tree *ft)
784 {
785           struct window       *w = ft->w;
786 
787           if (w == NULL)
788                     return (NULL);
789 
790           return (layout_dump(w->layout_root));
791 }
792 
793 /* Callback for pane_start_command. */
794 static void *
format_cb_start_command(struct format_tree * ft)795 format_cb_start_command(struct format_tree *ft)
796 {
797           struct window_pane  *wp = ft->wp;
798 
799           if (wp == NULL)
800                     return (NULL);
801 
802           return (cmd_stringify_argv(wp->argc, wp->argv));
803 }
804 
805 /* Callback for pane_start_path. */
806 static void *
format_cb_start_path(struct format_tree * ft)807 format_cb_start_path(struct format_tree *ft)
808 {
809           struct window_pane  *wp = ft->wp;
810 
811           if (wp == NULL)
812                     return (NULL);
813 
814           if (wp->cwd == NULL)
815                     return (xstrdup(""));
816           return (xstrdup(wp->cwd));
817 }
818 
819 /* Callback for pane_current_command. */
820 static void *
format_cb_current_command(struct format_tree * ft)821 format_cb_current_command(struct format_tree *ft)
822 {
823           struct window_pane  *wp = ft->wp;
824           char                          *cmd, *value;
825 
826           if (wp == NULL || wp->shell == NULL)
827                     return (NULL);
828 
829           cmd = osdep_get_name(wp->fd, wp->tty);
830           if (cmd == NULL || *cmd == '\0') {
831                     free(cmd);
832                     cmd = cmd_stringify_argv(wp->argc, wp->argv);
833                     if (cmd == NULL || *cmd == '\0') {
834                               free(cmd);
835                               cmd = xstrdup(wp->shell);
836                     }
837           }
838           value = parse_window_name(cmd);
839           free(cmd);
840           return (value);
841 }
842 
843 /* Callback for pane_current_path. */
844 static void *
format_cb_current_path(struct format_tree * ft)845 format_cb_current_path(struct format_tree *ft)
846 {
847           struct window_pane  *wp = ft->wp;
848           char                          *cwd;
849 
850           if (wp == NULL)
851                     return (NULL);
852 
853           cwd = osdep_get_cwd(wp->fd);
854           if (cwd == NULL)
855                     return (NULL);
856           return (xstrdup(cwd));
857 }
858 
859 /* Callback for history_bytes. */
860 static void *
format_cb_history_bytes(struct format_tree * ft)861 format_cb_history_bytes(struct format_tree *ft)
862 {
863           struct window_pane  *wp = ft->wp;
864           struct grid                   *gd;
865           struct grid_line    *gl;
866           size_t                       size = 0;
867           u_int                          i;
868           char                          *value;
869 
870           if (wp == NULL)
871                     return (NULL);
872           gd = wp->base.grid;
873 
874           for (i = 0; i < gd->hsize + gd->sy; i++) {
875                     gl = grid_get_line(gd, i);
876                     size += gl->cellsize * sizeof *gl->celldata;
877                     size += gl->extdsize * sizeof *gl->extddata;
878           }
879           size += (gd->hsize + gd->sy) * sizeof *gl;
880 
881           xasprintf(&value, "%zu", size);
882           return (value);
883 }
884 
885 /* Callback for history_all_bytes. */
886 static void *
format_cb_history_all_bytes(struct format_tree * ft)887 format_cb_history_all_bytes(struct format_tree *ft)
888 {
889           struct window_pane  *wp = ft->wp;
890           struct grid                   *gd;
891           struct grid_line    *gl;
892           u_int                          i, lines, cells = 0, extended_cells = 0;
893           char                          *value;
894 
895           if (wp == NULL)
896                     return (NULL);
897           gd = wp->base.grid;
898 
899           lines = gd->hsize + gd->sy;
900           for (i = 0; i < lines; i++) {
901                     gl = grid_get_line(gd, i);
902                     cells += gl->cellsize;
903                     extended_cells += gl->extdsize;
904           }
905 
906           xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines,
907               lines * sizeof *gl, cells, cells * sizeof *gl->celldata,
908               extended_cells, extended_cells * sizeof *gl->extddata);
909           return (value);
910 }
911 
912 /* Callback for pane_tabs. */
913 static void *
format_cb_pane_tabs(struct format_tree * ft)914 format_cb_pane_tabs(struct format_tree *ft)
915 {
916           struct window_pane  *wp = ft->wp;
917           struct evbuffer               *buffer;
918           u_int                          i;
919           int                            size;
920           char                          *value = NULL;
921 
922           if (wp == NULL)
923                     return (NULL);
924 
925           buffer = evbuffer_new();
926           if (buffer == NULL)
927                     fatalx("out of memory");
928           for (i = 0; i < wp->base.grid->sx; i++) {
929                     if (!bit_test(wp->base.tabs, i))
930                               continue;
931 
932                     if (EVBUFFER_LENGTH(buffer) > 0)
933                               evbuffer_add(buffer, ",", 1);
934                     evbuffer_add_printf(buffer, "%u", i);
935           }
936           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
937                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
938           evbuffer_free(buffer);
939           return (value);
940 }
941 
942 /* Callback for pane_fg. */
943 static void *
format_cb_pane_fg(struct format_tree * ft)944 format_cb_pane_fg(struct format_tree *ft)
945 {
946           struct window_pane  *wp = ft->wp;
947           struct grid_cell     gc;
948 
949           if (wp == NULL)
950                     return (NULL);
951 
952           tty_default_colours(&gc, wp);
953           return (xstrdup(colour_tostring(gc.fg)));
954 }
955 
956 /* Callback for pane_bg. */
957 static void *
format_cb_pane_bg(struct format_tree * ft)958 format_cb_pane_bg(struct format_tree *ft)
959 {
960           struct window_pane  *wp = ft->wp;
961           struct grid_cell     gc;
962 
963           if (wp == NULL)
964                     return (NULL);
965 
966           tty_default_colours(&gc, wp);
967           return (xstrdup(colour_tostring(gc.bg)));
968 }
969 
970 /* Callback for session_group_list. */
971 static void *
format_cb_session_group_list(struct format_tree * ft)972 format_cb_session_group_list(struct format_tree *ft)
973 {
974           struct session                *s = ft->s;
975           struct session_group          *sg;
976           struct session                *loop;
977           struct evbuffer               *buffer;
978           int                            size;
979           char                          *value = NULL;
980 
981           if (s == NULL)
982                     return (NULL);
983           sg = session_group_contains(s);
984           if (sg == NULL)
985                     return (NULL);
986 
987           buffer = evbuffer_new();
988           if (buffer == NULL)
989                     fatalx("out of memory");
990 
991           TAILQ_FOREACH(loop, &sg->sessions, gentry) {
992                     if (EVBUFFER_LENGTH(buffer) > 0)
993                               evbuffer_add(buffer, ",", 1);
994                     evbuffer_add_printf(buffer, "%s", loop->name);
995           }
996 
997           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
998                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
999           evbuffer_free(buffer);
1000           return (value);
1001 }
1002 
1003 /* Callback for session_group_attached_list. */
1004 static void *
format_cb_session_group_attached_list(struct format_tree * ft)1005 format_cb_session_group_attached_list(struct format_tree *ft)
1006 {
1007           struct session                *s = ft->s, *client_session, *session_loop;
1008           struct session_group          *sg;
1009           struct client                 *loop;
1010           struct evbuffer               *buffer;
1011           int                            size;
1012           char                          *value = NULL;
1013 
1014           if (s == NULL)
1015                     return (NULL);
1016           sg = session_group_contains(s);
1017           if (sg == NULL)
1018                     return (NULL);
1019 
1020           buffer = evbuffer_new();
1021           if (buffer == NULL)
1022                     fatalx("out of memory");
1023 
1024           TAILQ_FOREACH(loop, &clients, entry) {
1025                     client_session = loop->session;
1026                     if (client_session == NULL)
1027                               continue;
1028                     TAILQ_FOREACH(session_loop, &sg->sessions, gentry) {
1029                               if (session_loop == client_session){
1030                                         if (EVBUFFER_LENGTH(buffer) > 0)
1031                                                   evbuffer_add(buffer, ",", 1);
1032                                         evbuffer_add_printf(buffer, "%s", loop->name);
1033                               }
1034                     }
1035           }
1036 
1037           if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1038                     xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
1039           evbuffer_free(buffer);
1040           return (value);
1041 }
1042 
1043 /* Callback for pane_in_mode. */
1044 static void *
format_cb_pane_in_mode(struct format_tree * ft)1045 format_cb_pane_in_mode(struct format_tree *ft)
1046 {
1047           struct window_pane            *wp = ft->wp;
1048           u_int                                    n = 0;
1049           struct window_mode_entry      *wme;
1050           char                                    *value;
1051 
1052           if (wp == NULL)
1053                     return (NULL);
1054 
1055           TAILQ_FOREACH(wme, &wp->modes, entry)
1056                     n++;
1057           xasprintf(&value, "%u", n);
1058           return (value);
1059 }
1060 
1061 /* Callback for pane_at_top. */
1062 static void *
format_cb_pane_at_top(struct format_tree * ft)1063 format_cb_pane_at_top(struct format_tree *ft)
1064 {
1065           struct window_pane  *wp = ft->wp;
1066           struct window                 *w;
1067           int                            status, flag;
1068           char                          *value;
1069 
1070           if (wp == NULL)
1071                     return (NULL);
1072           w = wp->window;
1073 
1074           status = options_get_number(w->options, "pane-border-status");
1075           if (status == PANE_STATUS_TOP)
1076                     flag = (wp->yoff == 1);
1077           else
1078                     flag = (wp->yoff == 0);
1079           xasprintf(&value, "%d", flag);
1080           return (value);
1081 }
1082 
1083 /* Callback for pane_at_bottom. */
1084 static void *
format_cb_pane_at_bottom(struct format_tree * ft)1085 format_cb_pane_at_bottom(struct format_tree *ft)
1086 {
1087           struct window_pane  *wp = ft->wp;
1088           struct window                 *w;
1089           int                            status, flag;
1090           char                          *value;
1091 
1092           if (wp == NULL)
1093                     return (NULL);
1094           w = wp->window;
1095 
1096           status = options_get_number(w->options, "pane-border-status");
1097           if (status == PANE_STATUS_BOTTOM)
1098                     flag = (wp->yoff + wp->sy == w->sy - 1);
1099           else
1100                     flag = (wp->yoff + wp->sy == w->sy);
1101           xasprintf(&value, "%d", flag);
1102           return (value);
1103 }
1104 
1105 /* Callback for cursor_character. */
1106 static void *
format_cb_cursor_character(struct format_tree * ft)1107 format_cb_cursor_character(struct format_tree *ft)
1108 {
1109           struct window_pane  *wp = ft->wp;
1110           struct grid_cell     gc;
1111           char                          *value = NULL;
1112 
1113           if (wp == NULL)
1114                     return (NULL);
1115 
1116           grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc);
1117           if (~gc.flags & GRID_FLAG_PADDING)
1118                     xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data);
1119           return (value);
1120 }
1121 
1122 /* Callback for mouse_word. */
1123 static void *
format_cb_mouse_word(struct format_tree * ft)1124 format_cb_mouse_word(struct format_tree *ft)
1125 {
1126           struct window_pane  *wp;
1127           struct grid                   *gd;
1128           u_int                          x, y;
1129 
1130           if (!ft->m.valid)
1131                     return (NULL);
1132           wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1133           if (wp == NULL)
1134                     return (NULL);
1135           if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1136                     return (NULL);
1137 
1138           if (!TAILQ_EMPTY(&wp->modes)) {
1139                     if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
1140                               return (window_copy_get_word(wp, x, y));
1141                     return (NULL);
1142           }
1143           gd = wp->base.grid;
1144           return (format_grid_word(gd, x, gd->hsize + y));
1145 }
1146 
1147 /* Callback for mouse_hyperlink. */
1148 static void *
format_cb_mouse_hyperlink(struct format_tree * ft)1149 format_cb_mouse_hyperlink(struct format_tree *ft)
1150 {
1151           struct window_pane  *wp;
1152           struct grid                   *gd;
1153           u_int                          x, y;
1154 
1155           if (!ft->m.valid)
1156                     return (NULL);
1157           wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1158           if (wp == NULL)
1159                     return (NULL);
1160           if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1161                     return (NULL);
1162           gd = wp->base.grid;
1163           return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
1164 }
1165 
1166 /* Callback for mouse_line. */
1167 static void *
format_cb_mouse_line(struct format_tree * ft)1168 format_cb_mouse_line(struct format_tree *ft)
1169 {
1170           struct window_pane  *wp;
1171           struct grid                   *gd;
1172           u_int                          x, y;
1173 
1174           if (!ft->m.valid)
1175                     return (NULL);
1176           wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1177           if (wp == NULL)
1178                     return (NULL);
1179           if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1180                     return (NULL);
1181 
1182           if (!TAILQ_EMPTY(&wp->modes)) {
1183                     if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
1184                               return (window_copy_get_line(wp, y));
1185                     return (NULL);
1186           }
1187           gd = wp->base.grid;
1188           return (format_grid_line(gd, gd->hsize + y));
1189 }
1190 
1191 /* Callback for mouse_status_line. */
1192 static void *
format_cb_mouse_status_line(struct format_tree * ft)1193 format_cb_mouse_status_line(struct format_tree *ft)
1194 {
1195           char      *value;
1196           u_int      y;
1197 
1198           if (!ft->m.valid)
1199                     return (NULL);
1200           if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
1201                     return (NULL);
1202 
1203           if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
1204                     y = ft->m.y;
1205           } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
1206                     y = ft->m.y - ft->m.statusat;
1207           } else
1208                     return (NULL);
1209           xasprintf(&value, "%u", y);
1210           return (value);
1211 
1212 }
1213 
1214 /* Callback for mouse_status_range. */
1215 static void *
format_cb_mouse_status_range(struct format_tree * ft)1216 format_cb_mouse_status_range(struct format_tree *ft)
1217 {
1218           struct style_range  *sr;
1219           u_int                          x, y;
1220 
1221           if (!ft->m.valid)
1222                     return (NULL);
1223           if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
1224                     return (NULL);
1225 
1226           if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
1227                     x = ft->m.x;
1228                     y = ft->m.y;
1229           } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
1230                     x = ft->m.x;
1231                     y = ft->m.y - ft->m.statusat;
1232           } else
1233                     return (NULL);
1234 
1235           sr = status_get_range(ft->c, x, y);
1236           if (sr == NULL)
1237                     return (NULL);
1238           switch (sr->type) {
1239           case STYLE_RANGE_NONE:
1240                     return (NULL);
1241           case STYLE_RANGE_LEFT:
1242                     return (xstrdup("left"));
1243           case STYLE_RANGE_RIGHT:
1244                     return (xstrdup("right"));
1245           case STYLE_RANGE_PANE:
1246                     return (xstrdup("pane"));
1247           case STYLE_RANGE_WINDOW:
1248                     return (xstrdup("window"));
1249           case STYLE_RANGE_SESSION:
1250                     return (xstrdup("session"));
1251           case STYLE_RANGE_USER:
1252                     return (xstrdup(sr->string));
1253           }
1254           return (NULL);
1255 }
1256 
1257 /* Callback for alternate_on. */
1258 static void *
format_cb_alternate_on(struct format_tree * ft)1259 format_cb_alternate_on(struct format_tree *ft)
1260 {
1261           if (ft->wp != NULL) {
1262                     if (ft->wp->base.saved_grid != NULL)
1263                               return (xstrdup("1"));
1264                     return (xstrdup("0"));
1265           }
1266           return (NULL);
1267 }
1268 
1269 /* Callback for alternate_saved_x. */
1270 static void *
format_cb_alternate_saved_x(struct format_tree * ft)1271 format_cb_alternate_saved_x(struct format_tree *ft)
1272 {
1273           if (ft->wp != NULL)
1274                     return (format_printf("%u", ft->wp->base.saved_cx));
1275           return (NULL);
1276 }
1277 
1278 /* Callback for alternate_saved_y. */
1279 static void *
format_cb_alternate_saved_y(struct format_tree * ft)1280 format_cb_alternate_saved_y(struct format_tree *ft)
1281 {
1282           if (ft->wp != NULL)
1283                     return (format_printf("%u", ft->wp->base.saved_cy));
1284           return (NULL);
1285 }
1286 
1287 /* Callback for buffer_name. */
1288 static void *
format_cb_buffer_name(struct format_tree * ft)1289 format_cb_buffer_name(struct format_tree *ft)
1290 {
1291           if (ft->pb != NULL)
1292                     return (xstrdup(paste_buffer_name(ft->pb)));
1293           return (NULL);
1294 }
1295 
1296 /* Callback for buffer_sample. */
1297 static void *
format_cb_buffer_sample(struct format_tree * ft)1298 format_cb_buffer_sample(struct format_tree *ft)
1299 {
1300           if (ft->pb != NULL)
1301                     return (paste_make_sample(ft->pb));
1302           return (NULL);
1303 }
1304 
1305 /* Callback for buffer_size. */
1306 static void *
format_cb_buffer_size(struct format_tree * ft)1307 format_cb_buffer_size(struct format_tree *ft)
1308 {
1309           size_t    size;
1310 
1311           if (ft->pb != NULL) {
1312                     paste_buffer_data(ft->pb, &size);
1313                     return (format_printf("%zu", size));
1314           }
1315           return (NULL);
1316 }
1317 
1318 /* Callback for client_cell_height. */
1319 static void *
format_cb_client_cell_height(struct format_tree * ft)1320 format_cb_client_cell_height(struct format_tree *ft)
1321 {
1322           if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1323                     return (format_printf("%u", ft->c->tty.ypixel));
1324           return (NULL);
1325 }
1326 
1327 /* Callback for client_cell_width. */
1328 static void *
format_cb_client_cell_width(struct format_tree * ft)1329 format_cb_client_cell_width(struct format_tree *ft)
1330 {
1331           if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1332                     return (format_printf("%u", ft->c->tty.xpixel));
1333           return (NULL);
1334 }
1335 
1336 /* Callback for client_control_mode. */
1337 static void *
format_cb_client_control_mode(struct format_tree * ft)1338 format_cb_client_control_mode(struct format_tree *ft)
1339 {
1340           if (ft->c != NULL) {
1341                     if (ft->c->flags & CLIENT_CONTROL)
1342                               return (xstrdup("1"));
1343                     return (xstrdup("0"));
1344           }
1345           return (NULL);
1346 }
1347 
1348 /* Callback for client_discarded. */
1349 static void *
format_cb_client_discarded(struct format_tree * ft)1350 format_cb_client_discarded(struct format_tree *ft)
1351 {
1352           if (ft->c != NULL)
1353                     return (format_printf("%zu", ft->c->discarded));
1354           return (NULL);
1355 }
1356 
1357 /* Callback for client_flags. */
1358 static void *
format_cb_client_flags(struct format_tree * ft)1359 format_cb_client_flags(struct format_tree *ft)
1360 {
1361           if (ft->c != NULL)
1362                     return (xstrdup(server_client_get_flags(ft->c)));
1363           return (NULL);
1364 }
1365 
1366 /* Callback for client_height. */
1367 static void *
format_cb_client_height(struct format_tree * ft)1368 format_cb_client_height(struct format_tree *ft)
1369 {
1370           if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1371                     return (format_printf("%u", ft->c->tty.sy));
1372           return (NULL);
1373 }
1374 
1375 /* Callback for client_key_table. */
1376 static void *
format_cb_client_key_table(struct format_tree * ft)1377 format_cb_client_key_table(struct format_tree *ft)
1378 {
1379           if (ft->c != NULL)
1380                     return (xstrdup(ft->c->keytable->name));
1381           return (NULL);
1382 }
1383 
1384 /* Callback for client_last_session. */
1385 static void *
format_cb_client_last_session(struct format_tree * ft)1386 format_cb_client_last_session(struct format_tree *ft)
1387 {
1388           if (ft->c != NULL &&
1389               ft->c->last_session != NULL &&
1390               session_alive(ft->c->last_session))
1391                     return (xstrdup(ft->c->last_session->name));
1392           return (NULL);
1393 }
1394 
1395 /* Callback for client_name. */
1396 static void *
format_cb_client_name(struct format_tree * ft)1397 format_cb_client_name(struct format_tree *ft)
1398 {
1399           if (ft->c != NULL)
1400                     return (xstrdup(ft->c->name));
1401           return (NULL);
1402 }
1403 
1404 /* Callback for client_pid. */
1405 static void *
format_cb_client_pid(struct format_tree * ft)1406 format_cb_client_pid(struct format_tree *ft)
1407 {
1408           if (ft->c != NULL)
1409                     return (format_printf("%ld", (long)ft->c->pid));
1410           return (NULL);
1411 }
1412 
1413 /* Callback for client_prefix. */
1414 static void *
format_cb_client_prefix(struct format_tree * ft)1415 format_cb_client_prefix(struct format_tree *ft)
1416 {
1417           const char          *name;
1418 
1419           if (ft->c != NULL) {
1420                     name = server_client_get_key_table(ft->c);
1421                     if (strcmp(ft->c->keytable->name, name) == 0)
1422                               return (xstrdup("0"));
1423                     return (xstrdup("1"));
1424           }
1425           return (NULL);
1426 }
1427 
1428 /* Callback for client_readonly. */
1429 static void *
format_cb_client_readonly(struct format_tree * ft)1430 format_cb_client_readonly(struct format_tree *ft)
1431 {
1432           if (ft->c != NULL) {
1433                     if (ft->c->flags & CLIENT_READONLY)
1434                               return (xstrdup("1"));
1435                     return (xstrdup("0"));
1436           }
1437           return (NULL);
1438 }
1439 
1440 /* Callback for client_session. */
1441 static void *
format_cb_client_session(struct format_tree * ft)1442 format_cb_client_session(struct format_tree *ft)
1443 {
1444           if (ft->c != NULL && ft->c->session != NULL)
1445                     return (xstrdup(ft->c->session->name));
1446           return (NULL);
1447 }
1448 
1449 /* Callback for client_termfeatures. */
1450 static void *
format_cb_client_termfeatures(struct format_tree * ft)1451 format_cb_client_termfeatures(struct format_tree *ft)
1452 {
1453           if (ft->c != NULL)
1454                     return (xstrdup(tty_get_features(ft->c->term_features)));
1455           return (NULL);
1456 }
1457 
1458 /* Callback for client_termname. */
1459 static void *
format_cb_client_termname(struct format_tree * ft)1460 format_cb_client_termname(struct format_tree *ft)
1461 {
1462           if (ft->c != NULL)
1463                     return (xstrdup(ft->c->term_name));
1464           return (NULL);
1465 }
1466 
1467 /* Callback for client_termtype. */
1468 static void *
format_cb_client_termtype(struct format_tree * ft)1469 format_cb_client_termtype(struct format_tree *ft)
1470 {
1471           if (ft->c != NULL) {
1472                     if (ft->c->term_type == NULL)
1473                               return (xstrdup(""));
1474                     return (xstrdup(ft->c->term_type));
1475           }
1476           return (NULL);
1477 }
1478 
1479 /* Callback for client_tty. */
1480 static void *
format_cb_client_tty(struct format_tree * ft)1481 format_cb_client_tty(struct format_tree *ft)
1482 {
1483           if (ft->c != NULL)
1484                     return (xstrdup(ft->c->ttyname));
1485           return (NULL);
1486 }
1487 
1488 /* Callback for client_uid. */
1489 static void *
format_cb_client_uid(struct format_tree * ft)1490 format_cb_client_uid(struct format_tree *ft)
1491 {
1492           uid_t     uid;
1493 
1494           if (ft->c != NULL) {
1495                     uid = proc_get_peer_uid(ft->c->peer);
1496                     if (uid != (uid_t)-1)
1497                               return (format_printf("%ld", (long)uid));
1498           }
1499           return (NULL);
1500 }
1501 
1502 /* Callback for client_user. */
1503 static void *
format_cb_client_user(struct format_tree * ft)1504 format_cb_client_user(struct format_tree *ft)
1505 {
1506           uid_t                uid;
1507           struct passwd       *pw;
1508 
1509           if (ft->c != NULL) {
1510                     uid = proc_get_peer_uid(ft->c->peer);
1511                     if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL)
1512                               return (xstrdup(pw->pw_name));
1513           }
1514           return (NULL);
1515 }
1516 
1517 /* Callback for client_utf8. */
1518 static void *
format_cb_client_utf8(struct format_tree * ft)1519 format_cb_client_utf8(struct format_tree *ft)
1520 {
1521           if (ft->c != NULL) {
1522                     if (ft->c->flags & CLIENT_UTF8)
1523                               return (xstrdup("1"));
1524                     return (xstrdup("0"));
1525           }
1526           return (NULL);
1527 }
1528 
1529 /* Callback for client_width. */
1530 static void *
format_cb_client_width(struct format_tree * ft)1531 format_cb_client_width(struct format_tree *ft)
1532 {
1533           if (ft->c != NULL)
1534                     return (format_printf("%u", ft->c->tty.sx));
1535           return (NULL);
1536 }
1537 
1538 /* Callback for client_written. */
1539 static void *
format_cb_client_written(struct format_tree * ft)1540 format_cb_client_written(struct format_tree *ft)
1541 {
1542           if (ft->c != NULL)
1543                     return (format_printf("%zu", ft->c->written));
1544           return (NULL);
1545 }
1546 
1547 /* Callback for config_files. */
1548 static void *
format_cb_config_files(__unused struct format_tree * ft)1549 format_cb_config_files(__unused struct format_tree *ft)
1550 {
1551           char      *s = NULL;
1552           size_t     slen = 0;
1553           u_int      i;
1554           size_t     n;
1555 
1556           for (i = 0; i < cfg_nfiles; i++) {
1557                     n = strlen(cfg_files[i]) + 1;
1558                     s = xrealloc(s, slen + n + 1);
1559                     slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]);
1560           }
1561           if (s == NULL)
1562                     return (xstrdup(""));
1563           s[slen - 1] = '\0';
1564           return (s);
1565 }
1566 
1567 /* Callback for cursor_flag. */
1568 static void *
format_cb_cursor_flag(struct format_tree * ft)1569 format_cb_cursor_flag(struct format_tree *ft)
1570 {
1571           if (ft->wp != NULL) {
1572                     if (ft->wp->base.mode & MODE_CURSOR)
1573                               return (xstrdup("1"));
1574                     return (xstrdup("0"));
1575           }
1576           return (NULL);
1577 }
1578 
1579 /* Callback for cursor_x. */
1580 static void *
format_cb_cursor_x(struct format_tree * ft)1581 format_cb_cursor_x(struct format_tree *ft)
1582 {
1583           if (ft->wp != NULL)
1584                     return (format_printf("%u", ft->wp->base.cx));
1585           return (NULL);
1586 }
1587 
1588 /* Callback for cursor_y. */
1589 static void *
format_cb_cursor_y(struct format_tree * ft)1590 format_cb_cursor_y(struct format_tree *ft)
1591 {
1592           if (ft->wp != NULL)
1593                     return (format_printf("%u", ft->wp->base.cy));
1594           return (NULL);
1595 }
1596 
1597 /* Callback for history_limit. */
1598 static void *
format_cb_history_limit(struct format_tree * ft)1599 format_cb_history_limit(struct format_tree *ft)
1600 {
1601           if (ft->wp != NULL)
1602                     return (format_printf("%u", ft->wp->base.grid->hlimit));
1603           return (NULL);
1604 }
1605 
1606 /* Callback for history_size. */
1607 static void *
format_cb_history_size(struct format_tree * ft)1608 format_cb_history_size(struct format_tree *ft)
1609 {
1610           if (ft->wp != NULL)
1611                     return (format_printf("%u", ft->wp->base.grid->hsize));
1612           return (NULL);
1613 }
1614 
1615 /* Callback for insert_flag. */
1616 static void *
format_cb_insert_flag(struct format_tree * ft)1617 format_cb_insert_flag(struct format_tree *ft)
1618 {
1619           if (ft->wp != NULL) {
1620                     if (ft->wp->base.mode & MODE_INSERT)
1621                               return (xstrdup("1"));
1622                     return (xstrdup("0"));
1623           }
1624           return (NULL);
1625 }
1626 
1627 /* Callback for keypad_cursor_flag. */
1628 static void *
format_cb_keypad_cursor_flag(struct format_tree * ft)1629 format_cb_keypad_cursor_flag(struct format_tree *ft)
1630 {
1631           if (ft->wp != NULL) {
1632                     if (ft->wp->base.mode & MODE_KCURSOR)
1633                               return (xstrdup("1"));
1634                     return (xstrdup("0"));
1635           }
1636           return (NULL);
1637 }
1638 
1639 /* Callback for keypad_flag. */
1640 static void *
format_cb_keypad_flag(struct format_tree * ft)1641 format_cb_keypad_flag(struct format_tree *ft)
1642 {
1643           if (ft->wp != NULL) {
1644                     if (ft->wp->base.mode & MODE_KKEYPAD)
1645                               return (xstrdup("1"));
1646                     return (xstrdup("0"));
1647           }
1648           return (NULL);
1649 }
1650 
1651 /* Callback for mouse_all_flag. */
1652 static void *
format_cb_mouse_all_flag(struct format_tree * ft)1653 format_cb_mouse_all_flag(struct format_tree *ft)
1654 {
1655           if (ft->wp != NULL) {
1656                     if (ft->wp->base.mode & MODE_MOUSE_ALL)
1657                               return (xstrdup("1"));
1658                     return (xstrdup("0"));
1659           }
1660           return (NULL);
1661 }
1662 
1663 /* Callback for mouse_any_flag. */
1664 static void *
format_cb_mouse_any_flag(struct format_tree * ft)1665 format_cb_mouse_any_flag(struct format_tree *ft)
1666 {
1667           if (ft->wp != NULL) {
1668                     if (ft->wp->base.mode & ALL_MOUSE_MODES)
1669                               return (xstrdup("1"));
1670                     return (xstrdup("0"));
1671           }
1672           return (NULL);
1673 }
1674 
1675 /* Callback for mouse_button_flag. */
1676 static void *
format_cb_mouse_button_flag(struct format_tree * ft)1677 format_cb_mouse_button_flag(struct format_tree *ft)
1678 {
1679           if (ft->wp != NULL) {
1680                     if (ft->wp->base.mode & MODE_MOUSE_BUTTON)
1681                               return (xstrdup("1"));
1682                     return (xstrdup("0"));
1683           }
1684           return (NULL);
1685 }
1686 
1687 /* Callback for mouse_pane. */
1688 static void *
format_cb_mouse_pane(struct format_tree * ft)1689 format_cb_mouse_pane(struct format_tree *ft)
1690 {
1691           struct window_pane  *wp;
1692 
1693           if (ft->m.valid) {
1694                     wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1695                     if (wp != NULL)
1696                               return (format_printf("%%%u", wp->id));
1697                     return (NULL);
1698           }
1699           return (NULL);
1700 }
1701 
1702 /* Callback for mouse_sgr_flag. */
1703 static void *
format_cb_mouse_sgr_flag(struct format_tree * ft)1704 format_cb_mouse_sgr_flag(struct format_tree *ft)
1705 {
1706           if (ft->wp != NULL) {
1707                     if (ft->wp->base.mode & MODE_MOUSE_SGR)
1708                               return (xstrdup("1"));
1709                     return (xstrdup("0"));
1710           }
1711           return (NULL);
1712 }
1713 
1714 /* Callback for mouse_standard_flag. */
1715 static void *
format_cb_mouse_standard_flag(struct format_tree * ft)1716 format_cb_mouse_standard_flag(struct format_tree *ft)
1717 {
1718           if (ft->wp != NULL) {
1719                     if (ft->wp->base.mode & MODE_MOUSE_STANDARD)
1720                               return (xstrdup("1"));
1721                     return (xstrdup("0"));
1722           }
1723           return (NULL);
1724 }
1725 
1726 /* Callback for mouse_utf8_flag. */
1727 static void *
format_cb_mouse_utf8_flag(struct format_tree * ft)1728 format_cb_mouse_utf8_flag(struct format_tree *ft)
1729 {
1730           if (ft->wp != NULL) {
1731                     if (ft->wp->base.mode & MODE_MOUSE_UTF8)
1732                               return (xstrdup("1"));
1733                     return (xstrdup("0"));
1734           }
1735           return (NULL);
1736 }
1737 
1738 /* Callback for mouse_x. */
1739 static void *
format_cb_mouse_x(struct format_tree * ft)1740 format_cb_mouse_x(struct format_tree *ft)
1741 {
1742           struct window_pane  *wp;
1743           u_int                          x, y;
1744 
1745           if (!ft->m.valid)
1746                     return (NULL);
1747           wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1748           if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1749                     return (format_printf("%u", x));
1750           if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1751                     if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1752                               return (format_printf("%u", ft->m.x));
1753                     if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1754                               return (format_printf("%u", ft->m.x));
1755           }
1756           return (NULL);
1757 }
1758 
1759 /* Callback for mouse_y. */
1760 static void *
format_cb_mouse_y(struct format_tree * ft)1761 format_cb_mouse_y(struct format_tree *ft)
1762 {
1763           struct window_pane  *wp;
1764           u_int                          x, y;
1765 
1766           if (!ft->m.valid)
1767                     return (NULL);
1768           wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1769           if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
1770                     return (format_printf("%u", y));
1771           if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
1772                     if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
1773                               return (format_printf("%u", ft->m.y));
1774                     if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
1775                               return (format_printf("%u", ft->m.y - ft->m.statusat));
1776           }
1777           return (NULL);
1778 }
1779 
1780 /* Callback for next_session_id. */
1781 static void *
format_cb_next_session_id(__unused struct format_tree * ft)1782 format_cb_next_session_id(__unused struct format_tree *ft)
1783 {
1784           return (format_printf("$%u", next_session_id));
1785 }
1786 
1787 /* Callback for origin_flag. */
1788 static void *
format_cb_origin_flag(struct format_tree * ft)1789 format_cb_origin_flag(struct format_tree *ft)
1790 {
1791           if (ft->wp != NULL) {
1792                     if (ft->wp->base.mode & MODE_ORIGIN)
1793                               return (xstrdup("1"));
1794                     return (xstrdup("0"));
1795           }
1796           return (NULL);
1797 }
1798 
1799 /* Callback for pane_active. */
1800 static void *
format_cb_pane_active(struct format_tree * ft)1801 format_cb_pane_active(struct format_tree *ft)
1802 {
1803           if (ft->wp != NULL) {
1804                     if (ft->wp == ft->wp->window->active)
1805                               return (xstrdup("1"));
1806                     return (xstrdup("0"));
1807           }
1808           return (NULL);
1809 }
1810 
1811 /* Callback for pane_at_left. */
1812 static void *
format_cb_pane_at_left(struct format_tree * ft)1813 format_cb_pane_at_left(struct format_tree *ft)
1814 {
1815           if (ft->wp != NULL) {
1816                     if (ft->wp->xoff == 0)
1817                               return (xstrdup("1"));
1818                     return (xstrdup("0"));
1819           }
1820           return (NULL);
1821 }
1822 
1823 /* Callback for pane_at_right. */
1824 static void *
format_cb_pane_at_right(struct format_tree * ft)1825 format_cb_pane_at_right(struct format_tree *ft)
1826 {
1827           if (ft->wp != NULL) {
1828                     if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx)
1829                               return (xstrdup("1"));
1830                     return (xstrdup("0"));
1831           }
1832           return (NULL);
1833 }
1834 
1835 /* Callback for pane_bottom. */
1836 static void *
format_cb_pane_bottom(struct format_tree * ft)1837 format_cb_pane_bottom(struct format_tree *ft)
1838 {
1839           if (ft->wp != NULL)
1840                     return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1));
1841           return (NULL);
1842 }
1843 
1844 /* Callback for pane_dead. */
1845 static void *
format_cb_pane_dead(struct format_tree * ft)1846 format_cb_pane_dead(struct format_tree *ft)
1847 {
1848           if (ft->wp != NULL) {
1849                     if (ft->wp->fd == -1)
1850                               return (xstrdup("1"));
1851                     return (xstrdup("0"));
1852           }
1853           return (NULL);
1854 }
1855 
1856 /* Callback for pane_dead_signal. */
1857 static void *
format_cb_pane_dead_signal(struct format_tree * ft)1858 format_cb_pane_dead_signal(struct format_tree *ft)
1859 {
1860           struct window_pane  *wp = ft->wp;
1861           const char                    *name;
1862 
1863           if (wp != NULL) {
1864                     if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) {
1865                               name = sig2name(WTERMSIG(wp->status));
1866                               return (format_printf("%s", name));
1867                     }
1868                     return (NULL);
1869           }
1870           return (NULL);
1871 }
1872 
1873 /* Callback for pane_dead_status. */
1874 static void *
format_cb_pane_dead_status(struct format_tree * ft)1875 format_cb_pane_dead_status(struct format_tree *ft)
1876 {
1877           struct window_pane  *wp = ft->wp;
1878 
1879           if (wp != NULL) {
1880                     if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status))
1881                               return (format_printf("%d", WEXITSTATUS(wp->status)));
1882                     return (NULL);
1883           }
1884           return (NULL);
1885 }
1886 
1887 /* Callback for pane_dead_time. */
1888 static void *
format_cb_pane_dead_time(struct format_tree * ft)1889 format_cb_pane_dead_time(struct format_tree *ft)
1890 {
1891           struct window_pane  *wp = ft->wp;
1892 
1893           if (wp != NULL) {
1894                     if (wp->flags & PANE_STATUSDRAWN)
1895                               return (&wp->dead_time);
1896                     return (NULL);
1897           }
1898           return (NULL);
1899 }
1900 
1901 /* Callback for pane_format. */
1902 static void *
format_cb_pane_format(struct format_tree * ft)1903 format_cb_pane_format(struct format_tree *ft)
1904 {
1905           if (ft->type == FORMAT_TYPE_PANE)
1906                     return (xstrdup("1"));
1907           return (xstrdup("0"));
1908 }
1909 
1910 /* Callback for pane_height. */
1911 static void *
format_cb_pane_height(struct format_tree * ft)1912 format_cb_pane_height(struct format_tree *ft)
1913 {
1914           if (ft->wp != NULL)
1915                     return (format_printf("%u", ft->wp->sy));
1916           return (NULL);
1917 }
1918 
1919 /* Callback for pane_id. */
1920 static void *
format_cb_pane_id(struct format_tree * ft)1921 format_cb_pane_id(struct format_tree *ft)
1922 {
1923           if (ft->wp != NULL)
1924                     return (format_printf("%%%u", ft->wp->id));
1925           return (NULL);
1926 }
1927 
1928 /* Callback for pane_index. */
1929 static void *
format_cb_pane_index(struct format_tree * ft)1930 format_cb_pane_index(struct format_tree *ft)
1931 {
1932           u_int     idx;
1933 
1934           if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0)
1935                     return (format_printf("%u", idx));
1936           return (NULL);
1937 }
1938 
1939 /* Callback for pane_input_off. */
1940 static void *
format_cb_pane_input_off(struct format_tree * ft)1941 format_cb_pane_input_off(struct format_tree *ft)
1942 {
1943           if (ft->wp != NULL) {
1944                     if (ft->wp->flags & PANE_INPUTOFF)
1945                               return (xstrdup("1"));
1946                     return (xstrdup("0"));
1947           }
1948           return (NULL);
1949 }
1950 
1951 /* Callback for pane_unseen_changes. */
1952 static void *
format_cb_pane_unseen_changes(struct format_tree * ft)1953 format_cb_pane_unseen_changes(struct format_tree *ft)
1954 {
1955           if (ft->wp != NULL) {
1956                     if (ft->wp->flags & PANE_UNSEENCHANGES)
1957                               return (xstrdup("1"));
1958                     return (xstrdup("0"));
1959           }
1960           return (NULL);
1961 }
1962 
1963 /* Callback for pane_key_mode. */
1964 static void *
format_cb_pane_key_mode(struct format_tree * ft)1965 format_cb_pane_key_mode(struct format_tree *ft)
1966 {
1967           if (ft->wp != NULL && ft->wp->screen != NULL) {
1968                     switch (ft->wp->screen->mode & EXTENDED_KEY_MODES) {
1969                     case MODE_KEYS_EXTENDED:
1970                               return (xstrdup("Ext 1"));
1971                     case MODE_KEYS_EXTENDED_2:
1972                               return (xstrdup("Ext 2"));
1973                     default:
1974                               return (xstrdup("VT10x"));
1975                     }
1976           }
1977           return (NULL);
1978 }
1979 
1980 /* Callback for pane_last. */
1981 static void *
format_cb_pane_last(struct format_tree * ft)1982 format_cb_pane_last(struct format_tree *ft)
1983 {
1984           if (ft->wp != NULL) {
1985                     if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes))
1986                               return (xstrdup("1"));
1987                     return (xstrdup("0"));
1988           }
1989           return (NULL);
1990 }
1991 
1992 /* Callback for pane_left. */
1993 static void *
format_cb_pane_left(struct format_tree * ft)1994 format_cb_pane_left(struct format_tree *ft)
1995 {
1996           if (ft->wp != NULL)
1997                     return (format_printf("%u", ft->wp->xoff));
1998           return (NULL);
1999 }
2000 
2001 /* Callback for pane_marked. */
2002 static void *
format_cb_pane_marked(struct format_tree * ft)2003 format_cb_pane_marked(struct format_tree *ft)
2004 {
2005           if (ft->wp != NULL) {
2006                     if (server_check_marked() && marked_pane.wp == ft->wp)
2007                               return (xstrdup("1"));
2008                     return (xstrdup("0"));
2009           }
2010           return (NULL);
2011 }
2012 
2013 /* Callback for pane_marked_set. */
2014 static void *
format_cb_pane_marked_set(struct format_tree * ft)2015 format_cb_pane_marked_set(struct format_tree *ft)
2016 {
2017           if (ft->wp != NULL) {
2018                     if (server_check_marked())
2019                               return (xstrdup("1"));
2020                     return (xstrdup("0"));
2021           }
2022           return (NULL);
2023 }
2024 
2025 /* Callback for pane_mode. */
2026 static void *
format_cb_pane_mode(struct format_tree * ft)2027 format_cb_pane_mode(struct format_tree *ft)
2028 {
2029           struct window_mode_entry      *wme;
2030 
2031           if (ft->wp != NULL) {
2032                     wme = TAILQ_FIRST(&ft->wp->modes);
2033                     if (wme != NULL)
2034                               return (xstrdup(wme->mode->name));
2035                     return (NULL);
2036           }
2037           return (NULL);
2038 }
2039 
2040 /* Callback for pane_path. */
2041 static void *
format_cb_pane_path(struct format_tree * ft)2042 format_cb_pane_path(struct format_tree *ft)
2043 {
2044           if (ft->wp != NULL) {
2045                     if (ft->wp->base.path == NULL)
2046                               return (xstrdup(""));
2047                     return (xstrdup(ft->wp->base.path));
2048           }
2049           return (NULL);
2050 }
2051 
2052 /* Callback for pane_pid. */
2053 static void *
format_cb_pane_pid(struct format_tree * ft)2054 format_cb_pane_pid(struct format_tree *ft)
2055 {
2056           if (ft->wp != NULL)
2057                     return (format_printf("%ld", (long)ft->wp->pid));
2058           return (NULL);
2059 }
2060 
2061 /* Callback for pane_pipe. */
2062 static void *
format_cb_pane_pipe(struct format_tree * ft)2063 format_cb_pane_pipe(struct format_tree *ft)
2064 {
2065           if (ft->wp != NULL) {
2066                     if (ft->wp->pipe_fd != -1)
2067                               return (xstrdup("1"));
2068                     return (xstrdup("0"));
2069           }
2070           return (NULL);
2071 }
2072 
2073 /* Callback for pane_right. */
2074 static void *
format_cb_pane_right(struct format_tree * ft)2075 format_cb_pane_right(struct format_tree *ft)
2076 {
2077           if (ft->wp != NULL)
2078                     return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1));
2079           return (NULL);
2080 }
2081 
2082 /* Callback for pane_search_string. */
2083 static void *
format_cb_pane_search_string(struct format_tree * ft)2084 format_cb_pane_search_string(struct format_tree *ft)
2085 {
2086           if (ft->wp != NULL) {
2087                     if (ft->wp->searchstr == NULL)
2088                               return (xstrdup(""));
2089                     return (xstrdup(ft->wp->searchstr));
2090           }
2091           return (NULL);
2092 }
2093 
2094 /* Callback for pane_synchronized. */
2095 static void *
format_cb_pane_synchronized(struct format_tree * ft)2096 format_cb_pane_synchronized(struct format_tree *ft)
2097 {
2098           if (ft->wp != NULL) {
2099                     if (options_get_number(ft->wp->options, "synchronize-panes"))
2100                               return (xstrdup("1"));
2101                     return (xstrdup("0"));
2102           }
2103           return (NULL);
2104 }
2105 
2106 /* Callback for pane_title. */
2107 static void *
format_cb_pane_title(struct format_tree * ft)2108 format_cb_pane_title(struct format_tree *ft)
2109 {
2110           if (ft->wp != NULL)
2111                     return (xstrdup(ft->wp->base.title));
2112           return (NULL);
2113 }
2114 
2115 /* Callback for pane_top. */
2116 static void *
format_cb_pane_top(struct format_tree * ft)2117 format_cb_pane_top(struct format_tree *ft)
2118 {
2119           if (ft->wp != NULL)
2120                     return (format_printf("%u", ft->wp->yoff));
2121           return (NULL);
2122 }
2123 
2124 /* Callback for pane_tty. */
2125 static void *
format_cb_pane_tty(struct format_tree * ft)2126 format_cb_pane_tty(struct format_tree *ft)
2127 {
2128           if (ft->wp != NULL)
2129                     return (xstrdup(ft->wp->tty));
2130           return (NULL);
2131 }
2132 
2133 /* Callback for pane_width. */
2134 static void *
format_cb_pane_width(struct format_tree * ft)2135 format_cb_pane_width(struct format_tree *ft)
2136 {
2137           if (ft->wp != NULL)
2138                     return (format_printf("%u", ft->wp->sx));
2139           return (NULL);
2140 }
2141 
2142 /* Callback for scroll_region_lower. */
2143 static void *
format_cb_scroll_region_lower(struct format_tree * ft)2144 format_cb_scroll_region_lower(struct format_tree *ft)
2145 {
2146           if (ft->wp != NULL)
2147                     return (format_printf("%u", ft->wp->base.rlower));
2148           return (NULL);
2149 }
2150 
2151 /* Callback for scroll_region_upper. */
2152 static void *
format_cb_scroll_region_upper(struct format_tree * ft)2153 format_cb_scroll_region_upper(struct format_tree *ft)
2154 {
2155           if (ft->wp != NULL)
2156                     return (format_printf("%u", ft->wp->base.rupper));
2157           return (NULL);
2158 }
2159 
2160 /* Callback for server_sessions. */
2161 static void *
format_cb_server_sessions(__unused struct format_tree * ft)2162 format_cb_server_sessions(__unused struct format_tree *ft)
2163 {
2164           struct session      *s;
2165           u_int                n = 0;
2166 
2167           RB_FOREACH(s, sessions, &sessions)
2168                     n++;
2169           return (format_printf("%u", n));
2170 }
2171 
2172 /* Callback for session_attached. */
2173 static void *
format_cb_session_attached(struct format_tree * ft)2174 format_cb_session_attached(struct format_tree *ft)
2175 {
2176           if (ft->s != NULL)
2177                     return (format_printf("%u", ft->s->attached));
2178           return (NULL);
2179 }
2180 
2181 /* Callback for session_format. */
2182 static void *
format_cb_session_format(struct format_tree * ft)2183 format_cb_session_format(struct format_tree *ft)
2184 {
2185           if (ft->type == FORMAT_TYPE_SESSION)
2186                     return (xstrdup("1"));
2187           return (xstrdup("0"));
2188 }
2189 
2190 /* Callback for session_group. */
2191 static void *
format_cb_session_group(struct format_tree * ft)2192 format_cb_session_group(struct format_tree *ft)
2193 {
2194           struct session_group          *sg;
2195 
2196           if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2197                     return (xstrdup(sg->name));
2198           return (NULL);
2199 }
2200 
2201 /* Callback for session_group_attached. */
2202 static void *
format_cb_session_group_attached(struct format_tree * ft)2203 format_cb_session_group_attached(struct format_tree *ft)
2204 {
2205           struct session_group          *sg;
2206 
2207           if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2208                     return (format_printf("%u", session_group_attached_count (sg)));
2209           return (NULL);
2210 }
2211 
2212 /* Callback for session_group_many_attached. */
2213 static void *
format_cb_session_group_many_attached(struct format_tree * ft)2214 format_cb_session_group_many_attached(struct format_tree *ft)
2215 {
2216           struct session_group          *sg;
2217 
2218           if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) {
2219                     if (session_group_attached_count (sg) > 1)
2220                               return (xstrdup("1"));
2221                     return (xstrdup("0"));
2222           }
2223           return (NULL);
2224 }
2225 
2226 /* Callback for session_group_size. */
2227 static void *
format_cb_session_group_size(struct format_tree * ft)2228 format_cb_session_group_size(struct format_tree *ft)
2229 {
2230           struct session_group          *sg;
2231 
2232           if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2233                     return (format_printf("%u", session_group_count (sg)));
2234           return (NULL);
2235 }
2236 
2237 /* Callback for session_grouped. */
2238 static void *
format_cb_session_grouped(struct format_tree * ft)2239 format_cb_session_grouped(struct format_tree *ft)
2240 {
2241           if (ft->s != NULL) {
2242                     if (session_group_contains(ft->s) != NULL)
2243                               return (xstrdup("1"));
2244                     return (xstrdup("0"));
2245           }
2246           return (NULL);
2247 }
2248 
2249 /* Callback for session_id. */
2250 static void *
format_cb_session_id(struct format_tree * ft)2251 format_cb_session_id(struct format_tree *ft)
2252 {
2253           if (ft->s != NULL)
2254                     return (format_printf("$%u", ft->s->id));
2255           return (NULL);
2256 }
2257 
2258 /* Callback for session_many_attached. */
2259 static void *
format_cb_session_many_attached(struct format_tree * ft)2260 format_cb_session_many_attached(struct format_tree *ft)
2261 {
2262           if (ft->s != NULL) {
2263                     if (ft->s->attached > 1)
2264                               return (xstrdup("1"));
2265                     return (xstrdup("0"));
2266           }
2267           return (NULL);
2268 }
2269 
2270 /* Callback for session_marked. */
2271 static void *
format_cb_session_marked(struct format_tree * ft)2272 format_cb_session_marked(struct format_tree *ft)
2273 {
2274           if (ft->s != NULL) {
2275                     if (server_check_marked() && marked_pane.s == ft->s)
2276                               return (xstrdup("1"));
2277                     return (xstrdup("0"));
2278           }
2279           return (NULL);
2280 }
2281 
2282 /* Callback for session_name. */
2283 static void *
format_cb_session_name(struct format_tree * ft)2284 format_cb_session_name(struct format_tree *ft)
2285 {
2286           if (ft->s != NULL)
2287                     return (xstrdup(ft->s->name));
2288           return (NULL);
2289 }
2290 
2291 /* Callback for session_path. */
2292 static void *
format_cb_session_path(struct format_tree * ft)2293 format_cb_session_path(struct format_tree *ft)
2294 {
2295           if (ft->s != NULL)
2296                     return (xstrdup(ft->s->cwd));
2297           return (NULL);
2298 }
2299 
2300 /* Callback for session_windows. */
2301 static void *
format_cb_session_windows(struct format_tree * ft)2302 format_cb_session_windows(struct format_tree *ft)
2303 {
2304           if (ft->s != NULL)
2305                     return (format_printf("%u", winlink_count(&ft->s->windows)));
2306           return (NULL);
2307 }
2308 
2309 /* Callback for socket_path. */
2310 static void *
format_cb_socket_path(__unused struct format_tree * ft)2311 format_cb_socket_path(__unused struct format_tree *ft)
2312 {
2313           return (xstrdup(socket_path));
2314 }
2315 
2316 /* Callback for version. */
2317 static void *
format_cb_version(__unused struct format_tree * ft)2318 format_cb_version(__unused struct format_tree *ft)
2319 {
2320           return (xstrdup(getversion()));
2321 }
2322 
2323 /* Callback for active_window_index. */
2324 static void *
format_cb_active_window_index(struct format_tree * ft)2325 format_cb_active_window_index(struct format_tree *ft)
2326 {
2327           if (ft->s != NULL)
2328                     return (format_printf("%u", ft->s->curw->idx));
2329           return (NULL);
2330 }
2331 
2332 /* Callback for last_window_index. */
2333 static void *
format_cb_last_window_index(struct format_tree * ft)2334 format_cb_last_window_index(struct format_tree *ft)
2335 {
2336           struct winlink      *wl;
2337 
2338           if (ft->s != NULL) {
2339                     wl = RB_MAX(winlinks, &ft->s->windows);
2340                     return (format_printf("%u", wl->idx));
2341           }
2342           return (NULL);
2343 }
2344 
2345 /* Callback for window_active. */
2346 static void *
format_cb_window_active(struct format_tree * ft)2347 format_cb_window_active(struct format_tree *ft)
2348 {
2349           if (ft->wl != NULL) {
2350                     if (ft->wl == ft->wl->session->curw)
2351                               return (xstrdup("1"));
2352                     return (xstrdup("0"));
2353           }
2354           return (NULL);
2355 }
2356 
2357 /* Callback for window_activity_flag. */
2358 static void *
format_cb_window_activity_flag(struct format_tree * ft)2359 format_cb_window_activity_flag(struct format_tree *ft)
2360 {
2361           if (ft->wl != NULL) {
2362                     if (ft->wl->flags & WINLINK_ACTIVITY)
2363                               return (xstrdup("1"));
2364                     return (xstrdup("0"));
2365           }
2366           return (NULL);
2367 }
2368 
2369 /* Callback for window_bell_flag. */
2370 static void *
format_cb_window_bell_flag(struct format_tree * ft)2371 format_cb_window_bell_flag(struct format_tree *ft)
2372 {
2373           if (ft->wl != NULL) {
2374                     if (ft->wl->flags & WINLINK_BELL)
2375                               return (xstrdup("1"));
2376                     return (xstrdup("0"));
2377           }
2378           return (NULL);
2379 }
2380 
2381 /* Callback for window_bigger. */
2382 static void *
format_cb_window_bigger(struct format_tree * ft)2383 format_cb_window_bigger(struct format_tree *ft)
2384 {
2385           u_int     ox, oy, sx, sy;
2386 
2387           if (ft->c != NULL) {
2388                     if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2389                               return (xstrdup("1"));
2390                     return (xstrdup("0"));
2391           }
2392           return (NULL);
2393 }
2394 
2395 /* Callback for window_cell_height. */
2396 static void *
format_cb_window_cell_height(struct format_tree * ft)2397 format_cb_window_cell_height(struct format_tree *ft)
2398 {
2399           if (ft->w != NULL)
2400                     return (format_printf("%u", ft->w->ypixel));
2401           return (NULL);
2402 }
2403 
2404 /* Callback for window_cell_width. */
2405 static void *
format_cb_window_cell_width(struct format_tree * ft)2406 format_cb_window_cell_width(struct format_tree *ft)
2407 {
2408           if (ft->w != NULL)
2409                     return (format_printf("%u", ft->w->xpixel));
2410           return (NULL);
2411 }
2412 
2413 /* Callback for window_end_flag. */
2414 static void *
format_cb_window_end_flag(struct format_tree * ft)2415 format_cb_window_end_flag(struct format_tree *ft)
2416 {
2417           if (ft->wl != NULL) {
2418                     if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows))
2419                               return (xstrdup("1"));
2420                     return (xstrdup("0"));
2421           }
2422           return (NULL);
2423 }
2424 
2425 /* Callback for window_flags. */
2426 static void *
format_cb_window_flags(struct format_tree * ft)2427 format_cb_window_flags(struct format_tree *ft)
2428 {
2429           if (ft->wl != NULL)
2430                     return (xstrdup(window_printable_flags(ft->wl, 1)));
2431           return (NULL);
2432 }
2433 
2434 /* Callback for window_format. */
2435 static void *
format_cb_window_format(struct format_tree * ft)2436 format_cb_window_format(struct format_tree *ft)
2437 {
2438           if (ft->type == FORMAT_TYPE_WINDOW)
2439                     return (xstrdup("1"));
2440           return (xstrdup("0"));
2441 }
2442 
2443 /* Callback for window_height. */
2444 static void *
format_cb_window_height(struct format_tree * ft)2445 format_cb_window_height(struct format_tree *ft)
2446 {
2447           if (ft->w != NULL)
2448                     return (format_printf("%u", ft->w->sy));
2449           return (NULL);
2450 }
2451 
2452 /* Callback for window_id. */
2453 static void *
format_cb_window_id(struct format_tree * ft)2454 format_cb_window_id(struct format_tree *ft)
2455 {
2456           if (ft->w != NULL)
2457                     return (format_printf("@%u", ft->w->id));
2458           return (NULL);
2459 }
2460 
2461 /* Callback for window_index. */
2462 static void *
format_cb_window_index(struct format_tree * ft)2463 format_cb_window_index(struct format_tree *ft)
2464 {
2465           if (ft->wl != NULL)
2466                     return (format_printf("%d", ft->wl->idx));
2467           return (NULL);
2468 }
2469 
2470 /* Callback for window_last_flag. */
2471 static void *
format_cb_window_last_flag(struct format_tree * ft)2472 format_cb_window_last_flag(struct format_tree *ft)
2473 {
2474           if (ft->wl != NULL) {
2475                     if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw))
2476                               return (xstrdup("1"));
2477                     return (xstrdup("0"));
2478           }
2479           return (NULL);
2480 }
2481 
2482 /* Callback for window_linked. */
2483 static void *
format_cb_window_linked(struct format_tree * ft)2484 format_cb_window_linked(struct format_tree *ft)
2485 {
2486           if (ft->wl != NULL) {
2487                     if (session_is_linked(ft->wl->session, ft->wl->window))
2488                               return (xstrdup("1"));
2489                     return (xstrdup("0"));
2490           }
2491           return (NULL);
2492 }
2493 
2494 /* Callback for window_linked_sessions. */
2495 static void *
format_cb_window_linked_sessions(struct format_tree * ft)2496 format_cb_window_linked_sessions(struct format_tree *ft)
2497 {
2498           if (ft->wl != NULL)
2499                     return (format_printf("%u", ft->wl->window->references));
2500           return (NULL);
2501 }
2502 
2503 /* Callback for window_marked_flag. */
2504 static void *
format_cb_window_marked_flag(struct format_tree * ft)2505 format_cb_window_marked_flag(struct format_tree *ft)
2506 {
2507           if (ft->wl != NULL) {
2508                     if (server_check_marked() && marked_pane.wl == ft->wl)
2509                               return (xstrdup("1"));
2510                     return (xstrdup("0"));
2511           }
2512           return (NULL);
2513 }
2514 
2515 /* Callback for window_name. */
2516 static void *
format_cb_window_name(struct format_tree * ft)2517 format_cb_window_name(struct format_tree *ft)
2518 {
2519           if (ft->w != NULL)
2520                     return (format_printf("%s", ft->w->name));
2521           return (NULL);
2522 }
2523 
2524 /* Callback for window_offset_x. */
2525 static void *
format_cb_window_offset_x(struct format_tree * ft)2526 format_cb_window_offset_x(struct format_tree *ft)
2527 {
2528           u_int     ox, oy, sx, sy;
2529 
2530           if (ft->c != NULL) {
2531                     if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2532                               return (format_printf("%u", ox));
2533                     return (NULL);
2534           }
2535           return (NULL);
2536 }
2537 
2538 /* Callback for window_offset_y. */
2539 static void *
format_cb_window_offset_y(struct format_tree * ft)2540 format_cb_window_offset_y(struct format_tree *ft)
2541 {
2542           u_int     ox, oy, sx, sy;
2543 
2544           if (ft->c != NULL) {
2545                     if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
2546                               return (format_printf("%u", oy));
2547                     return (NULL);
2548           }
2549           return (NULL);
2550 }
2551 
2552 /* Callback for window_panes. */
2553 static void *
format_cb_window_panes(struct format_tree * ft)2554 format_cb_window_panes(struct format_tree *ft)
2555 {
2556           if (ft->w != NULL)
2557                     return (format_printf("%u", window_count_panes(ft->w)));
2558           return (NULL);
2559 }
2560 
2561 /* Callback for window_raw_flags. */
2562 static void *
format_cb_window_raw_flags(struct format_tree * ft)2563 format_cb_window_raw_flags(struct format_tree *ft)
2564 {
2565           if (ft->wl != NULL)
2566                     return (xstrdup(window_printable_flags(ft->wl, 0)));
2567           return (NULL);
2568 }
2569 
2570 /* Callback for window_silence_flag. */
2571 static void *
format_cb_window_silence_flag(struct format_tree * ft)2572 format_cb_window_silence_flag(struct format_tree *ft)
2573 {
2574           if (ft->wl != NULL) {
2575                     if (ft->wl->flags & WINLINK_SILENCE)
2576                               return (xstrdup("1"));
2577                     return (xstrdup("0"));
2578           }
2579           return (NULL);
2580 }
2581 
2582 /* Callback for window_start_flag. */
2583 static void *
format_cb_window_start_flag(struct format_tree * ft)2584 format_cb_window_start_flag(struct format_tree *ft)
2585 {
2586           if (ft->wl != NULL) {
2587                     if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows))
2588                               return (xstrdup("1"));
2589                     return (xstrdup("0"));
2590           }
2591           return (NULL);
2592 }
2593 
2594 /* Callback for window_width. */
2595 static void *
format_cb_window_width(struct format_tree * ft)2596 format_cb_window_width(struct format_tree *ft)
2597 {
2598           if (ft->w != NULL)
2599                     return (format_printf("%u", ft->w->sx));
2600           return (NULL);
2601 }
2602 
2603 /* Callback for window_zoomed_flag. */
2604 static void *
format_cb_window_zoomed_flag(struct format_tree * ft)2605 format_cb_window_zoomed_flag(struct format_tree *ft)
2606 {
2607           if (ft->w != NULL) {
2608                     if (ft->w->flags & WINDOW_ZOOMED)
2609                               return (xstrdup("1"));
2610                     return (xstrdup("0"));
2611           }
2612           return (NULL);
2613 }
2614 
2615 /* Callback for wrap_flag. */
2616 static void *
format_cb_wrap_flag(struct format_tree * ft)2617 format_cb_wrap_flag(struct format_tree *ft)
2618 {
2619           if (ft->wp != NULL) {
2620                     if (ft->wp->base.mode & MODE_WRAP)
2621                               return (xstrdup("1"));
2622                     return (xstrdup("0"));
2623           }
2624           return (NULL);
2625 }
2626 
2627 /* Callback for buffer_created. */
2628 static void *
format_cb_buffer_created(struct format_tree * ft)2629 format_cb_buffer_created(struct format_tree *ft)
2630 {
2631           static struct timeval          tv;
2632 
2633           if (ft->pb != NULL) {
2634                     timerclear(&tv);
2635                     tv.tv_sec = paste_buffer_created(ft->pb);
2636                     return (&tv);
2637           }
2638           return (NULL);
2639 }
2640 
2641 /* Callback for client_activity. */
2642 static void *
format_cb_client_activity(struct format_tree * ft)2643 format_cb_client_activity(struct format_tree *ft)
2644 {
2645           if (ft->c != NULL)
2646                     return (&ft->c->activity_time);
2647           return (NULL);
2648 }
2649 
2650 /* Callback for client_created. */
2651 static void *
format_cb_client_created(struct format_tree * ft)2652 format_cb_client_created(struct format_tree *ft)
2653 {
2654           if (ft->c != NULL)
2655                     return (&ft->c->creation_time);
2656           return (NULL);
2657 }
2658 
2659 /* Callback for session_activity. */
2660 static void *
format_cb_session_activity(struct format_tree * ft)2661 format_cb_session_activity(struct format_tree *ft)
2662 {
2663           if (ft->s != NULL)
2664                     return (&ft->s->activity_time);
2665           return (NULL);
2666 }
2667 
2668 /* Callback for session_created. */
2669 static void *
format_cb_session_created(struct format_tree * ft)2670 format_cb_session_created(struct format_tree *ft)
2671 {
2672           if (ft->s != NULL)
2673                     return (&ft->s->creation_time);
2674           return (NULL);
2675 }
2676 
2677 /* Callback for session_last_attached. */
2678 static void *
format_cb_session_last_attached(struct format_tree * ft)2679 format_cb_session_last_attached(struct format_tree *ft)
2680 {
2681           if (ft->s != NULL)
2682                     return (&ft->s->last_attached_time);
2683           return (NULL);
2684 }
2685 
2686 /* Callback for start_time. */
2687 static void *
format_cb_start_time(__unused struct format_tree * ft)2688 format_cb_start_time(__unused struct format_tree *ft)
2689 {
2690           return (&start_time);
2691 }
2692 
2693 /* Callback for window_activity. */
2694 static void *
format_cb_window_activity(struct format_tree * ft)2695 format_cb_window_activity(struct format_tree *ft)
2696 {
2697           if (ft->w != NULL)
2698                     return (&ft->w->activity_time);
2699           return (NULL);
2700 }
2701 
2702 /* Callback for buffer_mode_format, */
2703 static void *
format_cb_buffer_mode_format(__unused struct format_tree * ft)2704 format_cb_buffer_mode_format(__unused struct format_tree *ft)
2705 {
2706           return (xstrdup(window_buffer_mode.default_format));
2707 }
2708 
2709 /* Callback for client_mode_format, */
2710 static void *
format_cb_client_mode_format(__unused struct format_tree * ft)2711 format_cb_client_mode_format(__unused struct format_tree *ft)
2712 {
2713           return (xstrdup(window_client_mode.default_format));
2714 }
2715 
2716 /* Callback for tree_mode_format, */
2717 static void *
format_cb_tree_mode_format(__unused struct format_tree * ft)2718 format_cb_tree_mode_format(__unused struct format_tree *ft)
2719 {
2720           return (xstrdup(window_tree_mode.default_format));
2721 }
2722 
2723 /* Callback for uid. */
2724 static void *
format_cb_uid(__unused struct format_tree * ft)2725 format_cb_uid(__unused struct format_tree *ft)
2726 {
2727           return (format_printf("%ld", (long)getuid()));
2728 }
2729 
2730 /* Callback for user. */
2731 static void *
format_cb_user(__unused struct format_tree * ft)2732 format_cb_user(__unused struct format_tree *ft)
2733 {
2734           struct passwd       *pw;
2735 
2736           if ((pw = getpwuid(getuid())) != NULL)
2737                     return (xstrdup(pw->pw_name));
2738           return (NULL);
2739 }
2740 
2741 /* Format table type. */
2742 enum format_table_type {
2743           FORMAT_TABLE_STRING,
2744           FORMAT_TABLE_TIME
2745 };
2746 
2747 /* Format table entry. */
2748 struct format_table_entry {
2749           const char                    *key;
2750           enum format_table_type         type;
2751           format_cb            cb;
2752 };
2753 
2754 /*
2755  * Format table. Default format variables (that are almost always in the tree
2756  * and where the value is expanded by a callback in this file) are listed here.
2757  * Only variables which are added by the caller go into the tree.
2758  */
2759 static const struct format_table_entry format_table[] = {
2760           { "active_window_index", FORMAT_TABLE_STRING,
2761             format_cb_active_window_index
2762           },
2763           { "alternate_on", FORMAT_TABLE_STRING,
2764             format_cb_alternate_on
2765           },
2766           { "alternate_saved_x", FORMAT_TABLE_STRING,
2767             format_cb_alternate_saved_x
2768           },
2769           { "alternate_saved_y", FORMAT_TABLE_STRING,
2770             format_cb_alternate_saved_y
2771           },
2772           { "buffer_created", FORMAT_TABLE_TIME,
2773             format_cb_buffer_created
2774           },
2775           { "buffer_mode_format", FORMAT_TABLE_STRING,
2776             format_cb_buffer_mode_format
2777           },
2778           { "buffer_name", FORMAT_TABLE_STRING,
2779             format_cb_buffer_name
2780           },
2781           { "buffer_sample", FORMAT_TABLE_STRING,
2782             format_cb_buffer_sample
2783           },
2784           { "buffer_size", FORMAT_TABLE_STRING,
2785             format_cb_buffer_size
2786           },
2787           { "client_activity", FORMAT_TABLE_TIME,
2788             format_cb_client_activity
2789           },
2790           { "client_cell_height", FORMAT_TABLE_STRING,
2791             format_cb_client_cell_height
2792           },
2793           { "client_cell_width", FORMAT_TABLE_STRING,
2794             format_cb_client_cell_width
2795           },
2796           { "client_control_mode", FORMAT_TABLE_STRING,
2797             format_cb_client_control_mode
2798           },
2799           { "client_created", FORMAT_TABLE_TIME,
2800             format_cb_client_created
2801           },
2802           { "client_discarded", FORMAT_TABLE_STRING,
2803             format_cb_client_discarded
2804           },
2805           { "client_flags", FORMAT_TABLE_STRING,
2806             format_cb_client_flags
2807           },
2808           { "client_height", FORMAT_TABLE_STRING,
2809             format_cb_client_height
2810           },
2811           { "client_key_table", FORMAT_TABLE_STRING,
2812             format_cb_client_key_table
2813           },
2814           { "client_last_session", FORMAT_TABLE_STRING,
2815             format_cb_client_last_session
2816           },
2817           { "client_mode_format", FORMAT_TABLE_STRING,
2818             format_cb_client_mode_format
2819           },
2820           { "client_name", FORMAT_TABLE_STRING,
2821             format_cb_client_name
2822           },
2823           { "client_pid", FORMAT_TABLE_STRING,
2824             format_cb_client_pid
2825           },
2826           { "client_prefix", FORMAT_TABLE_STRING,
2827             format_cb_client_prefix
2828           },
2829           { "client_readonly", FORMAT_TABLE_STRING,
2830             format_cb_client_readonly
2831           },
2832           { "client_session", FORMAT_TABLE_STRING,
2833             format_cb_client_session
2834           },
2835           { "client_termfeatures", FORMAT_TABLE_STRING,
2836             format_cb_client_termfeatures
2837           },
2838           { "client_termname", FORMAT_TABLE_STRING,
2839             format_cb_client_termname
2840           },
2841           { "client_termtype", FORMAT_TABLE_STRING,
2842             format_cb_client_termtype
2843           },
2844           { "client_tty", FORMAT_TABLE_STRING,
2845             format_cb_client_tty
2846           },
2847           { "client_uid", FORMAT_TABLE_STRING,
2848             format_cb_client_uid
2849           },
2850           { "client_user", FORMAT_TABLE_STRING,
2851             format_cb_client_user
2852           },
2853           { "client_utf8", FORMAT_TABLE_STRING,
2854             format_cb_client_utf8
2855           },
2856           { "client_width", FORMAT_TABLE_STRING,
2857             format_cb_client_width
2858           },
2859           { "client_written", FORMAT_TABLE_STRING,
2860             format_cb_client_written
2861           },
2862           { "config_files", FORMAT_TABLE_STRING,
2863             format_cb_config_files
2864           },
2865           { "cursor_character", FORMAT_TABLE_STRING,
2866             format_cb_cursor_character
2867           },
2868           { "cursor_flag", FORMAT_TABLE_STRING,
2869             format_cb_cursor_flag
2870           },
2871           { "cursor_x", FORMAT_TABLE_STRING,
2872             format_cb_cursor_x
2873           },
2874           { "cursor_y", FORMAT_TABLE_STRING,
2875             format_cb_cursor_y
2876           },
2877           { "history_all_bytes", FORMAT_TABLE_STRING,
2878             format_cb_history_all_bytes
2879           },
2880           { "history_bytes", FORMAT_TABLE_STRING,
2881             format_cb_history_bytes
2882           },
2883           { "history_limit", FORMAT_TABLE_STRING,
2884             format_cb_history_limit
2885           },
2886           { "history_size", FORMAT_TABLE_STRING,
2887             format_cb_history_size
2888           },
2889           { "host", FORMAT_TABLE_STRING,
2890             format_cb_host
2891           },
2892           { "host_short", FORMAT_TABLE_STRING,
2893             format_cb_host_short
2894           },
2895           { "insert_flag", FORMAT_TABLE_STRING,
2896             format_cb_insert_flag
2897           },
2898           { "keypad_cursor_flag", FORMAT_TABLE_STRING,
2899             format_cb_keypad_cursor_flag
2900           },
2901           { "keypad_flag", FORMAT_TABLE_STRING,
2902             format_cb_keypad_flag
2903           },
2904           { "last_window_index", FORMAT_TABLE_STRING,
2905             format_cb_last_window_index
2906           },
2907           { "mouse_all_flag", FORMAT_TABLE_STRING,
2908             format_cb_mouse_all_flag
2909           },
2910           { "mouse_any_flag", FORMAT_TABLE_STRING,
2911             format_cb_mouse_any_flag
2912           },
2913           { "mouse_button_flag", FORMAT_TABLE_STRING,
2914             format_cb_mouse_button_flag
2915           },
2916           { "mouse_hyperlink", FORMAT_TABLE_STRING,
2917             format_cb_mouse_hyperlink
2918           },
2919           { "mouse_line", FORMAT_TABLE_STRING,
2920             format_cb_mouse_line
2921           },
2922           { "mouse_pane", FORMAT_TABLE_STRING,
2923             format_cb_mouse_pane
2924           },
2925           { "mouse_sgr_flag", FORMAT_TABLE_STRING,
2926             format_cb_mouse_sgr_flag
2927           },
2928           { "mouse_standard_flag", FORMAT_TABLE_STRING,
2929             format_cb_mouse_standard_flag
2930           },
2931           { "mouse_status_line", FORMAT_TABLE_STRING,
2932             format_cb_mouse_status_line
2933           },
2934           { "mouse_status_range", FORMAT_TABLE_STRING,
2935             format_cb_mouse_status_range
2936           },
2937           { "mouse_utf8_flag", FORMAT_TABLE_STRING,
2938             format_cb_mouse_utf8_flag
2939           },
2940           { "mouse_word", FORMAT_TABLE_STRING,
2941             format_cb_mouse_word
2942           },
2943           { "mouse_x", FORMAT_TABLE_STRING,
2944             format_cb_mouse_x
2945           },
2946           { "mouse_y", FORMAT_TABLE_STRING,
2947             format_cb_mouse_y
2948           },
2949           { "next_session_id", FORMAT_TABLE_STRING,
2950             format_cb_next_session_id
2951           },
2952           { "origin_flag", FORMAT_TABLE_STRING,
2953             format_cb_origin_flag
2954           },
2955           { "pane_active", FORMAT_TABLE_STRING,
2956             format_cb_pane_active
2957           },
2958           { "pane_at_bottom", FORMAT_TABLE_STRING,
2959             format_cb_pane_at_bottom
2960           },
2961           { "pane_at_left", FORMAT_TABLE_STRING,
2962             format_cb_pane_at_left
2963           },
2964           { "pane_at_right", FORMAT_TABLE_STRING,
2965             format_cb_pane_at_right
2966           },
2967           { "pane_at_top", FORMAT_TABLE_STRING,
2968             format_cb_pane_at_top
2969           },
2970           { "pane_bg", FORMAT_TABLE_STRING,
2971             format_cb_pane_bg
2972           },
2973           { "pane_bottom", FORMAT_TABLE_STRING,
2974             format_cb_pane_bottom
2975           },
2976           { "pane_current_command", FORMAT_TABLE_STRING,
2977             format_cb_current_command
2978           },
2979           { "pane_current_path", FORMAT_TABLE_STRING,
2980             format_cb_current_path
2981           },
2982           { "pane_dead", FORMAT_TABLE_STRING,
2983             format_cb_pane_dead
2984           },
2985           { "pane_dead_signal", FORMAT_TABLE_STRING,
2986             format_cb_pane_dead_signal
2987           },
2988           { "pane_dead_status", FORMAT_TABLE_STRING,
2989             format_cb_pane_dead_status
2990           },
2991           { "pane_dead_time", FORMAT_TABLE_TIME,
2992             format_cb_pane_dead_time
2993           },
2994           { "pane_fg", FORMAT_TABLE_STRING,
2995             format_cb_pane_fg
2996           },
2997           { "pane_format", FORMAT_TABLE_STRING,
2998             format_cb_pane_format
2999           },
3000           { "pane_height", FORMAT_TABLE_STRING,
3001             format_cb_pane_height
3002           },
3003           { "pane_id", FORMAT_TABLE_STRING,
3004             format_cb_pane_id
3005           },
3006           { "pane_in_mode", FORMAT_TABLE_STRING,
3007             format_cb_pane_in_mode
3008           },
3009           { "pane_index", FORMAT_TABLE_STRING,
3010             format_cb_pane_index
3011           },
3012           { "pane_input_off", FORMAT_TABLE_STRING,
3013             format_cb_pane_input_off
3014           },
3015           { "pane_key_mode", FORMAT_TABLE_STRING,
3016             format_cb_pane_key_mode
3017           },
3018           { "pane_last", FORMAT_TABLE_STRING,
3019             format_cb_pane_last
3020           },
3021           { "pane_left", FORMAT_TABLE_STRING,
3022             format_cb_pane_left
3023           },
3024           { "pane_marked", FORMAT_TABLE_STRING,
3025             format_cb_pane_marked
3026           },
3027           { "pane_marked_set", FORMAT_TABLE_STRING,
3028             format_cb_pane_marked_set
3029           },
3030           { "pane_mode", FORMAT_TABLE_STRING,
3031             format_cb_pane_mode
3032           },
3033           { "pane_path", FORMAT_TABLE_STRING,
3034             format_cb_pane_path
3035           },
3036           { "pane_pid", FORMAT_TABLE_STRING,
3037             format_cb_pane_pid
3038           },
3039           { "pane_pipe", FORMAT_TABLE_STRING,
3040             format_cb_pane_pipe
3041           },
3042           { "pane_right", FORMAT_TABLE_STRING,
3043             format_cb_pane_right
3044           },
3045           { "pane_search_string", FORMAT_TABLE_STRING,
3046             format_cb_pane_search_string
3047           },
3048           { "pane_start_command", FORMAT_TABLE_STRING,
3049             format_cb_start_command
3050           },
3051           { "pane_start_path", FORMAT_TABLE_STRING,
3052             format_cb_start_path
3053           },
3054           { "pane_synchronized", FORMAT_TABLE_STRING,
3055             format_cb_pane_synchronized
3056           },
3057           { "pane_tabs", FORMAT_TABLE_STRING,
3058             format_cb_pane_tabs
3059           },
3060           { "pane_title", FORMAT_TABLE_STRING,
3061             format_cb_pane_title
3062           },
3063           { "pane_top", FORMAT_TABLE_STRING,
3064             format_cb_pane_top
3065           },
3066           { "pane_tty", FORMAT_TABLE_STRING,
3067             format_cb_pane_tty
3068           },
3069           { "pane_unseen_changes", FORMAT_TABLE_STRING,
3070             format_cb_pane_unseen_changes
3071           },
3072           { "pane_width", FORMAT_TABLE_STRING,
3073             format_cb_pane_width
3074           },
3075           { "pid", FORMAT_TABLE_STRING,
3076             format_cb_pid
3077           },
3078           { "scroll_region_lower", FORMAT_TABLE_STRING,
3079             format_cb_scroll_region_lower
3080           },
3081           { "scroll_region_upper", FORMAT_TABLE_STRING,
3082             format_cb_scroll_region_upper
3083           },
3084           { "server_sessions", FORMAT_TABLE_STRING,
3085             format_cb_server_sessions
3086           },
3087           { "session_activity", FORMAT_TABLE_TIME,
3088             format_cb_session_activity
3089           },
3090           { "session_alerts", FORMAT_TABLE_STRING,
3091             format_cb_session_alerts
3092           },
3093           { "session_attached", FORMAT_TABLE_STRING,
3094             format_cb_session_attached
3095           },
3096           { "session_attached_list", FORMAT_TABLE_STRING,
3097             format_cb_session_attached_list
3098           },
3099           { "session_created", FORMAT_TABLE_TIME,
3100             format_cb_session_created
3101           },
3102           { "session_format", FORMAT_TABLE_STRING,
3103             format_cb_session_format
3104           },
3105           { "session_group", FORMAT_TABLE_STRING,
3106             format_cb_session_group
3107           },
3108           { "session_group_attached", FORMAT_TABLE_STRING,
3109             format_cb_session_group_attached
3110           },
3111           { "session_group_attached_list", FORMAT_TABLE_STRING,
3112             format_cb_session_group_attached_list
3113           },
3114           { "session_group_list", FORMAT_TABLE_STRING,
3115             format_cb_session_group_list
3116           },
3117           { "session_group_many_attached", FORMAT_TABLE_STRING,
3118             format_cb_session_group_many_attached
3119           },
3120           { "session_group_size", FORMAT_TABLE_STRING,
3121             format_cb_session_group_size
3122           },
3123           { "session_grouped", FORMAT_TABLE_STRING,
3124             format_cb_session_grouped
3125           },
3126           { "session_id", FORMAT_TABLE_STRING,
3127             format_cb_session_id
3128           },
3129           { "session_last_attached", FORMAT_TABLE_TIME,
3130             format_cb_session_last_attached
3131           },
3132           { "session_many_attached", FORMAT_TABLE_STRING,
3133             format_cb_session_many_attached
3134           },
3135           { "session_marked", FORMAT_TABLE_STRING,
3136             format_cb_session_marked,
3137           },
3138           { "session_name", FORMAT_TABLE_STRING,
3139             format_cb_session_name
3140           },
3141           { "session_path", FORMAT_TABLE_STRING,
3142             format_cb_session_path
3143           },
3144           { "session_stack", FORMAT_TABLE_STRING,
3145             format_cb_session_stack
3146           },
3147           { "session_windows", FORMAT_TABLE_STRING,
3148             format_cb_session_windows
3149           },
3150           { "socket_path", FORMAT_TABLE_STRING,
3151             format_cb_socket_path
3152           },
3153           { "start_time", FORMAT_TABLE_TIME,
3154             format_cb_start_time
3155           },
3156           { "tree_mode_format", FORMAT_TABLE_STRING,
3157             format_cb_tree_mode_format
3158           },
3159           { "uid", FORMAT_TABLE_STRING,
3160             format_cb_uid
3161           },
3162           { "user", FORMAT_TABLE_STRING,
3163             format_cb_user
3164           },
3165           { "version", FORMAT_TABLE_STRING,
3166             format_cb_version
3167           },
3168           { "window_active", FORMAT_TABLE_STRING,
3169             format_cb_window_active
3170           },
3171           { "window_active_clients", FORMAT_TABLE_STRING,
3172             format_cb_window_active_clients
3173           },
3174           { "window_active_clients_list", FORMAT_TABLE_STRING,
3175             format_cb_window_active_clients_list
3176           },
3177           { "window_active_sessions", FORMAT_TABLE_STRING,
3178             format_cb_window_active_sessions
3179           },
3180           { "window_active_sessions_list", FORMAT_TABLE_STRING,
3181             format_cb_window_active_sessions_list
3182           },
3183           { "window_activity", FORMAT_TABLE_TIME,
3184             format_cb_window_activity
3185           },
3186           { "window_activity_flag", FORMAT_TABLE_STRING,
3187             format_cb_window_activity_flag
3188           },
3189           { "window_bell_flag", FORMAT_TABLE_STRING,
3190             format_cb_window_bell_flag
3191           },
3192           { "window_bigger", FORMAT_TABLE_STRING,
3193             format_cb_window_bigger
3194           },
3195           { "window_cell_height", FORMAT_TABLE_STRING,
3196             format_cb_window_cell_height
3197           },
3198           { "window_cell_width", FORMAT_TABLE_STRING,
3199             format_cb_window_cell_width
3200           },
3201           { "window_end_flag", FORMAT_TABLE_STRING,
3202             format_cb_window_end_flag
3203           },
3204           { "window_flags", FORMAT_TABLE_STRING,
3205             format_cb_window_flags
3206           },
3207           { "window_format", FORMAT_TABLE_STRING,
3208             format_cb_window_format
3209           },
3210           { "window_height", FORMAT_TABLE_STRING,
3211             format_cb_window_height
3212           },
3213           { "window_id", FORMAT_TABLE_STRING,
3214             format_cb_window_id
3215           },
3216           { "window_index", FORMAT_TABLE_STRING,
3217             format_cb_window_index
3218           },
3219           { "window_last_flag", FORMAT_TABLE_STRING,
3220             format_cb_window_last_flag
3221           },
3222           { "window_layout", FORMAT_TABLE_STRING,
3223             format_cb_window_layout
3224           },
3225           { "window_linked", FORMAT_TABLE_STRING,
3226             format_cb_window_linked
3227           },
3228           { "window_linked_sessions", FORMAT_TABLE_STRING,
3229             format_cb_window_linked_sessions
3230           },
3231           { "window_linked_sessions_list", FORMAT_TABLE_STRING,
3232             format_cb_window_linked_sessions_list
3233           },
3234           { "window_marked_flag", FORMAT_TABLE_STRING,
3235             format_cb_window_marked_flag
3236           },
3237           { "window_name", FORMAT_TABLE_STRING,
3238             format_cb_window_name
3239           },
3240           { "window_offset_x", FORMAT_TABLE_STRING,
3241             format_cb_window_offset_x
3242           },
3243           { "window_offset_y", FORMAT_TABLE_STRING,
3244             format_cb_window_offset_y
3245           },
3246           { "window_panes", FORMAT_TABLE_STRING,
3247             format_cb_window_panes
3248           },
3249           { "window_raw_flags", FORMAT_TABLE_STRING,
3250             format_cb_window_raw_flags
3251           },
3252           { "window_silence_flag", FORMAT_TABLE_STRING,
3253             format_cb_window_silence_flag
3254           },
3255           { "window_stack_index", FORMAT_TABLE_STRING,
3256             format_cb_window_stack_index
3257           },
3258           { "window_start_flag", FORMAT_TABLE_STRING,
3259             format_cb_window_start_flag
3260           },
3261           { "window_visible_layout", FORMAT_TABLE_STRING,
3262             format_cb_window_visible_layout
3263           },
3264           { "window_width", FORMAT_TABLE_STRING,
3265             format_cb_window_width
3266           },
3267           { "window_zoomed_flag", FORMAT_TABLE_STRING,
3268             format_cb_window_zoomed_flag
3269           },
3270           { "wrap_flag", FORMAT_TABLE_STRING,
3271             format_cb_wrap_flag
3272           }
3273 };
3274 
3275 /* Compare format table entries. */
3276 static int
format_table_compare(const void * key0,const void * entry0)3277 format_table_compare(const void *key0, const void *entry0)
3278 {
3279           const char                              *key = key0;
3280           const struct format_table_entry         *entry = entry0;
3281 
3282           return (strcmp(key, entry->key));
3283 }
3284 
3285 /* Get a format callback. */
3286 static struct format_table_entry *
format_table_get(const char * key)3287 format_table_get(const char *key)
3288 {
3289           return (bsearch(key, format_table, nitems(format_table),
3290               sizeof *format_table, format_table_compare));
3291 }
3292 
3293 /* Merge one format tree into another. */
3294 void
format_merge(struct format_tree * ft,struct format_tree * from)3295 format_merge(struct format_tree *ft, struct format_tree *from)
3296 {
3297           struct format_entry *fe;
3298 
3299           RB_FOREACH(fe, format_entry_tree, &from->tree) {
3300                     if (fe->value != NULL)
3301                               format_add(ft, fe->key, "%s", fe->value);
3302           }
3303 }
3304 
3305 /* Get format pane. */
3306 struct window_pane *
format_get_pane(struct format_tree * ft)3307 format_get_pane(struct format_tree *ft)
3308 {
3309           return (ft->wp);
3310 }
3311 
3312 /* Add item bits to tree. */
3313 static void
format_create_add_item(struct format_tree * ft,struct cmdq_item * item)3314 format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
3315 {
3316           struct key_event    *event = cmdq_get_event(item);
3317           struct mouse_event  *m = &event->m;
3318 
3319           cmdq_merge_formats(item, ft);
3320           memcpy(&ft->m, m, sizeof ft->m);
3321 }
3322 
3323 /* Create a new tree. */
3324 struct format_tree *
format_create(struct client * c,struct cmdq_item * item,int tag,int flags)3325 format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
3326 {
3327           struct format_tree  *ft;
3328 
3329           ft = xcalloc(1, sizeof *ft);
3330           RB_INIT(&ft->tree);
3331 
3332           if (c != NULL) {
3333                     ft->client = c;
3334                     ft->client->references++;
3335           }
3336           ft->item = item;
3337 
3338           ft->tag = tag;
3339           ft->flags = flags;
3340 
3341           if (item != NULL)
3342                     format_create_add_item(ft, item);
3343 
3344           return (ft);
3345 }
3346 
3347 /* Free a tree. */
3348 void
format_free(struct format_tree * ft)3349 format_free(struct format_tree *ft)
3350 {
3351           struct format_entry *fe, *fe1;
3352 
3353           RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
3354                     RB_REMOVE(format_entry_tree, &ft->tree, fe);
3355                     free(fe->value);
3356                     free(fe->key);
3357                     free(fe);
3358           }
3359 
3360           if (ft->client != NULL)
3361                     server_client_unref(ft->client);
3362           free(ft);
3363 }
3364 
3365 /* Log each format. */
3366 static void
format_log_debug_cb(const char * key,const char * value,void * arg)3367 format_log_debug_cb(const char *key, const char *value, void *arg)
3368 {
3369           const char          *prefix = arg;
3370 
3371           log_debug("%s: %s=%s", prefix, key, value);
3372 }
3373 
3374 /* Log a format tree. */
3375 void
format_log_debug(struct format_tree * ft,const char * prefix)3376 format_log_debug(struct format_tree *ft, const char *prefix)
3377 {
3378           format_each(ft, format_log_debug_cb, __UNCONST(prefix));
3379 }
3380 
3381 /* Walk each format. */
3382 void
format_each(struct format_tree * ft,void (* cb)(const char *,const char *,void *),void * arg)3383 format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
3384     void *), void *arg)
3385 {
3386           const struct format_table_entry         *fte;
3387           struct format_entry           *fe;
3388           u_int                                    i;
3389           char                                     s[64];
3390           void                                    *value;
3391           struct timeval                          *tv;
3392 
3393           for (i = 0; i < nitems(format_table); i++) {
3394                     fte = &format_table[i];
3395 
3396                     value = fte->cb(ft);
3397                     if (value == NULL)
3398                               continue;
3399                     if (fte->type == FORMAT_TABLE_TIME) {
3400                               tv = value;
3401                               xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec);
3402                               cb(fte->key, s, arg);
3403                     } else {
3404                               cb(fte->key, value, arg);
3405                               free(value);
3406                     }
3407           }
3408           RB_FOREACH(fe, format_entry_tree, &ft->tree) {
3409                     if (fe->time != 0) {
3410                               xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
3411                               cb(fe->key, s, arg);
3412                     } else {
3413                               if (fe->value == NULL && fe->cb != NULL) {
3414                                         fe->value = fe->cb(ft);
3415                                         if (fe->value == NULL)
3416                                                   fe->value = xstrdup("");
3417                               }
3418                               cb(fe->key, fe->value, arg);
3419                     }
3420           }
3421 }
3422 
3423 /* Add a key-value pair. */
3424 void
format_add(struct format_tree * ft,const char * key,const char * fmt,...)3425 format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
3426 {
3427           struct format_entry *fe;
3428           struct format_entry *fe_now;
3429           va_list                        ap;
3430 
3431           fe = xmalloc(sizeof *fe);
3432           fe->key = xstrdup(key);
3433 
3434           fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3435           if (fe_now != NULL) {
3436                     free(fe->key);
3437                     free(fe);
3438                     free(fe_now->value);
3439                     fe = fe_now;
3440           }
3441 
3442           fe->cb = NULL;
3443           fe->time = 0;
3444 
3445           va_start(ap, fmt);
3446           xvasprintf(&fe->value, fmt, ap);
3447           va_end(ap);
3448 }
3449 
3450 /* Add a key and time. */
3451 void
format_add_tv(struct format_tree * ft,const char * key,struct timeval * tv)3452 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
3453 {
3454           struct format_entry *fe, *fe_now;
3455 
3456           fe = xmalloc(sizeof *fe);
3457           fe->key = xstrdup(key);
3458 
3459           fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3460           if (fe_now != NULL) {
3461                     free(fe->key);
3462                     free(fe);
3463                     free(fe_now->value);
3464                     fe = fe_now;
3465           }
3466 
3467           fe->cb = NULL;
3468           fe->time = tv->tv_sec;
3469 
3470           fe->value = NULL;
3471 }
3472 
3473 /* Add a key and function. */
3474 void
format_add_cb(struct format_tree * ft,const char * key,format_cb cb)3475 format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
3476 {
3477           struct format_entry *fe;
3478           struct format_entry *fe_now;
3479 
3480           fe = xmalloc(sizeof *fe);
3481           fe->key = xstrdup(key);
3482 
3483           fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
3484           if (fe_now != NULL) {
3485                     free(fe->key);
3486                     free(fe);
3487                     free(fe_now->value);
3488                     fe = fe_now;
3489           }
3490 
3491           fe->cb = cb;
3492           fe->time = 0;
3493 
3494           fe->value = NULL;
3495 }
3496 
3497 /* Quote shell special characters in string. */
3498 static char *
format_quote_shell(const char * s)3499 format_quote_shell(const char *s)
3500 {
3501           const char          *cp;
3502           char                *out, *at;
3503 
3504           at = out = xmalloc(strlen(s) * 2 + 1);
3505           for (cp = s; *cp != '\0'; cp++) {
3506                     if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL)
3507                               *at++ = '\\';
3508                     *at++ = *cp;
3509           }
3510           *at = '\0';
3511           return (out);
3512 }
3513 
3514 /* Quote #s in string. */
3515 static char *
format_quote_style(const char * s)3516 format_quote_style(const char *s)
3517 {
3518           const char          *cp;
3519           char                *out, *at;
3520 
3521           at = out = xmalloc(strlen(s) * 2 + 1);
3522           for (cp = s; *cp != '\0'; cp++) {
3523                     if (*cp == '#')
3524                               *at++ = '#';
3525                     *at++ = *cp;
3526           }
3527           *at = '\0';
3528           return (out);
3529 }
3530 
3531 /* Make a prettier time. */
3532 char *
format_pretty_time(time_t t,int seconds)3533 format_pretty_time(time_t t, int seconds)
3534 {
3535           struct tm       now_tm, tm;
3536           time_t              now, age;
3537           char                s[9];
3538 
3539           time(&now);
3540           if (now < t)
3541                     now = t;
3542           age = now - t;
3543 
3544           localtime_r(&now, &now_tm);
3545           localtime_r(&t, &tm);
3546 
3547           /* Last 24 hours. */
3548           if (age < 24 * 3600) {
3549                     if (seconds)
3550                               strftime(s, sizeof s, "%H:%M:%S", &tm);
3551                     else
3552                               strftime(s, sizeof s, "%H:%M", &tm);
3553                     return (xstrdup(s));
3554           }
3555 
3556           /* This month or last 28 days. */
3557           if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) ||
3558               age < 28 * 24 * 3600) {
3559                     strftime(s, sizeof s, "%a%d", &tm);
3560                     return (xstrdup(s));
3561           }
3562 
3563           /* Last 12 months. */
3564           if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) ||
3565               (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) {
3566                     strftime(s, sizeof s, "%d%b", &tm);
3567                     return (xstrdup(s));
3568           }
3569 
3570           /* Older than that. */
3571           strftime(s, sizeof s, "%h%Y", &tm);
3572           return (xstrdup(s));
3573 }
3574 
3575 /* Find a format entry. */
3576 static char *
format_find(struct format_tree * ft,const char * key,int modifiers,const char * time_format)3577 format_find(struct format_tree *ft, const char *key, int modifiers,
3578     const char *time_format)
3579 {
3580           struct format_table_entry     *fte;
3581           void                                    *value;
3582           struct format_entry           *fe, fe_find;
3583           struct environ_entry                    *envent;
3584           struct options_entry                    *o;
3585           int                                      idx;
3586           char                                    *found = NULL, *saved, s[512];
3587           const char                              *errstr;
3588           time_t                                   t = 0;
3589           struct tm                      tm;
3590 
3591           o = options_parse_get(global_options, key, &idx, 0);
3592           if (o == NULL && ft->wp != NULL)
3593                     o = options_parse_get(ft->wp->options, key, &idx, 0);
3594           if (o == NULL && ft->w != NULL)
3595                     o = options_parse_get(ft->w->options, key, &idx, 0);
3596           if (o == NULL)
3597                     o = options_parse_get(global_w_options, key, &idx, 0);
3598           if (o == NULL && ft->s != NULL)
3599                     o = options_parse_get(ft->s->options, key, &idx, 0);
3600           if (o == NULL)
3601                     o = options_parse_get(global_s_options, key, &idx, 0);
3602           if (o != NULL) {
3603                     found = options_to_string(o, idx, 1);
3604                     goto found;
3605           }
3606 
3607           fte = format_table_get(key);
3608           if (fte != NULL) {
3609                     value = fte->cb(ft);
3610                     if (fte->type == FORMAT_TABLE_TIME && value != NULL)
3611                               t = ((struct timeval *)value)->tv_sec;
3612                     else
3613                               found = value;
3614                     goto found;
3615           }
3616           fe_find.key = __UNCONST(key);
3617           fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
3618           if (fe != NULL) {
3619                     if (fe->time != 0) {
3620                               t = fe->time;
3621                               goto found;
3622                     }
3623                     if (fe->value == NULL && fe->cb != NULL) {
3624                               fe->value = fe->cb(ft);
3625                               if (fe->value == NULL)
3626                                         fe->value = xstrdup("");
3627                     }
3628                     found = xstrdup(fe->value);
3629                     goto found;
3630           }
3631 
3632           if (~modifiers & FORMAT_TIMESTRING) {
3633                     envent = NULL;
3634                     if (ft->s != NULL)
3635                               envent = environ_find(ft->s->environ, key);
3636                     if (envent == NULL)
3637                               envent = environ_find(global_environ, key);
3638                     if (envent != NULL && envent->value != NULL) {
3639                               found = xstrdup(envent->value);
3640                               goto found;
3641                     }
3642           }
3643 
3644           return (NULL);
3645 
3646 found:
3647           if (modifiers & FORMAT_TIMESTRING) {
3648                     if (t == 0 && found != NULL) {
3649                               t = strtonum(found, 0, INT64_MAX, &errstr);
3650                               if (errstr != NULL)
3651                                         t = 0;
3652                               free(found);
3653                     }
3654                     if (t == 0)
3655                               return (NULL);
3656                     if (modifiers & FORMAT_PRETTY)
3657                               found = format_pretty_time(t, 0);
3658                     else {
3659                               if (time_format != NULL) {
3660                                         localtime_r(&t, &tm);
3661                                         strftime(s, sizeof s, time_format, &tm);
3662                               } else {
3663                                         ctime_r(&t, s);
3664                                         s[strcspn(s, "\n")] = '\0';
3665                               }
3666                               found = xstrdup(s);
3667                     }
3668                     return (found);
3669           }
3670 
3671           if (t != 0)
3672                     xasprintf(&found, "%lld", (long long)t);
3673           else if (found == NULL)
3674                     return (NULL);
3675           if (modifiers & FORMAT_BASENAME) {
3676                     saved = found;
3677                     found = xstrdup(basename(saved));
3678                     free(saved);
3679           }
3680           if (modifiers & FORMAT_DIRNAME) {
3681                     saved = found;
3682                     found = xstrdup(dirname(saved));
3683                     free(saved);
3684           }
3685           if (modifiers & FORMAT_QUOTE_SHELL) {
3686                     saved = found;
3687                     found = format_quote_shell(saved);
3688                     free(saved);
3689           }
3690           if (modifiers & FORMAT_QUOTE_STYLE) {
3691                     saved = found;
3692                     found = format_quote_style(saved);
3693                     free(saved);
3694           }
3695           return (found);
3696 }
3697 
3698 /* Unescape escaped characters. */
3699 static char *
format_unescape(const char * s)3700 format_unescape(const char *s)
3701 {
3702           char      *out, *cp;
3703           int        brackets = 0;
3704 
3705           cp = out = xmalloc(strlen(s) + 1);
3706           for (; *s != '\0'; s++) {
3707                     if (*s == '#' && s[1] == '{')
3708                               brackets++;
3709                     if (brackets == 0 &&
3710                         *s == '#' &&
3711                         strchr(",#{}:", s[1]) != NULL) {
3712                               *cp++ = *++s;
3713                               continue;
3714                     }
3715                     if (*s == '}')
3716                               brackets--;
3717                     *cp++ = *s;
3718           }
3719           *cp = '\0';
3720           return (out);
3721 }
3722 
3723 /* Remove escaped characters. */
3724 static char *
format_strip(const char * s)3725 format_strip(const char *s)
3726 {
3727           char      *out, *cp;
3728           int        brackets = 0;
3729 
3730           cp = out = xmalloc(strlen(s) + 1);
3731           for (; *s != '\0'; s++) {
3732                     if (*s == '#' && s[1] == '{')
3733                               brackets++;
3734                     if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
3735                               if (brackets != 0)
3736                                         *cp++ = *s;
3737                               continue;
3738                     }
3739                     if (*s == '}')
3740                               brackets--;
3741                     *cp++ = *s;
3742           }
3743           *cp = '\0';
3744           return (out);
3745 }
3746 
3747 /* Skip until end. */
3748 const char *
format_skip(const char * s,const char * end)3749 format_skip(const char *s, const char *end)
3750 {
3751           int       brackets = 0;
3752 
3753           for (; *s != '\0'; s++) {
3754                     if (*s == '#' && s[1] == '{')
3755                               brackets++;
3756                     if (*s == '#' &&
3757                         s[1] != '\0' &&
3758                         strchr(",#{}:", s[1]) != NULL) {
3759                               s++;
3760                               continue;
3761                     }
3762                     if (*s == '}')
3763                               brackets--;
3764                     if (strchr(end, *s) != NULL && brackets == 0)
3765                               break;
3766           }
3767           if (*s == '\0')
3768                     return (NULL);
3769           return __UNCONST(s);
3770 }
3771 
3772 /* Return left and right alternatives separated by commas. */
3773 static int
format_choose(struct format_expand_state * es,const char * s,char ** left,char ** right,int expand)3774 format_choose(struct format_expand_state *es, const char *s, char **left,
3775     char **right, int expand)
3776 {
3777           const char          *cp;
3778           char                *left0, *right0;
3779 
3780           cp = format_skip(s, ",");
3781           if (cp == NULL)
3782                     return (-1);
3783           left0 = xstrndup(s, cp - s);
3784           right0 = xstrdup(cp + 1);
3785 
3786           if (expand) {
3787                     *left = format_expand1(es, left0);
3788                     free(left0);
3789                     *right = format_expand1(es, right0);
3790                     free(right0);
3791           } else {
3792                     *left = left0;
3793                     *right = right0;
3794           }
3795           return (0);
3796 }
3797 
3798 /* Is this true? */
3799 int
format_true(const char * s)3800 format_true(const char *s)
3801 {
3802           if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
3803                     return (1);
3804           return (0);
3805 }
3806 
3807 /* Check if modifier end. */
3808 static int
format_is_end(char c)3809 format_is_end(char c)
3810 {
3811           return (c == ';' || c == ':');
3812 }
3813 
3814 /* Add to modifier list. */
3815 static void
format_add_modifier(struct format_modifier ** list,u_int * count,const char * c,size_t n,char ** argv,int argc)3816 format_add_modifier(struct format_modifier **list, u_int *count,
3817     const char *c, size_t n, char **argv, int argc)
3818 {
3819           struct format_modifier *fm;
3820 
3821           *list = xreallocarray(*list, (*count) + 1, sizeof **list);
3822           fm = &(*list)[(*count)++];
3823 
3824           memcpy(fm->modifier, c, n);
3825           fm->modifier[n] = '\0';
3826           fm->size = n;
3827 
3828           fm->argv = argv;
3829           fm->argc = argc;
3830 }
3831 
3832 /* Free modifier list. */
3833 static void
format_free_modifiers(struct format_modifier * list,u_int count)3834 format_free_modifiers(struct format_modifier *list, u_int count)
3835 {
3836           u_int     i;
3837 
3838           for (i = 0; i < count; i++)
3839                     cmd_free_argv(list[i].argc, list[i].argv);
3840           free(list);
3841 }
3842 
3843 /* Build modifier list. */
3844 static struct format_modifier *
format_build_modifiers(struct format_expand_state * es,const char ** s,u_int * count)3845 format_build_modifiers(struct format_expand_state *es, const char **s,
3846     u_int *count)
3847 {
3848           const char                    *cp = *s, *end;
3849           struct format_modifier        *list = NULL;
3850           char                           c, last[] = "X;:", **argv, *value;
3851           int                            argc;
3852 
3853           /*
3854            * Modifiers are a ; separated list of the forms:
3855            *      l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,>
3856            *        =a
3857            *        =/a
3858            *      =/a/
3859            *        s/a/b/
3860            *        s/a/b
3861            *        ||,&&,!=,==,<=,>=
3862            */
3863 
3864           *count = 0;
3865 
3866           while (*cp != '\0' && *cp != ':') {
3867                     /* Skip any separator character. */
3868                     if (*cp == ';')
3869                               cp++;
3870 
3871                     /* Check single character modifiers with no arguments. */
3872                     if (strchr("labcdnwETSWPL<>", cp[0]) != NULL &&
3873                         format_is_end(cp[1])) {
3874                               format_add_modifier(&list, count, cp, 1, NULL, 0);
3875                               cp++;
3876                               continue;
3877                     }
3878 
3879                     /* Then try double character with no arguments. */
3880                     if ((memcmp("||", cp, 2) == 0 ||
3881                         memcmp("&&", cp, 2) == 0 ||
3882                         memcmp("!=", cp, 2) == 0 ||
3883                         memcmp("==", cp, 2) == 0 ||
3884                         memcmp("<=", cp, 2) == 0 ||
3885                         memcmp(">=", cp, 2) == 0) &&
3886                         format_is_end(cp[2])) {
3887                               format_add_modifier(&list, count, cp, 2, NULL, 0);
3888                               cp += 2;
3889                               continue;
3890                     }
3891 
3892                     /* Now try single character with arguments. */
3893                     if (strchr("mCNst=peq", cp[0]) == NULL)
3894                               break;
3895                     c = cp[0];
3896 
3897                     /* No arguments provided. */
3898                     if (format_is_end(cp[1])) {
3899                               format_add_modifier(&list, count, cp, 1, NULL, 0);
3900                               cp++;
3901                               continue;
3902                     }
3903                     argv = NULL;
3904                     argc = 0;
3905 
3906                     /* Single argument with no wrapper character. */
3907                     if (!ispunct((u_char)cp[1]) || cp[1] == '-') {
3908                               end = format_skip(cp + 1, ":;");
3909                               if (end == NULL)
3910                                         break;
3911 
3912                               argv = xcalloc(1, sizeof *argv);
3913                               value = xstrndup(cp + 1, end - (cp + 1));
3914                               argv[0] = format_expand1(es, value);
3915                               free(value);
3916                               argc = 1;
3917 
3918                               format_add_modifier(&list, count, &c, 1, argv, argc);
3919                               cp = end;
3920                               continue;
3921                     }
3922 
3923                     /* Multiple arguments with a wrapper character. */
3924                     last[0] = cp[1];
3925                     cp++;
3926                     do {
3927                               if (cp[0] == last[0] && format_is_end(cp[1])) {
3928                                         cp++;
3929                                         break;
3930                               }
3931                               end = format_skip(cp + 1, last);
3932                               if (end == NULL)
3933                                         break;
3934                               cp++;
3935 
3936                               argv = xreallocarray(argv, argc + 1, sizeof *argv);
3937                               value = xstrndup(cp, end - cp);
3938                               argv[argc++] = format_expand1(es, value);
3939                               free(value);
3940 
3941                               cp = end;
3942                     } while (!format_is_end(cp[0]));
3943                     format_add_modifier(&list, count, &c, 1, argv, argc);
3944           }
3945           if (*cp != ':') {
3946                     format_free_modifiers(list, *count);
3947                     *count = 0;
3948                     return (NULL);
3949           }
3950           *s = cp + 1;
3951           return (list);
3952 }
3953 
3954 /* Match against an fnmatch(3) pattern or regular expression. */
3955 static char *
format_match(struct format_modifier * fm,const char * pattern,const char * text)3956 format_match(struct format_modifier *fm, const char *pattern, const char *text)
3957 {
3958           const char          *s = "";
3959           regex_t              r;
3960           int                  flags = 0;
3961 
3962           if (fm->argc >= 1)
3963                     s = fm->argv[0];
3964           if (strchr(s, 'r') == NULL) {
3965                     if (strchr(s, 'i') != NULL)
3966                               flags |= FNM_CASEFOLD;
3967                     if (fnmatch(pattern, text, flags) != 0)
3968                               return (xstrdup("0"));
3969           } else {
3970                     flags = REG_EXTENDED|REG_NOSUB;
3971                     if (strchr(s, 'i') != NULL)
3972                               flags |= REG_ICASE;
3973                     if (regcomp(&r, pattern, flags) != 0)
3974                               return (xstrdup("0"));
3975                     if (regexec(&r, text, 0, NULL, 0) != 0) {
3976                               regfree(&r);
3977                               return (xstrdup("0"));
3978                     }
3979                     regfree(&r);
3980           }
3981           return (xstrdup("1"));
3982 }
3983 
3984 /* Perform substitution in string. */
3985 static char *
format_sub(struct format_modifier * fm,const char * text,const char * pattern,const char * with)3986 format_sub(struct format_modifier *fm, const char *text, const char *pattern,
3987     const char *with)
3988 {
3989           char      *value;
3990           int        flags = REG_EXTENDED;
3991 
3992           if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
3993                     flags |= REG_ICASE;
3994           value = regsub(pattern, with, text, flags);
3995           if (value == NULL)
3996                     return (xstrdup(text));
3997           return (value);
3998 }
3999 
4000 /* Search inside pane. */
4001 static char *
format_search(struct format_modifier * fm,struct window_pane * wp,const char * s)4002 format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
4003 {
4004           int        ignore = 0, regex = 0;
4005           char      *value;
4006 
4007           if (fm->argc >= 1) {
4008                     if (strchr(fm->argv[0], 'i') != NULL)
4009                               ignore = 1;
4010                     if (strchr(fm->argv[0], 'r') != NULL)
4011                               regex = 1;
4012           }
4013           xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
4014           return (value);
4015 }
4016 
4017 /* Does session name exist? */
4018 static char *
format_session_name(struct format_expand_state * es,const char * fmt)4019 format_session_name(struct format_expand_state *es, const char *fmt)
4020 {
4021           char                *name;
4022           struct session      *s;
4023 
4024           name = format_expand1(es, fmt);
4025           RB_FOREACH(s, sessions, &sessions) {
4026                     if (strcmp(s->name, name) == 0) {
4027                               free(name);
4028                               return (xstrdup("1"));
4029                     }
4030           }
4031           free(name);
4032           return (xstrdup("0"));
4033 }
4034 
4035 /* Loop over sessions. */
4036 static char *
format_loop_sessions(struct format_expand_state * es,const char * fmt)4037 format_loop_sessions(struct format_expand_state *es, const char *fmt)
4038 {
4039           struct format_tree            *ft = es->ft;
4040           struct client                           *c = ft->client;
4041           struct cmdq_item              *item = ft->item;
4042           struct format_tree            *nft;
4043           struct format_expand_state     next;
4044           char                                    *expanded, *value;
4045           size_t                                   valuelen;
4046           struct session                          *s;
4047 
4048           value = xcalloc(1, 1);
4049           valuelen = 1;
4050 
4051           RB_FOREACH(s, sessions, &sessions) {
4052                     format_log(es, "session loop: $%u", s->id);
4053                     nft = format_create(c, item, FORMAT_NONE, ft->flags);
4054                     format_defaults(nft, ft->c, s, NULL, NULL);
4055                     format_copy_state(&next, es, 0);
4056                     next.ft = nft;
4057                     expanded = format_expand1(&next, fmt);
4058                     format_free(next.ft);
4059 
4060                     valuelen += strlen(expanded);
4061                     value = xrealloc(value, valuelen);
4062 
4063                     strlcat(value, expanded, valuelen);
4064                     free(expanded);
4065           }
4066 
4067           return (value);
4068 }
4069 
4070 /* Does window name exist? */
4071 static char *
format_window_name(struct format_expand_state * es,const char * fmt)4072 format_window_name(struct format_expand_state *es, const char *fmt)
4073 {
4074           struct format_tree  *ft = es->ft;
4075           char                          *name;
4076           struct winlink                *wl;
4077 
4078           if (ft->s == NULL) {
4079                     format_log(es, "window name but no session");
4080                     return (NULL);
4081           }
4082 
4083           name = format_expand1(es, fmt);
4084           RB_FOREACH(wl, winlinks, &ft->s->windows) {
4085                     if (strcmp(wl->window->name, name) == 0) {
4086                               free(name);
4087                               return (xstrdup("1"));
4088                     }
4089           }
4090           free(name);
4091           return (xstrdup("0"));
4092 }
4093 
4094 /* Loop over windows. */
4095 static char *
format_loop_windows(struct format_expand_state * es,const char * fmt)4096 format_loop_windows(struct format_expand_state *es, const char *fmt)
4097 {
4098           struct format_tree            *ft = es->ft;
4099           struct client                           *c = ft->client;
4100           struct cmdq_item              *item = ft->item;
4101           struct format_tree            *nft;
4102           struct format_expand_state     next;
4103           char                                    *all, *active, *use, *expanded, *value;
4104           size_t                                   valuelen;
4105           struct winlink                          *wl;
4106           struct window                           *w;
4107 
4108           if (ft->s == NULL) {
4109                     format_log(es, "window loop but no session");
4110                     return (NULL);
4111           }
4112 
4113           if (format_choose(es, fmt, &all, &active, 0) != 0) {
4114                     all = xstrdup(fmt);
4115                     active = NULL;
4116           }
4117 
4118           value = xcalloc(1, 1);
4119           valuelen = 1;
4120 
4121           RB_FOREACH(wl, winlinks, &ft->s->windows) {
4122                     w = wl->window;
4123                     format_log(es, "window loop: %u @%u", wl->idx, w->id);
4124                     if (active != NULL && wl == ft->s->curw)
4125                               use = active;
4126                     else
4127                               use = all;
4128                     nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
4129                     format_defaults(nft, ft->c, ft->s, wl, NULL);
4130                     format_copy_state(&next, es, 0);
4131                     next.ft = nft;
4132                     expanded = format_expand1(&next, use);
4133                     format_free(nft);
4134 
4135                     valuelen += strlen(expanded);
4136                     value = xrealloc(value, valuelen);
4137 
4138                     strlcat(value, expanded, valuelen);
4139                     free(expanded);
4140           }
4141 
4142           free(active);
4143           free(all);
4144 
4145           return (value);
4146 }
4147 
4148 /* Loop over panes. */
4149 static char *
format_loop_panes(struct format_expand_state * es,const char * fmt)4150 format_loop_panes(struct format_expand_state *es, const char *fmt)
4151 {
4152           struct format_tree            *ft = es->ft;
4153           struct client                           *c = ft->client;
4154           struct cmdq_item              *item = ft->item;
4155           struct format_tree            *nft;
4156           struct format_expand_state     next;
4157           char                                    *all, *active, *use, *expanded, *value;
4158           size_t                                   valuelen;
4159           struct window_pane            *wp;
4160 
4161           if (ft->w == NULL) {
4162                     format_log(es, "pane loop but no window");
4163                     return (NULL);
4164           }
4165 
4166           if (format_choose(es, fmt, &all, &active, 0) != 0) {
4167                     all = xstrdup(fmt);
4168                     active = NULL;
4169           }
4170 
4171           value = xcalloc(1, 1);
4172           valuelen = 1;
4173 
4174           TAILQ_FOREACH(wp, &ft->w->panes, entry) {
4175                     format_log(es, "pane loop: %%%u", wp->id);
4176                     if (active != NULL && wp == ft->w->active)
4177                               use = active;
4178                     else
4179                               use = all;
4180                     nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
4181                     format_defaults(nft, ft->c, ft->s, ft->wl, wp);
4182                     format_copy_state(&next, es, 0);
4183                     next.ft = nft;
4184                     expanded = format_expand1(&next, use);
4185                     format_free(nft);
4186 
4187                     valuelen += strlen(expanded);
4188                     value = xrealloc(value, valuelen);
4189 
4190                     strlcat(value, expanded, valuelen);
4191                     free(expanded);
4192           }
4193 
4194           free(active);
4195           free(all);
4196 
4197           return (value);
4198 }
4199 
4200 /* Loop over clients. */
4201 static char *
format_loop_clients(struct format_expand_state * es,const char * fmt)4202 format_loop_clients(struct format_expand_state *es, const char *fmt)
4203 {
4204           struct format_tree            *ft = es->ft;
4205           struct client                           *c;
4206           struct cmdq_item              *item = ft->item;
4207           struct format_tree            *nft;
4208           struct format_expand_state     next;
4209           char                                    *expanded, *value;
4210           size_t                                   valuelen;
4211 
4212           value = xcalloc(1, 1);
4213           valuelen = 1;
4214 
4215           TAILQ_FOREACH(c, &clients, entry) {
4216                     format_log(es, "client loop: %s", c->name);
4217                     nft = format_create(c, item, 0, ft->flags);
4218                     format_defaults(nft, c, ft->s, ft->wl, ft->wp);
4219                     format_copy_state(&next, es, 0);
4220                     next.ft = nft;
4221                     expanded = format_expand1(&next, fmt);
4222                     format_free(nft);
4223 
4224                     valuelen += strlen(expanded);
4225                     value = xrealloc(value, valuelen);
4226 
4227                     strlcat(value, expanded, valuelen);
4228                     free(expanded);
4229           }
4230 
4231           return (value);
4232 }
4233 
4234 static char *
format_replace_expression(struct format_modifier * mexp,struct format_expand_state * es,const char * copy)4235 format_replace_expression(struct format_modifier *mexp,
4236     struct format_expand_state *es, const char *copy)
4237 {
4238           int                            argc = mexp->argc;
4239           const char                    *errstr;
4240           char                          *endch, *value, *left = NULL, *right = NULL;
4241           int                            use_fp = 0;
4242           u_int                          prec = 0;
4243           double                         mleft, mright, result;
4244           enum { ADD,
4245                  SUBTRACT,
4246                  MULTIPLY,
4247                  DIVIDE,
4248                  MODULUS,
4249                  EQUAL,
4250                  NOT_EQUAL,
4251                  GREATER_THAN,
4252                  GREATER_THAN_EQUAL,
4253                  LESS_THAN,
4254                  LESS_THAN_EQUAL } operator;
4255 
4256           if (strcmp(mexp->argv[0], "+") == 0)
4257                     operator = ADD;
4258           else if (strcmp(mexp->argv[0], "-") == 0)
4259                     operator = SUBTRACT;
4260           else if (strcmp(mexp->argv[0], "*") == 0)
4261                     operator = MULTIPLY;
4262           else if (strcmp(mexp->argv[0], "/") == 0)
4263                     operator = DIVIDE;
4264           else if (strcmp(mexp->argv[0], "%") == 0 ||
4265               strcmp(mexp->argv[0], "m") == 0)
4266                     operator = MODULUS;
4267           else if (strcmp(mexp->argv[0], "==") == 0)
4268                     operator = EQUAL;
4269           else if (strcmp(mexp->argv[0], "!=") == 0)
4270                     operator = NOT_EQUAL;
4271           else if (strcmp(mexp->argv[0], ">") == 0)
4272                     operator = GREATER_THAN;
4273           else if (strcmp(mexp->argv[0], "<") == 0)
4274                     operator = LESS_THAN;
4275           else if (strcmp(mexp->argv[0], ">=") == 0)
4276                     operator = GREATER_THAN_EQUAL;
4277           else if (strcmp(mexp->argv[0], "<=") == 0)
4278                     operator = LESS_THAN_EQUAL;
4279           else {
4280                     format_log(es, "expression has no valid operator: '%s'",
4281                         mexp->argv[0]);
4282                     goto fail;
4283           }
4284 
4285           /* The second argument may be flags. */
4286           if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) {
4287                     use_fp = 1;
4288                     prec = 2;
4289           }
4290 
4291           /* The third argument may be precision. */
4292           if (argc >= 3) {
4293                     prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr);
4294                     if (errstr != NULL) {
4295                               format_log(es, "expression precision %s: %s", errstr,
4296                                   mexp->argv[2]);
4297                               goto fail;
4298                     }
4299           }
4300 
4301           if (format_choose(es, copy, &left, &right, 1) != 0) {
4302                     format_log(es, "expression syntax error");
4303                     goto fail;
4304           }
4305 
4306           mleft = strtod(left, &endch);
4307           if (*endch != '\0') {
4308                     format_log(es, "expression left side is invalid: %s", left);
4309                     goto fail;
4310           }
4311 
4312           mright = strtod(right, &endch);
4313           if (*endch != '\0') {
4314                     format_log(es, "expression right side is invalid: %s", right);
4315                     goto fail;
4316           }
4317 
4318           if (!use_fp) {
4319                     mleft = (long long)mleft;
4320                     mright = (long long)mright;
4321           }
4322           format_log(es, "expression left side is: %.*f", prec, mleft);
4323           format_log(es, "expression right side is: %.*f", prec, mright);
4324 
4325           switch (operator) {
4326           case ADD:
4327                     result = mleft + mright;
4328                     break;
4329           case SUBTRACT:
4330                     result = mleft - mright;
4331                     break;
4332           case MULTIPLY:
4333                     result = mleft * mright;
4334                     break;
4335           case DIVIDE:
4336                     result = mleft / mright;
4337                     break;
4338           case MODULUS:
4339                     result = fmod(mleft, mright);
4340                     break;
4341           case EQUAL:
4342                     result = fabs(mleft - mright) < 1e-9;
4343                     break;
4344           case NOT_EQUAL:
4345                     result = fabs(mleft - mright) > 1e-9;
4346                     break;
4347           case GREATER_THAN:
4348                     result = (mleft > mright);
4349                     break;
4350           case GREATER_THAN_EQUAL:
4351                     result = (mleft >= mright);
4352                     break;
4353           case LESS_THAN:
4354                     result = (mleft < mright);
4355                     break;
4356           case LESS_THAN_EQUAL:
4357                     result = (mleft <= mright);
4358                     break;
4359           }
4360           if (use_fp)
4361                     xasprintf(&value, "%.*f", prec, result);
4362           else
4363                     xasprintf(&value, "%.*f", prec, (double)(long long)result);
4364           format_log(es, "expression result is %s", value);
4365 
4366           free(right);
4367           free(left);
4368           return (value);
4369 
4370 fail:
4371           free(right);
4372           free(left);
4373           return (NULL);
4374 }
4375 
4376 /* Replace a key. */
4377 static int
format_replace(struct format_expand_state * es,const char * key,size_t keylen,char ** buf,size_t * len,size_t * off)4378 format_replace(struct format_expand_state *es, const char *key, size_t keylen,
4379     char **buf, size_t *len, size_t *off)
4380 {
4381           struct format_tree             *ft = es->ft;
4382           struct window_pane             *wp = ft->wp;
4383           const char                               *errstr, *copy, *cp, *marker = NULL;
4384           const char                               *time_format = NULL;
4385           char                                     *copy0, *condition, *found, *new;
4386           char                                     *value, *left, *right;
4387           size_t                                    valuelen;
4388           int                                       modifiers = 0, limit = 0, width = 0;
4389           int                                       j, c;
4390           struct format_modifier                   *list, *cmp = NULL, *search = NULL;
4391           struct format_modifier                  **sub = NULL, *mexp = NULL, *fm = NULL;
4392           u_int                                     i, count, nsub = 0;
4393           struct format_expand_state      next;
4394 
4395           /* Make a copy of the key. */
4396           copy = copy0 = xstrndup(key, keylen);
4397 
4398           /* Process modifier list. */
4399           list = format_build_modifiers(es, &copy, &count);
4400           for (i = 0; i < count; i++) {
4401                     fm = &list[i];
4402                     if (format_logging(ft)) {
4403                               format_log(es, "modifier %u is %s", i, fm->modifier);
4404                               for (j = 0; j < fm->argc; j++) {
4405                                         format_log(es, "modifier %u argument %d: %s", i,
4406                                             j, fm->argv[j]);
4407                               }
4408                     }
4409                     if (fm->size == 1) {
4410                               switch (fm->modifier[0]) {
4411                               case 'm':
4412                               case '<':
4413                               case '>':
4414                                         cmp = fm;
4415                                         break;
4416                               case 'C':
4417                                         search = fm;
4418                                         break;
4419                               case 's':
4420                                         if (fm->argc < 2)
4421                                                   break;
4422                                         sub = xreallocarray(sub, nsub + 1, sizeof *sub);
4423                                         sub[nsub++] = fm;
4424                                         break;
4425                               case '=':
4426                                         if (fm->argc < 1)
4427                                                   break;
4428                                         limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4429                                             &errstr);
4430                                         if (errstr != NULL)
4431                                                   limit = 0;
4432                                         if (fm->argc >= 2 && fm->argv[1] != NULL)
4433                                                   marker = fm->argv[1];
4434                                         break;
4435                               case 'p':
4436                                         if (fm->argc < 1)
4437                                                   break;
4438                                         width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
4439                                             &errstr);
4440                                         if (errstr != NULL)
4441                                                   width = 0;
4442                                         break;
4443                               case 'w':
4444                                         modifiers |= FORMAT_WIDTH;
4445                                         break;
4446                               case 'e':
4447                                         if (fm->argc < 1 || fm->argc > 3)
4448                                                   break;
4449                                         mexp = fm;
4450                                         break;
4451                               case 'l':
4452                                         modifiers |= FORMAT_LITERAL;
4453                                         break;
4454                               case 'a':
4455                                         modifiers |= FORMAT_CHARACTER;
4456                                         break;
4457                               case 'b':
4458                                         modifiers |= FORMAT_BASENAME;
4459                                         break;
4460                               case 'c':
4461                                         modifiers |= FORMAT_COLOUR;
4462                                         break;
4463                               case 'd':
4464                                         modifiers |= FORMAT_DIRNAME;
4465                                         break;
4466                               case 'n':
4467                                         modifiers |= FORMAT_LENGTH;
4468                                         break;
4469                               case 't':
4470                                         modifiers |= FORMAT_TIMESTRING;
4471                                         if (fm->argc < 1)
4472                                                   break;
4473                                         if (strchr(fm->argv[0], 'p') != NULL)
4474                                                   modifiers |= FORMAT_PRETTY;
4475                                         else if (fm->argc >= 2 &&
4476                                             strchr(fm->argv[0], 'f') != NULL)
4477                                                   time_format = format_strip(fm->argv[1]);
4478                                         break;
4479                               case 'q':
4480                                         if (fm->argc < 1)
4481                                                   modifiers |= FORMAT_QUOTE_SHELL;
4482                                         else if (strchr(fm->argv[0], 'e') != NULL ||
4483                                             strchr(fm->argv[0], 'h') != NULL)
4484                                                   modifiers |= FORMAT_QUOTE_STYLE;
4485                                         break;
4486                               case 'E':
4487                                         modifiers |= FORMAT_EXPAND;
4488                                         break;
4489                               case 'T':
4490                                         modifiers |= FORMAT_EXPANDTIME;
4491                                         break;
4492                               case 'N':
4493                                         if (fm->argc < 1 ||
4494                                             strchr(fm->argv[0], 'w') != NULL)
4495                                                   modifiers |= FORMAT_WINDOW_NAME;
4496                                         else if (strchr(fm->argv[0], 's') != NULL)
4497                                                   modifiers |= FORMAT_SESSION_NAME;
4498                                         break;
4499                               case 'S':
4500                                         modifiers |= FORMAT_SESSIONS;
4501                                         break;
4502                               case 'W':
4503                                         modifiers |= FORMAT_WINDOWS;
4504                                         break;
4505                               case 'P':
4506                                         modifiers |= FORMAT_PANES;
4507                                         break;
4508                               case 'L':
4509                                         modifiers |= FORMAT_CLIENTS;
4510                                         break;
4511                               }
4512                     } else if (fm->size == 2) {
4513                               if (strcmp(fm->modifier, "||") == 0 ||
4514                                   strcmp(fm->modifier, "&&") == 0 ||
4515                                   strcmp(fm->modifier, "==") == 0 ||
4516                                   strcmp(fm->modifier, "!=") == 0 ||
4517                                   strcmp(fm->modifier, ">=") == 0 ||
4518                                   strcmp(fm->modifier, "<=") == 0)
4519                                         cmp = fm;
4520                     }
4521           }
4522 
4523           /* Is this a literal string? */
4524           if (modifiers & FORMAT_LITERAL) {
4525                     format_log(es, "literal string is '%s'", copy);
4526                     value = format_unescape(copy);
4527                     goto done;
4528           }
4529 
4530           /* Is this a character? */
4531           if (modifiers & FORMAT_CHARACTER) {
4532                     new = format_expand1(es, copy);
4533                     c = strtonum(new, 32, 126, &errstr);
4534                     if (errstr != NULL)
4535                               value = xstrdup("");
4536                     else
4537                               xasprintf(&value, "%c", c);
4538                     free(new);
4539                     goto done;
4540           }
4541 
4542           /* Is this a colour? */
4543           if (modifiers & FORMAT_COLOUR) {
4544                     new = format_expand1(es, copy);
4545                     c = colour_fromstring(new);
4546                     if (c == -1 || (c = colour_force_rgb(c)) == -1)
4547                               value = xstrdup("");
4548                     else
4549                               xasprintf(&value, "%06x", c & 0xffffff);
4550                     free(new);
4551                     goto done;
4552           }
4553 
4554           /* Is this a loop, comparison or condition? */
4555           if (modifiers & FORMAT_SESSIONS) {
4556                     value = format_loop_sessions(es, copy);
4557                     if (value == NULL)
4558                               goto fail;
4559           } else if (modifiers & FORMAT_WINDOWS) {
4560                     value = format_loop_windows(es, copy);
4561                     if (value == NULL)
4562                               goto fail;
4563           } else if (modifiers & FORMAT_PANES) {
4564                     value = format_loop_panes(es, copy);
4565                     if (value == NULL)
4566                               goto fail;
4567           } else if (modifiers & FORMAT_CLIENTS) {
4568                     value = format_loop_clients(es, copy);
4569                     if (value == NULL)
4570                               goto fail;
4571           } else if (modifiers & FORMAT_WINDOW_NAME) {
4572                     value = format_window_name(es, copy);
4573                     if (value == NULL)
4574                               goto fail;
4575           } else if (modifiers & FORMAT_SESSION_NAME) {
4576                     value = format_session_name(es, copy);
4577                     if (value == NULL)
4578                               goto fail;
4579           } else if (search != NULL) {
4580                     /* Search in pane. */
4581                     new = format_expand1(es, copy);
4582                     if (wp == NULL) {
4583                               format_log(es, "search '%s' but no pane", new);
4584                               value = xstrdup("0");
4585                     } else {
4586                               format_log(es, "search '%s' pane %%%u", new, wp->id);
4587                               value = format_search(search, wp, new);
4588                     }
4589                     free(new);
4590           } else if (cmp != NULL) {
4591                     /* Comparison of left and right. */
4592                     if (format_choose(es, copy, &left, &right, 1) != 0) {
4593                               format_log(es, "compare %s syntax error: %s",
4594                                   cmp->modifier, copy);
4595                               goto fail;
4596                     }
4597                     format_log(es, "compare %s left is: %s", cmp->modifier, left);
4598                     format_log(es, "compare %s right is: %s", cmp->modifier, right);
4599 
4600                     if (strcmp(cmp->modifier, "||") == 0) {
4601                               if (format_true(left) || format_true(right))
4602                                         value = xstrdup("1");
4603                               else
4604                                         value = xstrdup("0");
4605                     } else if (strcmp(cmp->modifier, "&&") == 0) {
4606                               if (format_true(left) && format_true(right))
4607                                         value = xstrdup("1");
4608                               else
4609                                         value = xstrdup("0");
4610                     } else if (strcmp(cmp->modifier, "==") == 0) {
4611                               if (strcmp(left, right) == 0)
4612                                         value = xstrdup("1");
4613                               else
4614                                         value = xstrdup("0");
4615                     } else if (strcmp(cmp->modifier, "!=") == 0) {
4616                               if (strcmp(left, right) != 0)
4617                                         value = xstrdup("1");
4618                               else
4619                                         value = xstrdup("0");
4620                     } else if (strcmp(cmp->modifier, "<") == 0) {
4621                               if (strcmp(left, right) < 0)
4622                                         value = xstrdup("1");
4623                               else
4624                                         value = xstrdup("0");
4625                     } else if (strcmp(cmp->modifier, ">") == 0) {
4626                               if (strcmp(left, right) > 0)
4627                                         value = xstrdup("1");
4628                               else
4629                                         value = xstrdup("0");
4630                     } else if (strcmp(cmp->modifier, "<=") == 0) {
4631                               if (strcmp(left, right) <= 0)
4632                                         value = xstrdup("1");
4633                               else
4634                                         value = xstrdup("0");
4635                     } else if (strcmp(cmp->modifier, ">=") == 0) {
4636                               if (strcmp(left, right) >= 0)
4637                                         value = xstrdup("1");
4638                               else
4639                                         value = xstrdup("0");
4640                     } else if (strcmp(cmp->modifier, "m") == 0)
4641                               value = format_match(cmp, left, right);
4642 
4643                     free(right);
4644                     free(left);
4645           } else if (*copy == '?') {
4646                     /* Conditional: check first and choose second or third. */
4647                     cp = format_skip(copy + 1, ",");
4648                     if (cp == NULL) {
4649                               format_log(es, "condition syntax error: %s", copy + 1);
4650                               goto fail;
4651                     }
4652                     condition = xstrndup(copy + 1, cp - (copy + 1));
4653                     format_log(es, "condition is: %s", condition);
4654 
4655                     found = format_find(ft, condition, modifiers, time_format);
4656                     if (found == NULL) {
4657                               /*
4658                                * If the condition not found, try to expand it. If
4659                                * the expansion doesn't have any effect, then assume
4660                                * false.
4661                                */
4662                               found = format_expand1(es, condition);
4663                               if (strcmp(found, condition) == 0) {
4664                                         free(found);
4665                                         found = xstrdup("");
4666                                         format_log(es,
4667                                             "condition '%s' not found; assuming false",
4668                                             condition);
4669                               }
4670                     } else {
4671                               format_log(es, "condition '%s' found: %s", condition,
4672                                   found);
4673                     }
4674 
4675                     if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
4676                               format_log(es, "condition '%s' syntax error: %s",
4677                                   condition, cp + 1);
4678                               free(found);
4679                               goto fail;
4680                     }
4681                     if (format_true(found)) {
4682                               format_log(es, "condition '%s' is true", condition);
4683                               value = format_expand1(es, left);
4684                     } else {
4685                               format_log(es, "condition '%s' is false", condition);
4686                               value = format_expand1(es, right);
4687                     }
4688                     free(right);
4689                     free(left);
4690 
4691                     free(condition);
4692                     free(found);
4693           } else if (mexp != NULL) {
4694                     value = format_replace_expression(mexp, es, copy);
4695                     if (value == NULL)
4696                               value = xstrdup("");
4697           } else {
4698                     if (strstr(copy, "#{") != 0) {
4699                               format_log(es, "expanding inner format '%s'", copy);
4700                               value = format_expand1(es, copy);
4701                     } else {
4702                               value = format_find(ft, copy, modifiers, time_format);
4703                               if (value == NULL) {
4704                                         format_log(es, "format '%s' not found", copy);
4705                                         value = xstrdup("");
4706                               } else {
4707                                         format_log(es, "format '%s' found: %s", copy,
4708                                             value);
4709                               }
4710                     }
4711           }
4712 
4713 done:
4714           /* Expand again if required. */
4715           if (modifiers & FORMAT_EXPAND) {
4716                     new = format_expand1(es, value);
4717                     free(value);
4718                     value = new;
4719           } else if (modifiers & FORMAT_EXPANDTIME) {
4720                     format_copy_state(&next, es, FORMAT_EXPAND_TIME);
4721                     new = format_expand1(&next, value);
4722                     free(value);
4723                     value = new;
4724           }
4725 
4726           /* Perform substitution if any. */
4727           for (i = 0; i < nsub; i++) {
4728                     left = format_expand1(es, sub[i]->argv[0]);
4729                     right = format_expand1(es, sub[i]->argv[1]);
4730                     new = format_sub(sub[i], value, left, right);
4731                     format_log(es, "substitute '%s' to '%s': %s", left, right, new);
4732                     free(value);
4733                     value = new;
4734                     free(right);
4735                     free(left);
4736           }
4737 
4738           /* Truncate the value if needed. */
4739           if (limit > 0) {
4740                     new = format_trim_left(value, limit);
4741                     if (marker != NULL && strcmp(new, value) != 0) {
4742                               free(value);
4743                               xasprintf(&value, "%s%s", new, marker);
4744                     } else {
4745                               free(value);
4746                               value = new;
4747                     }
4748                     format_log(es, "applied length limit %d: %s", limit, value);
4749           } else if (limit < 0) {
4750                     new = format_trim_right(value, -limit);
4751                     if (marker != NULL && strcmp(new, value) != 0) {
4752                               free(value);
4753                               xasprintf(&value, "%s%s", marker, new);
4754                     } else {
4755                               free(value);
4756                               value = new;
4757                     }
4758                     format_log(es, "applied length limit %d: %s", limit, value);
4759           }
4760 
4761           /* Pad the value if needed. */
4762           if (width > 0) {
4763                     new = utf8_padcstr(value, width);
4764                     free(value);
4765                     value = new;
4766                     format_log(es, "applied padding width %d: %s", width, value);
4767           } else if (width < 0) {
4768                     new = utf8_rpadcstr(value, -width);
4769                     free(value);
4770                     value = new;
4771                     format_log(es, "applied padding width %d: %s", width, value);
4772           }
4773 
4774           /* Replace with the length or width if needed. */
4775           if (modifiers & FORMAT_LENGTH) {
4776                     xasprintf(&new, "%zu", strlen(value));
4777                     free(value);
4778                     value = new;
4779                     format_log(es, "replacing with length: %s", new);
4780           }
4781           if (modifiers & FORMAT_WIDTH) {
4782                     xasprintf(&new, "%u", format_width(value));
4783                     free(value);
4784                     value = new;
4785                     format_log(es, "replacing with width: %s", new);
4786           }
4787 
4788           /* Expand the buffer and copy in the value. */
4789           valuelen = strlen(value);
4790           while (*len - *off < valuelen + 1) {
4791                     *buf = xreallocarray(*buf, 2, *len);
4792                     *len *= 2;
4793           }
4794           memcpy(*buf + *off, value, valuelen);
4795           *off += valuelen;
4796 
4797           format_log(es, "replaced '%s' with '%s'", copy0, value);
4798           free(value);
4799 
4800           free(sub);
4801           format_free_modifiers(list, count);
4802           free(copy0);
4803           return (0);
4804 
4805 fail:
4806           format_log(es, "failed %s", copy0);
4807 
4808           free(sub);
4809           format_free_modifiers(list, count);
4810           free(copy0);
4811           return (-1);
4812 }
4813 
4814 /* Expand keys in a template. */
4815 static char *
format_expand1(struct format_expand_state * es,const char * fmt)4816 format_expand1(struct format_expand_state *es, const char *fmt)
4817 {
4818           struct format_tree  *ft = es->ft;
4819           char                          *buf, *out, *name;
4820           const char                    *ptr, *s, *style_end = NULL;
4821           size_t                         off, len, n, outlen;
4822           int                  ch, brackets;
4823           char                           expanded[8192];
4824 
4825           if (fmt == NULL || *fmt == '\0')
4826                     return (xstrdup(""));
4827 
4828           if (es->loop == FORMAT_LOOP_LIMIT) {
4829                     format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT);
4830                     return (xstrdup(""));
4831           }
4832           es->loop++;
4833 
4834           format_log(es, "expanding format: %s", fmt);
4835 
4836           if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) {
4837                     if (es->time == 0) {
4838                               es->time = time(NULL);
4839                               localtime_r(&es->time, &es->tm);
4840                     }
4841                     if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) {
4842                               format_log(es, "format is too long");
4843                               return (xstrdup(""));
4844                     }
4845                     if (format_logging(ft) && strcmp(expanded, fmt) != 0)
4846                               format_log(es, "after time expanded: %s", expanded);
4847                     fmt = expanded;
4848           }
4849 
4850           len = 64;
4851           buf = xmalloc(len);
4852           off = 0;
4853 
4854           while (*fmt != '\0') {
4855                     if (*fmt != '#') {
4856                               while (len - off < 2) {
4857                                         buf = xreallocarray(buf, 2, len);
4858                                         len *= 2;
4859                               }
4860                               buf[off++] = *fmt++;
4861                               continue;
4862                     }
4863                     fmt++;
4864 
4865                     ch = (u_char)*fmt++;
4866                     switch (ch) {
4867                     case '(':
4868                               brackets = 1;
4869                               for (ptr = fmt; *ptr != '\0'; ptr++) {
4870                                         if (*ptr == '(')
4871                                                   brackets++;
4872                                         if (*ptr == ')' && --brackets == 0)
4873                                                   break;
4874                               }
4875                               if (*ptr != ')' || brackets != 0)
4876                                         break;
4877                               n = ptr - fmt;
4878 
4879                               name = xstrndup(fmt, n);
4880                               format_log(es, "found #(): %s", name);
4881 
4882                               if ((ft->flags & FORMAT_NOJOBS) ||
4883                                   (es->flags & FORMAT_EXPAND_NOJOBS)) {
4884                                         out = xstrdup("");
4885                                         format_log(es, "#() is disabled");
4886                               } else {
4887                                         out = format_job_get(es, name);
4888                                         format_log(es, "#() result: %s", out);
4889                               }
4890                               free(name);
4891 
4892                               outlen = strlen(out);
4893                               while (len - off < outlen + 1) {
4894                                         buf = xreallocarray(buf, 2, len);
4895                                         len *= 2;
4896                               }
4897                               memcpy(buf + off, out, outlen);
4898                               off += outlen;
4899 
4900                               free(out);
4901 
4902                               fmt += n + 1;
4903                               continue;
4904                     case '{':
4905                               ptr = format_skip((const char *)fmt - 2, "}");
4906                               if (ptr == NULL)
4907                                         break;
4908                               n = ptr - fmt;
4909 
4910                               format_log(es, "found #{}: %.*s", (int)n, fmt);
4911                               if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
4912                                         break;
4913                               fmt += n + 1;
4914                               continue;
4915                     case '[':
4916                     case '#':
4917                               /*
4918                                * If ##[ (with two or more #s), then it is a style and
4919                                * can be left for format_draw to handle.
4920                                */
4921                               ptr = fmt - (ch == '[');
4922                               n = 2 - (ch == '[');
4923                               while (*ptr == '#') {
4924                                         ptr++;
4925                                         n++;
4926                               }
4927                               if (*ptr == '[') {
4928                                         style_end = format_skip(fmt - 2, "]");
4929                                         format_log(es, "found #*%zu[", n);
4930                                         while (len - off < n + 2) {
4931                                                   buf = xreallocarray(buf, 2, len);
4932                                                   len *= 2;
4933                                         }
4934                                         memcpy(buf + off, fmt - 2, n + 1);
4935                                         off += n + 1;
4936                                         fmt = ptr + 1;
4937                                         continue;
4938                               }
4939                               /* FALLTHROUGH */
4940                     case '}':
4941                     case ',':
4942                               format_log(es, "found #%c", ch);
4943                               while (len - off < 2) {
4944                                         buf = xreallocarray(buf, 2, len);
4945                                         len *= 2;
4946                               }
4947                               buf[off++] = ch;
4948                               continue;
4949                     default:
4950                               s = NULL;
4951                               if (fmt > style_end) { /* skip inside #[] */
4952                                         if (ch >= 'A' && ch <= 'Z')
4953                                                   s = format_upper[ch - 'A'];
4954                                         else if (ch >= 'a' && ch <= 'z')
4955                                                   s = format_lower[ch - 'a'];
4956                               }
4957                               if (s == NULL) {
4958                                         while (len - off < 3) {
4959                                                   buf = xreallocarray(buf, 2, len);
4960                                                   len *= 2;
4961                                         }
4962                                         buf[off++] = '#';
4963                                         buf[off++] = ch;
4964                                         continue;
4965                               }
4966                               n = strlen(s);
4967                               format_log(es, "found #%c: %s", ch, s);
4968                               if (format_replace(es, s, n, &buf, &len, &off) != 0)
4969                                         break;
4970                               continue;
4971                     }
4972 
4973                     break;
4974           }
4975           buf[off] = '\0';
4976 
4977           format_log(es, "result is: %s", buf);
4978           es->loop--;
4979 
4980           return (buf);
4981 }
4982 
4983 /* Expand keys in a template, passing through strftime first. */
4984 char *
format_expand_time(struct format_tree * ft,const char * fmt)4985 format_expand_time(struct format_tree *ft, const char *fmt)
4986 {
4987           struct format_expand_state    es;
4988 
4989           memset(&es, 0, sizeof es);
4990           es.ft = ft;
4991           es.flags = FORMAT_EXPAND_TIME;
4992           return (format_expand1(&es, fmt));
4993 }
4994 
4995 /* Expand keys in a template. */
4996 char *
format_expand(struct format_tree * ft,const char * fmt)4997 format_expand(struct format_tree *ft, const char *fmt)
4998 {
4999           struct format_expand_state    es;
5000 
5001           memset(&es, 0, sizeof es);
5002           es.ft = ft;
5003           es.flags = 0;
5004           return (format_expand1(&es, fmt));
5005 }
5006 
5007 /* Expand a single string. */
5008 char *
format_single(struct cmdq_item * item,const char * fmt,struct client * c,struct session * s,struct winlink * wl,struct window_pane * wp)5009 format_single(struct cmdq_item *item, const char *fmt, struct client *c,
5010     struct session *s, struct winlink *wl, struct window_pane *wp)
5011 {
5012           struct format_tree  *ft;
5013           char                          *expanded;
5014 
5015           ft = format_create_defaults(item, c, s, wl, wp);
5016           expanded = format_expand(ft, fmt);
5017           format_free(ft);
5018           return (expanded);
5019 }
5020 
5021 /* Expand a single string using state. */
5022 char *
format_single_from_state(struct cmdq_item * item,const char * fmt,struct client * c,struct cmd_find_state * fs)5023 format_single_from_state(struct cmdq_item *item, const char *fmt,
5024     struct client *c, struct cmd_find_state *fs)
5025 {
5026           return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp));
5027 }
5028 
5029 /* Expand a single string using target. */
5030 char *
format_single_from_target(struct cmdq_item * item,const char * fmt)5031 format_single_from_target(struct cmdq_item *item, const char *fmt)
5032 {
5033           struct client       *tc = cmdq_get_target_client(item);
5034 
5035           return (format_single_from_state(item, fmt, tc, cmdq_get_target(item)));
5036 }
5037 
5038 /* Create and add defaults. */
5039 struct format_tree *
format_create_defaults(struct cmdq_item * item,struct client * c,struct session * s,struct winlink * wl,struct window_pane * wp)5040 format_create_defaults(struct cmdq_item *item, struct client *c,
5041     struct session *s, struct winlink *wl, struct window_pane *wp)
5042 {
5043           struct format_tree  *ft;
5044 
5045           if (item != NULL)
5046                     ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
5047           else
5048                     ft = format_create(NULL, item, FORMAT_NONE, 0);
5049           format_defaults(ft, c, s, wl, wp);
5050           return (ft);
5051 }
5052 
5053 /* Create and add defaults using state. */
5054 struct format_tree *
format_create_from_state(struct cmdq_item * item,struct client * c,struct cmd_find_state * fs)5055 format_create_from_state(struct cmdq_item *item, struct client *c,
5056     struct cmd_find_state *fs)
5057 {
5058           return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp));
5059 }
5060 
5061 /* Create and add defaults using target. */
5062 struct format_tree *
format_create_from_target(struct cmdq_item * item)5063 format_create_from_target(struct cmdq_item *item)
5064 {
5065           struct client       *tc = cmdq_get_target_client(item);
5066 
5067           return (format_create_from_state(item, tc, cmdq_get_target(item)));
5068 }
5069 
5070 /* Set defaults for any of arguments that are not NULL. */
5071 void
format_defaults(struct format_tree * ft,struct client * c,struct session * s,struct winlink * wl,struct window_pane * wp)5072 format_defaults(struct format_tree *ft, struct client *c, struct session *s,
5073     struct winlink *wl, struct window_pane *wp)
5074 {
5075           struct paste_buffer *pb;
5076 
5077           if (c != NULL && c->name != NULL)
5078                     log_debug("%s: c=%s", __func__, c->name);
5079           else
5080                     log_debug("%s: c=none", __func__);
5081           if (s != NULL)
5082                     log_debug("%s: s=$%u", __func__, s->id);
5083           else
5084                     log_debug("%s: s=none", __func__);
5085           if (wl != NULL)
5086                     log_debug("%s: wl=%u", __func__, wl->idx);
5087           else
5088                     log_debug("%s: wl=none", __func__);
5089           if (wp != NULL)
5090                     log_debug("%s: wp=%%%u", __func__, wp->id);
5091           else
5092                     log_debug("%s: wp=none", __func__);
5093 
5094           if (c != NULL && s != NULL && c->session != s)
5095                     log_debug("%s: session does not match", __func__);
5096 
5097           if (wp != NULL)
5098                     ft->type = FORMAT_TYPE_PANE;
5099           else if (wl != NULL)
5100                     ft->type = FORMAT_TYPE_WINDOW;
5101           else if (s != NULL)
5102                     ft->type = FORMAT_TYPE_SESSION;
5103           else
5104                     ft->type = FORMAT_TYPE_UNKNOWN;
5105 
5106           if (s == NULL && c != NULL)
5107                     s = c->session;
5108           if (wl == NULL && s != NULL)
5109                     wl = s->curw;
5110           if (wp == NULL && wl != NULL)
5111                     wp = wl->window->active;
5112 
5113           if (c != NULL)
5114                     format_defaults_client(ft, c);
5115           if (s != NULL)
5116                     format_defaults_session(ft, s);
5117           if (wl != NULL)
5118                     format_defaults_winlink(ft, wl);
5119           if (wp != NULL)
5120                     format_defaults_pane(ft, wp);
5121 
5122           pb = paste_get_top(NULL);
5123           if (pb != NULL)
5124                     format_defaults_paste_buffer(ft, pb);
5125 }
5126 
5127 /* Set default format keys for a session. */
5128 static void
format_defaults_session(struct format_tree * ft,struct session * s)5129 format_defaults_session(struct format_tree *ft, struct session *s)
5130 {
5131           ft->s = s;
5132 }
5133 
5134 /* Set default format keys for a client. */
5135 static void
format_defaults_client(struct format_tree * ft,struct client * c)5136 format_defaults_client(struct format_tree *ft, struct client *c)
5137 {
5138           if (ft->s == NULL)
5139                     ft->s = c->session;
5140           ft->c = c;
5141 }
5142 
5143 /* Set default format keys for a window. */
5144 void
format_defaults_window(struct format_tree * ft,struct window * w)5145 format_defaults_window(struct format_tree *ft, struct window *w)
5146 {
5147           ft->w = w;
5148 }
5149 
5150 /* Set default format keys for a winlink. */
5151 static void
format_defaults_winlink(struct format_tree * ft,struct winlink * wl)5152 format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
5153 {
5154           if (ft->w == NULL)
5155                     format_defaults_window(ft, wl->window);
5156           ft->wl = wl;
5157 }
5158 
5159 /* Set default format keys for a window pane. */
5160 void
format_defaults_pane(struct format_tree * ft,struct window_pane * wp)5161 format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
5162 {
5163           struct window_mode_entry      *wme;
5164 
5165           if (ft->w == NULL)
5166                     format_defaults_window(ft, wp->window);
5167           ft->wp = wp;
5168 
5169           wme = TAILQ_FIRST(&wp->modes);
5170           if (wme != NULL && wme->mode->formats != NULL)
5171                     wme->mode->formats(wme, ft);
5172 }
5173 
5174 /* Set default format keys for paste buffer. */
5175 void
format_defaults_paste_buffer(struct format_tree * ft,struct paste_buffer * pb)5176 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
5177 {
5178           ft->pb = pb;
5179 }
5180 
5181 /* Return word at given coordinates. Caller frees. */
5182 char *
format_grid_word(struct grid * gd,u_int x,u_int y)5183 format_grid_word(struct grid *gd, u_int x, u_int y)
5184 {
5185           const struct grid_line        *gl;
5186           struct grid_cell     gc;
5187           const char                    *ws;
5188           struct utf8_data    *ud = NULL;
5189           u_int                          end;
5190           size_t                         size = 0;
5191           int                            found = 0;
5192           char                          *s = NULL;
5193 
5194           ws = options_get_string(global_s_options, "word-separators");
5195 
5196           for (;;) {
5197                     grid_get_cell(gd, x, y, &gc);
5198                     if (gc.flags & GRID_FLAG_PADDING)
5199                               break;
5200                     if (utf8_cstrhas(ws, &gc.data) ||
5201                         (gc.data.size == 1 && *gc.data.data == ' ')) {
5202                               found = 1;
5203                               break;
5204                     }
5205 
5206                     if (x == 0) {
5207                               if (y == 0)
5208                                         break;
5209                               gl = grid_peek_line(gd, y - 1);
5210                               if (~gl->flags & GRID_LINE_WRAPPED)
5211                                         break;
5212                               y--;
5213                               x = grid_line_length(gd, y);
5214                               if (x == 0)
5215                                         break;
5216                     }
5217                     x--;
5218           }
5219           for (;;) {
5220                     if (found) {
5221                               end = grid_line_length(gd, y);
5222                               if (end == 0 || x == end - 1) {
5223                                         if (y == gd->hsize + gd->sy - 1)
5224                                                   break;
5225                                         gl = grid_peek_line(gd, y);
5226                                         if (~gl->flags & GRID_LINE_WRAPPED)
5227                                                   break;
5228                                         y++;
5229                                         x = 0;
5230                               } else
5231                                         x++;
5232                     }
5233                     found = 1;
5234 
5235                     grid_get_cell(gd, x, y, &gc);
5236                     if (gc.flags & GRID_FLAG_PADDING)
5237                               break;
5238                     if (utf8_cstrhas(ws, &gc.data) ||
5239                         (gc.data.size == 1 && *gc.data.data == ' '))
5240                               break;
5241 
5242                     ud = xreallocarray(ud, size + 2, sizeof *ud);
5243                     memcpy(&ud[size++], &gc.data, sizeof *ud);
5244           }
5245           if (size != 0) {
5246                     ud[size].size = 0;
5247                     s = utf8_tocstr(ud);
5248                     free(ud);
5249           }
5250           return (s);
5251 }
5252 
5253 /* Return line at given coordinates. Caller frees. */
5254 char *
format_grid_line(struct grid * gd,u_int y)5255 format_grid_line(struct grid *gd, u_int y)
5256 {
5257           struct grid_cell     gc;
5258           struct utf8_data    *ud = NULL;
5259           u_int                          x;
5260           size_t                         size = 0;
5261           char                          *s = NULL;
5262 
5263           for (x = 0; x < grid_line_length(gd, y); x++) {
5264                     grid_get_cell(gd, x, y, &gc);
5265                     if (gc.flags & GRID_FLAG_PADDING)
5266                               break;
5267 
5268                     ud = xreallocarray(ud, size + 2, sizeof *ud);
5269                     memcpy(&ud[size++], &gc.data, sizeof *ud);
5270           }
5271           if (size != 0) {
5272                     ud[size].size = 0;
5273                     s = utf8_tocstr(ud);
5274                     free(ud);
5275           }
5276           return (s);
5277 }
5278 
5279 /* Return hyperlink at given coordinates. Caller frees. */
5280 char *
format_grid_hyperlink(struct grid * gd,u_int x,u_int y,struct screen * s)5281 format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
5282 {
5283           const char                    *uri;
5284           struct grid_cell     gc;
5285 
5286           grid_get_cell(gd, x, y, &gc);
5287           if (gc.flags & GRID_FLAG_PADDING)
5288                     return (NULL);
5289           if (s->hyperlinks == NULL || gc.link == 0)
5290                     return (NULL);
5291           if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
5292                     return (NULL);
5293           return (xstrdup(uri));
5294 }
5295