Coverage Report

Created: 2026-09-13 06:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/format.c
Line
Count
Source
1
/* $OpenBSD: format.c,v 1.417 2026/09/08 15:42:26 nicm Exp $ */
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_quote_shell_single(const char *);
46
static char *format_expand1(struct format_expand_state *, const char *);
47
static int   format_replace(struct format_expand_state *, const char *,
48
         size_t, char **, size_t *, size_t *);
49
static void  format_defaults_session(struct format_tree *,
50
         struct session *);
51
static void  format_defaults_client(struct format_tree *, struct client *);
52
static void  format_defaults_winlink(struct format_tree *,
53
         struct winlink *);
54
55
/* Entry in format job tree. */
56
struct format_job {
57
  struct client   *client;
58
  u_int      tag;
59
  const char    *cmd;
60
  const char    *expanded;
61
62
  time_t       last;
63
  char      *out;
64
  int      updated;
65
66
  struct job    *job;
67
  int      status;
68
69
  RB_ENTRY(format_job)   entry;
70
};
71
72
/* Format job tree. */
73
static int format_job_cmp(struct format_job *, struct format_job *);
74
static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
75
0
RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp);
Unexecuted instantiation: format.c:format_job_tree_RB_MINMAX
Unexecuted instantiation: format.c:format_job_tree_RB_REMOVE
Unexecuted instantiation: format.c:format_job_tree_RB_REMOVE_COLOR
Unexecuted instantiation: format.c:format_job_tree_RB_FIND
Unexecuted instantiation: format.c:format_job_tree_RB_INSERT
76
0
77
0
/* Format job tree comparison function. */
78
0
static int
79
0
format_job_cmp(struct format_job *fj1, struct format_job *fj2)
80
0
{
81
0
  if (fj1->tag < fj2->tag)
82
0
    return (-1);
83
0
  if (fj1->tag > fj2->tag)
84
0
    return (1);
85
0
  return (strcmp(fj1->cmd, fj2->cmd));
86
0
}
87
88
/* Maximum pad and trim width. */
89
0
#define FORMAT_MAX_WIDTH 10000
90
91
/* Maximum repeat size. */
92
0
#define FORMAT_MAX_REPEAT 10000
93
94
/* Maximum precision. */
95
0
#define FORMAT_MAX_PRECISION 100
96
97
/* Format modifiers. */
98
0
#define FORMAT_TIMESTRING 0x1
99
0
#define FORMAT_BASENAME 0x2
100
0
#define FORMAT_DIRNAME 0x4
101
0
#define FORMAT_QUOTE_SHELL 0x8
102
0
#define FORMAT_LITERAL 0x10
103
0
#define FORMAT_EXPAND 0x20
104
0
#define FORMAT_EXPANDTIME 0x40
105
0
#define FORMAT_SESSIONS 0x80
106
0
#define FORMAT_WINDOWS 0x100
107
0
#define FORMAT_PANES 0x200
108
0
#define FORMAT_PRETTY 0x400
109
0
#define FORMAT_LENGTH 0x800
110
0
#define FORMAT_WIDTH 0x1000
111
0
#define FORMAT_QUOTE_STYLE 0x2000
112
0
#define FORMAT_WINDOW_NAME 0x4000
113
0
#define FORMAT_SESSION_NAME 0x8000
114
0
#define FORMAT_CHARACTER 0x10000
115
0
#define FORMAT_COLOUR 0x20000
116
0
#define FORMAT_CLIENTS 0x40000
117
0
#define FORMAT_NOT 0x80000
118
0
#define FORMAT_NOT_NOT 0x100000
119
0
#define FORMAT_REPEAT 0x200000
120
0
#define FORMAT_QUOTE_ARGUMENTS 0x400000
121
0
#define FORMAT_RELATIVE 0x800000
122
0
#define FORMAT_CLIENT_TERMCAP 0x1000000
123
0
#define FORMAT_CLIENT_TERMFEAT 0x2000000
124
0
#define FORMAT_CLIENT_ENVIRON 0x4000000
125
0
#define FORMAT_COLOUR_ESC_FG 0x8000000
126
0
#define FORMAT_COLOUR_ESC_BG 0x10000000
127
0
#define FORMAT_QUOTE_SHELL_SQ 0x20000000
128
0
#define FORMAT_OPTIONS 0x40000000
129
0
#define FORMAT_ENVIRON 0x80000000ULL
130
0
#define FORMAT_DIFFERENCE 0x100000000ULL
131
0
#define FORMAT_CYCLE 0x200000000ULL
132
133
/* Limit on recursion. */
134
0
#define FORMAT_LOOP_LIMIT 100
135
136
/* Limit on time taken (milliseconds). */
137
0
#define FORMAT_TIME_LIMIT 100
138
139
/* How often to check the time in long loops. */
140
0
#define FORMAT_TIME_LOOP_CHECK 10000
141
142
/* Fixed animation period (ms): redraw interval and shortest frame step. */
143
0
#define FORMAT_CYCLE_PERIOD 100
144
145
/* Format expand flags. */
146
0
#define FORMAT_EXPAND_TIME 0x1
147
0
#define FORMAT_EXPAND_NOJOBS 0x2
148
0
#define FORMAT_EXPAND_NOCYCLE 0x4
149
150
/* Entry in format tree. */
151
struct format_entry {
152
  char      *key;
153
  char      *value;
154
  time_t       time;
155
  format_cb    cb;
156
  RB_ENTRY(format_entry)   entry;
157
};
158
159
/* Format type. */
160
enum format_type {
161
  FORMAT_TYPE_UNKNOWN,
162
  FORMAT_TYPE_SESSION,
163
  FORMAT_TYPE_WINDOW,
164
  FORMAT_TYPE_PANE
165
};
166
167
static struct sort_criteria sort_crit;
168
169
struct format_tree {
170
  enum format_type   type;
171
172
  struct client   *c;
173
  struct session    *s;
174
  struct winlink    *wl;
175
  struct window   *w;
176
  struct window_pane  *wp;
177
  struct paste_buffer *pb;
178
179
  struct cmdq_item  *item;
180
  struct client   *client;
181
  int      flags;
182
  u_int      tag;
183
184
  struct mouse_event   m;
185
186
  RB_HEAD(format_entry_tree, format_entry) tree;
187
};
188
static int format_entry_cmp(struct format_entry *, struct format_entry *);
189
0
RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
Unexecuted instantiation: format.c:format_entry_tree_RB_MINMAX
Unexecuted instantiation: format.c:format_entry_tree_RB_REMOVE
Unexecuted instantiation: format.c:format_entry_tree_RB_REMOVE_COLOR
Unexecuted instantiation: format.c:format_entry_tree_RB_INSERT
Unexecuted instantiation: format.c:format_entry_tree_RB_FIND
190
0
191
0
/* Format expand state. */
192
0
struct format_expand_state {
193
0
  struct format_tree  *ft;
194
0
  u_int      loop;
195
0
  uint64_t     start_time;
196
0
  int      flags;
197
0
198
0
  time_t       time;
199
0
  struct tm    tm;
200
0
};
201
0
202
0
/* Format modifier. */
203
0
struct format_modifier {
204
0
  char    modifier[3];
205
0
  u_int   size;
206
0
207
0
  char  **argv;
208
0
  int   argc;
209
0
};
210
0
211
0
/* Format entry tree comparison function. */
212
0
static int
213
0
format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
214
0
{
215
0
  return (strcmp(fe1->key, fe2->key));
216
0
}
217
218
/* Single-character uppercase aliases. */
219
static const char *format_upper[] = {
220
  NULL,   /* A */
221
  NULL,   /* B */
222
  NULL,   /* C */
223
  "pane_id",  /* D */
224
  NULL,   /* E */
225
  "window_flags", /* F */
226
  NULL,   /* G */
227
  "host",   /* H */
228
  "window_index", /* I */
229
  NULL,   /* J */
230
  NULL,   /* K */
231
  NULL,   /* L */
232
  NULL,   /* M */
233
  NULL,   /* N */
234
  NULL,   /* O */
235
  "pane_index", /* P */
236
  NULL,   /* Q */
237
  NULL,   /* R */
238
  "session_name", /* S */
239
  "pane_title", /* T */
240
  NULL,   /* U */
241
  NULL,   /* V */
242
  "window_name",  /* W */
243
  NULL,   /* X */
244
  NULL,   /* Y */
245
  NULL    /* Z */
246
};
247
248
/* Single-character lowercase aliases. */
249
static const char *format_lower[] = {
250
  NULL,   /* a */
251
  NULL,   /* b */
252
  NULL,   /* c */
253
  NULL,   /* d */
254
  NULL,   /* e */
255
  NULL,   /* f */
256
  NULL,   /* g */
257
  "host_short", /* h */
258
  NULL,   /* i */
259
  NULL,   /* j */
260
  NULL,   /* k */
261
  NULL,   /* l */
262
  NULL,   /* m */
263
  NULL,   /* n */
264
  NULL,   /* o */
265
  NULL,   /* p */
266
  NULL,   /* q */
267
  NULL,   /* r */
268
  NULL,   /* s */
269
  NULL,   /* t */
270
  NULL,   /* u */
271
  NULL,   /* v */
272
  NULL,   /* w */
273
  NULL,   /* x */
274
  NULL,   /* y */
275
  NULL    /* z */
276
};
277
278
/* Is logging enabled? */
279
static inline int
280
format_logging(struct format_tree *ft)
281
0
{
282
0
  return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE));
283
0
}
284
285
/* Log a message if verbose. */
286
static void printflike(3, 4)
287
format_log1(struct format_expand_state *es, const char *from, const char *fmt,
288
    ...)
289
0
{
290
0
  struct format_tree  *ft = es->ft;
291
0
  va_list      ap;
292
0
  char      *s;
293
0
  static const char  spaces[] = "          ";
294
295
0
  if (!format_logging(ft))
296
0
    return;
297
298
0
  va_start(ap, fmt);
299
0
  xvasprintf(&s, fmt, ap);
300
0
  va_end(ap);
301
302
0
  log_debug("%s: %s", from, s);
303
0
  if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
304
0
    cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
305
306
0
  free(s);
307
0
}
308
0
#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
309
310
/* Copy expand state. */
311
static void
312
format_copy_state(struct format_expand_state *to,
313
    struct format_expand_state *from, int flags)
314
0
{
315
0
  to->ft = from->ft;
316
0
  to->loop = from->loop;
317
0
  to->time = from->time;
318
0
  memcpy(&to->tm, &from->tm, sizeof to->tm);
319
0
  to->flags = from->flags|flags;
320
0
  to->start_time = from->start_time;
321
0
}
322
323
/* Format job update callback. */
324
static void
325
format_job_update(struct job *job)
326
0
{
327
0
  struct format_job *fj = job_get_data(job);
328
0
  struct evbuffer   *evb = job_get_event(job)->input;
329
0
  char      *line = NULL, *next;
330
0
  time_t       t;
331
332
0
  while ((next = evbuffer_readline(evb)) != NULL) {
333
0
    free(line);
334
0
    line = next;
335
0
  }
336
0
  if (line == NULL)
337
0
    return;
338
0
  fj->updated = 1;
339
340
0
  free(fj->out);
341
0
  fj->out = line;
342
343
0
  log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
344
345
0
  t = time(NULL);
346
0
  if (fj->status && fj->last != t) {
347
0
    if (fj->client != NULL)
348
0
      server_status_client(fj->client);
349
0
    fj->last = t;
350
0
  }
351
0
}
352
353
/* Format job complete callback. */
354
static void
355
format_job_complete(struct job *job)
356
0
{
357
0
  struct format_job *fj = job_get_data(job);
358
0
  struct evbuffer   *evb = job_get_event(job)->input;
359
0
  char      *line, *buf;
360
0
  size_t       len;
361
362
0
  fj->job = NULL;
363
364
0
  buf = NULL;
365
0
  if ((line = evbuffer_readline(evb)) == NULL) {
366
0
    len = EVBUFFER_LENGTH(evb);
367
0
    buf = xmalloc(len + 1);
368
0
    if (len != 0)
369
0
      memcpy(buf, EVBUFFER_DATA(evb), len);
370
0
    buf[len] = '\0';
371
0
  } else
372
0
    buf = line;
373
374
0
  log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
375
376
0
  if (*buf != '\0' || !fj->updated) {
377
0
    free(fj->out);
378
0
    fj->out = buf;
379
0
  } else
380
0
    free(buf);
381
382
0
  if (fj->status) {
383
0
    if (fj->client != NULL)
384
0
      server_status_client(fj->client);
385
0
    fj->status = 0;
386
0
  }
387
0
}
388
389
/* Find a job. */
390
static char *
391
format_job_get(struct format_expand_state *es, const char *cmd)
392
0
{
393
0
  struct format_tree    *ft = es->ft;
394
0
  struct format_job_tree    *jobs;
395
0
  struct format_job    fj0, *fj;
396
0
  time_t         t;
397
0
  char        *expanded;
398
0
  int        force;
399
0
  struct format_expand_state   next;
400
401
0
  if (ft->client == NULL)
402
0
    jobs = &format_jobs;
403
0
  else if (ft->client->jobs != NULL)
404
0
    jobs = ft->client->jobs;
405
0
  else {
406
0
    jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs);
407
0
    RB_INIT(jobs);
408
0
  }
409
410
0
  fj0.tag = ft->tag;
411
0
  fj0.cmd = cmd;
412
0
  if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) {
413
0
    fj = xcalloc(1, sizeof *fj);
414
0
    fj->client = ft->client;
415
0
    fj->tag = ft->tag;
416
0
    fj->cmd = xstrdup(cmd);
417
418
0
    RB_INSERT(format_job_tree, jobs, fj);
419
0
  }
420
421
0
  format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS|
422
0
      FORMAT_EXPAND_NOCYCLE);
423
0
  next.flags &= ~FORMAT_EXPAND_TIME;
424
425
0
  expanded = format_expand1(&next, cmd);
426
0
  if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
427
0
    free((void *)fj->expanded);
428
0
    fj->expanded = xstrdup(expanded);
429
0
    force = 1;
430
0
  } else
431
0
    force = (ft->flags & FORMAT_FORCE);
432
433
0
  t = time(NULL);
434
0
  if (force && fj->job != NULL)
435
0
         job_free(fj->job);
436
0
  if (force || (fj->job == NULL && fj->last != t)) {
437
0
    fj->job = job_run(expanded, 0, NULL, NULL, NULL,
438
0
        server_client_get_cwd(ft->client, NULL), format_job_update,
439
0
        format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1);
440
0
    if (fj->job == NULL) {
441
0
      free(fj->out);
442
0
      xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
443
0
    }
444
0
    fj->last = t;
445
0
    fj->updated = 0;
446
0
  } else if (fj->job != NULL && (t - fj->last) > 1 && fj->out == NULL)
447
0
    xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
448
0
  free(expanded);
449
450
0
  if (ft->flags & FORMAT_STATUS)
451
0
    fj->status = 1;
452
0
  if (fj->out == NULL)
453
0
    return (xstrdup(""));
454
0
  return (format_expand1(&next, fj->out));
455
0
}
456
457
/* Remove old jobs. */
458
static void
459
format_job_tidy(struct format_job_tree *jobs, int force)
460
0
{
461
0
  struct format_job *fj, *fj1;
462
0
  time_t       now;
463
464
0
  now = time(NULL);
465
0
  RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) {
466
0
    if (!force && (fj->last > now || now - fj->last < 3600))
467
0
      continue;
468
0
    RB_REMOVE(format_job_tree, jobs, fj);
469
470
0
    log_debug("%s: %s", __func__, fj->cmd);
471
472
0
    if (fj->job != NULL)
473
0
      job_free(fj->job);
474
475
0
    free((void *)fj->expanded);
476
0
    free((void *)fj->cmd);
477
0
    free(fj->out);
478
479
0
    free(fj);
480
0
  }
481
0
}
482
483
/* Work around needless -Wformat-nonliteral gcc warning. */
484
#ifdef __GNUC__
485
#pragma GCC diagnostic push
486
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
487
#endif
488
static size_t
489
format_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
490
0
{
491
0
  return (strftime(s, max, fmt, tm));
492
0
}
493
#ifdef __GNUC__
494
#pragma GCC diagnostic pop
495
#endif
496
497
/* Tidy old jobs for all clients. */
498
void
499
format_tidy_jobs(void)
500
0
{
501
0
  struct client *c;
502
503
0
  format_job_tidy(&format_jobs, 0);
504
0
  TAILQ_FOREACH(c, &clients, entry) {
505
0
    if (c->jobs != NULL)
506
0
      format_job_tidy(c->jobs, 0);
507
0
  }
508
0
}
509
510
/* Remove old jobs for client. */
511
void
512
format_lost_client(struct client *c)
513
0
{
514
0
  if (c->jobs != NULL)
515
0
    format_job_tidy(c->jobs, 1);
516
0
  free(c->jobs);
517
0
}
518
519
/* Wrapper for asprintf. */
520
static char * printflike(1, 2)
521
format_printf(const char *fmt, ...)
522
0
{
523
0
  va_list  ap;
524
0
  char  *s;
525
526
0
  va_start(ap, fmt);
527
0
  xvasprintf(&s, fmt, ap);
528
0
  va_end(ap);
529
0
  return (s);
530
0
}
531
532
/* Callback for host. */
533
static void *
534
format_cb_host(__unused struct format_tree *ft)
535
0
{
536
0
  char host[HOST_NAME_MAX + 1];
537
538
0
  if (gethostname(host, sizeof host) != 0)
539
0
    return (xstrdup(""));
540
0
  return (xstrdup(host));
541
0
}
542
543
/* Callback for host_short. */
544
static void *
545
format_cb_host_short(__unused struct format_tree *ft)
546
0
{
547
0
  char host[HOST_NAME_MAX + 1], *cp;
548
549
0
  if (gethostname(host, sizeof host) != 0)
550
0
    return (xstrdup(""));
551
0
  if ((cp = strchr(host, '.')) != NULL)
552
0
    *cp = '\0';
553
0
  return (xstrdup(host));
554
0
}
555
556
/* Callback for pid. */
557
static void *
558
format_cb_pid(__unused struct format_tree *ft)
559
0
{
560
0
  char  *value;
561
562
0
  xasprintf(&value, "%ld", (long)getpid());
563
0
  return (value);
564
0
}
565
566
/* Callback for session_attached_list. */
567
static void *
568
format_cb_session_attached_list(struct format_tree *ft)
569
0
{
570
0
  struct session  *s = ft->s;
571
0
  struct client *loop;
572
0
  struct evbuffer *buffer;
573
0
  int    size;
574
0
  char    *value = NULL;
575
576
0
  if (s == NULL)
577
0
    return (NULL);
578
579
0
  buffer = evbuffer_new();
580
0
  if (buffer == NULL)
581
0
    fatalx("out of memory");
582
583
0
  TAILQ_FOREACH(loop, &clients, entry) {
584
0
    if (loop->session == s) {
585
0
      if (EVBUFFER_LENGTH(buffer) > 0)
586
0
        evbuffer_add(buffer, ",", 1);
587
0
      evbuffer_add_printf(buffer, "%s", loop->name);
588
0
    }
589
0
  }
590
591
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
592
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
593
0
  evbuffer_free(buffer);
594
0
  return (value);
595
0
}
596
597
/* Callback for session_alert. */
598
static void *
599
format_cb_session_alert(struct format_tree *ft)
600
0
{
601
0
  struct session  *s = ft->s;
602
0
  struct winlink  *wl;
603
0
  char     alerts[1024];
604
0
  int    alerted = 0;
605
606
0
  if (s == NULL)
607
0
    return (NULL);
608
609
0
  *alerts = '\0';
610
0
  RB_FOREACH(wl, winlinks, &s->windows) {
611
0
    if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
612
0
      continue;
613
0
    if (~alerted & wl->flags & WINLINK_ACTIVITY) {
614
0
      strlcat(alerts, "#", sizeof alerts);
615
0
      alerted |= WINLINK_ACTIVITY;
616
0
    }
617
0
    if (~alerted & wl->flags & WINLINK_BELL) {
618
0
      strlcat(alerts, "!", sizeof alerts);
619
0
      alerted |= WINLINK_BELL;
620
0
    }
621
0
    if (~alerted & wl->flags & WINLINK_SILENCE) {
622
0
      strlcat(alerts, "~", sizeof alerts);
623
0
      alerted |= WINLINK_SILENCE;
624
0
    }
625
0
  }
626
0
  return (xstrdup(alerts));
627
0
}
628
629
/* Callback for session_alerts. */
630
static void *
631
format_cb_session_alerts(struct format_tree *ft)
632
0
{
633
0
  struct session  *s = ft->s;
634
0
  struct winlink  *wl;
635
0
  char     alerts[1024], tmp[16];
636
637
0
  if (s == NULL)
638
0
    return (NULL);
639
640
0
  *alerts = '\0';
641
0
  RB_FOREACH(wl, winlinks, &s->windows) {
642
0
    if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
643
0
      continue;
644
0
    xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
645
646
0
    if (*alerts != '\0')
647
0
      strlcat(alerts, ",", sizeof alerts);
648
0
    strlcat(alerts, tmp, sizeof alerts);
649
0
    if (wl->flags & WINLINK_ACTIVITY)
650
0
      strlcat(alerts, "#", sizeof alerts);
651
0
    if (wl->flags & WINLINK_BELL)
652
0
      strlcat(alerts, "!", sizeof alerts);
653
0
    if (wl->flags & WINLINK_SILENCE)
654
0
      strlcat(alerts, "~", sizeof alerts);
655
0
  }
656
0
  return (xstrdup(alerts));
657
0
}
658
659
/* Callback for session_stack. */
660
static void *
661
format_cb_session_stack(struct format_tree *ft)
662
0
{
663
0
  struct session  *s = ft->s;
664
0
  struct winlink  *wl;
665
0
  char     result[1024], tmp[16];
666
667
0
  if (s == NULL)
668
0
    return (NULL);
669
670
0
  xsnprintf(result, sizeof result, "%u", s->curw->idx);
671
0
  TAILQ_FOREACH(wl, &s->lastw, sentry) {
672
0
    xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
673
674
0
    if (*result != '\0')
675
0
      strlcat(result, ",", sizeof result);
676
0
    strlcat(result, tmp, sizeof result);
677
0
  }
678
0
  return (xstrdup(result));
679
0
}
680
681
/* Callback for window_stack_index. */
682
static void *
683
format_cb_window_stack_index(struct format_tree *ft)
684
0
{
685
0
  struct session  *s;
686
0
  struct winlink  *wl;
687
0
  u_int    idx;
688
0
  char    *value = NULL;
689
690
0
  if (ft->wl == NULL)
691
0
    return (NULL);
692
0
  s = ft->wl->session;
693
694
0
  idx = 0;
695
0
  TAILQ_FOREACH(wl, &s->lastw, sentry) {
696
0
    idx++;
697
0
    if (wl == ft->wl)
698
0
      break;
699
0
  }
700
0
  if (wl == NULL)
701
0
    return (xstrdup("0"));
702
0
  xasprintf(&value, "%u", idx);
703
0
  return (value);
704
0
}
705
706
/* Callback for window_linked_sessions_list. */
707
static void *
708
format_cb_window_linked_sessions_list(struct format_tree *ft)
709
0
{
710
0
  struct window *w;
711
0
  struct winlink  *wl;
712
0
  struct evbuffer *buffer;
713
0
  int    size;
714
0
  char    *value = NULL;
715
716
0
  if (ft->wl == NULL)
717
0
    return (NULL);
718
0
  w = ft->wl->window;
719
720
0
  buffer = evbuffer_new();
721
0
  if (buffer == NULL)
722
0
    fatalx("out of memory");
723
724
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
725
0
    if (EVBUFFER_LENGTH(buffer) > 0)
726
0
      evbuffer_add(buffer, ",", 1);
727
0
    evbuffer_add_printf(buffer, "%s", wl->session->name);
728
0
  }
729
730
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
731
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
732
0
  evbuffer_free(buffer);
733
0
  return (value);
734
0
}
735
736
/* Callback for window_active_sessions. */
737
static void *
738
format_cb_window_active_sessions(struct format_tree *ft)
739
0
{
740
0
  struct window *w;
741
0
  struct winlink  *wl;
742
0
  u_int    n = 0;
743
0
  char    *value;
744
745
0
  if (ft->wl == NULL)
746
0
    return (NULL);
747
0
  w = ft->wl->window;
748
749
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
750
0
    if (wl->session->curw == wl)
751
0
      n++;
752
0
  }
753
754
0
  xasprintf(&value, "%u", n);
755
0
  return (value);
756
0
}
757
758
/* Callback for window_active_sessions_list. */
759
static void *
760
format_cb_window_active_sessions_list(struct format_tree *ft)
761
0
{
762
0
  struct window *w;
763
0
  struct winlink  *wl;
764
0
  struct evbuffer *buffer;
765
0
  int    size;
766
0
  char    *value = NULL;
767
768
0
  if (ft->wl == NULL)
769
0
    return (NULL);
770
0
  w = ft->wl->window;
771
772
0
  buffer = evbuffer_new();
773
0
  if (buffer == NULL)
774
0
    fatalx("out of memory");
775
776
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
777
0
    if (wl->session->curw == wl) {
778
0
      if (EVBUFFER_LENGTH(buffer) > 0)
779
0
        evbuffer_add(buffer, ",", 1);
780
0
      evbuffer_add_printf(buffer, "%s", wl->session->name);
781
0
    }
782
0
  }
783
784
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
785
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
786
0
  evbuffer_free(buffer);
787
0
  return (value);
788
0
}
789
790
/* Callback for window_active_clients. */
791
static void *
792
format_cb_window_active_clients(struct format_tree *ft)
793
0
{
794
0
  struct window *w;
795
0
  struct client *loop;
796
0
  struct session  *client_session;
797
0
  u_int    n = 0;
798
0
  char    *value;
799
800
0
  if (ft->wl == NULL)
801
0
    return (NULL);
802
0
  w = ft->wl->window;
803
804
0
  TAILQ_FOREACH(loop, &clients, entry) {
805
0
    client_session = loop->session;
806
0
    if (client_session == NULL)
807
0
      continue;
808
809
0
    if (w == client_session->curw->window)
810
0
      n++;
811
0
  }
812
813
0
  xasprintf(&value, "%u", n);
814
0
  return (value);
815
0
}
816
817
/* Callback for window_active_clients_list. */
818
static void *
819
format_cb_window_active_clients_list(struct format_tree *ft)
820
0
{
821
0
  struct window *w;
822
0
  struct client *loop;
823
0
  struct session  *client_session;
824
0
  struct evbuffer *buffer;
825
0
  int    size;
826
0
  char    *value = NULL;
827
828
0
  if (ft->wl == NULL)
829
0
    return (NULL);
830
0
  w = ft->wl->window;
831
832
0
  buffer = evbuffer_new();
833
0
  if (buffer == NULL)
834
0
    fatalx("out of memory");
835
836
0
  TAILQ_FOREACH(loop, &clients, entry) {
837
0
    client_session = loop->session;
838
0
    if (client_session == NULL)
839
0
      continue;
840
841
0
    if (w == client_session->curw->window) {
842
0
      if (EVBUFFER_LENGTH(buffer) > 0)
843
0
        evbuffer_add(buffer, ",", 1);
844
0
      evbuffer_add_printf(buffer, "%s", loop->name);
845
0
    }
846
0
  }
847
848
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
849
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
850
0
  evbuffer_free(buffer);
851
0
  return (value);
852
0
}
853
854
/* Callback for window_layout. */
855
static void *
856
format_cb_window_layout(struct format_tree *ft)
857
0
{
858
0
  struct client   *c = ft->client;
859
0
  struct window   *w = ft->w;
860
0
  struct layout_cell  *lcroot;
861
0
  int      flags = 0;
862
863
0
  if (w == NULL)
864
0
    return (NULL);
865
866
0
  if (w->saved_layout_root != NULL)
867
0
    lcroot = w->saved_layout_root;
868
0
  else
869
0
    lcroot = w->layout_root;
870
871
0
  if (c != NULL &&
872
0
      (c->flags & CLIENT_CONTROL) &&
873
0
      (~c->flags & CLIENT_CONTROL_NEWLAYOUTS))
874
0
    flags |= LAYOUT_CUSTOM_OLD_FORMAT;
875
0
  return (layout_dump(w, lcroot, flags));
876
0
}
877
878
/* Callback for window_visible_layout. */
879
static void *
880
format_cb_window_visible_layout(struct format_tree *ft)
881
0
{
882
0
  struct client *c = ft->client;
883
0
  struct window *w = ft->w;
884
0
  int    flags = 0;
885
886
0
  if (w == NULL)
887
0
    return (NULL);
888
889
0
  if (c != NULL &&
890
0
      (c->flags & CLIENT_CONTROL) &&
891
0
      (~c->flags & CLIENT_CONTROL_NEWLAYOUTS))
892
0
    flags |= LAYOUT_CUSTOM_OLD_FORMAT;
893
0
  return (layout_dump(w, w->layout_root, flags));
894
0
}
895
896
/* Callback for pane_start_command. */
897
static void *
898
format_cb_start_command(struct format_tree *ft)
899
0
{
900
0
  struct window_pane  *wp = ft->wp;
901
902
0
  if (wp == NULL)
903
0
    return (NULL);
904
905
0
  return (cmd_stringify_argv(wp->argc, wp->argv));
906
0
}
907
908
/* Callback for pane_start_command_list. */
909
static void *
910
format_cb_start_command_list(struct format_tree *ft)
911
0
{
912
0
  struct window_pane  *wp = ft->wp;
913
0
  char      *buf = NULL, *s;
914
0
  size_t       len = 0;
915
0
  int      i;
916
917
0
  if (wp == NULL)
918
0
    return (NULL);
919
0
  if (wp->argc == 0)
920
0
    return (xstrdup(""));
921
922
0
  for (i = 0; i < wp->argc; i++) {
923
0
    s = format_quote_shell_single(wp->argv[i]);
924
925
0
    len += strlen(s) + 1;
926
0
    buf = xrealloc(buf, len);
927
928
0
    if (i == 0)
929
0
      *buf = '\0';
930
0
    else
931
0
      strlcat(buf, " ", len);
932
0
    strlcat(buf, s, len);
933
934
0
    free(s);
935
0
  }
936
0
  return (buf);
937
0
}
938
939
/* Callback for pane_start_path. */
940
static void *
941
format_cb_start_path(struct format_tree *ft)
942
0
{
943
0
  struct window_pane  *wp = ft->wp;
944
945
0
  if (wp == NULL)
946
0
    return (NULL);
947
948
0
  if (wp->cwd == NULL)
949
0
    return (xstrdup(""));
950
0
  return (xstrdup(wp->cwd));
951
0
}
952
953
/* Callback for pane_current_command. */
954
static void *
955
format_cb_current_command(struct format_tree *ft)
956
0
{
957
0
  struct window_pane  *wp = ft->wp;
958
0
  char      *cmd, *value;
959
960
0
  if (wp == NULL || wp->shell == NULL)
961
0
    return (NULL);
962
963
0
  cmd = osdep_get_name(wp->fd, wp->tty);
964
0
  if (cmd == NULL || *cmd == '\0') {
965
0
    free(cmd);
966
0
    cmd = cmd_stringify_argv(wp->argc, wp->argv);
967
0
    if (cmd == NULL || *cmd == '\0') {
968
0
      free(cmd);
969
0
      cmd = xstrdup(wp->shell);
970
0
    }
971
0
  }
972
0
  value = parse_window_name(cmd);
973
0
  free(cmd);
974
0
  return (value);
975
0
}
976
977
/* Callback for pane_current_path. */
978
static void *
979
format_cb_current_path(struct format_tree *ft)
980
0
{
981
0
  struct window_pane  *wp = ft->wp;
982
0
  char      *cwd;
983
984
0
  if (wp == NULL)
985
0
    return (NULL);
986
987
0
  cwd = osdep_get_cwd(wp->fd);
988
0
  if (cwd == NULL)
989
0
    return (NULL);
990
0
  return (xstrdup(cwd));
991
0
}
992
993
/* Callback for history_bytes. */
994
static void *
995
format_cb_history_bytes(struct format_tree *ft)
996
0
{
997
0
  struct window_pane  *wp = ft->wp;
998
0
  struct grid   *gd;
999
0
  struct grid_line  *gl;
1000
0
  size_t       size = 0;
1001
0
  u_int      i;
1002
0
  char      *value;
1003
1004
0
  if (wp == NULL)
1005
0
    return (NULL);
1006
0
  gd = wp->base.grid;
1007
1008
0
  for (i = 0; i < gd->hsize + gd->sy; i++) {
1009
0
    gl = grid_get_line(gd, i);
1010
0
    size += gl->cellsize * sizeof *gl->celldata;
1011
0
    size += gl->extdsize * sizeof *gl->extddata;
1012
0
  }
1013
0
  size += (gd->hsize + gd->sy) * sizeof *gl;
1014
1015
0
  xasprintf(&value, "%zu", size);
1016
0
  return (value);
1017
0
}
1018
1019
/* Callback for history_all_bytes. */
1020
static void *
1021
format_cb_history_all_bytes(struct format_tree *ft)
1022
0
{
1023
0
  struct window_pane  *wp = ft->wp;
1024
0
  struct grid   *gd;
1025
0
  struct grid_line  *gl;
1026
0
  u_int      i, lines, cells = 0, extended_cells = 0;
1027
0
  char      *value;
1028
1029
0
  if (wp == NULL)
1030
0
    return (NULL);
1031
0
  gd = wp->base.grid;
1032
1033
0
  lines = gd->hsize + gd->sy;
1034
0
  for (i = 0; i < lines; i++) {
1035
0
    gl = grid_get_line(gd, i);
1036
0
    cells += gl->cellsize;
1037
0
    extended_cells += gl->extdsize;
1038
0
  }
1039
1040
0
  xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines,
1041
0
      lines * sizeof *gl, cells, cells * sizeof *gl->celldata,
1042
0
      extended_cells, extended_cells * sizeof *gl->extddata);
1043
0
  return (value);
1044
0
}
1045
1046
/* Callback for pane_tabs. */
1047
static void *
1048
format_cb_pane_tabs(struct format_tree *ft)
1049
0
{
1050
0
  struct window_pane  *wp = ft->wp;
1051
0
  struct evbuffer   *buffer;
1052
0
  u_int      i;
1053
0
  int      size;
1054
0
  char      *value = NULL;
1055
1056
0
  if (wp == NULL)
1057
0
    return (NULL);
1058
1059
0
  buffer = evbuffer_new();
1060
0
  if (buffer == NULL)
1061
0
    fatalx("out of memory");
1062
0
  for (i = 0; i < wp->base.grid->sx; i++) {
1063
0
    if (!bit_test(wp->base.tabs, i))
1064
0
      continue;
1065
1066
0
    if (EVBUFFER_LENGTH(buffer) > 0)
1067
0
      evbuffer_add(buffer, ",", 1);
1068
0
    evbuffer_add_printf(buffer, "%u", i);
1069
0
  }
1070
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1071
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
1072
0
  evbuffer_free(buffer);
1073
0
  return (value);
1074
0
}
1075
1076
/* Callback for pane_fg. */
1077
static void *
1078
format_cb_pane_fg(struct format_tree *ft)
1079
0
{
1080
0
  struct window_pane  *wp = ft->wp;
1081
0
  struct grid_cell   gc;
1082
1083
0
  if (wp == NULL)
1084
0
    return (NULL);
1085
1086
0
  tty_default_colours(&gc, wp, NULL);
1087
0
  return (xstrdup(colour_tostring(gc.fg)));
1088
0
}
1089
1090
/* Callback for pane_flags. */
1091
static void *
1092
format_cb_pane_flags(struct format_tree *ft)
1093
0
{
1094
0
  if (ft->wp != NULL)
1095
0
    return (xstrdup(window_pane_printable_flags(ft->wp)));
1096
0
  return (NULL);
1097
0
}
1098
1099
/* Callback for pane_floating_flag. */
1100
static void *
1101
format_cb_pane_floating_flag(struct format_tree *ft)
1102
0
{
1103
0
  struct window_pane  *wp = ft->wp;
1104
1105
0
  if (wp != NULL) {
1106
0
    if (window_pane_is_floating(wp))
1107
0
      return (xstrdup("1"));
1108
0
    return (xstrdup("0"));
1109
0
  }
1110
0
  return (NULL);
1111
0
}
1112
1113
/* Callback for pane_modal_flag. */
1114
static void *
1115
format_cb_pane_modal_flag(struct format_tree *ft)
1116
0
{
1117
0
  struct window_pane  *wp = ft->wp;
1118
1119
0
  if (wp != NULL) {
1120
0
    if (wp == wp->window->modal)
1121
0
      return (xstrdup("1"));
1122
0
    return (xstrdup("0"));
1123
0
  }
1124
0
  return (NULL);
1125
0
}
1126
1127
/* Callback for pane_bg. */
1128
static void *
1129
format_cb_pane_bg(struct format_tree *ft)
1130
0
{
1131
0
  struct window_pane  *wp = ft->wp;
1132
0
  struct grid_cell   gc;
1133
1134
0
  if (wp == NULL)
1135
0
    return (NULL);
1136
1137
0
  tty_default_colours(&gc, wp, NULL);
1138
0
  return (xstrdup(colour_tostring(gc.bg)));
1139
0
}
1140
1141
/* Callback for session_group_list. */
1142
static void *
1143
format_cb_session_group_list(struct format_tree *ft)
1144
0
{
1145
0
  struct session    *s = ft->s;
1146
0
  struct session_group  *sg;
1147
0
  struct session    *loop;
1148
0
  struct evbuffer   *buffer;
1149
0
  int      size;
1150
0
  char      *value = NULL;
1151
1152
0
  if (s == NULL)
1153
0
    return (NULL);
1154
0
  sg = session_group_contains(s);
1155
0
  if (sg == NULL)
1156
0
    return (NULL);
1157
1158
0
  buffer = evbuffer_new();
1159
0
  if (buffer == NULL)
1160
0
    fatalx("out of memory");
1161
1162
0
  TAILQ_FOREACH(loop, &sg->sessions, gentry) {
1163
0
    if (EVBUFFER_LENGTH(buffer) > 0)
1164
0
      evbuffer_add(buffer, ",", 1);
1165
0
    evbuffer_add_printf(buffer, "%s", loop->name);
1166
0
  }
1167
1168
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1169
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
1170
0
  evbuffer_free(buffer);
1171
0
  return (value);
1172
0
}
1173
1174
/* Callback for session_group_attached_list. */
1175
static void *
1176
format_cb_session_group_attached_list(struct format_tree *ft)
1177
0
{
1178
0
  struct session    *s = ft->s, *client_session, *session_loop;
1179
0
  struct session_group  *sg;
1180
0
  struct client   *loop;
1181
0
  struct evbuffer   *buffer;
1182
0
  int      size;
1183
0
  char      *value = NULL;
1184
1185
0
  if (s == NULL)
1186
0
    return (NULL);
1187
0
  sg = session_group_contains(s);
1188
0
  if (sg == NULL)
1189
0
    return (NULL);
1190
1191
0
  buffer = evbuffer_new();
1192
0
  if (buffer == NULL)
1193
0
    fatalx("out of memory");
1194
1195
0
  TAILQ_FOREACH(loop, &clients, entry) {
1196
0
    client_session = loop->session;
1197
0
    if (client_session == NULL)
1198
0
      continue;
1199
0
    TAILQ_FOREACH(session_loop, &sg->sessions, gentry) {
1200
0
      if (session_loop == client_session){
1201
0
        if (EVBUFFER_LENGTH(buffer) > 0)
1202
0
          evbuffer_add(buffer, ",", 1);
1203
0
        evbuffer_add_printf(buffer, "%s", loop->name);
1204
0
      }
1205
0
    }
1206
0
  }
1207
1208
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
1209
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
1210
0
  evbuffer_free(buffer);
1211
0
  return (value);
1212
0
}
1213
1214
/* Callback for pane_in_mode. */
1215
static void *
1216
format_cb_pane_in_mode(struct format_tree *ft)
1217
0
{
1218
0
  struct window_pane    *wp = ft->wp;
1219
0
  u_int        n = 0;
1220
0
  struct window_mode_entry  *wme;
1221
0
  char        *value;
1222
1223
0
  if (wp == NULL)
1224
0
    return (NULL);
1225
1226
0
  TAILQ_FOREACH(wme, &wp->modes, entry)
1227
0
    n++;
1228
0
  xasprintf(&value, "%u", n);
1229
0
  return (value);
1230
0
}
1231
1232
/* Callback for pane_at_top. */
1233
static void *
1234
format_cb_pane_at_top(struct format_tree *ft)
1235
0
{
1236
0
  struct window_pane  *wp = ft->wp;
1237
0
  int      status, flag;
1238
0
  char      *value;
1239
1240
0
  if (wp == NULL)
1241
0
    return (NULL);
1242
1243
0
  status = window_pane_get_pane_status(wp);
1244
0
  if (status == PANE_STATUS_TOP)
1245
0
    flag = (wp->yoff == 1);
1246
0
  else
1247
0
    flag = (wp->yoff == 0);
1248
0
  xasprintf(&value, "%d", flag);
1249
0
  return (value);
1250
0
}
1251
1252
/* Callback for pane_at_bottom. */
1253
static void *
1254
format_cb_pane_at_bottom(struct format_tree *ft)
1255
0
{
1256
0
  struct window_pane  *wp = ft->wp;
1257
0
  struct window   *w;
1258
0
  int      status, flag;
1259
0
  char      *value;
1260
1261
0
  if (wp == NULL)
1262
0
    return (NULL);
1263
0
  w = wp->window;
1264
1265
0
  status = window_pane_get_pane_status(wp);
1266
0
  if (status == PANE_STATUS_BOTTOM)
1267
0
    flag = (wp->yoff + (int)wp->sy == (int)w->sy - 1);
1268
0
  else
1269
0
    flag = (wp->yoff + (int)wp->sy == (int)w->sy);
1270
0
  xasprintf(&value, "%d", flag);
1271
0
  return (value);
1272
0
}
1273
1274
/* Callback for cursor_character. */
1275
static void *
1276
format_cb_cursor_character(struct format_tree *ft)
1277
0
{
1278
0
  struct window_pane  *wp = ft->wp;
1279
0
  struct grid_cell   gc;
1280
0
  char      *value = NULL;
1281
1282
0
  if (wp == NULL)
1283
0
    return (NULL);
1284
1285
0
  grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc);
1286
0
  if (~gc.flags & GRID_FLAG_PADDING)
1287
0
    xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data);
1288
0
  return (value);
1289
0
}
1290
1291
/* Callback for cursor_colour. */
1292
static void *
1293
format_cb_cursor_colour(struct format_tree *ft)
1294
0
{
1295
0
  struct window_pane  *wp = ft->wp;
1296
1297
0
  if (wp == NULL || wp->screen == NULL)
1298
0
    return (NULL);
1299
1300
0
  if (wp->screen->ccolour != -1)
1301
0
    return (xstrdup(colour_tostring(wp->screen->ccolour)));
1302
0
  return (xstrdup(colour_tostring(wp->screen->default_ccolour)));
1303
0
}
1304
1305
/* Callback for mouse_word. */
1306
static void *
1307
format_cb_mouse_word(struct format_tree *ft)
1308
0
{
1309
0
  struct window_pane  *wp;
1310
0
  struct grid   *gd;
1311
0
  u_int      x, y;
1312
1313
0
  if (!ft->m.valid)
1314
0
    return (NULL);
1315
0
  wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1316
0
  if (wp == NULL)
1317
0
    return (NULL);
1318
0
  if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1319
0
    return (NULL);
1320
1321
0
  if (!TAILQ_EMPTY(&wp->modes)) {
1322
0
    if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
1323
0
      return (window_copy_get_word(wp, x, y));
1324
0
    return (NULL);
1325
0
  }
1326
0
  gd = wp->base.grid;
1327
0
  return (format_grid_word(gd, x, gd->hsize + y));
1328
0
}
1329
1330
/* Callback for mouse_hyperlink. */
1331
static void *
1332
format_cb_mouse_hyperlink(struct format_tree *ft)
1333
0
{
1334
0
  struct window_pane  *wp;
1335
0
  struct grid   *gd;
1336
0
  u_int      x, y;
1337
1338
0
  if (!ft->m.valid)
1339
0
    return (NULL);
1340
0
  wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1341
0
  if (wp == NULL)
1342
0
    return (NULL);
1343
0
  if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1344
0
    return (NULL);
1345
1346
0
  if (!TAILQ_EMPTY(&wp->modes)) {
1347
0
    if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
1348
0
      return (window_copy_get_hyperlink(wp, x, y));
1349
0
    return (NULL);
1350
0
  }
1351
0
  gd = wp->base.grid;
1352
0
  return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
1353
0
}
1354
1355
/* Callback for mouse_line. */
1356
static void *
1357
format_cb_mouse_line(struct format_tree *ft)
1358
0
{
1359
0
  struct window_pane  *wp;
1360
0
  struct grid   *gd;
1361
0
  u_int      x, y;
1362
1363
0
  if (!ft->m.valid)
1364
0
    return (NULL);
1365
0
  wp = cmd_mouse_pane(&ft->m, NULL, NULL);
1366
0
  if (wp == NULL)
1367
0
    return (NULL);
1368
0
  if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
1369
0
    return (NULL);
1370
1371
0
  if (!TAILQ_EMPTY(&wp->modes)) {
1372
0
    if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
1373
0
      return (window_copy_get_line(wp, y));
1374
0
    return (NULL);
1375
0
  }
1376
0
  gd = wp->base.grid;
1377
0
  return (format_grid_line(gd, gd->hsize + y));
1378
0
}
1379
1380
/* Callback for mouse_status_line. */
1381
static void *
1382
format_cb_mouse_status_line(struct format_tree *ft)
1383
0
{
1384
0
  char  *value;
1385
0
  u_int  y;
1386
1387
0
  if (!ft->m.valid)
1388
0
    return (NULL);
1389
0
  if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
1390
0
    return (NULL);
1391
1392
0
  if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
1393
0
    y = ft->m.y;
1394
0
  } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
1395
0
    y = ft->m.y - ft->m.statusat;
1396
0
  } else
1397
0
    return (NULL);
1398
0
  xasprintf(&value, "%u", y);
1399
0
  return (value);
1400
1401
0
}
1402
1403
/* Callback for mouse_status_range. */
1404
static void *
1405
format_cb_mouse_status_range(struct format_tree *ft)
1406
0
{
1407
0
  struct style_range  *sr;
1408
0
  u_int      x, y;
1409
1410
0
  if (!ft->m.valid)
1411
0
    return (NULL);
1412
0
  if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
1413
0
    return (NULL);
1414
1415
0
  if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
1416
0
    x = ft->m.x;
1417
0
    y = ft->m.y;
1418
0
  } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
1419
0
    x = ft->m.x;
1420
0
    y = ft->m.y - ft->m.statusat;
1421
0
  } else
1422
0
    return (NULL);
1423
1424
0
  sr = status_get_range(ft->c, x, y);
1425
0
  if (sr == NULL)
1426
0
    return (NULL);
1427
0
  switch (sr->type) {
1428
0
  case STYLE_RANGE_NONE:
1429
0
    return (NULL);
1430
0
  case STYLE_RANGE_LEFT:
1431
0
    return (xstrdup("left"));
1432
0
  case STYLE_RANGE_RIGHT:
1433
0
    return (xstrdup("right"));
1434
0
  case STYLE_RANGE_PANE:
1435
0
    return (xstrdup("pane"));
1436
0
  case STYLE_RANGE_WINDOW:
1437
0
    return (xstrdup("window"));
1438
0
  case STYLE_RANGE_SESSION:
1439
0
    return (xstrdup("session"));
1440
0
  case STYLE_RANGE_USER:
1441
0
    return (xstrdup(sr->string));
1442
0
  case STYLE_RANGE_CONTROL:
1443
0
    return (xstrdup("control"));
1444
0
  }
1445
0
  return (NULL);
1446
0
}
1447
1448
/* Callback for alternate_on. */
1449
static void *
1450
format_cb_alternate_on(struct format_tree *ft)
1451
0
{
1452
0
  if (ft->wp != NULL) {
1453
0
    if (ft->wp->base.saved_grid != NULL)
1454
0
      return (xstrdup("1"));
1455
0
    return (xstrdup("0"));
1456
0
  }
1457
0
  return (NULL);
1458
0
}
1459
1460
/* Callback for alternate_saved_x. */
1461
static void *
1462
format_cb_alternate_saved_x(struct format_tree *ft)
1463
0
{
1464
0
  if (ft->wp != NULL)
1465
0
    return (format_printf("%u", ft->wp->base.saved_cx));
1466
0
  return (NULL);
1467
0
}
1468
1469
/* Callback for alternate_saved_y. */
1470
static void *
1471
format_cb_alternate_saved_y(struct format_tree *ft)
1472
0
{
1473
0
  if (ft->wp != NULL)
1474
0
    return (format_printf("%u", ft->wp->base.saved_cy));
1475
0
  return (NULL);
1476
0
}
1477
1478
/* Callback for bracket_paste_flag. */
1479
static void *
1480
format_cb_bracket_paste_flag(struct format_tree *ft)
1481
0
{
1482
0
  if (ft->wp != NULL && ft->wp->screen != NULL) {
1483
0
    if (ft->wp->screen->mode & MODE_BRACKETPASTE)
1484
0
      return (xstrdup("1"));
1485
0
    return (xstrdup("0"));
1486
0
  }
1487
0
  return (NULL);
1488
0
}
1489
1490
/* Callback for buffer_name. */
1491
static void *
1492
format_cb_buffer_name(struct format_tree *ft)
1493
0
{
1494
0
  if (ft->pb != NULL)
1495
0
    return (xstrdup(paste_buffer_name(ft->pb)));
1496
0
  return (NULL);
1497
0
}
1498
1499
/* Callback for buffer_sample. */
1500
static void *
1501
format_cb_buffer_sample(struct format_tree *ft)
1502
0
{
1503
0
  if (ft->pb != NULL)
1504
0
    return (paste_make_sample(ft->pb));
1505
0
  return (NULL);
1506
0
}
1507
1508
/* Callback for buffer_full. */
1509
static void *
1510
format_cb_buffer_full(struct format_tree *ft)
1511
0
{
1512
0
  size_t     size;
1513
0
  const char  *s;
1514
1515
0
  if (ft->pb != NULL) {
1516
0
    s = paste_buffer_data(ft->pb, &size);
1517
0
    if (s != NULL)
1518
0
      return (xstrndup(s, size));
1519
0
  }
1520
0
  return (NULL);
1521
0
}
1522
1523
/* Callback for buffer_size. */
1524
static void *
1525
format_cb_buffer_size(struct format_tree *ft)
1526
0
{
1527
0
  size_t  size;
1528
1529
0
  if (ft->pb != NULL) {
1530
0
    paste_buffer_data(ft->pb, &size);
1531
0
    return (format_printf("%zu", size));
1532
0
  }
1533
0
  return (NULL);
1534
0
}
1535
1536
/* Callback for client_cell_height. */
1537
static void *
1538
format_cb_client_cell_height(struct format_tree *ft)
1539
0
{
1540
0
  if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1541
0
    return (format_printf("%u", ft->c->tty.ypixel));
1542
0
  return (NULL);
1543
0
}
1544
1545
/* Callback for client_cell_width. */
1546
static void *
1547
format_cb_client_cell_width(struct format_tree *ft)
1548
0
{
1549
0
  if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1550
0
    return (format_printf("%u", ft->c->tty.xpixel));
1551
0
  return (NULL);
1552
0
}
1553
1554
/* Callback for client_colours. */
1555
static void *
1556
format_cb_client_colours(struct format_tree *ft)
1557
0
{
1558
0
  struct tty_term *term;
1559
0
  u_int    colours;
1560
1561
0
  if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
1562
0
    return (NULL);
1563
0
  term = ft->c->tty.term;
1564
1565
0
  if (term->flags & TERM_RGBCOLOURS)
1566
0
    colours = 16777216;
1567
0
  else if (term->flags & TERM_256COLOURS)
1568
0
    colours = 256;
1569
0
  else {
1570
0
    colours = tty_term_number(term, TTYC_COLORS);
1571
0
    if (colours < 8)
1572
0
      colours = 2;
1573
0
    else if (colours < 16)
1574
0
      colours = 8;
1575
0
    else
1576
0
      colours = 16;
1577
0
  }
1578
0
  return (format_printf("%u", colours));
1579
0
}
1580
1581
/* Callback for client_control_mode. */
1582
static void *
1583
format_cb_client_control_mode(struct format_tree *ft)
1584
0
{
1585
0
  if (ft->c != NULL) {
1586
0
    if (ft->c->flags & CLIENT_CONTROL)
1587
0
      return (xstrdup("1"));
1588
0
    return (xstrdup("0"));
1589
0
  }
1590
0
  return (NULL);
1591
0
}
1592
1593
/* Callback for client_discarded. */
1594
static void *
1595
format_cb_client_discarded(struct format_tree *ft)
1596
0
{
1597
0
  if (ft->c != NULL)
1598
0
    return (format_printf("%zu", ft->c->discarded));
1599
0
  return (NULL);
1600
0
}
1601
1602
/* Callback for client_flags. */
1603
static void *
1604
format_cb_client_flags(struct format_tree *ft)
1605
0
{
1606
0
  if (ft->c != NULL)
1607
0
    return (xstrdup(server_client_get_flags(ft->c)));
1608
0
  return (NULL);
1609
0
}
1610
1611
/* Callback for client_height. */
1612
static void *
1613
format_cb_client_height(struct format_tree *ft)
1614
0
{
1615
0
  if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
1616
0
    return (format_printf("%u", ft->c->tty.sy));
1617
0
  return (NULL);
1618
0
}
1619
1620
/* Callback for client_key_table. */
1621
static void *
1622
format_cb_client_key_table(struct format_tree *ft)
1623
0
{
1624
0
  if (ft->c != NULL)
1625
0
    return (xstrdup(ft->c->keytable->name));
1626
0
  return (NULL);
1627
0
}
1628
1629
/* Callback for client_last_session. */
1630
static void *
1631
format_cb_client_last_session(struct format_tree *ft)
1632
0
{
1633
0
  if (ft->c != NULL &&
1634
0
      ft->c->last_session != NULL &&
1635
0
      session_alive(ft->c->last_session))
1636
0
    return (xstrdup(ft->c->last_session->name));
1637
0
  return (NULL);
1638
0
}
1639
1640
/* Callback for client_name. */
1641
static void *
1642
format_cb_client_name(struct format_tree *ft)
1643
0
{
1644
0
  if (ft->c != NULL)
1645
0
    return (xstrdup(ft->c->name));
1646
0
  return (NULL);
1647
0
}
1648
1649
/* Callback for client_pid. */
1650
static void *
1651
format_cb_client_pid(struct format_tree *ft)
1652
0
{
1653
0
  if (ft->c != NULL)
1654
0
    return (format_printf("%ld", (long)ft->c->pid));
1655
0
  return (NULL);
1656
0
}
1657
1658
/* Callback for client_prefix. */
1659
static void *
1660
format_cb_client_prefix(struct format_tree *ft)
1661
0
{
1662
0
  const char  *name;
1663
1664
0
  if (ft->c != NULL) {
1665
0
    name = server_client_get_key_table(ft->c);
1666
0
    if (strcmp(ft->c->keytable->name, name) == 0)
1667
0
      return (xstrdup("0"));
1668
0
    return (xstrdup("1"));
1669
0
  }
1670
0
  return (NULL);
1671
0
}
1672
1673
/* Callback for client_readonly. */
1674
static void *
1675
format_cb_client_readonly(struct format_tree *ft)
1676
0
{
1677
0
  if (ft->c != NULL) {
1678
0
    if (ft->c->flags & CLIENT_READONLY)
1679
0
      return (xstrdup("1"));
1680
0
    return (xstrdup("0"));
1681
0
  }
1682
0
  return (NULL);
1683
0
}
1684
1685
/* Callback for client_session. */
1686
static void *
1687
format_cb_client_session(struct format_tree *ft)
1688
0
{
1689
0
  if (ft->c != NULL && ft->c->session != NULL)
1690
0
    return (xstrdup(ft->c->session->name));
1691
0
  return (NULL);
1692
0
}
1693
1694
/* Callback for client_termfeatures. */
1695
static void *
1696
format_cb_client_termfeatures(struct format_tree *ft)
1697
0
{
1698
0
  if (ft->c != NULL)
1699
0
    return (xstrdup(tty_get_features(ft->c->term_features)));
1700
0
  return (NULL);
1701
0
}
1702
1703
/* Callback for client_termname. */
1704
static void *
1705
format_cb_client_termname(struct format_tree *ft)
1706
0
{
1707
0
  if (ft->c != NULL)
1708
0
    return (xstrdup(ft->c->term_name));
1709
0
  return (NULL);
1710
0
}
1711
1712
/* Callback for client_termtype. */
1713
static void *
1714
format_cb_client_termtype(struct format_tree *ft)
1715
0
{
1716
0
  if (ft->c != NULL) {
1717
0
    if (ft->c->term_type == NULL)
1718
0
      return (xstrdup(""));
1719
0
    return (xstrdup(ft->c->term_type));
1720
0
  }
1721
0
  return (NULL);
1722
0
}
1723
1724
/* Callback for client_tty. */
1725
static void *
1726
format_cb_client_tty(struct format_tree *ft)
1727
0
{
1728
0
  if (ft->c != NULL)
1729
0
    return (xstrdup(ft->c->ttyname));
1730
0
  return (NULL);
1731
0
}
1732
1733
/* Callback for client_uid. */
1734
static void *
1735
format_cb_client_uid(struct format_tree *ft)
1736
0
{
1737
0
  uid_t uid;
1738
1739
0
  if (ft->c != NULL) {
1740
0
    uid = proc_get_peer_uid(ft->c->peer);
1741
0
    if (uid != (uid_t)-1)
1742
0
      return (format_printf("%ld", (long)uid));
1743
0
  }
1744
0
  return (NULL);
1745
0
}
1746
1747
/* Callback for client_user. */
1748
static void *
1749
format_cb_client_user(struct format_tree *ft)
1750
0
{
1751
0
  uid_t    uid;
1752
0
  struct passwd *pw;
1753
1754
0
  if (ft->c != NULL) {
1755
0
    if (ft->c->user != NULL)
1756
0
      return (xstrdup(ft->c->user));
1757
0
    uid = proc_get_peer_uid(ft->c->peer);
1758
0
    if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) {
1759
0
      ft->c->user = xstrdup(pw->pw_name);
1760
0
      return (xstrdup(ft->c->user));
1761
0
    }
1762
0
  }
1763
0
  return (NULL);
1764
0
}
1765
1766
/* Callback for client_utf8. */
1767
static void *
1768
format_cb_client_utf8(struct format_tree *ft)
1769
0
{
1770
0
  if (ft->c != NULL) {
1771
0
    if (ft->c->flags & CLIENT_UTF8)
1772
0
      return (xstrdup("1"));
1773
0
    return (xstrdup("0"));
1774
0
  }
1775
0
  return (NULL);
1776
0
}
1777
1778
/* Callback for client_width. */
1779
static void *
1780
format_cb_client_width(struct format_tree *ft)
1781
0
{
1782
0
  if (ft->c != NULL)
1783
0
    return (format_printf("%u", ft->c->tty.sx));
1784
0
  return (NULL);
1785
0
}
1786
1787
/* Callback for client_written. */
1788
static void *
1789
format_cb_client_written(struct format_tree *ft)
1790
0
{
1791
0
  if (ft->c != NULL)
1792
0
    return (format_printf("%zu", ft->c->written));
1793
0
  return (NULL);
1794
0
}
1795
1796
/* Callback for client_theme. */
1797
static void *
1798
format_cb_client_theme(struct format_tree *ft)
1799
0
{
1800
0
  if (ft->c != NULL) {
1801
0
    switch (ft->c->theme) {
1802
0
    case THEME_DARK:
1803
0
      return (xstrdup("dark"));
1804
0
    case THEME_LIGHT:
1805
0
      return (xstrdup("light"));
1806
0
    case THEME_UNKNOWN:
1807
0
      return (NULL);
1808
0
    }
1809
0
  }
1810
0
  return (NULL);
1811
0
}
1812
1813
/* Callback for config_files. */
1814
static void *
1815
format_cb_config_files(__unused struct format_tree *ft)
1816
0
{
1817
0
  char  *s = NULL;
1818
0
  size_t   slen = 0;
1819
0
  u_int  i;
1820
0
  size_t   n;
1821
1822
0
  for (i = 0; i < cfg_nfiles; i++) {
1823
0
    n = strlen(cfg_files[i]) + 1;
1824
0
    s = xrealloc(s, slen + n + 1);
1825
0
    slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]);
1826
0
  }
1827
0
  if (s == NULL)
1828
0
    return (xstrdup(""));
1829
0
  s[slen - 1] = '\0';
1830
0
  return (s);
1831
0
}
1832
1833
/* Callback for cursor_flag. */
1834
static void *
1835
format_cb_cursor_flag(struct format_tree *ft)
1836
0
{
1837
0
  if (ft->wp != NULL) {
1838
0
    if (ft->wp->base.mode & MODE_CURSOR)
1839
0
      return (xstrdup("1"));
1840
0
    return (xstrdup("0"));
1841
0
  }
1842
0
  return (NULL);
1843
0
}
1844
1845
/* Callback for cursor_shape. */
1846
static void *
1847
format_cb_cursor_shape(struct format_tree *ft)
1848
0
{
1849
0
  if (ft->wp != NULL && ft->wp->screen != NULL) {
1850
0
    switch (ft->wp->screen->cstyle) {
1851
0
    case SCREEN_CURSOR_BLOCK:
1852
0
      return (xstrdup("block"));
1853
0
    case SCREEN_CURSOR_UNDERLINE:
1854
0
      return (xstrdup("underline"));
1855
0
    case SCREEN_CURSOR_BAR:
1856
0
      return (xstrdup("bar"));
1857
0
    default:
1858
0
      return (xstrdup("default"));
1859
0
    }
1860
0
  }
1861
0
  return (NULL);
1862
0
}
1863
1864
/* Callback for cursor_very_visible. */
1865
static void *
1866
format_cb_cursor_very_visible(struct format_tree *ft)
1867
0
{
1868
0
  if (ft->wp != NULL && ft->wp->screen != NULL) {
1869
0
    if (ft->wp->screen->mode & MODE_CURSOR_VERY_VISIBLE)
1870
0
      return (xstrdup("1"));
1871
0
    return (xstrdup("0"));
1872
0
  }
1873
0
  return (NULL);
1874
0
}
1875
1876
/* Callback for cursor_x. */
1877
static void *
1878
format_cb_cursor_x(struct format_tree *ft)
1879
0
{
1880
0
  if (ft->wp != NULL)
1881
0
    return (format_printf("%u", ft->wp->base.cx));
1882
0
  return (NULL);
1883
0
}
1884
1885
/* Callback for cursor_y. */
1886
static void *
1887
format_cb_cursor_y(struct format_tree *ft)
1888
0
{
1889
0
  if (ft->wp != NULL)
1890
0
    return (format_printf("%u", ft->wp->base.cy));
1891
0
  return (NULL);
1892
0
}
1893
1894
/* Callback for cursor_blinking. */
1895
static void *
1896
format_cb_cursor_blinking(struct format_tree *ft)
1897
0
{
1898
0
  if (ft->wp != NULL && ft->wp->screen != NULL) {
1899
0
    if (ft->wp->screen->mode & MODE_CURSOR_BLINKING)
1900
0
      return (xstrdup("1"));
1901
0
    return (xstrdup("0"));
1902
0
  }
1903
0
  return (NULL);
1904
0
}
1905
1906
/* Callback for history_added. */
1907
static void *
1908
format_cb_history_added(struct format_tree *ft)
1909
0
{
1910
0
  if (ft->wp != NULL)
1911
0
    return (format_printf("%u", ft->wp->base.grid->scroll_added));
1912
0
  return (NULL);
1913
0
}
1914
1915
/* Callback for history_collected. */
1916
static void *
1917
format_cb_history_collected(struct format_tree *ft)
1918
0
{
1919
0
  struct window_pane  *wp = ft->wp;
1920
1921
0
  if (wp != NULL)
1922
0
    return (format_printf("%u", wp->base.grid->scroll_collected));
1923
0
  return (NULL);
1924
0
}
1925
1926
/* Callback for history_generation. */
1927
static void *
1928
format_cb_history_generation(struct format_tree *ft)
1929
0
{
1930
0
  struct window_pane  *wp = ft->wp;
1931
1932
0
  if (wp != NULL)
1933
0
    return (format_printf("%u", wp->base.grid->scroll_generation));
1934
0
  return (NULL);
1935
0
}
1936
1937
/* Callback for history_limit. */
1938
static void *
1939
format_cb_history_limit(struct format_tree *ft)
1940
0
{
1941
0
  if (ft->wp != NULL)
1942
0
    return (format_printf("%u", ft->wp->base.grid->hlimit));
1943
0
  return (NULL);
1944
0
}
1945
1946
/* Callback for history_size. */
1947
static void *
1948
format_cb_history_size(struct format_tree *ft)
1949
0
{
1950
0
  if (ft->wp != NULL)
1951
0
    return (format_printf("%u", ft->wp->base.grid->hsize));
1952
0
  return (NULL);
1953
0
}
1954
1955
/* Callback for insert_flag. */
1956
static void *
1957
format_cb_insert_flag(struct format_tree *ft)
1958
0
{
1959
0
  if (ft->wp != NULL) {
1960
0
    if (ft->wp->base.mode & MODE_INSERT)
1961
0
      return (xstrdup("1"));
1962
0
    return (xstrdup("0"));
1963
0
  }
1964
0
  return (NULL);
1965
0
}
1966
1967
/* Callback for keypad_cursor_flag. */
1968
static void *
1969
format_cb_keypad_cursor_flag(struct format_tree *ft)
1970
0
{
1971
0
  if (ft->wp != NULL) {
1972
0
    if (ft->wp->base.mode & MODE_KCURSOR)
1973
0
      return (xstrdup("1"));
1974
0
    return (xstrdup("0"));
1975
0
  }
1976
0
  return (NULL);
1977
0
}
1978
1979
/* Callback for keypad_flag. */
1980
static void *
1981
format_cb_keypad_flag(struct format_tree *ft)
1982
0
{
1983
0
  if (ft->wp != NULL) {
1984
0
    if (ft->wp->base.mode & MODE_KKEYPAD)
1985
0
      return (xstrdup("1"));
1986
0
    return (xstrdup("0"));
1987
0
  }
1988
0
  return (NULL);
1989
0
}
1990
1991
/* Callback for mouse_all_flag. */
1992
static void *
1993
format_cb_mouse_all_flag(struct format_tree *ft)
1994
0
{
1995
0
  if (ft->wp != NULL) {
1996
0
    if (ft->wp->base.mode & MODE_MOUSE_ALL)
1997
0
      return (xstrdup("1"));
1998
0
    return (xstrdup("0"));
1999
0
  }
2000
0
  return (NULL);
2001
0
}
2002
2003
/* Callback for mouse_any_flag. */
2004
static void *
2005
format_cb_mouse_any_flag(struct format_tree *ft)
2006
0
{
2007
0
  if (ft->wp != NULL) {
2008
0
    if (ft->wp->base.mode & ALL_MOUSE_MODES)
2009
0
      return (xstrdup("1"));
2010
0
    return (xstrdup("0"));
2011
0
  }
2012
0
  return (NULL);
2013
0
}
2014
2015
/* Callback for mouse_button_flag. */
2016
static void *
2017
format_cb_mouse_button_flag(struct format_tree *ft)
2018
0
{
2019
0
  if (ft->wp != NULL) {
2020
0
    if (ft->wp->base.mode & MODE_MOUSE_BUTTON)
2021
0
      return (xstrdup("1"));
2022
0
    return (xstrdup("0"));
2023
0
  }
2024
0
  return (NULL);
2025
0
}
2026
2027
/* Callback for mouse_pane. */
2028
static void *
2029
format_cb_mouse_pane(struct format_tree *ft)
2030
0
{
2031
0
  struct window_pane  *wp;
2032
2033
0
  if (ft->m.valid) {
2034
0
    wp = cmd_mouse_pane(&ft->m, NULL, NULL);
2035
0
    if (wp != NULL)
2036
0
      return (format_printf("%%%u", wp->id));
2037
0
    return (NULL);
2038
0
  }
2039
0
  return (NULL);
2040
0
}
2041
2042
/* Callback for mouse_sgr_flag. */
2043
static void *
2044
format_cb_mouse_sgr_flag(struct format_tree *ft)
2045
0
{
2046
0
  if (ft->wp != NULL) {
2047
0
    if (ft->wp->base.mode & MODE_MOUSE_SGR)
2048
0
      return (xstrdup("1"));
2049
0
    return (xstrdup("0"));
2050
0
  }
2051
0
  return (NULL);
2052
0
}
2053
2054
/* Callback for mouse_standard_flag. */
2055
static void *
2056
format_cb_mouse_standard_flag(struct format_tree *ft)
2057
0
{
2058
0
  if (ft->wp != NULL) {
2059
0
    if (ft->wp->base.mode & MODE_MOUSE_STANDARD)
2060
0
      return (xstrdup("1"));
2061
0
    return (xstrdup("0"));
2062
0
  }
2063
0
  return (NULL);
2064
0
}
2065
2066
/* Callback for mouse_utf8_flag. */
2067
static void *
2068
format_cb_mouse_utf8_flag(struct format_tree *ft)
2069
0
{
2070
0
  if (ft->wp != NULL) {
2071
0
    if (ft->wp->base.mode & MODE_MOUSE_UTF8)
2072
0
      return (xstrdup("1"));
2073
0
    return (xstrdup("0"));
2074
0
  }
2075
0
  return (NULL);
2076
0
}
2077
2078
/* Callback for mouse_x. */
2079
static void *
2080
format_cb_mouse_x(struct format_tree *ft)
2081
0
{
2082
0
  struct window_pane  *wp;
2083
0
  u_int      x, y;
2084
2085
0
  if (!ft->m.valid)
2086
0
    return (NULL);
2087
0
  wp = cmd_mouse_pane(&ft->m, NULL, NULL);
2088
0
  if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
2089
0
    return (format_printf("%u", x));
2090
0
  if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
2091
0
    if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
2092
0
      return (format_printf("%u", ft->m.x));
2093
0
    if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
2094
0
      return (format_printf("%u", ft->m.x));
2095
0
  }
2096
0
  return (NULL);
2097
0
}
2098
2099
/* Callback for mouse_y. */
2100
static void *
2101
format_cb_mouse_y(struct format_tree *ft)
2102
0
{
2103
0
  struct window_pane  *wp;
2104
0
  u_int      x, y;
2105
2106
0
  if (!ft->m.valid)
2107
0
    return (NULL);
2108
0
  wp = cmd_mouse_pane(&ft->m, NULL, NULL);
2109
0
  if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
2110
0
    return (format_printf("%u", y));
2111
0
  if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED)) {
2112
0
    if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines)
2113
0
      return (format_printf("%u", ft->m.y));
2114
0
    if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat)
2115
0
      return (format_printf("%u", ft->m.y - ft->m.statusat));
2116
0
  }
2117
0
  return (NULL);
2118
0
}
2119
2120
/* Callback for next_session_id. */
2121
static void *
2122
format_cb_next_session_id(__unused struct format_tree *ft)
2123
0
{
2124
0
  return (format_printf("$%u", next_session_id));
2125
0
}
2126
2127
/* Callback for origin_flag. */
2128
static void *
2129
format_cb_origin_flag(struct format_tree *ft)
2130
0
{
2131
0
  if (ft->wp != NULL) {
2132
0
    if (ft->wp->base.mode & MODE_ORIGIN)
2133
0
      return (xstrdup("1"));
2134
0
    return (xstrdup("0"));
2135
0
  }
2136
0
  return (NULL);
2137
0
}
2138
2139
/* Callback for synchronized_output_flag. */
2140
static void *
2141
format_cb_synchronized_output_flag(struct format_tree *ft)
2142
0
{
2143
0
  if (ft->wp != NULL) {
2144
0
    if (ft->wp->base.mode & MODE_SYNC)
2145
0
      return (xstrdup("1"));
2146
0
    return (xstrdup("0"));
2147
0
  }
2148
0
  return (NULL);
2149
0
}
2150
2151
/* Callback for pane_private_modes. */
2152
static void *
2153
format_cb_pane_private_modes(struct format_tree *ft)
2154
0
{
2155
0
  static const struct {
2156
0
    int mode;
2157
0
    int number;
2158
0
  } table[] = {
2159
0
    { MODE_KCURSOR,   1 },  /* DECCKM */
2160
0
    { MODE_ORIGIN,   6 },  /* DECOM */
2161
0
    { MODE_WRAP,   7 },  /* DECAWM */
2162
0
    { MODE_CURSOR_BLINKING, 12 }, /* cursor blinking */
2163
0
    { MODE_CURSOR,   25 }, /* DECTCEM */
2164
0
    { MODE_MOUSE_STANDARD, 1000 }, /* mouse normal tracking */
2165
0
    { MODE_MOUSE_BUTTON, 1002 }, /* mouse button tracking */
2166
0
    { MODE_MOUSE_ALL, 1003 }, /* mouse any tracking */
2167
0
    { MODE_FOCUSON,   1004 }, /* focus reporting */
2168
0
    { MODE_MOUSE_UTF8, 1005 }, /* mouse: UTF-8 */
2169
0
    { MODE_MOUSE_SGR, 1006 }, /* mouse: SGR */
2170
0
    { MODE_BRACKETPASTE, 2004 }, /* bracketed paste */
2171
0
    { MODE_SYNC,   2026 }, /* synchronized output */
2172
0
    { MODE_THEME_UPDATES, 2031 }, /* theme update notifications */
2173
0
  };
2174
0
  int  mode;
2175
0
  char  *value = NULL, *tmp;
2176
0
  u_int  i;
2177
2178
0
  if (ft->wp == NULL)
2179
0
    return (NULL);
2180
0
  mode = ft->wp->base.mode;
2181
2182
0
  for (i = 0; i < nitems(table); i++) {
2183
0
    if (~mode & table[i].mode)
2184
0
      continue;
2185
    /*
2186
     * Only report cursor blinking when set by the application, not
2187
     * when it comes from the cursor-style option.
2188
     */
2189
0
    if (table[i].mode == MODE_CURSOR_BLINKING &&
2190
0
        (~mode & MODE_CURSOR_BLINKING_SET))
2191
0
      continue;
2192
0
    if (value == NULL)
2193
0
      xasprintf(&value, "%d", table[i].number);
2194
0
    else {
2195
0
      xasprintf(&tmp, "%s,%d", value, table[i].number);
2196
0
      free(value);
2197
0
      value = tmp;
2198
0
    }
2199
0
  }
2200
0
  if (value == NULL)
2201
0
    return (xstrdup(""));
2202
0
  return (value);
2203
0
}
2204
2205
/* Callback for pane_active. */
2206
static void *
2207
format_cb_pane_active(struct format_tree *ft)
2208
0
{
2209
0
  if (ft->wp != NULL) {
2210
0
    if (ft->wp == ft->wp->window->active)
2211
0
      return (xstrdup("1"));
2212
0
    return (xstrdup("0"));
2213
0
  }
2214
0
  return (NULL);
2215
0
}
2216
2217
/* Callback for pane_at_left. */
2218
static void *
2219
format_cb_pane_at_left(struct format_tree *ft)
2220
0
{
2221
0
  if (ft->wp != NULL) {
2222
0
    if (ft->wp->xoff == 0)
2223
0
      return (xstrdup("1"));
2224
0
    return (xstrdup("0"));
2225
0
  }
2226
0
  return (NULL);
2227
0
}
2228
2229
/* Callback for pane_at_right. */
2230
static void *
2231
format_cb_pane_at_right(struct format_tree *ft)
2232
0
{
2233
0
  if (ft->wp != NULL) {
2234
0
    if (ft->wp->xoff + (int)ft->wp->sx == (int)ft->wp->window->sx)
2235
0
      return (xstrdup("1"));
2236
0
    return (xstrdup("0"));
2237
0
  }
2238
0
  return (NULL);
2239
0
}
2240
2241
/* Callback for pane_bottom. */
2242
static void *
2243
format_cb_pane_bottom(struct format_tree *ft)
2244
0
{
2245
0
  struct window_pane  *wp = ft->wp;
2246
2247
0
  if (wp != NULL)
2248
0
    return (format_printf("%d", wp->yoff + (int)wp->sy - 1));
2249
0
  return (NULL);
2250
0
}
2251
2252
/* Callback for pane_dead. */
2253
static void *
2254
format_cb_pane_dead(struct format_tree *ft)
2255
0
{
2256
0
  struct window_pane  *wp = ft->wp;
2257
2258
0
  if (wp != NULL) {
2259
0
    if (wp->fd == -1 && (wp->flags & PANE_STATUSREADY))
2260
0
      return (xstrdup("1"));
2261
0
    return (xstrdup("0"));
2262
0
  }
2263
0
  return (NULL);
2264
0
}
2265
2266
/* Callback for pane_dead_signal. */
2267
static void *
2268
format_cb_pane_dead_signal(struct format_tree *ft)
2269
0
{
2270
0
  struct window_pane  *wp = ft->wp;
2271
0
  const char    *name;
2272
2273
0
  if (wp != NULL) {
2274
0
    if ((wp->flags & PANE_STATUSREADY) && WIFSIGNALED(wp->status)) {
2275
0
      name = sig2name(WTERMSIG(wp->status));
2276
0
      return (format_printf("%s", name));
2277
0
    }
2278
0
    return (NULL);
2279
0
  }
2280
0
  return (NULL);
2281
0
}
2282
2283
/* Callback for pane_dead_status. */
2284
static void *
2285
format_cb_pane_dead_status(struct format_tree *ft)
2286
0
{
2287
0
  struct window_pane  *wp = ft->wp;
2288
2289
0
  if (wp != NULL) {
2290
0
    if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status))
2291
0
      return (format_printf("%d", WEXITSTATUS(wp->status)));
2292
0
    return (NULL);
2293
0
  }
2294
0
  return (NULL);
2295
0
}
2296
2297
/* Callback for pane_dead_time. */
2298
static void *
2299
format_cb_pane_dead_time(struct format_tree *ft)
2300
0
{
2301
0
  struct window_pane  *wp = ft->wp;
2302
2303
0
  if (wp != NULL) {
2304
0
    if (wp->flags & PANE_STATUSDRAWN)
2305
0
      return (&wp->dead_time);
2306
0
    return (NULL);
2307
0
  }
2308
0
  return (NULL);
2309
0
}
2310
2311
/* Callback for pane_last_output_time. */
2312
static void *
2313
format_cb_pane_last_output_time(struct format_tree *ft)
2314
0
{
2315
0
  struct window_pane  *wp = ft->wp;
2316
0
  static struct timeval  tv;
2317
2318
0
  if (wp != NULL && wp->last_output_time != 0) {
2319
0
    tv.tv_sec = wp->last_output_time;
2320
0
    tv.tv_usec = 0;
2321
0
    return (&tv);
2322
0
  }
2323
0
  return (NULL);
2324
0
}
2325
2326
/* Callback for pane_output_generation. */
2327
static void *
2328
format_cb_pane_output_generation(struct format_tree *ft)
2329
0
{
2330
0
  unsigned long long   value;
2331
2332
0
  if (ft->wp != NULL) {
2333
0
    value = ft->wp->output_generation;
2334
0
    return (format_printf("%llu", value));
2335
0
  }
2336
0
  return (NULL);
2337
0
}
2338
2339
/* Callback for pane_last_prompt_time. */
2340
static void *
2341
format_cb_pane_last_prompt_time(struct format_tree *ft)
2342
0
{
2343
0
  struct window_pane  *wp = ft->wp;
2344
0
  static struct timeval  tv;
2345
2346
0
  if (wp != NULL && wp->last_prompt_time != 0) {
2347
0
    tv.tv_sec = wp->last_prompt_time;
2348
0
    tv.tv_usec = 0;
2349
0
    return (&tv);
2350
0
  }
2351
0
  return (NULL);
2352
0
}
2353
2354
/* Callback for pane_command_start_time. */
2355
static void *
2356
format_cb_pane_command_start_time(struct format_tree *ft)
2357
0
{
2358
0
  struct window_pane  *wp = ft->wp;
2359
0
  static struct timeval  tv;
2360
2361
0
  if (wp != NULL && wp->cmd_start_time != 0) {
2362
0
    tv.tv_sec = wp->cmd_start_time;
2363
0
    tv.tv_usec = 0;
2364
0
    return (&tv);
2365
0
  }
2366
0
  return (NULL);
2367
0
}
2368
2369
/* Callback for pane_command_end_time. */
2370
static void *
2371
format_cb_pane_command_end_time(struct format_tree *ft)
2372
0
{
2373
0
  struct window_pane  *wp = ft->wp;
2374
0
  static struct timeval  tv;
2375
2376
0
  if (wp != NULL && wp->cmd_end_time != 0) {
2377
0
    tv.tv_sec = wp->cmd_end_time;
2378
0
    tv.tv_usec = 0;
2379
0
    return (&tv);
2380
0
  }
2381
0
  return (NULL);
2382
0
}
2383
2384
/* Callback for pane_command_running. */
2385
static void *
2386
format_cb_pane_command_running(struct format_tree *ft)
2387
0
{
2388
0
  struct window_pane  *wp = ft->wp;
2389
2390
0
  if (wp != NULL)
2391
0
    return (format_printf("%d", !!(wp->flags & PANE_CMDRUNNING)));
2392
0
  return (NULL);
2393
0
}
2394
2395
/* Callback for pane_command_duration. */
2396
static void *
2397
format_cb_pane_command_duration(struct format_tree *ft)
2398
0
{
2399
0
  struct window_pane  *wp = ft->wp;
2400
0
  time_t       end;
2401
2402
0
  if (wp == NULL || wp->cmd_start_time == 0)
2403
0
    return (NULL);
2404
0
  if (wp->flags & PANE_CMDRUNNING)
2405
0
    end = time(NULL);
2406
0
  else
2407
0
    end = wp->cmd_end_time;
2408
0
  if (end < wp->cmd_start_time)
2409
0
    end = wp->cmd_start_time;
2410
0
  return (format_printf("%lld", (long long)(end - wp->cmd_start_time)));
2411
0
}
2412
2413
/* Callback for pane_command_status. */
2414
static void *
2415
format_cb_pane_command_status(struct format_tree *ft)
2416
0
{
2417
0
  struct window_pane  *wp = ft->wp;
2418
2419
0
  if (wp != NULL && wp->cmd_status != -1)
2420
0
    return (format_printf("%d", wp->cmd_status));
2421
0
  return (NULL);
2422
0
}
2423
2424
/* Callback for pane_format. */
2425
static void *
2426
format_cb_pane_format(struct format_tree *ft)
2427
0
{
2428
0
  if (ft->type == FORMAT_TYPE_PANE)
2429
0
    return (xstrdup("1"));
2430
0
  return (xstrdup("0"));
2431
0
}
2432
2433
/* Callback for pane_height. */
2434
static void *
2435
format_cb_pane_height(struct format_tree *ft)
2436
0
{
2437
0
  if (ft->wp != NULL)
2438
0
    return (format_printf("%u", ft->wp->sy));
2439
0
  return (NULL);
2440
0
}
2441
2442
/* Callback for pane_id. */
2443
static void *
2444
format_cb_pane_id(struct format_tree *ft)
2445
0
{
2446
0
  if (ft->wp != NULL)
2447
0
    return (format_printf("%%%u", ft->wp->id));
2448
0
  return (NULL);
2449
0
}
2450
2451
/* Callback for pane_index. */
2452
static void *
2453
format_cb_pane_index(struct format_tree *ft)
2454
0
{
2455
0
  u_int idx;
2456
2457
0
  if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0)
2458
0
    return (format_printf("%u", idx));
2459
0
  return (NULL);
2460
0
}
2461
2462
/* Callback for pane_input_off. */
2463
static void *
2464
format_cb_pane_input_off(struct format_tree *ft)
2465
0
{
2466
0
  if (ft->wp != NULL) {
2467
0
    if (ft->wp->flags & PANE_INPUTOFF)
2468
0
      return (xstrdup("1"));
2469
0
    return (xstrdup("0"));
2470
0
  }
2471
0
  return (NULL);
2472
0
}
2473
2474
/* Callback for pane_unseen_changes. */
2475
static void *
2476
format_cb_pane_unseen_changes(struct format_tree *ft)
2477
0
{
2478
0
  if (ft->wp != NULL) {
2479
0
    if (ft->wp->flags & PANE_UNSEENCHANGES)
2480
0
      return (xstrdup("1"));
2481
0
    return (xstrdup("0"));
2482
0
  }
2483
0
  return (NULL);
2484
0
}
2485
2486
/* Callback for pane_key_mode. */
2487
static void *
2488
format_cb_pane_key_mode(struct format_tree *ft)
2489
0
{
2490
0
  if (ft->wp != NULL && ft->wp->screen != NULL) {
2491
0
    switch (ft->wp->screen->mode & EXTENDED_KEY_MODES) {
2492
0
    case MODE_KEYS_EXTENDED:
2493
0
      return (xstrdup("Ext 1"));
2494
0
    case MODE_KEYS_EXTENDED_2:
2495
0
      return (xstrdup("Ext 2"));
2496
0
    default:
2497
0
      return (xstrdup("VT10x"));
2498
0
    }
2499
0
  }
2500
0
  return (NULL);
2501
0
}
2502
2503
/* Callback for pane_last. */
2504
static void *
2505
format_cb_pane_last(struct format_tree *ft)
2506
0
{
2507
0
  if (ft->wp != NULL) {
2508
0
    if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes))
2509
0
      return (xstrdup("1"));
2510
0
    return (xstrdup("0"));
2511
0
  }
2512
0
  return (NULL);
2513
0
}
2514
2515
/* Callback for pane_left. */
2516
static void *
2517
format_cb_pane_left(struct format_tree *ft)
2518
0
{
2519
0
  if (ft->wp != NULL)
2520
0
    return (format_printf("%d", ft->wp->xoff));
2521
0
  return (NULL);
2522
0
}
2523
2524
/* Callback for pane_marked. */
2525
static void *
2526
format_cb_pane_marked(struct format_tree *ft)
2527
0
{
2528
0
  if (ft->wp != NULL) {
2529
0
    if (server_check_marked() && marked_pane.wp == ft->wp)
2530
0
      return (xstrdup("1"));
2531
0
    return (xstrdup("0"));
2532
0
  }
2533
0
  return (NULL);
2534
0
}
2535
2536
/* Callback for pane_marked_set. */
2537
static void *
2538
format_cb_pane_marked_set(struct format_tree *ft)
2539
0
{
2540
0
  if (ft->wp != NULL) {
2541
0
    if (server_check_marked())
2542
0
      return (xstrdup("1"));
2543
0
    return (xstrdup("0"));
2544
0
  }
2545
0
  return (NULL);
2546
0
}
2547
2548
/* Callback for pane_mode. */
2549
static void *
2550
format_cb_pane_mode(struct format_tree *ft)
2551
0
{
2552
0
  struct window_mode_entry  *wme;
2553
2554
0
  if (ft->wp != NULL) {
2555
0
    wme = TAILQ_FIRST(&ft->wp->modes);
2556
0
    if (wme != NULL)
2557
0
      return (xstrdup(wme->mode->name));
2558
0
    return (NULL);
2559
0
  }
2560
0
  return (NULL);
2561
0
}
2562
2563
/* Callback for pane_path. */
2564
static void *
2565
format_cb_pane_path(struct format_tree *ft)
2566
0
{
2567
0
  if (ft->wp != NULL) {
2568
0
    if (ft->wp->base.path == NULL)
2569
0
      return (xstrdup(""));
2570
0
    return (xstrdup(ft->wp->base.path));
2571
0
  }
2572
0
  return (NULL);
2573
0
}
2574
2575
/* Callback for pane_pid. */
2576
static void *
2577
format_cb_pane_pid(struct format_tree *ft)
2578
0
{
2579
0
  if (ft->wp != NULL && ft->wp->fd != -1)
2580
0
    return (format_printf("%ld", (long)ft->wp->pid));
2581
0
  return (NULL);
2582
0
}
2583
2584
/* Callback for pane_pipe. */
2585
static void *
2586
format_cb_pane_pipe(struct format_tree *ft)
2587
0
{
2588
0
  if (ft->wp != NULL) {
2589
0
    if (ft->wp->pipe_fd != -1)
2590
0
      return (xstrdup("1"));
2591
0
    return (xstrdup("0"));
2592
0
  }
2593
0
  return (NULL);
2594
0
}
2595
2596
/* Callback for pane_pipe_pid. */
2597
static void *
2598
format_cb_pane_pipe_pid(struct format_tree *ft)
2599
0
{
2600
0
  char  *value = NULL;
2601
2602
0
  if (ft->wp != NULL && ft->wp->pipe_fd != -1)
2603
0
    xasprintf(&value, "%ld", (long)ft->wp->pipe_pid);
2604
0
  return (value);
2605
0
}
2606
2607
/* Callback for pane_pb_progress. */
2608
static void *
2609
format_cb_pane_pb_progress(struct format_tree *ft)
2610
0
{
2611
0
  char  *value = NULL;
2612
2613
0
  if (ft->wp != NULL)
2614
0
    xasprintf(&value, "%d", ft->wp->base.progress_bar.progress);
2615
0
  return (value);
2616
0
}
2617
2618
/* Callback for pane_pb_state. */
2619
static void *
2620
format_cb_pane_pb_state(struct format_tree *ft)
2621
0
{
2622
0
  if (ft->wp != NULL) {
2623
0
    switch (ft->wp->base.progress_bar.state) {
2624
0
    case PROGRESS_BAR_HIDDEN:
2625
0
      return xstrdup("hidden");
2626
0
    case PROGRESS_BAR_NORMAL:
2627
0
      return xstrdup("normal");
2628
0
    case PROGRESS_BAR_ERROR:
2629
0
      return xstrdup("error");
2630
0
    case PROGRESS_BAR_INDETERMINATE:
2631
0
      return xstrdup("indeterminate");
2632
0
    case PROGRESS_BAR_PAUSED:
2633
0
      return xstrdup("paused");
2634
0
    }
2635
0
  }
2636
0
  return (NULL);
2637
0
}
2638
2639
/* Callback for pane_right. */
2640
static void *
2641
format_cb_pane_right(struct format_tree *ft)
2642
0
{
2643
0
  struct window_pane  *wp = ft->wp;
2644
2645
0
  if (wp != NULL)
2646
0
    return (format_printf("%d", wp->xoff + (int)wp->sx - 1));
2647
0
  return (NULL);
2648
0
}
2649
2650
/* Callback for pane_search_string. */
2651
static void *
2652
format_cb_pane_search_string(struct format_tree *ft)
2653
0
{
2654
0
  if (ft->wp != NULL) {
2655
0
    if (ft->wp->searchstr == NULL)
2656
0
      return (xstrdup(""));
2657
0
    return (xstrdup(ft->wp->searchstr));
2658
0
  }
2659
0
  return (NULL);
2660
0
}
2661
2662
/* Callback for pane_synchronized. */
2663
static void *
2664
format_cb_pane_synchronized(struct format_tree *ft)
2665
0
{
2666
0
  if (ft->wp != NULL) {
2667
0
    if (options_get_number(ft->wp->options, "synchronize-panes"))
2668
0
      return (xstrdup("1"));
2669
0
    return (xstrdup("0"));
2670
0
  }
2671
0
  return (NULL);
2672
0
}
2673
2674
/* Callback for pane_title. */
2675
static void *
2676
format_cb_pane_title(struct format_tree *ft)
2677
0
{
2678
0
  if (ft->wp != NULL)
2679
0
    return (xstrdup(ft->wp->base.title));
2680
0
  return (NULL);
2681
0
}
2682
2683
/* Callback for pane_top. */
2684
static void *
2685
format_cb_pane_top(struct format_tree *ft)
2686
0
{
2687
0
  if (ft->wp != NULL)
2688
0
    return (format_printf("%d", ft->wp->yoff));
2689
0
  return (NULL);
2690
0
}
2691
2692
/* Callback for pane_tty. */
2693
static void *
2694
format_cb_pane_tty(struct format_tree *ft)
2695
0
{
2696
0
  if (ft->wp != NULL)
2697
0
    return (xstrdup(ft->wp->tty));
2698
0
  return (NULL);
2699
0
}
2700
2701
/* Callback for pane_unzoomed_height. */
2702
static void *
2703
format_cb_pane_unzoomed_height(struct format_tree *ft)
2704
0
{
2705
0
  struct window_pane  *wp = ft->wp;
2706
0
  struct window   *w;
2707
0
  struct layout_cell  *lc, *root;
2708
0
  int      status, floating;
2709
0
  u_int      sy;
2710
2711
0
  if (wp == NULL)
2712
0
    return (NULL);
2713
0
  w = wp->window;
2714
2715
0
  lc = wp->saved_layout_cell;
2716
0
  if (lc == NULL)
2717
0
    lc = wp->layout_cell;
2718
0
  if (lc == NULL)
2719
0
    return (NULL);
2720
0
  sy = lc->g.sy;
2721
0
  floating = (lc->flags & LAYOUT_CELL_FLOATING);
2722
2723
0
  root = w->saved_layout_root;
2724
0
  if (root == NULL)
2725
0
    root = w->layout_root;
2726
0
  if (lc == wp->saved_layout_cell && !floating)
2727
0
    status = window_get_pane_status(w);
2728
0
  else
2729
0
    status = window_pane_get_pane_status(wp);
2730
0
  if (!floating &&
2731
0
      root != NULL &&
2732
0
      layout_add_horizontal_border(root, lc, status) &&
2733
0
      sy > 1)
2734
0
    sy--;
2735
2736
0
  return (format_printf("%u", sy));
2737
0
}
2738
2739
/* Callback for pane_unzoomed_width. */
2740
static void *
2741
format_cb_pane_unzoomed_width(struct format_tree *ft)
2742
0
{
2743
0
  struct window_pane  *wp = ft->wp;
2744
0
  struct layout_cell  *lc;
2745
0
  int      saved, sb_w, sb_pad;
2746
0
  u_int      sx;
2747
2748
0
  if (wp == NULL)
2749
0
    return (NULL);
2750
2751
0
  lc = wp->saved_layout_cell;
2752
0
  saved = (lc != NULL);
2753
0
  if (lc == NULL)
2754
0
    lc = wp->layout_cell;
2755
0
  if (lc == NULL)
2756
0
    return (NULL);
2757
0
  sx = lc->g.sx;
2758
2759
0
  if ((saved && !SCREEN_IS_ALTERNATE(&wp->base) &&
2760
0
      wp->window->sb == PANE_SCROLLBARS_ALWAYS) ||
2761
0
      (!saved && window_pane_scrollbar_reserve(wp))) {
2762
0
    sb_w = wp->scrollbar_style.width;
2763
0
    sb_pad = wp->scrollbar_style.pad;
2764
0
    if (sb_w < 1)
2765
0
      sb_w = 1;
2766
0
    if (sb_pad < 0)
2767
0
      sb_pad = 0;
2768
0
    if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
2769
0
      sx = PANE_MINIMUM;
2770
0
    else
2771
0
      sx -= sb_w + sb_pad;
2772
0
  }
2773
2774
0
  return (format_printf("%u", sx));
2775
0
}
2776
2777
/* Callback for pane_width. */
2778
static void *
2779
format_cb_pane_width(struct format_tree *ft)
2780
0
{
2781
0
  if (ft->wp != NULL)
2782
0
    return (format_printf("%u", ft->wp->sx));
2783
0
  return (NULL);
2784
0
}
2785
2786
/* Callback for pane_x. */
2787
static void *
2788
format_cb_pane_x(struct format_tree *ft)
2789
0
{
2790
0
  if (ft->wp != NULL)
2791
0
    return (format_printf("%d", ft->wp->xoff));
2792
0
  return (NULL);
2793
0
}
2794
2795
/* Callback for pane_y. */
2796
static void *
2797
format_cb_pane_y(struct format_tree *ft)
2798
0
{
2799
0
  if (ft->wp != NULL)
2800
0
    return (format_printf("%d", ft->wp->yoff));
2801
0
  return (NULL);
2802
0
}
2803
2804
/* Callback for pane_z. */
2805
static void *
2806
format_cb_pane_z(struct format_tree *ft)
2807
0
{
2808
0
  u_int idx;
2809
2810
0
  if (ft->wp != NULL && window_pane_zindex(ft->wp, &idx) == 0)
2811
0
    return (format_printf("%u", idx));
2812
0
  return (NULL);
2813
0
}
2814
2815
/* Callback for pane_zoomed_flag. */
2816
static void *
2817
format_cb_pane_zoomed_flag(struct format_tree *ft)
2818
0
{
2819
0
  struct window_pane  *wp = ft->wp;
2820
2821
0
  if (wp != NULL) {
2822
0
    if (wp->flags & PANE_ZOOMED)
2823
0
      return (xstrdup("1"));
2824
0
    return (xstrdup("0"));
2825
0
  }
2826
0
  return (NULL);
2827
0
}
2828
2829
/* Callback for scroll_region_lower. */
2830
static void *
2831
format_cb_scroll_region_lower(struct format_tree *ft)
2832
0
{
2833
0
  if (ft->wp != NULL)
2834
0
    return (format_printf("%u", ft->wp->base.rlower));
2835
0
  return (NULL);
2836
0
}
2837
2838
/* Callback for scroll_region_upper. */
2839
static void *
2840
format_cb_scroll_region_upper(struct format_tree *ft)
2841
0
{
2842
0
  if (ft->wp != NULL)
2843
0
    return (format_printf("%u", ft->wp->base.rupper));
2844
0
  return (NULL);
2845
0
}
2846
2847
/* Callback for server_sessions. */
2848
static void *
2849
format_cb_server_sessions(__unused struct format_tree *ft)
2850
0
{
2851
0
  struct session  *s;
2852
0
  u_int    n = 0;
2853
2854
0
  RB_FOREACH(s, sessions, &sessions)
2855
0
    n++;
2856
0
  return (format_printf("%u", n));
2857
0
}
2858
2859
/* Callback for session_active. */
2860
static void *
2861
format_cb_session_active(struct format_tree *ft)
2862
0
{
2863
0
  if (ft->s == NULL || ft->c == NULL)
2864
0
    return (NULL);
2865
2866
0
  if (ft->c->session == ft->s)
2867
0
    return (xstrdup("1"));
2868
0
  return (xstrdup("0"));
2869
0
}
2870
2871
/* Callback for session_activity_flag. */
2872
static void *
2873
format_cb_session_activity_flag(struct format_tree *ft)
2874
0
{
2875
0
  struct winlink    *wl;
2876
2877
0
  if (ft->s != NULL) {
2878
0
    RB_FOREACH(wl, winlinks, &ft->s->windows) {
2879
0
      if (ft->wl->flags & WINLINK_ACTIVITY)
2880
0
        return (xstrdup("1"));
2881
0
      return (xstrdup("0"));
2882
0
    }
2883
0
  }
2884
0
  return (NULL);
2885
0
}
2886
2887
/* Callback for session_bell_flag. */
2888
static void *
2889
format_cb_session_bell_flag(struct format_tree *ft)
2890
0
{
2891
0
  struct winlink    *wl;
2892
2893
0
  if (ft->s != NULL) {
2894
0
    RB_FOREACH(wl, winlinks, &ft->s->windows) {
2895
0
      if (wl->flags & WINLINK_BELL)
2896
0
        return (xstrdup("1"));
2897
0
      return (xstrdup("0"));
2898
0
    }
2899
0
  }
2900
0
  return (NULL);
2901
0
}
2902
2903
/* Callback for session_silence_flag. */
2904
static void *
2905
format_cb_session_silence_flag(struct format_tree *ft)
2906
0
{
2907
0
  struct winlink    *wl;
2908
2909
0
  if (ft->s != NULL) {
2910
0
    RB_FOREACH(wl, winlinks, &ft->s->windows) {
2911
0
      if (ft->wl->flags & WINLINK_SILENCE)
2912
0
        return (xstrdup("1"));
2913
0
      return (xstrdup("0"));
2914
0
    }
2915
0
  }
2916
0
  return (NULL);
2917
0
}
2918
2919
/* Callback for session_attached. */
2920
static void *
2921
format_cb_session_attached(struct format_tree *ft)
2922
0
{
2923
0
  if (ft->s != NULL)
2924
0
    return (format_printf("%u", ft->s->attached));
2925
0
  return (NULL);
2926
0
}
2927
2928
/* Callback for session_format. */
2929
static void *
2930
format_cb_session_format(struct format_tree *ft)
2931
0
{
2932
0
  if (ft->type == FORMAT_TYPE_SESSION)
2933
0
    return (xstrdup("1"));
2934
0
  return (xstrdup("0"));
2935
0
}
2936
2937
/* Callback for session_group. */
2938
static void *
2939
format_cb_session_group(struct format_tree *ft)
2940
0
{
2941
0
  struct session_group  *sg;
2942
2943
0
  if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2944
0
    return (xstrdup(sg->name));
2945
0
  return (NULL);
2946
0
}
2947
2948
/* Callback for session_group_attached. */
2949
static void *
2950
format_cb_session_group_attached(struct format_tree *ft)
2951
0
{
2952
0
  struct session_group  *sg;
2953
2954
0
  if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2955
0
    return (format_printf("%u", session_group_attached_count (sg)));
2956
0
  return (NULL);
2957
0
}
2958
2959
/* Callback for session_group_many_attached. */
2960
static void *
2961
format_cb_session_group_many_attached(struct format_tree *ft)
2962
0
{
2963
0
  struct session_group  *sg;
2964
2965
0
  if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) {
2966
0
    if (session_group_attached_count (sg) > 1)
2967
0
      return (xstrdup("1"));
2968
0
    return (xstrdup("0"));
2969
0
  }
2970
0
  return (NULL);
2971
0
}
2972
2973
/* Callback for session_group_size. */
2974
static void *
2975
format_cb_session_group_size(struct format_tree *ft)
2976
0
{
2977
0
  struct session_group  *sg;
2978
2979
0
  if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
2980
0
    return (format_printf("%u", session_group_count (sg)));
2981
0
  return (NULL);
2982
0
}
2983
2984
/* Callback for session_grouped. */
2985
static void *
2986
format_cb_session_grouped(struct format_tree *ft)
2987
0
{
2988
0
  if (ft->s != NULL) {
2989
0
    if (session_group_contains(ft->s) != NULL)
2990
0
      return (xstrdup("1"));
2991
0
    return (xstrdup("0"));
2992
0
  }
2993
0
  return (NULL);
2994
0
}
2995
2996
/* Callback for session_id. */
2997
static void *
2998
format_cb_session_id(struct format_tree *ft)
2999
0
{
3000
0
  if (ft->s != NULL)
3001
0
    return (format_printf("$%u", ft->s->id));
3002
0
  return (NULL);
3003
0
}
3004
3005
/* Callback for session_many_attached. */
3006
static void *
3007
format_cb_session_many_attached(struct format_tree *ft)
3008
0
{
3009
0
  if (ft->s != NULL) {
3010
0
    if (ft->s->attached > 1)
3011
0
      return (xstrdup("1"));
3012
0
    return (xstrdup("0"));
3013
0
  }
3014
0
  return (NULL);
3015
0
}
3016
3017
/* Callback for session_marked. */
3018
static void *
3019
format_cb_session_marked(struct format_tree *ft)
3020
0
{
3021
0
  if (ft->s != NULL) {
3022
0
    if (server_check_marked() && marked_pane.s == ft->s)
3023
0
      return (xstrdup("1"));
3024
0
    return (xstrdup("0"));
3025
0
  }
3026
0
  return (NULL);
3027
0
}
3028
3029
/* Callback for session_name. */
3030
static void *
3031
format_cb_session_name(struct format_tree *ft)
3032
0
{
3033
0
  if (ft->s != NULL)
3034
0
    return (xstrdup(ft->s->name));
3035
0
  return (NULL);
3036
0
}
3037
3038
/* Callback for session_path. */
3039
static void *
3040
format_cb_session_path(struct format_tree *ft)
3041
0
{
3042
0
  if (ft->s != NULL)
3043
0
    return (xstrdup(ft->s->cwd));
3044
0
  return (NULL);
3045
0
}
3046
3047
/* Callback for session_windows. */
3048
static void *
3049
format_cb_session_windows(struct format_tree *ft)
3050
0
{
3051
0
  if (ft->s != NULL)
3052
0
    return (format_printf("%u", winlink_count(&ft->s->windows)));
3053
0
  return (NULL);
3054
0
}
3055
3056
/* Callback for socket_path. */
3057
static void *
3058
format_cb_socket_path(__unused struct format_tree *ft)
3059
0
{
3060
0
  return (xstrdup(socket_path));
3061
0
}
3062
3063
/* Callback for version. */
3064
static void *
3065
format_cb_version(__unused struct format_tree *ft)
3066
0
{
3067
0
  return (xstrdup(getversion()));
3068
0
}
3069
3070
/* Callback for sixel_support. */
3071
static void *
3072
format_cb_sixel_support(__unused struct format_tree *ft)
3073
0
{
3074
#ifdef ENABLE_SIXEL
3075
  return (xstrdup("1"));
3076
#else
3077
0
  return (xstrdup("0"));
3078
0
#endif
3079
0
}
3080
3081
/* Callback for active_window_index. */
3082
static void *
3083
format_cb_active_window_index(struct format_tree *ft)
3084
0
{
3085
0
  if (ft->s != NULL)
3086
0
    return (format_printf("%u", ft->s->curw->idx));
3087
0
  return (NULL);
3088
0
}
3089
3090
/* Callback for last_window_index. */
3091
static void *
3092
format_cb_last_window_index(struct format_tree *ft)
3093
0
{
3094
0
  struct winlink  *wl;
3095
3096
0
  if (ft->s != NULL) {
3097
0
    wl = RB_MAX(winlinks, &ft->s->windows);
3098
0
    return (format_printf("%u", wl->idx));
3099
0
  }
3100
0
  return (NULL);
3101
0
}
3102
3103
/* Callback for window_active. */
3104
static void *
3105
format_cb_window_active(struct format_tree *ft)
3106
0
{
3107
0
  if (ft->wl != NULL) {
3108
0
    if (ft->wl == ft->wl->session->curw)
3109
0
      return (xstrdup("1"));
3110
0
    return (xstrdup("0"));
3111
0
  }
3112
0
  return (NULL);
3113
0
}
3114
3115
/* Callback for window_activity_flag. */
3116
static void *
3117
format_cb_window_activity_flag(struct format_tree *ft)
3118
0
{
3119
0
  if (ft->wl != NULL) {
3120
0
    if (ft->wl->flags & WINLINK_ACTIVITY)
3121
0
      return (xstrdup("1"));
3122
0
    return (xstrdup("0"));
3123
0
  }
3124
0
  return (NULL);
3125
0
}
3126
3127
/* Callback for window_bell_flag. */
3128
static void *
3129
format_cb_window_bell_flag(struct format_tree *ft)
3130
0
{
3131
0
  if (ft->wl != NULL) {
3132
0
    if (ft->wl->flags & WINLINK_BELL)
3133
0
      return (xstrdup("1"));
3134
0
    return (xstrdup("0"));
3135
0
  }
3136
0
  return (NULL);
3137
0
}
3138
3139
/* Callback for window_bigger. */
3140
static void *
3141
format_cb_window_bigger(struct format_tree *ft)
3142
0
{
3143
0
  u_int ox, oy, sx, sy;
3144
3145
0
  if (ft->c != NULL) {
3146
0
    if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
3147
0
      return (xstrdup("1"));
3148
0
    return (xstrdup("0"));
3149
0
  }
3150
0
  return (NULL);
3151
0
}
3152
3153
/* Callback for window_cell_height. */
3154
static void *
3155
format_cb_window_cell_height(struct format_tree *ft)
3156
0
{
3157
0
  if (ft->w != NULL)
3158
0
    return (format_printf("%u", ft->w->ypixel));
3159
0
  return (NULL);
3160
0
}
3161
3162
/* Callback for window_cell_width. */
3163
static void *
3164
format_cb_window_cell_width(struct format_tree *ft)
3165
0
{
3166
0
  if (ft->w != NULL)
3167
0
    return (format_printf("%u", ft->w->xpixel));
3168
0
  return (NULL);
3169
0
}
3170
3171
/* Callback for window_end_flag. */
3172
static void *
3173
format_cb_window_end_flag(struct format_tree *ft)
3174
0
{
3175
0
  if (ft->wl != NULL) {
3176
0
    if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows))
3177
0
      return (xstrdup("1"));
3178
0
    return (xstrdup("0"));
3179
0
  }
3180
0
  return (NULL);
3181
0
}
3182
3183
/* Callback for window_flags. */
3184
static void *
3185
format_cb_window_flags(struct format_tree *ft)
3186
0
{
3187
0
  if (ft->wl != NULL)
3188
0
    return (xstrdup(window_printable_flags(ft->wl, 1)));
3189
0
  return (NULL);
3190
0
}
3191
3192
/* Callback for window_format. */
3193
static void *
3194
format_cb_window_format(struct format_tree *ft)
3195
0
{
3196
0
  if (ft->type == FORMAT_TYPE_WINDOW)
3197
0
    return (xstrdup("1"));
3198
0
  return (xstrdup("0"));
3199
0
}
3200
3201
/* Callback for window_height. */
3202
static void *
3203
format_cb_window_height(struct format_tree *ft)
3204
0
{
3205
0
  if (ft->w != NULL)
3206
0
    return (format_printf("%u", ft->w->sy));
3207
0
  return (NULL);
3208
0
}
3209
3210
/* Callback for window_manual_height. */
3211
static void *
3212
format_cb_window_manual_height(struct format_tree *ft)
3213
0
{
3214
0
  struct window *w = ft->w;
3215
3216
0
  if (w == NULL)
3217
0
    return (NULL);
3218
0
  if (options_get_number(w->options, "window-size") != WINDOW_SIZE_MANUAL)
3219
0
    return (xstrdup(""));
3220
0
  return (format_printf("%u", w->manual_sy));
3221
0
}
3222
3223
/* Callback for window_id. */
3224
static void *
3225
format_cb_window_id(struct format_tree *ft)
3226
0
{
3227
0
  if (ft->w != NULL)
3228
0
    return (format_printf("@%u", ft->w->id));
3229
0
  return (NULL);
3230
0
}
3231
3232
/* Callback for window_index. */
3233
static void *
3234
format_cb_window_index(struct format_tree *ft)
3235
0
{
3236
0
  if (ft->wl != NULL)
3237
0
    return (format_printf("%d", ft->wl->idx));
3238
0
  return (NULL);
3239
0
}
3240
3241
/* Callback for window_last_flag. */
3242
static void *
3243
format_cb_window_last_flag(struct format_tree *ft)
3244
0
{
3245
0
  if (ft->wl != NULL) {
3246
0
    if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw))
3247
0
      return (xstrdup("1"));
3248
0
    return (xstrdup("0"));
3249
0
  }
3250
0
  return (NULL);
3251
0
}
3252
3253
/* Callback for window_linked. */
3254
static void *
3255
format_cb_window_linked(struct format_tree *ft)
3256
0
{
3257
0
  struct winlink  *wl;
3258
0
  struct session  *s;
3259
0
  int    found = 0;
3260
3261
0
  if (ft->wl != NULL) {
3262
0
    RB_FOREACH(s, sessions, &sessions) {
3263
0
      RB_FOREACH(wl, winlinks, &s->windows) {
3264
0
        if (wl->window == ft->wl->window) {
3265
0
          if (found)
3266
0
            return (xstrdup("1"));
3267
0
          found = 1;
3268
0
        }
3269
0
      }
3270
0
    }
3271
0
    return (xstrdup("0"));
3272
0
  }
3273
0
  return (NULL);
3274
0
}
3275
3276
/* Callback for window_linked_sessions. */
3277
static void *
3278
format_cb_window_linked_sessions(struct format_tree *ft)
3279
0
{
3280
0
  struct window   *w;
3281
0
  struct session_group  *sg;
3282
0
  struct session    *s;
3283
0
  u_int      n = 0;
3284
3285
0
  if (ft->wl == NULL)
3286
0
    return (NULL);
3287
0
  w = ft->wl->window;
3288
3289
0
  RB_FOREACH(sg, session_groups, &session_groups) {
3290
0
    s = TAILQ_FIRST(&sg->sessions);
3291
0
    if (winlink_find_by_window(&s->windows, w) != NULL)
3292
0
      n++;
3293
0
  }
3294
0
  RB_FOREACH(s, sessions, &sessions) {
3295
0
    if (session_group_contains(s) != NULL)
3296
0
      continue;
3297
0
    if (winlink_find_by_window(&s->windows, w) != NULL)
3298
0
      n++;
3299
0
  }
3300
0
  return (format_printf("%u", n));
3301
0
}
3302
3303
/* Callback for window_marked_flag. */
3304
static void *
3305
format_cb_window_marked_flag(struct format_tree *ft)
3306
0
{
3307
0
  if (ft->wl != NULL) {
3308
0
    if (server_check_marked() && marked_pane.wl == ft->wl)
3309
0
      return (xstrdup("1"));
3310
0
    return (xstrdup("0"));
3311
0
  }
3312
0
  return (NULL);
3313
0
}
3314
3315
/* Callback for window_modal_pane. */
3316
static void *
3317
format_cb_window_modal_pane(struct format_tree *ft)
3318
0
{
3319
0
  if (ft->w != NULL && ft->w->modal != NULL)
3320
0
    return (format_printf("%%%u", ft->w->modal->id));
3321
0
  return (NULL);
3322
0
}
3323
3324
/* Callback for window_name. */
3325
static void *
3326
format_cb_window_name(struct format_tree *ft)
3327
0
{
3328
0
  if (ft->w != NULL)
3329
0
    return (format_printf("%s", ft->w->name));
3330
0
  return (NULL);
3331
0
}
3332
3333
/* Callback for window_offset_x. */
3334
static void *
3335
format_cb_window_offset_x(struct format_tree *ft)
3336
0
{
3337
0
  u_int ox, oy, sx, sy;
3338
3339
0
  if (ft->c != NULL) {
3340
0
    if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
3341
0
      return (format_printf("%u", ox));
3342
0
    return (NULL);
3343
0
  }
3344
0
  return (NULL);
3345
0
}
3346
3347
/* Callback for window_offset_y. */
3348
static void *
3349
format_cb_window_offset_y(struct format_tree *ft)
3350
0
{
3351
0
  u_int ox, oy, sx, sy;
3352
3353
0
  if (ft->c != NULL) {
3354
0
    if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
3355
0
      return (format_printf("%u", oy));
3356
0
    return (NULL);
3357
0
  }
3358
0
  return (NULL);
3359
0
}
3360
3361
/* Callback for window_panes. */
3362
static void *
3363
format_cb_window_panes(struct format_tree *ft)
3364
0
{
3365
0
  if (ft->w != NULL)
3366
0
    return (format_printf("%u", window_count_panes(ft->w, 1)));
3367
0
  return (NULL);
3368
0
}
3369
3370
/* Callback for window_raw_flags. */
3371
static void *
3372
format_cb_window_raw_flags(struct format_tree *ft)
3373
0
{
3374
0
  if (ft->wl != NULL)
3375
0
    return (xstrdup(window_printable_flags(ft->wl, 0)));
3376
0
  return (NULL);
3377
0
}
3378
3379
/* Callback for window_silence_flag. */
3380
static void *
3381
format_cb_window_silence_flag(struct format_tree *ft)
3382
0
{
3383
0
  if (ft->wl != NULL) {
3384
0
    if (ft->wl->flags & WINLINK_SILENCE)
3385
0
      return (xstrdup("1"));
3386
0
    return (xstrdup("0"));
3387
0
  }
3388
0
  return (NULL);
3389
0
}
3390
3391
/* Callback for window_start_flag. */
3392
static void *
3393
format_cb_window_start_flag(struct format_tree *ft)
3394
0
{
3395
0
  if (ft->wl != NULL) {
3396
0
    if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows))
3397
0
      return (xstrdup("1"));
3398
0
    return (xstrdup("0"));
3399
0
  }
3400
0
  return (NULL);
3401
0
}
3402
3403
/* Callback for window_width. */
3404
static void *
3405
format_cb_window_width(struct format_tree *ft)
3406
0
{
3407
0
  if (ft->w != NULL)
3408
0
    return (format_printf("%u", ft->w->sx));
3409
0
  return (NULL);
3410
0
}
3411
3412
/* Callback for window_manual_width. */
3413
static void *
3414
format_cb_window_manual_width(struct format_tree *ft)
3415
0
{
3416
0
  struct window *w = ft->w;
3417
3418
0
  if (w == NULL)
3419
0
    return (NULL);
3420
0
  if (options_get_number(w->options, "window-size") != WINDOW_SIZE_MANUAL)
3421
0
    return (xstrdup(""));
3422
0
  return (format_printf("%u", w->manual_sx));
3423
0
}
3424
3425
/* Callback for window_zoomed_flag. */
3426
static void *
3427
format_cb_window_zoomed_flag(struct format_tree *ft)
3428
0
{
3429
0
  if (ft->w != NULL) {
3430
0
    if (ft->w->flags & WINDOW_ZOOMED)
3431
0
      return (xstrdup("1"));
3432
0
    return (xstrdup("0"));
3433
0
  }
3434
0
  return (NULL);
3435
0
}
3436
3437
/* Callback for wrap_flag. */
3438
static void *
3439
format_cb_wrap_flag(struct format_tree *ft)
3440
0
{
3441
0
  if (ft->wp != NULL) {
3442
0
    if (ft->wp->base.mode & MODE_WRAP)
3443
0
      return (xstrdup("1"));
3444
0
    return (xstrdup("0"));
3445
0
  }
3446
0
  return (NULL);
3447
0
}
3448
3449
/* Callback for buffer_created. */
3450
static void *
3451
format_cb_buffer_created(struct format_tree *ft)
3452
0
{
3453
0
  static struct timeval  tv;
3454
3455
0
  if (ft->pb != NULL) {
3456
0
    timerclear(&tv);
3457
0
    tv.tv_sec = paste_buffer_created(ft->pb);
3458
0
    return (&tv);
3459
0
  }
3460
0
  return (NULL);
3461
0
}
3462
3463
/* Callback for client_activity. */
3464
static void *
3465
format_cb_client_activity(struct format_tree *ft)
3466
0
{
3467
0
  if (ft->c != NULL)
3468
0
    return (&ft->c->activity_time);
3469
0
  return (NULL);
3470
0
}
3471
3472
/* Callback for client_created. */
3473
static void *
3474
format_cb_client_created(struct format_tree *ft)
3475
0
{
3476
0
  if (ft->c != NULL)
3477
0
    return (&ft->c->creation_time);
3478
0
  return (NULL);
3479
0
}
3480
3481
/* Callback for session_activity. */
3482
static void *
3483
format_cb_session_activity(struct format_tree *ft)
3484
0
{
3485
0
  if (ft->s != NULL)
3486
0
    return (&ft->s->activity_time);
3487
0
  return (NULL);
3488
0
}
3489
3490
/* Callback for session_created. */
3491
static void *
3492
format_cb_session_created(struct format_tree *ft)
3493
0
{
3494
0
  if (ft->s != NULL)
3495
0
    return (&ft->s->creation_time);
3496
0
  return (NULL);
3497
0
}
3498
3499
/* Callback for session_last_attached. */
3500
static void *
3501
format_cb_session_last_attached(struct format_tree *ft)
3502
0
{
3503
0
  if (ft->s != NULL)
3504
0
    return (&ft->s->last_attached_time);
3505
0
  return (NULL);
3506
0
}
3507
3508
/* Callback for start_time. */
3509
static void *
3510
format_cb_start_time(__unused struct format_tree *ft)
3511
0
{
3512
0
  return (&start_time);
3513
0
}
3514
3515
/* Callback for window_activity. */
3516
static void *
3517
format_cb_window_activity(struct format_tree *ft)
3518
0
{
3519
0
  if (ft->w != NULL)
3520
0
    return (&ft->w->activity_time);
3521
0
  return (NULL);
3522
0
}
3523
3524
/* Callback for buffer_mode_format, */
3525
static void *
3526
format_cb_buffer_mode_format(__unused struct format_tree *ft)
3527
0
{
3528
0
  return (xstrdup(window_buffer_mode.default_format));
3529
0
}
3530
3531
/* Callback for client_mode_format, */
3532
static void *
3533
format_cb_client_mode_format(__unused struct format_tree *ft)
3534
0
{
3535
0
  return (xstrdup(window_client_mode.default_format));
3536
0
}
3537
3538
/* Callback for tree_mode_format, */
3539
static void *
3540
format_cb_tree_mode_format(__unused struct format_tree *ft)
3541
0
{
3542
0
  return (xstrdup(window_tree_mode.default_format));
3543
0
}
3544
3545
/* Callback for uid. */
3546
static void *
3547
format_cb_uid(__unused struct format_tree *ft)
3548
0
{
3549
0
  return (format_printf("%ld", (long)getuid()));
3550
0
}
3551
3552
/* Callback for user. */
3553
static void *
3554
format_cb_user(__unused struct format_tree *ft)
3555
0
{
3556
0
  static char *cached;
3557
0
  struct passwd *pw;
3558
3559
0
  if (cached == NULL && (pw = getpwuid(getuid())) != NULL)
3560
0
    cached = xstrdup(pw->pw_name);
3561
0
  if (cached != NULL)
3562
0
    return (xstrdup(cached));
3563
0
  return (NULL);
3564
0
}
3565
3566
/* Format table type. */
3567
enum format_table_type {
3568
  FORMAT_TABLE_STRING,
3569
  FORMAT_TABLE_TIME
3570
};
3571
3572
/* Format table entry. */
3573
struct format_table_entry {
3574
  const char    *key;
3575
  enum format_table_type   type;
3576
  format_cb    cb;
3577
};
3578
3579
/*
3580
 * Format table. Default format variables (that are almost always in the tree
3581
 * and where the value is expanded by a callback in this file) are listed here.
3582
 * Only variables which are added by the caller go into the tree.
3583
 */
3584
static const struct format_table_entry format_table[] = {
3585
  { "active_window_index", FORMAT_TABLE_STRING,
3586
    format_cb_active_window_index
3587
  },
3588
  { "alternate_on", FORMAT_TABLE_STRING,
3589
    format_cb_alternate_on
3590
  },
3591
  { "alternate_saved_x", FORMAT_TABLE_STRING,
3592
    format_cb_alternate_saved_x
3593
  },
3594
  { "alternate_saved_y", FORMAT_TABLE_STRING,
3595
    format_cb_alternate_saved_y
3596
  },
3597
  { "bracket_paste_flag", FORMAT_TABLE_STRING,
3598
    format_cb_bracket_paste_flag
3599
  },
3600
  { "buffer_created", FORMAT_TABLE_TIME,
3601
    format_cb_buffer_created
3602
  },
3603
  { "buffer_full", FORMAT_TABLE_STRING,
3604
    format_cb_buffer_full
3605
  },
3606
  { "buffer_mode_format", FORMAT_TABLE_STRING,
3607
    format_cb_buffer_mode_format
3608
  },
3609
  { "buffer_name", FORMAT_TABLE_STRING,
3610
    format_cb_buffer_name
3611
  },
3612
  { "buffer_sample", FORMAT_TABLE_STRING,
3613
    format_cb_buffer_sample
3614
  },
3615
  { "buffer_size", FORMAT_TABLE_STRING,
3616
    format_cb_buffer_size
3617
  },
3618
  { "client_activity", FORMAT_TABLE_TIME,
3619
    format_cb_client_activity
3620
  },
3621
  { "client_cell_height", FORMAT_TABLE_STRING,
3622
    format_cb_client_cell_height
3623
  },
3624
  { "client_cell_width", FORMAT_TABLE_STRING,
3625
    format_cb_client_cell_width
3626
  },
3627
  { "client_colours", FORMAT_TABLE_STRING,
3628
    format_cb_client_colours
3629
  },
3630
  { "client_control_mode", FORMAT_TABLE_STRING,
3631
    format_cb_client_control_mode
3632
  },
3633
  { "client_created", FORMAT_TABLE_TIME,
3634
    format_cb_client_created
3635
  },
3636
  { "client_discarded", FORMAT_TABLE_STRING,
3637
    format_cb_client_discarded
3638
  },
3639
  { "client_flags", FORMAT_TABLE_STRING,
3640
    format_cb_client_flags
3641
  },
3642
  { "client_height", FORMAT_TABLE_STRING,
3643
    format_cb_client_height
3644
  },
3645
  { "client_key_table", FORMAT_TABLE_STRING,
3646
    format_cb_client_key_table
3647
  },
3648
  { "client_last_session", FORMAT_TABLE_STRING,
3649
    format_cb_client_last_session
3650
  },
3651
  { "client_mode_format", FORMAT_TABLE_STRING,
3652
    format_cb_client_mode_format
3653
  },
3654
  { "client_name", FORMAT_TABLE_STRING,
3655
    format_cb_client_name
3656
  },
3657
  { "client_pid", FORMAT_TABLE_STRING,
3658
    format_cb_client_pid
3659
  },
3660
  { "client_prefix", FORMAT_TABLE_STRING,
3661
    format_cb_client_prefix
3662
  },
3663
  { "client_readonly", FORMAT_TABLE_STRING,
3664
    format_cb_client_readonly
3665
  },
3666
  { "client_session", FORMAT_TABLE_STRING,
3667
    format_cb_client_session
3668
  },
3669
  { "client_termfeatures", FORMAT_TABLE_STRING,
3670
    format_cb_client_termfeatures
3671
  },
3672
  { "client_termname", FORMAT_TABLE_STRING,
3673
    format_cb_client_termname
3674
  },
3675
  { "client_termtype", FORMAT_TABLE_STRING,
3676
    format_cb_client_termtype
3677
  },
3678
  { "client_theme", FORMAT_TABLE_STRING,
3679
    format_cb_client_theme
3680
  },
3681
  { "client_tty", FORMAT_TABLE_STRING,
3682
    format_cb_client_tty
3683
  },
3684
  { "client_uid", FORMAT_TABLE_STRING,
3685
    format_cb_client_uid
3686
  },
3687
  { "client_user", FORMAT_TABLE_STRING,
3688
    format_cb_client_user
3689
  },
3690
  { "client_utf8", FORMAT_TABLE_STRING,
3691
    format_cb_client_utf8
3692
  },
3693
  { "client_width", FORMAT_TABLE_STRING,
3694
    format_cb_client_width
3695
  },
3696
  { "client_written", FORMAT_TABLE_STRING,
3697
    format_cb_client_written
3698
  },
3699
  { "config_files", FORMAT_TABLE_STRING,
3700
    format_cb_config_files
3701
  },
3702
  { "cursor_blinking", FORMAT_TABLE_STRING,
3703
    format_cb_cursor_blinking
3704
  },
3705
  { "cursor_character", FORMAT_TABLE_STRING,
3706
    format_cb_cursor_character
3707
  },
3708
  { "cursor_colour", FORMAT_TABLE_STRING,
3709
    format_cb_cursor_colour
3710
  },
3711
  { "cursor_flag", FORMAT_TABLE_STRING,
3712
    format_cb_cursor_flag
3713
  },
3714
  { "cursor_shape", FORMAT_TABLE_STRING,
3715
    format_cb_cursor_shape
3716
  },
3717
  { "cursor_very_visible", FORMAT_TABLE_STRING,
3718
    format_cb_cursor_very_visible
3719
  },
3720
  { "cursor_x", FORMAT_TABLE_STRING,
3721
    format_cb_cursor_x
3722
  },
3723
  { "cursor_y", FORMAT_TABLE_STRING,
3724
    format_cb_cursor_y
3725
  },
3726
  { "history_added", FORMAT_TABLE_STRING,
3727
    format_cb_history_added
3728
  },
3729
  { "history_all_bytes", FORMAT_TABLE_STRING,
3730
    format_cb_history_all_bytes
3731
  },
3732
  { "history_bytes", FORMAT_TABLE_STRING,
3733
    format_cb_history_bytes
3734
  },
3735
  { "history_collected", FORMAT_TABLE_STRING,
3736
    format_cb_history_collected
3737
  },
3738
  { "history_generation", FORMAT_TABLE_STRING,
3739
    format_cb_history_generation
3740
  },
3741
  { "history_limit", FORMAT_TABLE_STRING,
3742
    format_cb_history_limit
3743
  },
3744
  { "history_size", FORMAT_TABLE_STRING,
3745
    format_cb_history_size
3746
  },
3747
  { "host", FORMAT_TABLE_STRING,
3748
    format_cb_host
3749
  },
3750
  { "host_short", FORMAT_TABLE_STRING,
3751
    format_cb_host_short
3752
  },
3753
  { "insert_flag", FORMAT_TABLE_STRING,
3754
    format_cb_insert_flag
3755
  },
3756
  { "keypad_cursor_flag", FORMAT_TABLE_STRING,
3757
    format_cb_keypad_cursor_flag
3758
  },
3759
  { "keypad_flag", FORMAT_TABLE_STRING,
3760
    format_cb_keypad_flag
3761
  },
3762
  { "last_window_index", FORMAT_TABLE_STRING,
3763
    format_cb_last_window_index
3764
  },
3765
  { "mouse_all_flag", FORMAT_TABLE_STRING,
3766
    format_cb_mouse_all_flag
3767
  },
3768
  { "mouse_any_flag", FORMAT_TABLE_STRING,
3769
    format_cb_mouse_any_flag
3770
  },
3771
  { "mouse_button_flag", FORMAT_TABLE_STRING,
3772
    format_cb_mouse_button_flag
3773
  },
3774
  { "mouse_hyperlink", FORMAT_TABLE_STRING,
3775
    format_cb_mouse_hyperlink
3776
  },
3777
  { "mouse_line", FORMAT_TABLE_STRING,
3778
    format_cb_mouse_line
3779
  },
3780
  { "mouse_pane", FORMAT_TABLE_STRING,
3781
    format_cb_mouse_pane
3782
  },
3783
  { "mouse_sgr_flag", FORMAT_TABLE_STRING,
3784
    format_cb_mouse_sgr_flag
3785
  },
3786
  { "mouse_standard_flag", FORMAT_TABLE_STRING,
3787
    format_cb_mouse_standard_flag
3788
  },
3789
  { "mouse_status_line", FORMAT_TABLE_STRING,
3790
    format_cb_mouse_status_line
3791
  },
3792
  { "mouse_status_range", FORMAT_TABLE_STRING,
3793
    format_cb_mouse_status_range
3794
  },
3795
  { "mouse_utf8_flag", FORMAT_TABLE_STRING,
3796
    format_cb_mouse_utf8_flag
3797
  },
3798
  { "mouse_word", FORMAT_TABLE_STRING,
3799
    format_cb_mouse_word
3800
  },
3801
  { "mouse_x", FORMAT_TABLE_STRING,
3802
    format_cb_mouse_x
3803
  },
3804
  { "mouse_y", FORMAT_TABLE_STRING,
3805
    format_cb_mouse_y
3806
  },
3807
  { "next_session_id", FORMAT_TABLE_STRING,
3808
    format_cb_next_session_id
3809
  },
3810
  { "origin_flag", FORMAT_TABLE_STRING,
3811
    format_cb_origin_flag
3812
  },
3813
  { "pane_active", FORMAT_TABLE_STRING,
3814
    format_cb_pane_active
3815
  },
3816
  { "pane_at_bottom", FORMAT_TABLE_STRING,
3817
    format_cb_pane_at_bottom
3818
  },
3819
  { "pane_at_left", FORMAT_TABLE_STRING,
3820
    format_cb_pane_at_left
3821
  },
3822
  { "pane_at_right", FORMAT_TABLE_STRING,
3823
    format_cb_pane_at_right
3824
  },
3825
  { "pane_at_top", FORMAT_TABLE_STRING,
3826
    format_cb_pane_at_top
3827
  },
3828
  { "pane_bg", FORMAT_TABLE_STRING,
3829
    format_cb_pane_bg
3830
  },
3831
  { "pane_bottom", FORMAT_TABLE_STRING,
3832
    format_cb_pane_bottom
3833
  },
3834
  { "pane_command_duration", FORMAT_TABLE_STRING,
3835
    format_cb_pane_command_duration
3836
  },
3837
  { "pane_command_end_time", FORMAT_TABLE_TIME,
3838
    format_cb_pane_command_end_time
3839
  },
3840
  { "pane_command_running", FORMAT_TABLE_STRING,
3841
    format_cb_pane_command_running
3842
  },
3843
  { "pane_command_start_time", FORMAT_TABLE_TIME,
3844
    format_cb_pane_command_start_time
3845
  },
3846
  { "pane_command_status", FORMAT_TABLE_STRING,
3847
    format_cb_pane_command_status
3848
  },
3849
  { "pane_current_command", FORMAT_TABLE_STRING,
3850
    format_cb_current_command
3851
  },
3852
  { "pane_current_path", FORMAT_TABLE_STRING,
3853
    format_cb_current_path
3854
  },
3855
  { "pane_dead", FORMAT_TABLE_STRING,
3856
    format_cb_pane_dead
3857
  },
3858
  { "pane_dead_signal", FORMAT_TABLE_STRING,
3859
    format_cb_pane_dead_signal
3860
  },
3861
  { "pane_dead_status", FORMAT_TABLE_STRING,
3862
    format_cb_pane_dead_status
3863
  },
3864
  { "pane_dead_time", FORMAT_TABLE_TIME,
3865
    format_cb_pane_dead_time
3866
  },
3867
  { "pane_fg", FORMAT_TABLE_STRING,
3868
    format_cb_pane_fg
3869
  },
3870
  { "pane_flags", FORMAT_TABLE_STRING,
3871
    format_cb_pane_flags
3872
  },
3873
  { "pane_floating_flag", FORMAT_TABLE_STRING,
3874
    format_cb_pane_floating_flag
3875
  },
3876
  { "pane_format", FORMAT_TABLE_STRING,
3877
    format_cb_pane_format
3878
  },
3879
  { "pane_height", FORMAT_TABLE_STRING,
3880
    format_cb_pane_height
3881
  },
3882
  { "pane_id", FORMAT_TABLE_STRING,
3883
    format_cb_pane_id
3884
  },
3885
  { "pane_in_mode", FORMAT_TABLE_STRING,
3886
    format_cb_pane_in_mode
3887
  },
3888
  { "pane_index", FORMAT_TABLE_STRING,
3889
    format_cb_pane_index
3890
  },
3891
  { "pane_input_off", FORMAT_TABLE_STRING,
3892
    format_cb_pane_input_off
3893
  },
3894
  { "pane_key_mode", FORMAT_TABLE_STRING,
3895
    format_cb_pane_key_mode
3896
  },
3897
  { "pane_last", FORMAT_TABLE_STRING,
3898
    format_cb_pane_last
3899
  },
3900
  { "pane_last_output_time", FORMAT_TABLE_TIME,
3901
    format_cb_pane_last_output_time
3902
  },
3903
  { "pane_last_prompt_time", FORMAT_TABLE_TIME,
3904
    format_cb_pane_last_prompt_time
3905
  },
3906
  { "pane_left", FORMAT_TABLE_STRING,
3907
    format_cb_pane_left
3908
  },
3909
  { "pane_marked", FORMAT_TABLE_STRING,
3910
    format_cb_pane_marked
3911
  },
3912
  { "pane_marked_set", FORMAT_TABLE_STRING,
3913
    format_cb_pane_marked_set
3914
  },
3915
  { "pane_modal_flag", FORMAT_TABLE_STRING,
3916
    format_cb_pane_modal_flag
3917
  },
3918
  { "pane_mode", FORMAT_TABLE_STRING,
3919
    format_cb_pane_mode
3920
  },
3921
  { "pane_output_generation", FORMAT_TABLE_STRING,
3922
    format_cb_pane_output_generation
3923
  },
3924
  { "pane_path", FORMAT_TABLE_STRING,
3925
    format_cb_pane_path
3926
  },
3927
  { "pane_pb_progress", FORMAT_TABLE_STRING,
3928
    format_cb_pane_pb_progress
3929
  },
3930
  { "pane_pb_state", FORMAT_TABLE_STRING,
3931
    format_cb_pane_pb_state
3932
  },
3933
  { "pane_pid", FORMAT_TABLE_STRING,
3934
    format_cb_pane_pid
3935
  },
3936
  { "pane_pipe", FORMAT_TABLE_STRING,
3937
    format_cb_pane_pipe
3938
  },
3939
  { "pane_pipe_pid", FORMAT_TABLE_STRING,
3940
    format_cb_pane_pipe_pid
3941
  },
3942
  { "pane_private_modes", FORMAT_TABLE_STRING,
3943
    format_cb_pane_private_modes
3944
  },
3945
  { "pane_right", FORMAT_TABLE_STRING,
3946
    format_cb_pane_right
3947
  },
3948
  { "pane_search_string", FORMAT_TABLE_STRING,
3949
    format_cb_pane_search_string
3950
  },
3951
  { "pane_start_command", FORMAT_TABLE_STRING,
3952
    format_cb_start_command
3953
  },
3954
  { "pane_start_command_list", FORMAT_TABLE_STRING,
3955
    format_cb_start_command_list
3956
  },
3957
  { "pane_start_path", FORMAT_TABLE_STRING,
3958
    format_cb_start_path
3959
  },
3960
  { "pane_synchronized", FORMAT_TABLE_STRING,
3961
    format_cb_pane_synchronized
3962
  },
3963
  { "pane_tabs", FORMAT_TABLE_STRING,
3964
    format_cb_pane_tabs
3965
  },
3966
  { "pane_title", FORMAT_TABLE_STRING,
3967
    format_cb_pane_title
3968
  },
3969
  { "pane_top", FORMAT_TABLE_STRING,
3970
    format_cb_pane_top
3971
  },
3972
  { "pane_tty", FORMAT_TABLE_STRING,
3973
    format_cb_pane_tty
3974
  },
3975
  { "pane_unseen_changes", FORMAT_TABLE_STRING,
3976
    format_cb_pane_unseen_changes
3977
  },
3978
  { "pane_unzoomed_height", FORMAT_TABLE_STRING,
3979
    format_cb_pane_unzoomed_height
3980
  },
3981
  { "pane_unzoomed_width", FORMAT_TABLE_STRING,
3982
    format_cb_pane_unzoomed_width
3983
  },
3984
  { "pane_width", FORMAT_TABLE_STRING,
3985
    format_cb_pane_width
3986
  },
3987
  { "pane_x", FORMAT_TABLE_STRING,
3988
    format_cb_pane_x
3989
  },
3990
  { "pane_y", FORMAT_TABLE_STRING,
3991
    format_cb_pane_y
3992
  },
3993
  { "pane_z", FORMAT_TABLE_STRING,
3994
    format_cb_pane_z
3995
  },
3996
  { "pane_zoomed_flag", FORMAT_TABLE_STRING,
3997
    format_cb_pane_zoomed_flag
3998
  },
3999
  { "pid", FORMAT_TABLE_STRING,
4000
    format_cb_pid
4001
  },
4002
  { "scroll_region_lower", FORMAT_TABLE_STRING,
4003
    format_cb_scroll_region_lower
4004
  },
4005
  { "scroll_region_upper", FORMAT_TABLE_STRING,
4006
    format_cb_scroll_region_upper
4007
  },
4008
  { "server_sessions", FORMAT_TABLE_STRING,
4009
    format_cb_server_sessions
4010
  },
4011
  { "session_active", FORMAT_TABLE_STRING,
4012
    format_cb_session_active
4013
  },
4014
  { "session_activity", FORMAT_TABLE_TIME,
4015
    format_cb_session_activity
4016
  },
4017
  { "session_activity_flag", FORMAT_TABLE_STRING,
4018
    format_cb_session_activity_flag
4019
  },
4020
  { "session_alert", FORMAT_TABLE_STRING,
4021
    format_cb_session_alert
4022
  },
4023
  { "session_alerts", FORMAT_TABLE_STRING,
4024
    format_cb_session_alerts
4025
  },
4026
  { "session_attached", FORMAT_TABLE_STRING,
4027
    format_cb_session_attached
4028
  },
4029
  { "session_attached_list", FORMAT_TABLE_STRING,
4030
    format_cb_session_attached_list
4031
  },
4032
  { "session_bell_flag", FORMAT_TABLE_STRING,
4033
    format_cb_session_bell_flag
4034
  },
4035
  { "session_created", FORMAT_TABLE_TIME,
4036
    format_cb_session_created
4037
  },
4038
  { "session_format", FORMAT_TABLE_STRING,
4039
    format_cb_session_format
4040
  },
4041
  { "session_group", FORMAT_TABLE_STRING,
4042
    format_cb_session_group
4043
  },
4044
  { "session_group_attached", FORMAT_TABLE_STRING,
4045
    format_cb_session_group_attached
4046
  },
4047
  { "session_group_attached_list", FORMAT_TABLE_STRING,
4048
    format_cb_session_group_attached_list
4049
  },
4050
  { "session_group_list", FORMAT_TABLE_STRING,
4051
    format_cb_session_group_list
4052
  },
4053
  { "session_group_many_attached", FORMAT_TABLE_STRING,
4054
    format_cb_session_group_many_attached
4055
  },
4056
  { "session_group_size", FORMAT_TABLE_STRING,
4057
    format_cb_session_group_size
4058
  },
4059
  { "session_grouped", FORMAT_TABLE_STRING,
4060
    format_cb_session_grouped
4061
  },
4062
  { "session_id", FORMAT_TABLE_STRING,
4063
    format_cb_session_id
4064
  },
4065
  { "session_last_attached", FORMAT_TABLE_TIME,
4066
    format_cb_session_last_attached
4067
  },
4068
  { "session_many_attached", FORMAT_TABLE_STRING,
4069
    format_cb_session_many_attached
4070
  },
4071
  { "session_marked", FORMAT_TABLE_STRING,
4072
    format_cb_session_marked,
4073
  },
4074
  { "session_name", FORMAT_TABLE_STRING,
4075
    format_cb_session_name
4076
  },
4077
  { "session_path", FORMAT_TABLE_STRING,
4078
    format_cb_session_path
4079
  },
4080
  { "session_silence_flag", FORMAT_TABLE_STRING,
4081
    format_cb_session_silence_flag
4082
  },
4083
  { "session_stack", FORMAT_TABLE_STRING,
4084
    format_cb_session_stack
4085
  },
4086
  { "session_windows", FORMAT_TABLE_STRING,
4087
    format_cb_session_windows
4088
  },
4089
  { "sixel_support", FORMAT_TABLE_STRING,
4090
    format_cb_sixel_support
4091
  },
4092
  { "socket_path", FORMAT_TABLE_STRING,
4093
    format_cb_socket_path
4094
  },
4095
  { "start_time", FORMAT_TABLE_TIME,
4096
    format_cb_start_time
4097
  },
4098
  { "synchronized_output_flag", FORMAT_TABLE_STRING,
4099
    format_cb_synchronized_output_flag
4100
  },
4101
  { "tree_mode_format", FORMAT_TABLE_STRING,
4102
    format_cb_tree_mode_format
4103
  },
4104
  { "uid", FORMAT_TABLE_STRING,
4105
    format_cb_uid
4106
  },
4107
  { "user", FORMAT_TABLE_STRING,
4108
    format_cb_user
4109
  },
4110
  { "version", FORMAT_TABLE_STRING,
4111
    format_cb_version
4112
  },
4113
  { "window_active", FORMAT_TABLE_STRING,
4114
    format_cb_window_active
4115
  },
4116
  { "window_active_clients", FORMAT_TABLE_STRING,
4117
    format_cb_window_active_clients
4118
  },
4119
  { "window_active_clients_list", FORMAT_TABLE_STRING,
4120
    format_cb_window_active_clients_list
4121
  },
4122
  { "window_active_sessions", FORMAT_TABLE_STRING,
4123
    format_cb_window_active_sessions
4124
  },
4125
  { "window_active_sessions_list", FORMAT_TABLE_STRING,
4126
    format_cb_window_active_sessions_list
4127
  },
4128
  { "window_activity", FORMAT_TABLE_TIME,
4129
    format_cb_window_activity
4130
  },
4131
  { "window_activity_flag", FORMAT_TABLE_STRING,
4132
    format_cb_window_activity_flag
4133
  },
4134
  { "window_bell_flag", FORMAT_TABLE_STRING,
4135
    format_cb_window_bell_flag
4136
  },
4137
  { "window_bigger", FORMAT_TABLE_STRING,
4138
    format_cb_window_bigger
4139
  },
4140
  { "window_cell_height", FORMAT_TABLE_STRING,
4141
    format_cb_window_cell_height
4142
  },
4143
  { "window_cell_width", FORMAT_TABLE_STRING,
4144
    format_cb_window_cell_width
4145
  },
4146
  { "window_end_flag", FORMAT_TABLE_STRING,
4147
    format_cb_window_end_flag
4148
  },
4149
  { "window_flags", FORMAT_TABLE_STRING,
4150
    format_cb_window_flags
4151
  },
4152
  { "window_format", FORMAT_TABLE_STRING,
4153
    format_cb_window_format
4154
  },
4155
  { "window_height", FORMAT_TABLE_STRING,
4156
    format_cb_window_height
4157
  },
4158
  { "window_id", FORMAT_TABLE_STRING,
4159
    format_cb_window_id
4160
  },
4161
  { "window_index", FORMAT_TABLE_STRING,
4162
    format_cb_window_index
4163
  },
4164
  { "window_last_flag", FORMAT_TABLE_STRING,
4165
    format_cb_window_last_flag
4166
  },
4167
  { "window_layout", FORMAT_TABLE_STRING,
4168
    format_cb_window_layout
4169
  },
4170
  { "window_linked", FORMAT_TABLE_STRING,
4171
    format_cb_window_linked
4172
  },
4173
  { "window_linked_sessions", FORMAT_TABLE_STRING,
4174
    format_cb_window_linked_sessions
4175
  },
4176
  { "window_linked_sessions_list", FORMAT_TABLE_STRING,
4177
    format_cb_window_linked_sessions_list
4178
  },
4179
  { "window_manual_height", FORMAT_TABLE_STRING,
4180
    format_cb_window_manual_height
4181
  },
4182
  { "window_manual_width", FORMAT_TABLE_STRING,
4183
    format_cb_window_manual_width
4184
  },
4185
  { "window_marked_flag", FORMAT_TABLE_STRING,
4186
    format_cb_window_marked_flag
4187
  },
4188
  { "window_modal_pane", FORMAT_TABLE_STRING,
4189
    format_cb_window_modal_pane
4190
  },
4191
  { "window_name", FORMAT_TABLE_STRING,
4192
    format_cb_window_name
4193
  },
4194
  { "window_offset_x", FORMAT_TABLE_STRING,
4195
    format_cb_window_offset_x
4196
  },
4197
  { "window_offset_y", FORMAT_TABLE_STRING,
4198
    format_cb_window_offset_y
4199
  },
4200
  { "window_panes", FORMAT_TABLE_STRING,
4201
    format_cb_window_panes
4202
  },
4203
  { "window_raw_flags", FORMAT_TABLE_STRING,
4204
    format_cb_window_raw_flags
4205
  },
4206
  { "window_silence_flag", FORMAT_TABLE_STRING,
4207
    format_cb_window_silence_flag
4208
  },
4209
  { "window_stack_index", FORMAT_TABLE_STRING,
4210
    format_cb_window_stack_index
4211
  },
4212
  { "window_start_flag", FORMAT_TABLE_STRING,
4213
    format_cb_window_start_flag
4214
  },
4215
  { "window_visible_layout", FORMAT_TABLE_STRING,
4216
    format_cb_window_visible_layout
4217
  },
4218
  { "window_width", FORMAT_TABLE_STRING,
4219
    format_cb_window_width
4220
  },
4221
  { "window_zoomed_flag", FORMAT_TABLE_STRING,
4222
    format_cb_window_zoomed_flag
4223
  },
4224
  { "wrap_flag", FORMAT_TABLE_STRING,
4225
    format_cb_wrap_flag
4226
  }
4227
};
4228
4229
/* Compare format table entries. */
4230
static int
4231
format_table_compare(const void *key0, const void *entry0)
4232
0
{
4233
0
  const char      *key = key0;
4234
0
  const struct format_table_entry *entry = entry0;
4235
4236
0
  return (strcmp(key, entry->key));
4237
0
}
4238
4239
/* Get a format callback. */
4240
static const struct format_table_entry *
4241
format_table_get(const char *key)
4242
0
{
4243
0
  return (bsearch(key, format_table, nitems(format_table),
4244
0
      sizeof *format_table, format_table_compare));
4245
0
}
4246
4247
/* Merge one format tree into another. */
4248
void
4249
format_merge(struct format_tree *ft, struct format_tree *from)
4250
0
{
4251
0
  struct format_entry *fe;
4252
4253
0
  RB_FOREACH(fe, format_entry_tree, &from->tree) {
4254
0
    if (fe->value != NULL)
4255
0
      format_add(ft, fe->key, "%s", fe->value);
4256
0
  }
4257
0
}
4258
4259
/* Get format pane. */
4260
struct window_pane *
4261
format_get_pane(struct format_tree *ft)
4262
0
{
4263
0
  return (ft->wp);
4264
0
}
4265
4266
/* Add item bits to tree. */
4267
static void
4268
format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
4269
0
{
4270
0
  struct key_event  *event = cmdq_get_event(item);
4271
0
  struct mouse_event  *m = &event->m;
4272
4273
0
  cmdq_merge_formats(item, ft);
4274
0
  memcpy(&ft->m, m, sizeof ft->m);
4275
0
}
4276
4277
/* Create a new tree. */
4278
struct format_tree *
4279
format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
4280
0
{
4281
0
  struct format_tree  *ft;
4282
4283
0
  ft = xcalloc(1, sizeof *ft);
4284
0
  RB_INIT(&ft->tree);
4285
4286
0
  if (c != NULL) {
4287
0
    ft->client = c;
4288
0
    ft->client->references++;
4289
0
  }
4290
0
  ft->item = item;
4291
4292
0
  ft->tag = tag;
4293
0
  ft->flags = flags;
4294
4295
0
  if (item != NULL)
4296
0
    format_create_add_item(ft, item);
4297
4298
0
  return (ft);
4299
0
}
4300
4301
/* Free a tree. */
4302
void
4303
format_free(struct format_tree *ft)
4304
0
{
4305
0
  struct format_entry *fe, *fe1;
4306
4307
0
  RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
4308
0
    RB_REMOVE(format_entry_tree, &ft->tree, fe);
4309
0
    free(fe->value);
4310
0
    free(fe->key);
4311
0
    free(fe);
4312
0
  }
4313
4314
0
  if (ft->client != NULL)
4315
0
    server_client_unref(ft->client);
4316
0
  free(ft);
4317
0
}
4318
4319
/* Log each format. */
4320
static void
4321
format_log_debug_cb(const char *key, const char *value, void *arg)
4322
0
{
4323
0
  const char  *prefix = arg;
4324
4325
0
  log_debug("%s: %s=%s", prefix, key, value);
4326
0
}
4327
4328
/* Log a format tree. */
4329
void
4330
format_log_debug(struct format_tree *ft, const char *prefix)
4331
0
{
4332
0
  if (log_get_level() == 0)
4333
0
    return;
4334
0
  format_each(ft, format_log_debug_cb, (void *)prefix);
4335
0
}
4336
4337
/* Walk each format. */
4338
void
4339
format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
4340
    void *), void *arg)
4341
0
{
4342
0
  const struct format_table_entry *fte;
4343
0
  struct format_entry   *fe;
4344
0
  u_int        i;
4345
0
  char         s[64];
4346
0
  void        *value;
4347
0
  struct timeval      *tv;
4348
4349
0
  for (i = 0; i < nitems(format_table); i++) {
4350
0
    fte = &format_table[i];
4351
4352
0
    value = fte->cb(ft);
4353
0
    if (value == NULL)
4354
0
      continue;
4355
0
    if (fte->type == FORMAT_TABLE_TIME) {
4356
0
      tv = value;
4357
0
      xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec);
4358
0
      cb(fte->key, s, arg);
4359
0
    } else {
4360
0
      cb(fte->key, value, arg);
4361
0
      free(value);
4362
0
    }
4363
0
  }
4364
0
  RB_FOREACH(fe, format_entry_tree, &ft->tree) {
4365
0
    if (fe->time != 0) {
4366
0
      xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
4367
0
      cb(fe->key, s, arg);
4368
0
    } else {
4369
0
      if (fe->value == NULL && fe->cb != NULL) {
4370
0
        fe->value = fe->cb(ft);
4371
0
        if (fe->value == NULL)
4372
0
          fe->value = xstrdup("");
4373
0
      }
4374
0
      cb(fe->key, fe->value, arg);
4375
0
    }
4376
0
  }
4377
0
}
4378
4379
/* Add a key-value pair. */
4380
void
4381
format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
4382
0
{
4383
0
  struct format_entry *fe;
4384
0
  struct format_entry *fe_now;
4385
0
  va_list      ap;
4386
4387
0
  fe = xmalloc(sizeof *fe);
4388
0
  fe->key = xstrdup(key);
4389
4390
0
  fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
4391
0
  if (fe_now != NULL) {
4392
0
    free(fe->key);
4393
0
    free(fe);
4394
0
    free(fe_now->value);
4395
0
    fe = fe_now;
4396
0
  }
4397
4398
0
  fe->cb = NULL;
4399
0
  fe->time = 0;
4400
4401
0
  va_start(ap, fmt);
4402
0
  xvasprintf(&fe->value, fmt, ap);
4403
0
  va_end(ap);
4404
0
}
4405
4406
/* Add a key and time. */
4407
void
4408
format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
4409
0
{
4410
0
  struct format_entry *fe, *fe_now;
4411
4412
0
  fe = xmalloc(sizeof *fe);
4413
0
  fe->key = xstrdup(key);
4414
4415
0
  fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
4416
0
  if (fe_now != NULL) {
4417
0
    free(fe->key);
4418
0
    free(fe);
4419
0
    free(fe_now->value);
4420
0
    fe = fe_now;
4421
0
  }
4422
4423
0
  fe->cb = NULL;
4424
0
  fe->time = tv->tv_sec;
4425
4426
0
  fe->value = NULL;
4427
0
}
4428
4429
/* Add a key and function. */
4430
void
4431
format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
4432
0
{
4433
0
  struct format_entry *fe;
4434
0
  struct format_entry *fe_now;
4435
4436
0
  fe = xmalloc(sizeof *fe);
4437
0
  fe->key = xstrdup(key);
4438
4439
0
  fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
4440
0
  if (fe_now != NULL) {
4441
0
    free(fe->key);
4442
0
    free(fe);
4443
0
    free(fe_now->value);
4444
0
    fe = fe_now;
4445
0
  }
4446
4447
0
  fe->cb = cb;
4448
0
  fe->time = 0;
4449
4450
0
  fe->value = NULL;
4451
0
}
4452
4453
/* Quote shell special characters in string. */
4454
static char *
4455
format_quote_shell(const char *s)
4456
0
{
4457
0
  const char  *cp;
4458
0
  char    *out, *at;
4459
4460
0
  at = out = xmalloc(strlen(s) * 2 + 1);
4461
0
  for (cp = s; *cp != '\0'; cp++) {
4462
0
    if (strchr("|&;<>(){}$`\\\"'*?[# =%\n\t", *cp) != NULL)
4463
0
      *at++ = '\\';
4464
0
    *at++ = *cp;
4465
0
  }
4466
0
  *at = '\0';
4467
0
  return (out);
4468
0
}
4469
4470
/* Quote string with POSIX shell single quotes. */
4471
static char *
4472
format_quote_shell_single(const char *s)
4473
0
{
4474
0
  const char  *cp;
4475
0
  char    *out, *at;
4476
4477
0
  at = out = xmalloc(strlen(s) * 4 + 3);
4478
0
  *at++ = '\'';
4479
0
  for (cp = s; *cp != '\0'; cp++) {
4480
0
    if (*cp == '\'') {
4481
0
      *at++ = '\'';
4482
0
      *at++ = '\\';
4483
0
      *at++ = '\'';
4484
0
      *at++ = '\'';
4485
0
    } else
4486
0
      *at++ = *cp;
4487
0
  }
4488
0
  *at++ = '\'';
4489
0
  *at = '\0';
4490
0
  return (out);
4491
0
}
4492
4493
/* Quote #s in string. */
4494
static char *
4495
format_quote_style(const char *s)
4496
0
{
4497
0
  const char  *cp;
4498
0
  char    *out, *at;
4499
4500
0
  at = out = xmalloc(strlen(s) * 2 + 1);
4501
0
  for (cp = s; *cp != '\0'; cp++) {
4502
0
    if (*cp == '#')
4503
0
      *at++ = '#';
4504
0
    *at++ = *cp;
4505
0
  }
4506
0
  *at = '\0';
4507
0
  return (out);
4508
0
}
4509
4510
/* Make a prettier time. */
4511
char *
4512
format_pretty_time(time_t t, int seconds)
4513
0
{
4514
0
  struct tm now_tm, tm;
4515
0
  time_t    now, age;
4516
0
  char    s[9];
4517
4518
0
  time(&now);
4519
0
  if (now < t)
4520
0
    now = t;
4521
0
  age = now - t;
4522
4523
0
  localtime_r(&now, &now_tm);
4524
0
  localtime_r(&t, &tm);
4525
4526
  /* Last 24 hours. */
4527
0
  if (age < 24 * 3600) {
4528
0
    if (seconds)
4529
0
      strftime(s, sizeof s, "%H:%M:%S", &tm);
4530
0
    else
4531
0
      strftime(s, sizeof s, "%H:%M", &tm);
4532
0
    return (xstrdup(s));
4533
0
  }
4534
4535
  /* This month or last 28 days. */
4536
0
  if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) ||
4537
0
      age < 28 * 24 * 3600) {
4538
0
    strftime(s, sizeof s, "%a%d", &tm);
4539
0
    return (xstrdup(s));
4540
0
  }
4541
4542
  /* Last 12 months. */
4543
0
  if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) ||
4544
0
      (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) {
4545
0
    strftime(s, sizeof s, "%d%b", &tm);
4546
0
    return (xstrdup(s));
4547
0
  }
4548
4549
  /* Older than that. */
4550
0
  strftime(s, sizeof s, "%h%y", &tm);
4551
0
  return (xstrdup(s));
4552
0
}
4553
4554
/* Make a relative time. */
4555
static char *
4556
format_relative_time(time_t t)
4557
0
{
4558
0
  time_t  now, age;
4559
0
  u_int d, h, m, s;
4560
0
  char  out[32];
4561
4562
0
  time(&now);
4563
0
  if (t > now)
4564
0
    return (NULL);
4565
0
  if (t == now)
4566
0
    return (xstrdup("0s"));
4567
0
  age = now - t;
4568
4569
0
  d = age / 86400;
4570
0
  h = (age % 86400) / 3600;
4571
0
  m = (age % 3600) / 60;
4572
0
  s = age % 60;
4573
4574
0
  if (d != 0) {
4575
0
    if (h != 0)
4576
0
      xsnprintf(out, sizeof out, "%ud%uh", d, h);
4577
0
    else
4578
0
      xsnprintf(out, sizeof out, "%ud", d);
4579
0
  } else if (h != 0) {
4580
0
    if (m != 0)
4581
0
      xsnprintf(out, sizeof out, "%uh%um", h, m);
4582
0
    else
4583
0
      xsnprintf(out, sizeof out, "%uh", h);
4584
0
  } else if (m != 0) {
4585
0
    if (s != 0)
4586
0
      xsnprintf(out, sizeof out, "%um%us", m, s);
4587
0
    else
4588
0
      xsnprintf(out, sizeof out, "%um", m);
4589
0
  } else
4590
0
    xsnprintf(out, sizeof out, "%us", s);
4591
0
  return (xstrdup(out));
4592
0
}
4593
4594
/* Make a time difference in seconds. */
4595
static char *
4596
format_time_difference(time_t t)
4597
0
{
4598
0
  time_t   now = time(NULL);
4599
0
  char  *out;
4600
4601
0
  xasprintf(&out, "%ld", (long)now - (long)t);
4602
0
  return (out);
4603
0
}
4604
4605
/* Find a format entry. */
4606
static char *
4607
format_find(struct format_tree *ft, const char *key, uint64_t modifiers,
4608
    const char *time_format)
4609
0
{
4610
0
  const struct format_table_entry *fte;
4611
0
  void        *value;
4612
0
  struct format_entry   *fe, fe_find;
4613
0
  struct environ_entry    *envent;
4614
0
  struct options_entry    *o;
4615
0
  char        *found = NULL, *saved, s[512];
4616
0
  char        *array_key = NULL;
4617
0
  const char      *errstr;
4618
0
  time_t         t = 0;
4619
0
  struct tm      tm;
4620
4621
0
  o = options_parse_get(global_options, key, &array_key, 0);
4622
0
  if (o == NULL && ft->wp != NULL)
4623
0
    o = options_parse_get(ft->wp->options, key, &array_key, 0);
4624
0
  if (o == NULL && ft->w != NULL)
4625
0
    o = options_parse_get(ft->w->options, key, &array_key, 0);
4626
0
  if (o == NULL)
4627
0
    o = options_parse_get(global_w_options, key, &array_key, 0);
4628
0
  if (o == NULL && ft->s != NULL)
4629
0
    o = options_parse_get(ft->s->options, key, &array_key, 0);
4630
0
  if (o == NULL)
4631
0
    o = options_parse_get(global_s_options, key, &array_key, 0);
4632
0
  if (o != NULL) {
4633
0
    found = options_to_string(o, array_key, 1);
4634
0
    free(array_key);
4635
0
    goto found;
4636
0
  }
4637
4638
0
  fte = format_table_get(key);
4639
0
  if (fte != NULL) {
4640
0
    value = fte->cb(ft);
4641
0
    if (fte->type == FORMAT_TABLE_TIME && value != NULL)
4642
0
      t = ((struct timeval *)value)->tv_sec;
4643
0
    else
4644
0
      found = value;
4645
0
    goto found;
4646
0
  }
4647
0
  fe_find.key = (char *)key;
4648
0
  fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
4649
0
  if (fe != NULL) {
4650
0
    if (fe->time != 0) {
4651
0
      t = fe->time;
4652
0
      goto found;
4653
0
    }
4654
0
    if (fe->value == NULL && fe->cb != NULL) {
4655
0
      fe->value = fe->cb(ft);
4656
0
      if (fe->value == NULL)
4657
0
        fe->value = xstrdup("");
4658
0
    }
4659
0
    found = xstrdup(fe->value);
4660
0
    goto found;
4661
0
  }
4662
4663
0
  if (~modifiers & FORMAT_TIMESTRING) {
4664
0
    envent = NULL;
4665
0
    if (ft->s != NULL)
4666
0
      envent = environ_find(ft->s->environ, key);
4667
0
    if (envent == NULL)
4668
0
      envent = environ_find(global_environ, key);
4669
0
    if (envent != NULL && envent->value != NULL) {
4670
0
      found = xstrdup(envent->value);
4671
0
      goto found;
4672
0
    }
4673
0
  }
4674
4675
0
  return (NULL);
4676
4677
0
found:
4678
0
  if (modifiers & FORMAT_TIMESTRING) {
4679
0
    if (t == 0 && found != NULL) {
4680
0
      t = strtonum(found, 0, INT64_MAX, &errstr);
4681
0
      if (errstr != NULL)
4682
0
        t = 0;
4683
0
      free(found);
4684
0
    }
4685
0
    if (t == 0)
4686
0
      return (NULL);
4687
0
    if (modifiers & FORMAT_RELATIVE)
4688
0
      found = format_relative_time(t);
4689
0
    else if (modifiers & FORMAT_DIFFERENCE)
4690
0
      found = format_time_difference(t);
4691
0
    else if (modifiers & FORMAT_PRETTY)
4692
0
      found = format_pretty_time(t, 0);
4693
0
    else {
4694
0
      if (time_format != NULL) {
4695
0
        localtime_r(&t, &tm);
4696
0
        format_strftime(s, sizeof s, time_format, &tm);
4697
0
      } else {
4698
0
        ctime_r(&t, s);
4699
0
        s[strcspn(s, "\n")] = '\0';
4700
0
      }
4701
0
      found = xstrdup(s);
4702
0
    }
4703
0
    return (found);
4704
0
  }
4705
4706
0
  if (t != 0)
4707
0
    xasprintf(&found, "%lld", (long long)t);
4708
0
  else if (found == NULL)
4709
0
    return (NULL);
4710
0
  if (modifiers & FORMAT_BASENAME) {
4711
0
    saved = found;
4712
0
    found = xstrdup(basename(saved));
4713
0
    free(saved);
4714
0
  }
4715
0
  if (modifiers & FORMAT_DIRNAME) {
4716
0
    saved = found;
4717
0
    found = xstrdup(dirname(saved));
4718
0
    free(saved);
4719
0
  }
4720
0
  if (modifiers & FORMAT_QUOTE_SHELL) {
4721
0
    saved = found;
4722
0
    found = format_quote_shell(saved);
4723
0
    free(saved);
4724
0
  }
4725
0
  if (modifiers & FORMAT_QUOTE_SHELL_SQ) {
4726
0
    saved = found;
4727
0
    found = format_quote_shell_single(saved);
4728
0
    free(saved);
4729
0
  }
4730
0
  if (modifiers & FORMAT_QUOTE_STYLE) {
4731
0
    saved = found;
4732
0
    found = format_quote_style(saved);
4733
0
    free(saved);
4734
0
  }
4735
0
  if (modifiers & FORMAT_QUOTE_ARGUMENTS) {
4736
0
    saved = found;
4737
0
    found = args_escape(saved);
4738
0
    free(saved);
4739
0
  }
4740
0
  return (found);
4741
0
}
4742
4743
/* Check if format has not taken too long. */
4744
static int
4745
format_check_time(struct format_expand_state *es, u_int *check)
4746
0
{
4747
0
  uint64_t t;
4748
4749
0
  if (check != NULL && ++*check % FORMAT_TIME_LOOP_CHECK != 0)
4750
0
    return (1);
4751
4752
0
  t = get_timer();
4753
0
  if (t - es->start_time < FORMAT_TIME_LIMIT)
4754
0
    return (1);
4755
0
  t -= es->start_time;
4756
4757
0
  format_log(es, "reached time limit (%llu)", (unsigned long long)t);
4758
0
  return (0);
4759
0
}
4760
4761
/* Unescape escaped characters. */
4762
static char *
4763
format_unescape(struct format_expand_state *es, const char *s, size_t n)
4764
0
{
4765
0
  const char  *end = s + n;
4766
0
  char    *out, *cp;
4767
0
  int    brackets = 0;
4768
0
  u_int    check = 0;
4769
4770
0
  cp = out = xmalloc(n + 1);
4771
0
  for (; s != end; s++) {
4772
0
    if (!format_check_time(es, &check)) {
4773
0
      free(out);
4774
0
      return (xstrdup(""));
4775
0
    }
4776
0
    if (*s == '#' && s + 1 != end && s[1] == '{')
4777
0
      brackets++;
4778
0
    if (brackets == 0 &&
4779
0
        *s == '#' &&
4780
0
        s + 1 != end &&
4781
0
        strchr(",#{}:", s[1]) != NULL) {
4782
0
      *cp++ = *++s;
4783
0
      continue;
4784
0
    }
4785
0
    if (*s == '}')
4786
0
      brackets--;
4787
0
    *cp++ = *s;
4788
0
  }
4789
0
  *cp = '\0';
4790
0
  return (out);
4791
0
}
4792
4793
/* Remove escaped characters. */
4794
static char *
4795
format_strip(struct format_expand_state *es, const char *s)
4796
0
{
4797
0
  char  *out, *cp;
4798
0
  int  brackets = 0;
4799
0
  u_int  check = 0;
4800
4801
0
  cp = out = xmalloc(strlen(s) + 1);
4802
0
  for (; *s != '\0'; s++) {
4803
0
    if (!format_check_time(es, &check)) {
4804
0
      free(out);
4805
0
      return (xstrdup(""));
4806
0
    }
4807
0
    if (*s == '#' && s[1] == '{')
4808
0
      brackets++;
4809
0
    if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
4810
0
      if (brackets != 0)
4811
0
        *cp++ = *s;
4812
0
      continue;
4813
0
    }
4814
0
    if (*s == '}')
4815
0
      brackets--;
4816
0
    *cp++ = *s;
4817
0
  }
4818
0
  *cp = '\0';
4819
0
  return (out);
4820
0
}
4821
4822
/* Skip until end. */
4823
static const char *
4824
format_skip1(struct format_expand_state *es, const char *s, const char *end)
4825
0
{
4826
0
  int brackets = 0;
4827
0
  u_int check = 0;
4828
4829
0
  for (; *s != '\0'; s++) {
4830
0
    if (es != NULL && !format_check_time(es, &check))
4831
0
      return (NULL);
4832
0
    if (*s == '#' && s[1] == '{')
4833
0
      brackets++;
4834
0
    if (*s == '#' &&
4835
0
        s[1] != '\0' &&
4836
0
        strchr(",#{}:", s[1]) != NULL) {
4837
0
      s++;
4838
0
      continue;
4839
0
    }
4840
0
    if (*s == '}')
4841
0
      brackets--;
4842
0
    if (strchr(end, *s) != NULL && brackets == 0)
4843
0
      break;
4844
0
  }
4845
0
  if (*s == '\0')
4846
0
    return (NULL);
4847
0
  return (s);
4848
0
}
4849
4850
/* Skip until end. */
4851
const char *
4852
format_skip(const char *s, const char *end)
4853
0
{
4854
0
    return (format_skip1(NULL, s, end));
4855
0
}
4856
4857
/* Return left and right alternatives separated by commas. */
4858
static int
4859
format_choose(struct format_expand_state *es, const char *s, char **left,
4860
    char **right, int expand)
4861
0
{
4862
0
  const char  *cp;
4863
0
  char    *left0, *right0;
4864
4865
0
  cp = format_skip1(es, s, ",");
4866
0
  if (cp == NULL)
4867
0
    return (-1);
4868
0
  left0 = xstrndup(s, cp - s);
4869
0
  right0 = xstrdup(cp + 1);
4870
4871
0
  if (expand) {
4872
0
    *left = format_expand1(es, left0);
4873
0
    free(left0);
4874
0
    *right = format_expand1(es, right0);
4875
0
    free(right0);
4876
0
  } else {
4877
0
    *left = left0;
4878
0
    *right = right0;
4879
0
  }
4880
0
  return (0);
4881
0
}
4882
4883
/* Is this true? */
4884
int
4885
format_true(const char *s)
4886
0
{
4887
0
  if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
4888
0
    return (1);
4889
0
  return (0);
4890
0
}
4891
4892
/* Check if modifier end. */
4893
static int
4894
format_is_end(char c)
4895
0
{
4896
0
  return (c == ';' || c == ':');
4897
0
}
4898
4899
/* Add to modifier list. */
4900
static void
4901
format_add_modifier(struct format_modifier **list, u_int *count,
4902
    const char *c, size_t n, char **argv, int argc)
4903
0
{
4904
0
  struct format_modifier *fm;
4905
4906
0
  *list = xreallocarray(*list, (*count) + 1, sizeof **list);
4907
0
  fm = &(*list)[(*count)++];
4908
4909
0
  memcpy(fm->modifier, c, n);
4910
0
  fm->modifier[n] = '\0';
4911
0
  fm->size = n;
4912
4913
0
  fm->argv = argv;
4914
0
  fm->argc = argc;
4915
0
}
4916
4917
/* Free modifier list. */
4918
static void
4919
format_free_modifiers(struct format_modifier *list, u_int count)
4920
0
{
4921
0
  u_int i;
4922
4923
0
  for (i = 0; i < count; i++)
4924
0
    cmd_free_argv(list[i].argc, list[i].argv);
4925
0
  free(list);
4926
0
}
4927
4928
/* Build modifier list. */
4929
static struct format_modifier *
4930
format_build_modifiers(struct format_expand_state *es, const char **s,
4931
    u_int *count)
4932
0
{
4933
0
  const char    *cp = *s, *end;
4934
0
  struct format_modifier  *list = NULL;
4935
0
  char       c, last[] = "X;:", **argv, *value;
4936
0
  int      argc;
4937
4938
  /*
4939
   * Modifiers are a ; separated list of the forms:
4940
   *  l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,A,<,>
4941
   *  =a
4942
   *  =/a
4943
   *  =/a/
4944
   *  s/a/b/
4945
   *  s/a/b
4946
   *  ||,&&,!=,==,<=,>=
4947
   */
4948
4949
0
  *count = 0;
4950
4951
0
  while (*cp != '\0' && *cp != ':') {
4952
    /* Skip any separator character. */
4953
0
    if (*cp == ';')
4954
0
      cp++;
4955
0
    if (*cp == '\0')
4956
0
      break;
4957
4958
    /* Check single character modifiers with no arguments. */
4959
0
    if (strchr("labdnwETSWPOVL!<>A", cp[0]) != NULL &&
4960
0
        format_is_end(cp[1])) {
4961
0
      format_add_modifier(&list, count, cp, 1, NULL, 0);
4962
0
      cp++;
4963
0
      continue;
4964
0
    }
4965
4966
    /* Then try double character with no arguments. */
4967
0
    if ((memcmp("||", cp, 2) == 0 ||
4968
0
        memcmp("&&", cp, 2) == 0 ||
4969
0
        memcmp("!!", cp, 2) == 0 ||
4970
0
        memcmp("!=", cp, 2) == 0 ||
4971
0
        memcmp("==", cp, 2) == 0 ||
4972
0
        memcmp("<=", cp, 2) == 0 ||
4973
0
        memcmp(">=", cp, 2) == 0) &&
4974
0
        format_is_end(cp[2])) {
4975
0
      format_add_modifier(&list, count, cp, 2, NULL, 0);
4976
0
      cp += 2;
4977
0
      continue;
4978
0
    }
4979
4980
    /* Now try single character with arguments. */
4981
0
    if (strchr("ImCLNPSOVst=pReqWcA", cp[0]) == NULL)
4982
0
      break;
4983
0
    c = cp[0];
4984
4985
    /* No arguments provided. */
4986
0
    if (format_is_end(cp[1])) {
4987
0
      format_add_modifier(&list, count, cp, 1, NULL, 0);
4988
0
      cp++;
4989
0
      continue;
4990
0
    }
4991
0
    argv = NULL;
4992
0
    argc = 0;
4993
4994
    /* Single argument with no wrapper character. */
4995
0
    if (!ispunct((u_char)cp[1]) || cp[1] == '-') {
4996
0
      end = format_skip1(es, cp + 1, ":;");
4997
0
      if (end == NULL)
4998
0
        break;
4999
5000
0
      argv = xcalloc(1, sizeof *argv);
5001
0
      value = format_unescape(es, cp + 1, end - (cp + 1));
5002
0
      argv[0] = format_expand1(es, value);
5003
0
      free(value);
5004
0
      argc = 1;
5005
5006
0
      format_add_modifier(&list, count, &c, 1, argv, argc);
5007
0
      cp = end;
5008
0
      continue;
5009
0
    }
5010
5011
    /* Multiple arguments with a wrapper character. */
5012
0
    last[0] = cp[1];
5013
0
    cp++;
5014
0
    do {
5015
0
      if (cp[0] == last[0] && format_is_end(cp[1])) {
5016
0
        cp++;
5017
0
        break;
5018
0
      }
5019
0
      end = format_skip1(es, cp + 1, last);
5020
0
      if (end == NULL)
5021
0
        break;
5022
0
      cp++;
5023
5024
0
      argv = xreallocarray(argv, argc + 1, sizeof *argv);
5025
0
      value = format_unescape(es, cp, end - cp);
5026
0
      argv[argc++] = format_expand1(es, value);
5027
0
      free(value);
5028
5029
0
      cp = end;
5030
0
    } while (!format_is_end(cp[0]));
5031
0
    format_add_modifier(&list, count, &c, 1, argv, argc);
5032
0
  }
5033
0
  if (*cp != ':') {
5034
0
    format_free_modifiers(list, *count);
5035
0
    *count = 0;
5036
0
    return (NULL);
5037
0
  }
5038
0
  *s = cp + 1;
5039
0
  return (list);
5040
0
}
5041
5042
/* Match using the fuzzy matcher. */
5043
static char *
5044
format_match_fuzzy(const char *pattern, const char *text, int positions)
5045
0
{
5046
0
  struct evbuffer *buffer;
5047
0
  bitstr_t  *bs;
5048
0
  char    *value;
5049
0
  size_t     size;
5050
0
  u_int    i, width;
5051
5052
0
  width = format_width(text);
5053
0
  if (width == 0)
5054
0
    width = 1;
5055
0
  bs = fuzzy_match(pattern, text, width, NULL);
5056
0
  if (bs == NULL)
5057
0
    return (xstrdup(positions ? "" : "0"));
5058
5059
0
  if (!positions) {
5060
0
    free(bs);
5061
0
    return (xstrdup("1"));
5062
0
  }
5063
5064
0
  buffer = evbuffer_new();
5065
0
  if (buffer == NULL)
5066
0
    fatalx("out of memory");
5067
0
  for (i = 0; i < width; i++) {
5068
0
    if (!bit_test(bs, i))
5069
0
      continue;
5070
0
    if (EVBUFFER_LENGTH(buffer) != 0)
5071
0
      evbuffer_add(buffer, ",", 1);
5072
0
    evbuffer_add_printf(buffer, "%u", i);
5073
0
  }
5074
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5075
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5076
0
  else
5077
0
    value = xstrdup("");
5078
0
  evbuffer_free(buffer);
5079
0
  free(bs);
5080
0
  return (value);
5081
0
}
5082
5083
/* Match against an fnmatch(3) pattern or regular expression. */
5084
static char *
5085
format_match(struct format_modifier *fm, const char *pattern, const char *text)
5086
0
{
5087
0
  const char  *s = "";
5088
0
  regex_t    r;
5089
0
  int    flags = 0;
5090
5091
0
  if (fm->argc >= 1)
5092
0
    s = fm->argv[0];
5093
0
  if (strchr(s, 'p') != NULL)
5094
0
    return (format_match_fuzzy(pattern, text, 1));
5095
0
  if (strchr(s, 'z') != NULL)
5096
0
    return (format_match_fuzzy(pattern, text, 0));
5097
0
  if (strchr(s, 'r') == NULL) {
5098
0
    if (strchr(s, 'i') != NULL)
5099
0
      flags |= FNM_CASEFOLD;
5100
0
    if (fnmatch(pattern, text, flags) != 0)
5101
0
      return (xstrdup("0"));
5102
0
  } else {
5103
0
    flags = REG_EXTENDED|REG_NOSUB;
5104
0
    if (strchr(s, 'i') != NULL)
5105
0
      flags |= REG_ICASE;
5106
0
    if (regcomp(&r, pattern, flags) != 0)
5107
0
      return (xstrdup("0"));
5108
0
    if (regexec(&r, text, 0, NULL, 0) != 0) {
5109
0
      regfree(&r);
5110
0
      return (xstrdup("0"));
5111
0
    }
5112
0
    regfree(&r);
5113
0
  }
5114
0
  return (xstrdup("1"));
5115
0
}
5116
5117
/* Perform substitution in string. */
5118
static char *
5119
format_sub(struct format_modifier *fm, const char *text, const char *pattern,
5120
    const char *with)
5121
0
{
5122
0
  char  *value;
5123
0
  int  flags = REG_EXTENDED;
5124
5125
0
  if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
5126
0
    flags |= REG_ICASE;
5127
0
  value = regsub(pattern, with, text, flags);
5128
0
  if (value == NULL)
5129
0
    return (xstrdup(text));
5130
0
  return (value);
5131
0
}
5132
5133
/* Search inside pane. */
5134
static char *
5135
format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
5136
0
{
5137
0
  int  ignore = 0, regex = 0;
5138
0
  char  *value;
5139
5140
0
  if (fm->argc >= 1) {
5141
0
    if (strchr(fm->argv[0], 'i') != NULL)
5142
0
      ignore = 1;
5143
0
    if (strchr(fm->argv[0], 'r') != NULL)
5144
0
      regex = 1;
5145
0
  }
5146
0
  xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
5147
0
  return (value);
5148
0
}
5149
5150
/* Handle unary boolean operators, "!" and "!!". */
5151
static char *
5152
format_bool_op_1(struct format_expand_state *es, const char *fmt, int not)
5153
0
{
5154
0
  int  result;
5155
0
  char  *expanded;
5156
5157
0
  expanded = format_expand1(es, fmt);
5158
0
  result = format_true(expanded);
5159
0
  if (not)
5160
0
    result = !result;
5161
0
  free(expanded);
5162
5163
0
  return (xstrdup(result ? "1" : "0"));
5164
0
}
5165
5166
/* Handle n-ary boolean operators, "&&" and "||". */
5167
static char *
5168
format_bool_op_n(struct format_expand_state *es, const char *fmt, int and)
5169
0
{
5170
0
  int    result;
5171
0
  const char  *cp1, *cp2;
5172
0
  char    *raw, *expanded;
5173
5174
0
  result = and ? 1 : 0;
5175
0
  cp1 = fmt;
5176
5177
0
  while (and ? result : !result) {
5178
0
    cp2 = format_skip1(es, cp1, ",");
5179
5180
0
    if (cp2 == NULL)
5181
0
      raw = xstrdup(cp1);
5182
0
    else
5183
0
      raw = xstrndup(cp1, cp2 - cp1);
5184
0
    expanded = format_expand1(es, raw);
5185
0
    free(raw);
5186
0
    format_log(es, "operator %s has operand: %s",
5187
0
        and ? "&&" : "||", expanded);
5188
5189
0
    if (and)
5190
0
      result = result && format_true(expanded);
5191
0
    else
5192
0
      result = result || format_true(expanded);
5193
0
    free(expanded);
5194
5195
0
    if (cp2 == NULL)
5196
0
      break;
5197
0
    else
5198
0
      cp1 = cp2 + 1;
5199
0
  }
5200
5201
0
  return (xstrdup(result ? "1" : "0"));
5202
0
}
5203
5204
/* Does session name exist? */
5205
static char *
5206
format_session_name(struct format_expand_state *es, const char *fmt)
5207
0
{
5208
0
  char    *name;
5209
0
  struct session  *s;
5210
5211
0
  name = format_expand1(es, fmt);
5212
0
  RB_FOREACH(s, sessions, &sessions) {
5213
0
    if (strcmp(s->name, name) == 0) {
5214
0
      free(name);
5215
0
      return (xstrdup("1"));
5216
0
    }
5217
0
  }
5218
0
  free(name);
5219
0
  return (xstrdup("0"));
5220
0
}
5221
5222
/* Loop over sessions. */
5223
static char *
5224
format_loop_sessions(struct format_expand_state *es, const char *fmt)
5225
0
{
5226
0
  struct sort_criteria     *sc = &sort_crit;
5227
0
  struct format_tree     *ft = es->ft;
5228
0
  struct client      *c = ft->client;
5229
0
  struct cmdq_item     *item = ft->item;
5230
0
  struct format_tree     *nft;
5231
0
  struct format_expand_state    next;
5232
0
  char         *all, *active, *use, *expanded, *value;
5233
0
  struct evbuffer      *buffer;
5234
0
  size_t          size;
5235
0
  struct session       *s, **l;
5236
0
  int         i, n;
5237
5238
0
  if (format_choose(es, fmt, &all, &active, 0) != 0) {
5239
0
    all = xstrdup(fmt);
5240
0
    active = NULL;
5241
0
  }
5242
5243
0
  buffer = evbuffer_new();
5244
0
  if (buffer == NULL)
5245
0
    fatalx("out of memory");
5246
5247
0
  l = sort_get_sessions(&n, sc);
5248
0
  for (i = 0; i < n; i++) {
5249
0
    s = l[i];
5250
0
    format_log(es, "session loop: $%u", s->id);
5251
0
    if (active != NULL &&
5252
0
        ft->c != NULL &&
5253
0
        ft->c->session != NULL &&
5254
0
        s->id == ft->c->session->id)
5255
0
      use = active;
5256
0
    else
5257
0
      use = all;
5258
0
    nft = format_create(c, item, FORMAT_NONE, ft->flags);
5259
5260
0
    format_add(nft, "loop_index", "%d", i);
5261
0
    format_add(nft, "loop_last_flag", "%d", i == n - 1);
5262
5263
0
    format_defaults(nft, ft->c, s, NULL, NULL);
5264
0
    format_copy_state(&next, es, 0);
5265
0
    next.ft = nft;
5266
5267
0
    expanded = format_expand1(&next, use);
5268
0
    format_free(next.ft);
5269
5270
0
    evbuffer_add(buffer, expanded, strlen(expanded));
5271
0
    free(expanded);
5272
0
  }
5273
5274
0
  free(active);
5275
0
  free(all);
5276
5277
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5278
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5279
0
  else
5280
0
    value = xstrdup("");
5281
0
  evbuffer_free(buffer);
5282
0
  return (value);
5283
0
}
5284
5285
/* Does window name exist? */
5286
static char *
5287
format_window_name(struct format_expand_state *es, const char *fmt)
5288
0
{
5289
0
  struct format_tree  *ft = es->ft;
5290
0
  char      *name;
5291
0
  struct winlink    *wl;
5292
5293
0
  if (ft->s == NULL) {
5294
0
    format_log(es, "window name but no session");
5295
0
    return (NULL);
5296
0
  }
5297
5298
0
  name = format_expand1(es, fmt);
5299
0
  RB_FOREACH(wl, winlinks, &ft->s->windows) {
5300
0
    if (strcmp(wl->window->name, name) == 0) {
5301
0
      free(name);
5302
0
      return (xstrdup("1"));
5303
0
    }
5304
0
  }
5305
0
  free(name);
5306
0
  return (xstrdup("0"));
5307
0
}
5308
5309
/* Add neighbour window variables to the format tree. */
5310
static void
5311
format_add_window_neighbour(struct format_tree *nft, struct winlink *wl,
5312
    struct session *s, const char *prefix)
5313
0
{
5314
0
  struct options_entry  *o;
5315
0
  const char    *oname;
5316
0
  char      *key, *prefixed, *oval;
5317
5318
0
  xasprintf(&key, "%s_window_index", prefix);
5319
0
  format_add(nft, key, "%u", wl->idx);
5320
0
  free(key);
5321
5322
0
  xasprintf(&key, "%s_window_active", prefix);
5323
0
  format_add(nft, key, "%d", wl == s->curw);
5324
0
  free(key);
5325
5326
0
  o = options_first(wl->window->options);
5327
0
  while (o != NULL) {
5328
0
    oname = options_name(o);
5329
0
    if (*oname == '@') {
5330
0
      xasprintf(&prefixed, "%s_%s", prefix, oname);
5331
0
      oval = options_to_string(o, NULL, 1);
5332
0
      format_add(nft, prefixed, "%s", oval);
5333
0
      free(oval);
5334
0
      free(prefixed);
5335
0
    }
5336
0
    o = options_next(o);
5337
0
  }
5338
0
}
5339
5340
/* Loop over windows. */
5341
static char *
5342
format_loop_windows(struct format_expand_state *es, const char *fmt)
5343
0
{
5344
0
  struct sort_criteria    *sc = &sort_crit;
5345
0
  struct format_tree    *ft = es->ft;
5346
0
  struct client     *c = ft->client;
5347
0
  struct session      *s = ft->s;
5348
0
  struct cmdq_item    *item = ft->item;
5349
0
  struct format_tree    *nft;
5350
0
  struct format_expand_state   next;
5351
0
  char        *all, *active, *use, *expanded, *value;
5352
0
  struct evbuffer     *buffer;
5353
0
  size_t         size;
5354
0
  struct winlink      *wl, **l;
5355
0
  struct window     *w;
5356
0
  int        i, n;
5357
5358
0
  if (s == NULL) {
5359
0
    format_log(es, "window loop but no session");
5360
0
    return (NULL);
5361
0
  }
5362
5363
0
  if (format_choose(es, fmt, &all, &active, 0) != 0) {
5364
0
    all = xstrdup(fmt);
5365
0
    active = NULL;
5366
0
  }
5367
5368
0
  buffer = evbuffer_new();
5369
0
  if (buffer == NULL)
5370
0
    fatalx("out of memory");
5371
5372
0
  l = sort_get_winlinks_session(s, &n, sc);
5373
0
  for (i = 0; i < n; i++) {
5374
0
    wl = l[i];
5375
0
    w = wl->window;
5376
0
    format_log(es, "window loop: %u @%u", wl->idx, w->id);
5377
0
    if (active != NULL && wl == s->curw)
5378
0
      use = active;
5379
0
    else
5380
0
      use = all;
5381
0
    nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
5382
5383
0
    format_add(nft, "loop_index", "%d", i);
5384
0
    format_add(nft, "loop_last_flag", "%d", i == n - 1);
5385
5386
    /* Add neighbour window data to the format tree. */
5387
0
    if (i > 0 && l[i - 1] == s->curw)
5388
0
      format_add(nft, "window_after_active", "1");
5389
0
    else
5390
0
      format_add(nft, "window_after_active", "0");
5391
0
    if (i + 1 < n && l[i + 1] == s->curw)
5392
0
      format_add(nft, "window_before_active", "1");
5393
0
    else
5394
0
      format_add(nft, "window_before_active", "0");
5395
0
    if (i + 1 < n)
5396
0
      format_add_window_neighbour(nft, l[i + 1], s, "next");
5397
0
    if (i > 0)
5398
0
      format_add_window_neighbour(nft, l[i - 1], s, "prev");
5399
5400
0
    format_defaults(nft, ft->c, s, wl, NULL);
5401
0
    format_copy_state(&next, es, 0);
5402
0
    next.ft = nft;
5403
5404
0
    expanded = format_expand1(&next, use);
5405
0
    format_free(nft);
5406
5407
0
    evbuffer_add(buffer, expanded, strlen(expanded));
5408
0
    free(expanded);
5409
0
  }
5410
5411
0
  free(active);
5412
0
  free(all);
5413
5414
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5415
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5416
0
  else
5417
0
    value = xstrdup("");
5418
0
  evbuffer_free(buffer);
5419
0
  return (value);
5420
0
}
5421
5422
/* Loop over panes. */
5423
static char *
5424
format_loop_panes(struct format_expand_state *es, const char *fmt)
5425
0
{
5426
0
  struct sort_criteria    *sc = &sort_crit;
5427
0
  struct format_tree    *ft = es->ft;
5428
0
  struct client     *c = ft->client;
5429
0
  struct cmdq_item    *item = ft->item;
5430
0
  struct format_tree    *nft;
5431
0
  struct format_expand_state   next;
5432
0
  char        *all, *active, *use, *expanded, *value;
5433
0
  struct evbuffer     *buffer;
5434
0
  size_t         size;
5435
0
  struct window_pane    *wp, **l;
5436
0
  int         i, n;
5437
5438
0
  if (ft->w == NULL) {
5439
0
    format_log(es, "pane loop but no window");
5440
0
    return (NULL);
5441
0
  }
5442
5443
0
  if (format_choose(es, fmt, &all, &active, 0) != 0) {
5444
0
    all = xstrdup(fmt);
5445
0
    active = NULL;
5446
0
  }
5447
5448
0
  buffer = evbuffer_new();
5449
0
  if (buffer == NULL)
5450
0
    fatalx("out of memory");
5451
5452
0
  l = sort_get_panes_window(ft->w, &n, sc);
5453
0
  for (i = 0; i < n; i++) {
5454
0
    wp = l[i];
5455
0
    format_log(es, "pane loop: %%%u", wp->id);
5456
0
    if (active != NULL && wp == ft->w->active)
5457
0
      use = active;
5458
0
    else
5459
0
      use = all;
5460
0
    nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
5461
5462
0
    format_add(nft, "loop_index", "%d", i);
5463
0
    format_add(nft, "loop_last_flag", "%d", i == n - 1);
5464
5465
0
    format_defaults(nft, ft->c, ft->s, ft->wl, wp);
5466
0
    format_copy_state(&next, es, 0);
5467
0
    next.ft = nft;
5468
5469
0
    expanded = format_expand1(&next, use);
5470
0
    format_free(nft);
5471
5472
0
    evbuffer_add(buffer, expanded, strlen(expanded));
5473
0
    free(expanded);
5474
0
  }
5475
5476
0
  free(active);
5477
0
  free(all);
5478
5479
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5480
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5481
0
  else
5482
0
    value = xstrdup("");
5483
0
  evbuffer_free(buffer);
5484
0
  return (value);
5485
0
}
5486
5487
/* Add an option to an options loop. */
5488
static void
5489
format_loop_add_option(struct format_expand_state *es, const char *fmt,
5490
    struct evbuffer *buffer, struct options_entry *o, u_int n, u_int i)
5491
0
{
5492
0
  struct format_tree      *ft = es->ft, *nft;
5493
0
  struct format_expand_state     next;
5494
0
  const struct options_table_entry  *oe = options_table_entry(o);
5495
0
  const char        *name = options_name(o);
5496
0
  char          *expanded, *s;
5497
0
  int          is_array = options_is_array(o);
5498
5499
0
  format_log(es, "option loop: %s", name);
5500
0
  nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
5501
5502
0
  format_add(nft, "option_name", "%s", name);
5503
0
  s = options_to_string(o, NULL, 0);
5504
0
  format_add(nft, "option_value", "%s", s);
5505
0
  free(s);
5506
5507
0
  format_add(nft, "option_is_array", "%d", is_array);
5508
0
  format_add(nft, "option_array_key", "%s", "");
5509
0
  format_add(nft, "option_array_index", "%s", "");
5510
0
  format_add(nft, "option_array_first", "%d", is_array);
5511
0
  format_add(nft, "option_array_last", "%d", is_array);
5512
0
  format_add(nft, "option_array_count", "%u", n);
5513
5514
0
  if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
5515
0
    format_add(nft, "option_is_hook", "1");
5516
0
  else
5517
0
    format_add(nft, "option_is_hook", "0");
5518
0
  format_add(nft, "option_is_user", "%d", oe == NULL);
5519
5520
0
  if (options_next(o) == NULL)
5521
0
    format_add(nft, "loop_last_flag", "1");
5522
0
  else
5523
0
    format_add(nft, "loop_last_flag", "0");
5524
0
  format_add(nft, "loop_index", "%u", i);
5525
5526
0
  format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
5527
0
  format_copy_state(&next, es, 0);
5528
0
  next.ft = nft;
5529
5530
0
  expanded = format_expand1(&next, fmt);
5531
0
  format_free(nft);
5532
0
  evbuffer_add(buffer, expanded, strlen(expanded));
5533
0
  free(expanded);
5534
0
}
5535
5536
/* Add an array option item to an options loop. */
5537
static void
5538
format_loop_add_array_item(struct format_expand_state *es, const char *fmt,
5539
    struct evbuffer *buffer, struct options_entry *o,
5540
    struct options_array_item *a, int n, u_int i)
5541
0
{
5542
0
  struct format_tree      *ft = es->ft, *nft;
5543
0
  struct format_expand_state     next;
5544
0
  const struct options_table_entry  *oe = options_table_entry(o);
5545
0
  const char        *name = options_name(o);
5546
0
  const char        *array_key;
5547
0
  char          *expanded, *s;
5548
5549
0
  array_key = options_array_item_key(a);
5550
0
  format_log(es, "option loop: %s[%s]", name, array_key);
5551
0
  nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
5552
5553
0
  format_add(nft, "option_name", "%s", name);
5554
0
  s = options_to_string(o, array_key, 0);
5555
0
  format_add(nft, "option_value", "%s", s);
5556
0
  free(s);
5557
5558
0
  format_add(nft, "option_is_array", "1");
5559
0
  format_add(nft, "option_array_key", "%s", array_key);
5560
0
  format_add(nft, "option_array_index", "%s", array_key);
5561
0
  if (a == options_array_first(o))
5562
0
    format_add(nft, "option_array_first", "1");
5563
0
  else
5564
0
    format_add(nft, "option_array_first", "0");
5565
0
  if (options_array_next(a) == NULL)
5566
0
    format_add(nft, "option_array_last", "1");
5567
0
  else
5568
0
    format_add(nft, "option_array_last", "0");
5569
0
  format_add(nft, "option_array_count", "%u", n);
5570
5571
0
  if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
5572
0
    format_add(nft, "option_is_hook", "1");
5573
0
  else
5574
0
    format_add(nft, "option_is_hook", "0");
5575
0
  format_add(nft, "option_is_user", "%d", oe == NULL);
5576
5577
0
  if (options_array_next(a) == NULL && options_next(o) == NULL)
5578
0
    format_add(nft, "loop_last_flag", "1");
5579
0
  else
5580
0
    format_add(nft, "loop_last_flag", "0");
5581
0
  format_add(nft, "loop_index", "%u", i);
5582
5583
0
  format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
5584
0
  format_copy_state(&next, es, 0);
5585
0
  next.ft = nft;
5586
5587
0
  expanded = format_expand1(&next, fmt);
5588
0
  format_free(nft);
5589
0
  evbuffer_add(buffer, expanded, strlen(expanded));
5590
0
  free(expanded);
5591
0
}
5592
5593
/* Loop over options. */
5594
static char *
5595
format_loop_options(struct format_expand_state *es, const char *fmt,
5596
    const char *flags)
5597
0
{
5598
0
  struct format_tree    *ft = es->ft;
5599
0
  struct options      *oo = NULL;
5600
0
  struct options_entry    *o;
5601
0
  struct options_array_item *a;
5602
0
  char        *value;
5603
0
  struct evbuffer     *buffer;
5604
0
  size_t         size;
5605
0
  u_int        i = 0, n;
5606
0
  int        global = 0;
5607
5608
0
  if (flags == NULL || *flags == '\0')
5609
0
    flags = "s";
5610
0
  if (strchr(flags, 'v') != NULL)
5611
0
    oo = global_options;
5612
0
  else {
5613
0
    if (strchr(flags, 'g') != NULL)
5614
0
      global = 1;
5615
0
    if (strchr(flags, 'w') != NULL) {
5616
0
      if (global)
5617
0
        oo = global_w_options;
5618
0
      else if (ft->w != NULL)
5619
0
        oo = ft->w->options;
5620
0
    } else if (strchr(flags, 's') != NULL) {
5621
0
      if (global)
5622
0
        oo = global_s_options;
5623
0
      else if (ft->s != NULL)
5624
0
        oo = ft->s->options;
5625
0
    } else if (strchr(flags, 'p') != NULL) {
5626
0
      if (global)
5627
0
        /* invalid */;
5628
0
      else if (ft->wp != NULL)
5629
0
        oo = ft->wp->options;
5630
0
    } else if (global)
5631
0
      oo = global_s_options;
5632
0
  }
5633
0
  if (oo == NULL)
5634
0
    return (xstrdup(""));
5635
5636
0
  buffer = evbuffer_new();
5637
0
  if (buffer == NULL)
5638
0
    fatalx("out of memory");
5639
5640
0
  o = options_first(oo);
5641
0
  while (o != NULL) {
5642
5643
0
    n = 0;
5644
0
    if (options_is_array(o)) {
5645
0
      a = options_array_first(o);
5646
0
      while (a != NULL) {
5647
0
        n++;
5648
0
        a = options_array_next(a);
5649
0
      }
5650
0
    }
5651
5652
0
    if (!options_is_array(o) || n == 0) {
5653
0
      format_loop_add_option(es, fmt, buffer, o, n, i);
5654
0
      i++;
5655
0
      o = options_next(o);
5656
0
      continue;
5657
0
    }
5658
5659
0
    a = options_array_first(o);
5660
0
    while (a != NULL) {
5661
0
      format_loop_add_array_item(es, fmt, buffer, o, a, n, i);
5662
0
      i++;
5663
0
      a = options_array_next(a);
5664
0
    }
5665
0
    o = options_next(o);
5666
0
  }
5667
5668
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5669
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5670
0
  else
5671
0
    value = xstrdup("");
5672
0
  evbuffer_free(buffer);
5673
0
  return (value);
5674
0
}
5675
5676
/* Loop over an environment. */
5677
static char *
5678
format_loop_environ(struct format_expand_state *es, const char *fmt,
5679
    const char *flags)
5680
0
{
5681
0
  struct format_tree    *ft = es->ft, *nft;
5682
0
  struct client     *c = ft->client;
5683
0
  struct cmdq_item    *item = ft->item;
5684
0
  struct format_expand_state   next;
5685
0
  struct environ      *env = NULL;
5686
0
  struct environ_entry    *envent;
5687
0
  char        *expanded, *value;
5688
0
  struct evbuffer     *buffer;
5689
0
  size_t         size;
5690
0
  u_int        i = 0;
5691
5692
0
  if (flags == NULL || *flags == '\0' || strcmp(flags, "s") == 0) {
5693
0
    if (ft->s != NULL)
5694
0
      env = ft->s->environ;
5695
0
  } else if (strcmp(flags, "g") == 0)
5696
0
    env = global_environ;
5697
0
  else if (strcmp(flags, "c") == 0) {
5698
0
    if (ft->client != NULL)
5699
0
      env = ft->client->environ;
5700
0
  }
5701
0
  if (env == NULL)
5702
0
    return (xstrdup(""));
5703
5704
0
  buffer = evbuffer_new();
5705
0
  if (buffer == NULL)
5706
0
    fatalx("out of memory");
5707
5708
0
  envent = environ_first(env);
5709
0
  while (envent != NULL) {
5710
0
    format_log(es, "environment loop: %s", envent->name);
5711
0
    nft = format_create(c, item, FORMAT_NONE, ft->flags);
5712
5713
0
    format_add(nft, "environ_name", "%s", envent->name);
5714
0
    if (envent->value == NULL)
5715
0
      format_add(nft, "environ_value", "%s", "");
5716
0
    else
5717
0
      format_add(nft, "environ_value", "%s", envent->value);
5718
5719
0
    if (envent->flags & ENVIRON_HIDDEN)
5720
0
      format_add(nft, "environ_hidden", "1");
5721
0
    else
5722
0
      format_add(nft, "environ_hidden", "0");
5723
0
    format_add(nft, "environ_removed", "%d", envent->value == NULL);
5724
5725
0
    if (environ_next(envent) == NULL)
5726
0
      format_add(nft, "loop_last_flag", "1");
5727
0
    else
5728
0
      format_add(nft, "loop_last_flag", "0");
5729
0
    format_add(nft, "loop_index", "%u", i);
5730
5731
0
    format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
5732
0
    format_copy_state(&next, es, 0);
5733
0
    next.ft = nft;
5734
5735
0
    expanded = format_expand1(&next, fmt);
5736
0
    format_free(nft);
5737
0
    evbuffer_add(buffer, expanded, strlen(expanded));
5738
0
    free(expanded);
5739
5740
0
    i++;
5741
0
    envent = environ_next(envent);
5742
0
  }
5743
5744
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5745
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5746
0
  else
5747
0
    value = xstrdup("");
5748
0
  evbuffer_free(buffer);
5749
0
  return (value);
5750
0
}
5751
5752
/* Loop over clients. */
5753
static char *
5754
format_loop_clients(struct format_expand_state *es, const char *fmt)
5755
0
{
5756
0
  struct sort_criteria     *sc = &sort_crit;
5757
0
  struct format_tree     *ft = es->ft;
5758
0
  struct client      *c, **l;
5759
0
  struct cmdq_item     *item = ft->item;
5760
0
  struct format_tree     *nft;
5761
0
  struct format_expand_state    next;
5762
0
  char         *expanded, *value;
5763
0
  struct evbuffer      *buffer;
5764
0
  size_t          size;
5765
0
  int         i, n;
5766
5767
0
  buffer = evbuffer_new();
5768
0
  if (buffer == NULL)
5769
0
    fatalx("out of memory");
5770
5771
0
  l = sort_get_clients(&n, sc);
5772
0
  for (i = 0; i < n; i++) {
5773
0
    c = l[i];
5774
0
    format_log(es, "client loop: %s", c->name);
5775
0
    nft = format_create(c, item, 0, ft->flags);
5776
5777
0
    format_add(nft, "loop_index", "%d", i);
5778
0
    format_add(nft, "loop_last_flag", "%d", i == n - 1);
5779
5780
0
    format_defaults(nft, c, ft->s, ft->wl, ft->wp);
5781
0
    format_copy_state(&next, es, 0);
5782
0
    next.ft = nft;
5783
5784
0
    expanded = format_expand1(&next, fmt);
5785
0
    format_free(nft);
5786
0
    evbuffer_add(buffer, expanded, strlen(expanded));
5787
0
    free(expanded);
5788
0
  }
5789
5790
0
  if ((size = EVBUFFER_LENGTH(buffer)) != 0)
5791
0
    value = xmemdup(EVBUFFER_DATA(buffer), size);
5792
0
  else
5793
0
    value = xstrdup("");
5794
0
  evbuffer_free(buffer);
5795
0
  return (value);
5796
0
}
5797
5798
static char *
5799
format_replace_expression(struct format_modifier *mexp,
5800
    struct format_expand_state *es, const char *copy)
5801
0
{
5802
0
  int      argc = mexp->argc;
5803
0
  const char    *errstr;
5804
0
  char      *endch, *value, *left = NULL, *right = NULL;
5805
0
  int      use_fp = 0;
5806
0
  u_int      prec = 0;
5807
0
  double       mleft, mright, result;
5808
0
  enum { ADD,
5809
0
         SUBTRACT,
5810
0
         MULTIPLY,
5811
0
         DIVIDE,
5812
0
         MODULUS,
5813
0
         EQUAL,
5814
0
         NOT_EQUAL,
5815
0
         GREATER_THAN,
5816
0
         GREATER_THAN_EQUAL,
5817
0
         LESS_THAN,
5818
0
         LESS_THAN_EQUAL } operator;
5819
5820
0
  if (strcmp(mexp->argv[0], "+") == 0)
5821
0
    operator = ADD;
5822
0
  else if (strcmp(mexp->argv[0], "-") == 0)
5823
0
    operator = SUBTRACT;
5824
0
  else if (strcmp(mexp->argv[0], "*") == 0)
5825
0
    operator = MULTIPLY;
5826
0
  else if (strcmp(mexp->argv[0], "/") == 0)
5827
0
    operator = DIVIDE;
5828
0
  else if (strcmp(mexp->argv[0], "%") == 0 ||
5829
0
      strcmp(mexp->argv[0], "m") == 0)
5830
0
    operator = MODULUS;
5831
0
  else if (strcmp(mexp->argv[0], "==") == 0)
5832
0
    operator = EQUAL;
5833
0
  else if (strcmp(mexp->argv[0], "!=") == 0)
5834
0
    operator = NOT_EQUAL;
5835
0
  else if (strcmp(mexp->argv[0], ">") == 0)
5836
0
    operator = GREATER_THAN;
5837
0
  else if (strcmp(mexp->argv[0], "<") == 0)
5838
0
    operator = LESS_THAN;
5839
0
  else if (strcmp(mexp->argv[0], ">=") == 0)
5840
0
    operator = GREATER_THAN_EQUAL;
5841
0
  else if (strcmp(mexp->argv[0], "<=") == 0)
5842
0
    operator = LESS_THAN_EQUAL;
5843
0
  else {
5844
0
    format_log(es, "expression has no valid operator: '%s'",
5845
0
        mexp->argv[0]);
5846
0
    goto fail;
5847
0
  }
5848
5849
  /* The second argument may be flags. */
5850
0
  if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) {
5851
0
    use_fp = 1;
5852
0
    prec = 2;
5853
0
  }
5854
5855
  /* The third argument may be precision. */
5856
0
  if (argc >= 3) {
5857
0
    prec = strtonum(mexp->argv[2], -FORMAT_MAX_PRECISION,
5858
0
        FORMAT_MAX_PRECISION, &errstr);
5859
0
    if (errstr != NULL) {
5860
0
      format_log(es, "expression precision %s: %s", errstr,
5861
0
          mexp->argv[2]);
5862
0
      goto fail;
5863
0
    }
5864
0
  }
5865
5866
0
  if (format_choose(es, copy, &left, &right, 1) != 0) {
5867
0
    format_log(es, "expression syntax error");
5868
0
    goto fail;
5869
0
  }
5870
5871
0
  mleft = strtod(left, &endch);
5872
0
  if (*endch != '\0') {
5873
0
    format_log(es, "expression left side is invalid: %s", left);
5874
0
    goto fail;
5875
0
  }
5876
5877
0
  mright = strtod(right, &endch);
5878
0
  if (*endch != '\0') {
5879
0
    format_log(es, "expression right side is invalid: %s", right);
5880
0
    goto fail;
5881
0
  }
5882
5883
0
  if (!use_fp) {
5884
0
    mleft = (long long)mleft;
5885
0
    mright = (long long)mright;
5886
0
  }
5887
0
  format_log(es, "expression left side is: %.*f", prec, mleft);
5888
0
  format_log(es, "expression right side is: %.*f", prec, mright);
5889
5890
0
  switch (operator) {
5891
0
  case ADD:
5892
0
    result = mleft + mright;
5893
0
    break;
5894
0
  case SUBTRACT:
5895
0
    result = mleft - mright;
5896
0
    break;
5897
0
  case MULTIPLY:
5898
0
    result = mleft * mright;
5899
0
    break;
5900
0
  case DIVIDE:
5901
0
    result = mleft / mright;
5902
0
    break;
5903
0
  case MODULUS:
5904
0
    result = fmod(mleft, mright);
5905
0
    break;
5906
0
  case EQUAL:
5907
0
    result = fabs(mleft - mright) < 1e-9;
5908
0
    break;
5909
0
  case NOT_EQUAL:
5910
0
    result = fabs(mleft - mright) > 1e-9;
5911
0
    break;
5912
0
  case GREATER_THAN:
5913
0
    result = (mleft > mright);
5914
0
    break;
5915
0
  case GREATER_THAN_EQUAL:
5916
0
    result = (mleft >= mright);
5917
0
    break;
5918
0
  case LESS_THAN:
5919
0
    result = (mleft < mright);
5920
0
    break;
5921
0
  case LESS_THAN_EQUAL:
5922
0
    result = (mleft <= mright);
5923
0
    break;
5924
0
  }
5925
0
  if (use_fp)
5926
0
    xasprintf(&value, "%.*f", prec, result);
5927
0
  else
5928
0
    xasprintf(&value, "%.*f", prec, (double)(long long)result);
5929
0
  format_log(es, "expression result is %s", value);
5930
5931
0
  free(right);
5932
0
  free(left);
5933
0
  return (value);
5934
5935
0
fail:
5936
0
  free(right);
5937
0
  free(left);
5938
0
  return (NULL);
5939
0
}
5940
5941
/* Callback for the cycle timer; redraw the status line. */
5942
static void
5943
format_cycle_callback(__unused int fd, __unused short events, void *arg)
5944
0
{
5945
0
  struct client *c = arg;
5946
5947
0
  if (c->message_string == NULL && c->prompt == NULL)
5948
0
    c->flags |= CLIENT_REDRAWSTATUS;
5949
0
}
5950
5951
/* Arm the cycle timer to redraw the status line if it is not already. */
5952
static void
5953
format_cycle_start_timer(struct client *c)
5954
0
{
5955
0
  struct timeval  tv;
5956
5957
0
  tv.tv_sec = FORMAT_CYCLE_PERIOD / 1000;
5958
0
  tv.tv_usec = (FORMAT_CYCLE_PERIOD % 1000) * 1000L;
5959
5960
0
  if (!event_initialized(&c->cycle_timer))
5961
0
    evtimer_set(&c->cycle_timer, format_cycle_callback, c);
5962
0
  if (!evtimer_pending(&c->cycle_timer, NULL))
5963
0
    evtimer_add(&c->cycle_timer, &tv);
5964
0
}
5965
5966
/* Expand the "A" animation modifier; see the manual for the syntax. */
5967
static char *
5968
format_cycle(struct format_expand_state *es, const char *frames, u_int count)
5969
0
{
5970
0
  struct format_tree  *ft = es->ft;
5971
0
  const char    *start, *end, *cp;
5972
0
  u_int      n, index, i;
5973
5974
  /*
5975
   * A cycle is only expanded in a status format, and never in the
5976
   * command or output of #() where it would change on every frame and
5977
   * make the job run again.
5978
   */
5979
0
  if (!(ft->flags & FORMAT_STATUS) || (es->flags & FORMAT_EXPAND_NOCYCLE))
5980
0
    return (xstrdup(""));
5981
0
  if (*frames == '\0')
5982
0
    return (xstrdup(""));
5983
5984
  /* Count the comma-separated frames (there is at least one). */
5985
0
  n = 1;
5986
0
  for (cp = frames; *cp != '\0'; cp++) {
5987
0
    if (*cp == ',')
5988
0
      n++;
5989
0
  }
5990
0
  index = (es->start_time / (count * FORMAT_CYCLE_PERIOD)) % n;
5991
5992
  /*
5993
   * Redraw the status line so the frames advance on their own; a
5994
   * single frame never changes so there is nothing to redraw for.
5995
   */
5996
0
  if (n > 1 && ft->client != NULL)
5997
0
    format_cycle_start_timer(ft->client);
5998
5999
  /* Walk to the chosen frame and return a copy of it. */
6000
0
  start = frames;
6001
0
  for (i = 0; i < index; i++)
6002
0
    start = strchr(start, ',') + 1;
6003
0
  end = strchr(start, ',');
6004
0
  if (end == NULL)
6005
0
    end = start + strlen(start);
6006
0
  return (xstrndup(start, end - start));
6007
0
}
6008
6009
/* Replace a key. */
6010
static int
6011
format_replace(struct format_expand_state *es, const char *key, size_t keylen,
6012
    char **buf, size_t *len, size_t *off)
6013
0
{
6014
0
  struct sort_criteria     *sc = &sort_crit;
6015
0
  struct format_tree     *ft = es->ft;
6016
0
  struct window_pane     *wp = ft->wp;
6017
0
  const char       *errstr, *copy, *cp, *cp2;
6018
0
  const char       *marker = NULL;
6019
0
  char         *time_format = NULL;
6020
0
  char         *copy0, *condition, *found, *new;
6021
0
  char         *value, *left, *right;
6022
0
  size_t          valuelen;
6023
0
  uint64_t        modifiers = 0;
6024
0
  int         limit = 0, width = 0;
6025
0
  int         j, c;
6026
0
  struct format_modifier     *list, *cmp = NULL, *search = NULL;
6027
0
  struct format_modifier    **sub = NULL, *mexp = NULL, *fm;
6028
0
  struct format_modifier     *bool_op_n = NULL;
6029
0
  u_int         cycle_count = 1;
6030
0
  u_int         i, count, nsub = 0, nrep, check = 0;
6031
0
  const char       *loop_flags = "";
6032
0
  struct format_expand_state    next;
6033
0
  struct environ_entry     *envent;
6034
6035
  /* Set sorting defaults. */
6036
0
  sc->order = SORT_ORDER;
6037
0
  sc->reversed = 0;
6038
6039
  /* Make a copy of the key. */
6040
0
  copy = copy0 = xstrndup(key, keylen);
6041
6042
  /* Process modifier list. */
6043
0
  list = format_build_modifiers(es, &copy, &count);
6044
0
  for (i = 0; i < count; i++) {
6045
0
    fm = &list[i];
6046
0
    if (format_logging(ft)) {
6047
0
      format_log(es, "modifier %u is %s", i, fm->modifier);
6048
0
      for (j = 0; j < fm->argc; j++) {
6049
0
        format_log(es, "modifier %u argument %d: %s", i,
6050
0
            j, fm->argv[j]);
6051
0
      }
6052
0
    }
6053
0
    if (fm->size == 1) {
6054
0
      switch (fm->modifier[0]) {
6055
0
      case 'm':
6056
0
      case '<':
6057
0
      case '>':
6058
0
        cmp = fm;
6059
0
        break;
6060
0
      case '!':
6061
0
        modifiers |= FORMAT_NOT;
6062
0
        break;
6063
0
      case 'C':
6064
0
        search = fm;
6065
0
        break;
6066
0
      case 's':
6067
0
        if (fm->argc < 2)
6068
0
          break;
6069
0
        sub = xreallocarray(sub, nsub + 1, sizeof *sub);
6070
0
        sub[nsub++] = fm;
6071
0
        break;
6072
0
      case '=':
6073
0
        if (fm->argc < 1)
6074
0
          break;
6075
0
        limit = strtonum(fm->argv[0], -FORMAT_MAX_WIDTH,
6076
0
            FORMAT_MAX_WIDTH, &errstr);
6077
0
        if (errstr != NULL)
6078
0
          limit = 0;
6079
0
        if (fm->argc >= 2 && fm->argv[1] != NULL)
6080
0
          marker = fm->argv[1];
6081
0
        break;
6082
0
      case 'p':
6083
0
        if (fm->argc < 1)
6084
0
          break;
6085
0
        width = strtonum(fm->argv[0], -FORMAT_MAX_WIDTH,
6086
0
            FORMAT_MAX_WIDTH, &errstr);
6087
0
        if (errstr != NULL)
6088
0
          width = 0;
6089
0
        break;
6090
0
      case 'A':
6091
0
        modifiers |= FORMAT_CYCLE;
6092
0
        if (fm->argc < 1)
6093
0
          break;
6094
0
        cycle_count = strtonum(fm->argv[0], 1, 100,
6095
0
            &errstr);
6096
0
        if (errstr != NULL)
6097
0
          cycle_count = 1;
6098
0
        break;
6099
0
      case 'w':
6100
0
        modifiers |= FORMAT_WIDTH;
6101
0
        break;
6102
0
      case 'e':
6103
0
        if (fm->argc < 1 || fm->argc > 3)
6104
0
          break;
6105
0
        mexp = fm;
6106
0
        break;
6107
0
      case 'l':
6108
0
        modifiers |= FORMAT_LITERAL;
6109
0
        break;
6110
0
      case 'a':
6111
0
        modifiers |= FORMAT_CHARACTER;
6112
0
        break;
6113
0
      case 'b':
6114
0
        modifiers |= FORMAT_BASENAME;
6115
0
        break;
6116
0
      case 'c':
6117
0
        modifiers |= FORMAT_COLOUR;
6118
0
        if (fm->argc < 1)
6119
0
          break;
6120
0
        if (strchr(fm->argv[0], 'f') != NULL)
6121
0
          modifiers |= FORMAT_COLOUR_ESC_FG;
6122
0
        if (strchr(fm->argv[0], 'b') != NULL)
6123
0
          modifiers |= FORMAT_COLOUR_ESC_BG;
6124
0
        break;
6125
0
      case 'd':
6126
0
        modifiers |= FORMAT_DIRNAME;
6127
0
        break;
6128
0
      case 'n':
6129
0
        modifiers |= FORMAT_LENGTH;
6130
0
        break;
6131
0
      case 'I':
6132
0
        if (fm->argc < 1)
6133
0
          break;
6134
0
        if (strchr(fm->argv[0], 'f') != NULL)
6135
0
          modifiers |= FORMAT_CLIENT_TERMFEAT;
6136
0
        if (strchr(fm->argv[0], 'c') != NULL)
6137
0
          modifiers |= FORMAT_CLIENT_TERMCAP;
6138
0
        if (strchr(fm->argv[0], 'e') != NULL)
6139
0
          modifiers |= FORMAT_CLIENT_ENVIRON;
6140
0
        break;
6141
0
      case 't':
6142
0
        modifiers |= FORMAT_TIMESTRING;
6143
0
        if (fm->argc < 1)
6144
0
          break;
6145
0
        if (strchr(fm->argv[0], 'p') != NULL)
6146
0
          modifiers |= FORMAT_PRETTY;
6147
0
        else if (strchr(fm->argv[0], 'r') != NULL)
6148
0
          modifiers |= FORMAT_RELATIVE;
6149
0
        else if (strchr(fm->argv[0], 'd') != NULL)
6150
0
          modifiers |= FORMAT_DIFFERENCE;
6151
0
        else if (fm->argc >= 2 &&
6152
0
            strchr(fm->argv[0], 'f') != NULL) {
6153
0
          free(time_format);
6154
0
          time_format = format_strip(es,
6155
0
              fm->argv[1]);
6156
0
        }
6157
0
        break;
6158
0
      case 'q':
6159
0
        if (fm->argc < 1)
6160
0
          modifiers |= FORMAT_QUOTE_SHELL;
6161
0
        else if (strchr(fm->argv[0], 's') != NULL)
6162
0
          modifiers |= FORMAT_QUOTE_SHELL_SQ;
6163
0
        else if (strchr(fm->argv[0], 'e') != NULL ||
6164
0
            strchr(fm->argv[0], 'h') != NULL)
6165
0
          modifiers |= FORMAT_QUOTE_STYLE;
6166
0
        else if (strchr(fm->argv[0], 'a') != NULL)
6167
0
          modifiers |= FORMAT_QUOTE_ARGUMENTS;
6168
0
        break;
6169
0
      case 'E':
6170
0
        modifiers |= FORMAT_EXPAND;
6171
0
        break;
6172
0
      case 'T':
6173
0
        modifiers |= FORMAT_EXPANDTIME;
6174
0
        break;
6175
0
      case 'N':
6176
0
        if (fm->argc < 1 ||
6177
0
            strchr(fm->argv[0], 'w') != NULL)
6178
0
          modifiers |= FORMAT_WINDOW_NAME;
6179
0
        else if (strchr(fm->argv[0], 's') != NULL)
6180
0
          modifiers |= FORMAT_SESSION_NAME;
6181
0
        break;
6182
0
      case 'S':
6183
0
        modifiers |= FORMAT_SESSIONS;
6184
0
        if (fm->argc < 1) {
6185
0
          sc->order= SORT_INDEX;
6186
0
          sc->reversed = 0;
6187
0
          break;
6188
0
        }
6189
0
        if (strchr(fm->argv[0], 'i') != NULL)
6190
0
          sc->order = SORT_INDEX;
6191
0
        else if (strchr(fm->argv[0], 'n') != NULL)
6192
0
          sc->order = SORT_NAME;
6193
0
        else if (strchr(fm->argv[0], 't') != NULL)
6194
0
          sc->order = SORT_ACTIVITY;
6195
0
        else
6196
0
          sc->order = SORT_INDEX;
6197
0
        if (strchr(fm->argv[0], 'r') != NULL)
6198
0
          sc->reversed = 1;
6199
0
        else
6200
0
          sc->reversed = 0;
6201
0
        break;
6202
0
      case 'W':
6203
0
        modifiers |= FORMAT_WINDOWS;
6204
0
        if (fm->argc < 1) {
6205
0
          sc->order = SORT_ORDER;
6206
0
          sc->reversed = 0;
6207
0
          break;
6208
0
        }
6209
0
        if (strchr(fm->argv[0], 'i') != NULL)
6210
0
          sc->order = SORT_ORDER;
6211
0
        else if (strchr(fm->argv[0], 'n') != NULL)
6212
0
          sc->order = SORT_NAME;
6213
0
        else if (strchr(fm->argv[0], 't') != NULL)
6214
0
          sc->order = SORT_ACTIVITY;
6215
0
        else
6216
0
          sc->order = SORT_ORDER;
6217
0
        if (strchr(fm->argv[0], 'r') != NULL)
6218
0
          sc->reversed = 1;
6219
0
        else
6220
0
          sc->reversed = 0;
6221
0
        break;
6222
0
      case 'P':
6223
0
        modifiers |= FORMAT_PANES;
6224
0
        sc->order = SORT_CREATION;
6225
0
        if (fm->argc < 1) {
6226
0
          sc->reversed = 0;
6227
0
          break;
6228
0
        }
6229
0
        if (strchr(fm->argv[0], 'i') != NULL)
6230
0
          sc->order = SORT_INDEX;
6231
0
        else if (strchr(fm->argv[0], 'z') != NULL)
6232
0
          sc->order = SORT_Z;
6233
0
        else
6234
0
          sc->order = SORT_CREATION;
6235
0
        if (strchr(fm->argv[0], 'r') != NULL)
6236
0
          sc->reversed = 1;
6237
0
        else
6238
0
          sc->reversed = 0;
6239
0
        break;
6240
0
      case 'O':
6241
0
        modifiers |= FORMAT_OPTIONS;
6242
0
        if (fm->argc == 1)
6243
0
          loop_flags = fm->argv[0];
6244
0
        break;
6245
0
      case 'V':
6246
0
        modifiers |= FORMAT_ENVIRON;
6247
0
        if (fm->argc == 1)
6248
0
          loop_flags = fm->argv[0];
6249
0
        break;
6250
0
      case 'L':
6251
0
        modifiers |= FORMAT_CLIENTS;
6252
0
        if (fm->argc < 1) {
6253
0
          sc->order = SORT_ORDER;
6254
0
          sc->reversed = 0;
6255
0
          break;
6256
0
        }
6257
0
        if (strchr(fm->argv[0], 'i') != NULL)
6258
0
          sc->order = SORT_ORDER;
6259
0
        else if (strchr(fm->argv[0], 'n') != NULL)
6260
0
          sc->order = SORT_NAME;
6261
0
        else if (strchr(fm->argv[0], 't') != NULL)
6262
0
          sc->order = SORT_ACTIVITY;
6263
0
        else
6264
0
          sc->order = SORT_ORDER;
6265
0
        if (strchr(fm->argv[0], 'r') != NULL)
6266
0
          sc->reversed = 1;
6267
0
        else
6268
0
          sc->reversed = 0;
6269
0
        break;
6270
0
      case 'R':
6271
0
        modifiers |= FORMAT_REPEAT;
6272
0
        break;
6273
0
      }
6274
0
    } else if (fm->size == 2) {
6275
0
      if (strcmp(fm->modifier, "||") == 0 ||
6276
0
          strcmp(fm->modifier, "&&") == 0)
6277
0
        bool_op_n = fm;
6278
0
      else if (strcmp(fm->modifier, "!!") == 0)
6279
0
        modifiers |= FORMAT_NOT_NOT;
6280
0
      else if (strcmp(fm->modifier, "==") == 0 ||
6281
0
          strcmp(fm->modifier, "!=") == 0 ||
6282
0
          strcmp(fm->modifier, ">=") == 0 ||
6283
0
          strcmp(fm->modifier, "<=") == 0)
6284
0
        cmp = fm;
6285
0
    }
6286
0
  }
6287
6288
  /* Look up client capability, feature or environment. */
6289
0
  if ((modifiers & FORMAT_CLIENT_TERMCAP) ||
6290
0
      (modifiers & FORMAT_CLIENT_TERMFEAT) ||
6291
0
      (modifiers & FORMAT_CLIENT_ENVIRON)) {
6292
0
    if (ft->c == NULL ||
6293
0
        ft->c->tty.term == NULL ||
6294
0
        ft->c->flags & CLIENT_UNATTACHEDFLAGS) {
6295
0
      value = xstrdup("");
6296
0
      goto done;
6297
0
    }
6298
0
    if (modifiers & FORMAT_CLIENT_TERMCAP) {
6299
0
      if (tty_term_has_name(ft->c->tty.term, copy))
6300
0
        value = xstrdup("1");
6301
0
      else
6302
0
        value = xstrdup("0");
6303
0
    }
6304
0
    if (modifiers & FORMAT_CLIENT_TERMFEAT) {
6305
0
      if (tty_feature_present(ft->c->tty.term, copy))
6306
0
        value = xstrdup("1");
6307
0
      else
6308
0
        value = xstrdup("0");
6309
0
    }
6310
0
    if (modifiers & FORMAT_CLIENT_ENVIRON) {
6311
0
      envent = environ_find(ft->c->environ, copy);
6312
0
      if (envent != NULL && envent->value != NULL)
6313
0
        value = xstrdup(envent->value);
6314
0
      else
6315
0
        value = xstrdup("");
6316
0
    }
6317
0
    goto done;
6318
0
  }
6319
6320
  /* Is this an animation cycle? */
6321
0
  if (modifiers & FORMAT_CYCLE) {
6322
0
    value = format_cycle(es, copy, cycle_count);
6323
0
    format_log(es, "cycle '%s' is: %s", copy, value);
6324
0
    goto done;
6325
0
  }
6326
6327
  /* Is this a literal string? */
6328
0
  if (modifiers & FORMAT_LITERAL) {
6329
0
    format_log(es, "literal string is '%s'", copy);
6330
0
    value = format_unescape(es, copy, strlen(copy));
6331
0
    goto done;
6332
0
  }
6333
6334
  /* Is this a character? */
6335
0
  if (modifiers & FORMAT_CHARACTER) {
6336
0
    new = format_expand1(es, copy);
6337
0
    c = strtonum(new, 32, 126, &errstr);
6338
0
    if (errstr != NULL)
6339
0
      value = xstrdup("");
6340
0
    else
6341
0
      xasprintf(&value, "%c", c);
6342
0
    free(new);
6343
0
    goto done;
6344
0
  }
6345
6346
  /* Is this a colour? */
6347
0
  if (modifiers & FORMAT_COLOUR) {
6348
0
    new = format_expand1(es, copy);
6349
0
    if (modifiers & (FORMAT_COLOUR_ESC_FG|FORMAT_COLOUR_ESC_BG)) {
6350
0
      if (strcasecmp(new, "none") == 0)
6351
0
        value = xstrdup("\033[0m");
6352
0
      else if ((c = colour_fromstring(new)) == -1)
6353
0
        value = xstrdup("");
6354
0
      else {
6355
0
        if (modifiers & FORMAT_COLOUR_ESC_BG)
6356
0
          cp = colour_toescape(ft->c, c, 1);
6357
0
        else
6358
0
          cp = colour_toescape(ft->c, c, 0);
6359
0
        if (cp == NULL)
6360
0
          value = xstrdup("");
6361
0
        else
6362
0
          value = xstrdup(cp);
6363
0
      }
6364
0
    } else {
6365
0
      c = colour_fromstring(new);
6366
0
      if (c == -1 || (c = colour_force_rgb(c)) == -1)
6367
0
        value = xstrdup("");
6368
0
      else
6369
0
        xasprintf(&value, "%06x", c & 0xffffff);
6370
0
    }
6371
0
    free(new);
6372
0
    goto done;
6373
0
  }
6374
6375
  /* Is this a loop, operator, comparison or condition? */
6376
0
  if (modifiers & FORMAT_SESSIONS) {
6377
0
    value = format_loop_sessions(es, copy);
6378
0
    if (value == NULL)
6379
0
      goto fail;
6380
0
  } else if (modifiers & FORMAT_WINDOWS) {
6381
0
    value = format_loop_windows(es, copy);
6382
0
    if (value == NULL)
6383
0
      goto fail;
6384
0
  } else if (modifiers & FORMAT_PANES) {
6385
0
    value = format_loop_panes(es, copy);
6386
0
    if (value == NULL)
6387
0
      goto fail;
6388
0
  } else if (modifiers & FORMAT_CLIENTS) {
6389
0
    value = format_loop_clients(es, copy);
6390
0
    if (value == NULL)
6391
0
      goto fail;
6392
0
  } else if (modifiers & FORMAT_OPTIONS) {
6393
0
    value = format_loop_options(es, copy, loop_flags);
6394
0
    if (value == NULL)
6395
0
      goto fail;
6396
0
  } else if (modifiers & FORMAT_ENVIRON) {
6397
0
    value = format_loop_environ(es, copy, loop_flags);
6398
0
    if (value == NULL)
6399
0
      goto fail;
6400
0
  } else if (modifiers & FORMAT_WINDOW_NAME) {
6401
0
    value = format_window_name(es, copy);
6402
0
    if (value == NULL)
6403
0
      goto fail;
6404
0
  } else if (modifiers & FORMAT_SESSION_NAME) {
6405
0
    value = format_session_name(es, copy);
6406
0
    if (value == NULL)
6407
0
      goto fail;
6408
0
  } else if (search != NULL) {
6409
    /* Search in pane. */
6410
0
    new = format_expand1(es, copy);
6411
0
    if (wp == NULL) {
6412
0
      format_log(es, "search '%s' but no pane", new);
6413
0
      value = xstrdup("0");
6414
0
    } else {
6415
0
      format_log(es, "search '%s' pane %%%u", new, wp->id);
6416
0
      value = format_search(search, wp, new);
6417
0
    }
6418
0
    free(new);
6419
0
  } else if (modifiers & FORMAT_REPEAT) {
6420
    /* Repeat multiple times. */
6421
0
    if (format_choose(es, copy, &left, &right, 1) != 0) {
6422
0
      format_log(es, "repeat syntax error: %s", copy);
6423
0
      goto fail;
6424
0
    }
6425
0
    nrep = strtonum(right, 1, FORMAT_MAX_REPEAT, &errstr);
6426
0
    if (errstr != NULL)
6427
0
      value = xstrdup("");
6428
0
    else {
6429
0
      value = xstrdup("");
6430
0
      for (i = 0; i < nrep; i++) {
6431
0
        if (!format_check_time(es, &check)) {
6432
0
          free(right);
6433
0
          free(left);
6434
0
          free(value);
6435
0
          goto fail;
6436
0
        }
6437
0
        xasprintf(&new, "%s%s", value, left);
6438
0
        free(value);
6439
0
        value = new;
6440
0
      }
6441
0
    }
6442
0
    free(right);
6443
0
    free(left);
6444
0
  } else if (modifiers & FORMAT_NOT) {
6445
0
    value = format_bool_op_1(es, copy, 1);
6446
0
  } else if (modifiers & FORMAT_NOT_NOT) {
6447
0
    value = format_bool_op_1(es, copy, 0);
6448
0
  } else if (bool_op_n != NULL) {
6449
    /* n-ary boolean operator. */
6450
0
    if (strcmp(bool_op_n->modifier, "||") == 0)
6451
0
      value = format_bool_op_n(es, copy, 0);
6452
0
    else if (strcmp(bool_op_n->modifier, "&&") == 0)
6453
0
      value = format_bool_op_n(es, copy, 1);
6454
0
  } else if (cmp != NULL) {
6455
    /* Comparison of left and right. */
6456
0
    if (format_choose(es, copy, &left, &right, 1) != 0) {
6457
0
      format_log(es, "compare %s syntax error: %s",
6458
0
          cmp->modifier, copy);
6459
0
      goto fail;
6460
0
    }
6461
0
    format_log(es, "compare %s left is: %s", cmp->modifier, left);
6462
0
    format_log(es, "compare %s right is: %s", cmp->modifier, right);
6463
6464
0
    if (strcmp(cmp->modifier, "==") == 0) {
6465
0
      if (strcmp(left, right) == 0)
6466
0
        value = xstrdup("1");
6467
0
      else
6468
0
        value = xstrdup("0");
6469
0
    } else if (strcmp(cmp->modifier, "!=") == 0) {
6470
0
      if (strcmp(left, right) != 0)
6471
0
        value = xstrdup("1");
6472
0
      else
6473
0
        value = xstrdup("0");
6474
0
    } else if (strcmp(cmp->modifier, "<") == 0) {
6475
0
      if (strcmp(left, right) < 0)
6476
0
        value = xstrdup("1");
6477
0
      else
6478
0
        value = xstrdup("0");
6479
0
    } else if (strcmp(cmp->modifier, ">") == 0) {
6480
0
      if (strcmp(left, right) > 0)
6481
0
        value = xstrdup("1");
6482
0
      else
6483
0
        value = xstrdup("0");
6484
0
    } else if (strcmp(cmp->modifier, "<=") == 0) {
6485
0
      if (strcmp(left, right) <= 0)
6486
0
        value = xstrdup("1");
6487
0
      else
6488
0
        value = xstrdup("0");
6489
0
    } else if (strcmp(cmp->modifier, ">=") == 0) {
6490
0
      if (strcmp(left, right) >= 0)
6491
0
        value = xstrdup("1");
6492
0
      else
6493
0
        value = xstrdup("0");
6494
0
    } else if (strcmp(cmp->modifier, "m") == 0)
6495
0
      value = format_match(cmp, left, right);
6496
6497
0
    free(right);
6498
0
    free(left);
6499
0
  } else if (*copy == '?') {
6500
    /*
6501
     * Conditional: For each pair of (condition, value), check the
6502
     * condition and return the value if true. If no condition
6503
     * matches, return the last unpaired arg if there is one, or the
6504
     * empty string if not.
6505
     */
6506
0
    cp = copy + 1;
6507
0
    while (1) {
6508
0
      cp2 = format_skip1(es, cp, ",");
6509
0
      if (cp2 == NULL) {
6510
0
        format_log(es,
6511
0
            "no condition matched in '%s'; using last "
6512
0
            "arg", copy + 1);
6513
0
        value = format_expand1(es, cp);
6514
0
        break;
6515
0
      }
6516
6517
0
      condition = xstrndup(cp, cp2 - cp);
6518
0
      format_log(es, "condition is: %s", condition);
6519
6520
0
      found = format_find(ft, condition, modifiers,
6521
0
          time_format);
6522
0
      if (found == NULL) {
6523
        /*
6524
         * If the condition not found, try to expand it.
6525
         * If the expansion doesn't have any effect,
6526
         * then assume false.
6527
         */
6528
0
        found = format_expand1(es, condition);
6529
0
        if (strcmp(found, condition) == 0) {
6530
0
          free(found);
6531
0
          found = xstrdup("");
6532
0
          format_log(es,
6533
0
              "condition '%s' not found; "
6534
0
              "assuming false",
6535
0
              condition);
6536
0
        }
6537
0
      } else {
6538
0
        format_log(es, "condition '%s' found: %s",
6539
0
            condition, found);
6540
0
      }
6541
6542
0
      cp = cp2 + 1;
6543
0
      cp2 = format_skip1(es, cp, ",");
6544
0
      if (format_true(found)) {
6545
0
        format_log(es, "condition '%s' is true",
6546
0
            condition);
6547
0
        if (cp2 == NULL)
6548
0
          value = format_expand1(es, cp);
6549
0
        else {
6550
0
          right = xstrndup(cp, cp2 - cp);
6551
0
          value = format_expand1(es, right);
6552
0
          free(right);
6553
0
        }
6554
0
        free(condition);
6555
0
        free(found);
6556
0
        break;
6557
0
      } else {
6558
0
        format_log(es, "condition '%s' is false",
6559
0
            condition);
6560
0
      }
6561
6562
0
      free(condition);
6563
0
      free(found);
6564
6565
0
      if (cp2 == NULL) {
6566
0
        format_log(es,
6567
0
            "no condition matched in '%s'; using empty "
6568
0
            "string", copy + 1);
6569
0
        value = xstrdup("");
6570
0
        break;
6571
0
      }
6572
6573
0
      cp = cp2 + 1;
6574
0
    }
6575
0
  } else if (mexp != NULL) {
6576
0
    value = format_replace_expression(mexp, es, copy);
6577
0
    if (value == NULL)
6578
0
      value = xstrdup("");
6579
0
  } else {
6580
0
    if (strstr(copy, "#{") != 0) {
6581
0
      format_log(es, "expanding inner format '%s'", copy);
6582
0
      value = format_expand1(es, copy);
6583
0
    } else {
6584
0
      value = format_find(ft, copy, modifiers, time_format);
6585
0
      if (value == NULL) {
6586
0
        format_log(es, "format '%s' not found", copy);
6587
0
        value = xstrdup("");
6588
0
      } else {
6589
0
        format_log(es, "format '%s' found: %s", copy,
6590
0
            value);
6591
0
      }
6592
0
    }
6593
0
  }
6594
6595
0
done:
6596
  /* Expand again if required. */
6597
0
  if (modifiers & FORMAT_EXPAND) {
6598
0
    new = format_expand1(es, value);
6599
0
    free(value);
6600
0
    value = new;
6601
0
  } else if (modifiers & FORMAT_EXPANDTIME) {
6602
0
    format_copy_state(&next, es, FORMAT_EXPAND_TIME);
6603
0
    new = format_expand1(&next, value);
6604
0
    free(value);
6605
0
    value = new;
6606
0
  }
6607
6608
  /* Perform substitution if any. */
6609
0
  for (i = 0; i < nsub; i++) {
6610
0
    left = format_expand1(es, sub[i]->argv[0]);
6611
0
    right = format_expand1(es, sub[i]->argv[1]);
6612
0
    new = format_sub(sub[i], value, left, right);
6613
0
    format_log(es, "substitute '%s' to '%s': %s", left, right, new);
6614
0
    free(value);
6615
0
    value = new;
6616
0
    free(right);
6617
0
    free(left);
6618
0
  }
6619
6620
  /* Truncate the value if needed. */
6621
0
  if (limit > 0) {
6622
0
    new = format_trim_left(value, limit);
6623
0
    if (marker != NULL && strcmp(new, value) != 0) {
6624
0
      free(value);
6625
0
      xasprintf(&value, "%s%s", new, marker);
6626
0
      free(new);
6627
0
    } else {
6628
0
      free(value);
6629
0
      value = new;
6630
0
    }
6631
0
    format_log(es, "applied length limit %d: %s", limit, value);
6632
0
  } else if (limit < 0) {
6633
0
    new = format_trim_right(value, -limit);
6634
0
    if (marker != NULL && strcmp(new, value) != 0) {
6635
0
      free(value);
6636
0
      xasprintf(&value, "%s%s", marker, new);
6637
0
      free(new);
6638
0
    } else {
6639
0
      free(value);
6640
0
      value = new;
6641
0
    }
6642
0
    format_log(es, "applied length limit %d: %s", limit, value);
6643
0
  }
6644
6645
  /* Pad the value if needed. */
6646
0
  if (width > 0) {
6647
0
    new = utf8_padcstr(value, width);
6648
0
    free(value);
6649
0
    value = new;
6650
0
    format_log(es, "applied padding width %d: %s", width, value);
6651
0
  } else if (width < 0) {
6652
0
    new = utf8_rpadcstr(value, -width);
6653
0
    free(value);
6654
0
    value = new;
6655
0
    format_log(es, "applied padding width %d: %s", width, value);
6656
0
  }
6657
6658
  /* Replace with the length or width if needed. */
6659
0
  if (modifiers & FORMAT_LENGTH) {
6660
0
    xasprintf(&new, "%zu", strlen(value));
6661
0
    free(value);
6662
0
    value = new;
6663
0
    format_log(es, "replacing with length: %s", new);
6664
0
  }
6665
0
  if (modifiers & FORMAT_WIDTH) {
6666
0
    xasprintf(&new, "%u", format_width(value));
6667
0
    free(value);
6668
0
    value = new;
6669
0
    format_log(es, "replacing with width: %s", new);
6670
0
  }
6671
6672
  /* Expand the buffer and copy in the value. */
6673
0
  valuelen = strlen(value);
6674
0
  while (*len - *off < valuelen + 1) {
6675
0
    *buf = xreallocarray(*buf, 2, *len);
6676
0
    *len *= 2;
6677
0
  }
6678
0
  memcpy(*buf + *off, value, valuelen);
6679
0
  *off += valuelen;
6680
6681
0
  format_log(es, "replaced '%s' with '%s'", copy0, value);
6682
0
  free(value);
6683
6684
0
  free(sub);
6685
0
  format_free_modifiers(list, count);
6686
0
  free(copy0);
6687
0
  free(time_format);
6688
0
  return (0);
6689
6690
0
fail:
6691
0
  format_log(es, "failed %s", copy0);
6692
6693
0
  free(sub);
6694
0
  format_free_modifiers(list, count);
6695
0
  free(copy0);
6696
0
  free(time_format);
6697
0
  return (-1);
6698
0
}
6699
6700
/* Expand keys in a template. */
6701
static char *
6702
format_expand1(struct format_expand_state *es, const char *fmt)
6703
0
{
6704
0
  struct format_tree  *ft = es->ft;
6705
0
  char      *buf, *out, *name;
6706
0
  const char    *ptr, *s, *style_end = NULL;
6707
0
  size_t       off, len, n, outlen;
6708
0
  int      ch, brackets;
6709
0
  char       expanded[8192];
6710
6711
0
  if (fmt == NULL || *fmt == '\0' || !format_check_time(es, NULL))
6712
0
    return (xstrdup(""));
6713
6714
0
  if (es->loop == FORMAT_LOOP_LIMIT) {
6715
0
    format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT);
6716
0
    return (xstrdup(""));
6717
0
  }
6718
0
  es->loop++;
6719
6720
0
  format_log(es, "expanding format: %s", fmt);
6721
6722
0
  if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) {
6723
0
    if (es->time == 0) {
6724
0
      es->time = time(NULL);
6725
0
      localtime_r(&es->time, &es->tm);
6726
0
    }
6727
0
    if (format_strftime(expanded, sizeof expanded, fmt,
6728
0
        &es->tm) == 0) {
6729
0
      format_log(es, "format is too long");
6730
0
      return (xstrdup(""));
6731
0
    }
6732
0
    if (format_logging(ft) && strcmp(expanded, fmt) != 0)
6733
0
      format_log(es, "after time expanded: %s", expanded);
6734
0
    fmt = expanded;
6735
0
  }
6736
6737
0
  len = 64;
6738
0
  buf = xmalloc(len);
6739
0
  off = 0;
6740
6741
0
  while (*fmt != '\0') {
6742
0
    if (*fmt != '#') {
6743
0
      while (len - off < 2) {
6744
0
        buf = xreallocarray(buf, 2, len);
6745
0
        len *= 2;
6746
0
      }
6747
0
      buf[off++] = *fmt++;
6748
0
      continue;
6749
0
    }
6750
0
    if (*++fmt == '\0')
6751
0
      break;
6752
6753
0
    ch = (u_char)*fmt++;
6754
0
    switch (ch) {
6755
0
    case '(':
6756
0
      brackets = 1;
6757
0
      for (ptr = fmt; *ptr != '\0'; ptr++) {
6758
0
        if (*ptr == '(')
6759
0
          brackets++;
6760
0
        if (*ptr == ')' && --brackets == 0)
6761
0
          break;
6762
0
      }
6763
0
      if (*ptr != ')' || brackets != 0)
6764
0
        break;
6765
0
      n = ptr - fmt;
6766
6767
0
      name = xstrndup(fmt, n);
6768
0
      format_log(es, "found #(): %s", name);
6769
6770
0
      if ((ft->flags & FORMAT_NOJOBS) ||
6771
0
          (es->flags & FORMAT_EXPAND_NOJOBS)) {
6772
0
        out = xstrdup("");
6773
0
        format_log(es, "#() is disabled");
6774
0
      } else {
6775
0
        out = format_job_get(es, name);
6776
0
        format_log(es, "#() result: %s", out);
6777
0
      }
6778
0
      free(name);
6779
6780
0
      outlen = strlen(out);
6781
0
      while (len - off < outlen + 1) {
6782
0
        buf = xreallocarray(buf, 2, len);
6783
0
        len *= 2;
6784
0
      }
6785
0
      memcpy(buf + off, out, outlen);
6786
0
      off += outlen;
6787
6788
0
      free(out);
6789
6790
0
      fmt += n + 1;
6791
0
      continue;
6792
0
    case '{':
6793
0
      ptr = format_skip1(es, (char *)fmt - 2, "}");
6794
0
      if (ptr == NULL)
6795
0
        break;
6796
0
      n = ptr - fmt;
6797
6798
0
      format_log(es, "found #{}: %.*s", (int)n, fmt);
6799
0
      if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
6800
0
        break;
6801
0
      fmt += n + 1;
6802
0
      continue;
6803
0
    case '[':
6804
0
    case '#':
6805
      /*
6806
       * If ##[ (with two or more #s), then it is a style and
6807
       * can be left for format_draw to handle.
6808
       */
6809
0
      ptr = fmt - (ch == '[');
6810
0
      n = 2 - (ch == '[');
6811
0
      while (*ptr == '#') {
6812
0
        ptr++;
6813
0
        n++;
6814
0
      }
6815
0
      if (*ptr == '[') {
6816
0
        style_end = format_skip1(es, fmt - 2, "]");
6817
0
        format_log(es, "found #*%zu[", n);
6818
0
        while (len - off < n + 2) {
6819
0
          buf = xreallocarray(buf, 2, len);
6820
0
          len *= 2;
6821
0
        }
6822
0
        memcpy(buf + off, fmt - 2, n + 1);
6823
0
        off += n + 1;
6824
0
        fmt = ptr + 1;
6825
0
        continue;
6826
0
      }
6827
      /* FALLTHROUGH */
6828
0
    case '}':
6829
0
    case ',':
6830
0
      format_log(es, "found #%c", ch);
6831
0
      while (len - off < 2) {
6832
0
        buf = xreallocarray(buf, 2, len);
6833
0
        len *= 2;
6834
0
      }
6835
0
      buf[off++] = ch;
6836
0
      continue;
6837
0
    default:
6838
0
      s = NULL;
6839
0
      if (fmt > style_end) { /* skip inside #[] */
6840
0
        if (ch >= 'A' && ch <= 'Z')
6841
0
          s = format_upper[ch - 'A'];
6842
0
        else if (ch >= 'a' && ch <= 'z')
6843
0
          s = format_lower[ch - 'a'];
6844
0
      }
6845
0
      if (s == NULL) {
6846
0
        while (len - off < 3) {
6847
0
          buf = xreallocarray(buf, 2, len);
6848
0
          len *= 2;
6849
0
        }
6850
0
        buf[off++] = '#';
6851
0
        buf[off++] = ch;
6852
0
        continue;
6853
0
      }
6854
0
      n = strlen(s);
6855
0
      format_log(es, "found #%c: %s", ch, s);
6856
0
      if (format_replace(es, s, n, &buf, &len, &off) != 0)
6857
0
        break;
6858
0
      continue;
6859
0
    }
6860
6861
0
    break;
6862
0
  }
6863
0
  buf[off] = '\0';
6864
6865
0
  format_log(es, "result is: %s", buf);
6866
0
  es->loop--;
6867
6868
0
  return (buf);
6869
0
}
6870
6871
/* Expand keys in a template, passing through strftime first. */
6872
char *
6873
format_expand_time(struct format_tree *ft, const char *fmt)
6874
0
{
6875
0
  struct format_expand_state  es;
6876
6877
0
  memset(&es, 0, sizeof es);
6878
0
  es.ft = ft;
6879
0
  es.flags = FORMAT_EXPAND_TIME;
6880
0
  es.start_time = get_timer();
6881
0
  return (format_expand1(&es, fmt));
6882
0
}
6883
6884
/* Expand keys in a template. */
6885
char *
6886
format_expand(struct format_tree *ft, const char *fmt)
6887
0
{
6888
0
  struct format_expand_state  es;
6889
6890
0
  memset(&es, 0, sizeof es);
6891
0
  es.ft = ft;
6892
0
  es.flags = 0;
6893
0
  es.start_time = get_timer();
6894
0
  return (format_expand1(&es, fmt));
6895
0
}
6896
6897
/* Expand a single string. */
6898
char *
6899
format_single(struct cmdq_item *item, const char *fmt, struct client *c,
6900
    struct session *s, struct winlink *wl, struct window_pane *wp)
6901
0
{
6902
0
  struct format_tree  *ft;
6903
0
  char      *expanded;
6904
6905
0
  ft = format_create_defaults(item, c, s, wl, wp);
6906
0
  expanded = format_expand(ft, fmt);
6907
0
  format_free(ft);
6908
0
  return (expanded);
6909
0
}
6910
6911
/* Expand a single string using state. */
6912
char *
6913
format_single_from_state(struct cmdq_item *item, const char *fmt,
6914
    struct client *c, struct cmd_find_state *fs)
6915
0
{
6916
0
  return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp));
6917
0
}
6918
6919
/* Expand a single string using target. */
6920
char *
6921
format_single_from_target(struct cmdq_item *item, const char *fmt)
6922
0
{
6923
0
  struct client *tc = cmdq_get_target_client(item);
6924
6925
0
  return (format_single_from_state(item, fmt, tc, cmdq_get_target(item)));
6926
0
}
6927
6928
/* Create and add defaults. */
6929
struct format_tree *
6930
format_create_defaults(struct cmdq_item *item, struct client *c,
6931
    struct session *s, struct winlink *wl, struct window_pane *wp)
6932
0
{
6933
0
  struct format_tree  *ft;
6934
6935
0
  if (item != NULL)
6936
0
    ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
6937
0
  else
6938
0
    ft = format_create(NULL, item, FORMAT_NONE, 0);
6939
0
  format_defaults(ft, c, s, wl, wp);
6940
0
  return (ft);
6941
0
}
6942
6943
/* Create and add defaults using state. */
6944
struct format_tree *
6945
format_create_from_state(struct cmdq_item *item, struct client *c,
6946
    struct cmd_find_state *fs)
6947
0
{
6948
0
  return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp));
6949
0
}
6950
6951
/* Create and add defaults using target. */
6952
struct format_tree *
6953
format_create_from_target(struct cmdq_item *item)
6954
0
{
6955
0
  struct client *tc = cmdq_get_target_client(item);
6956
6957
0
  return (format_create_from_state(item, tc, cmdq_get_target(item)));
6958
0
}
6959
6960
/* Set defaults for any of arguments that are not NULL. */
6961
void
6962
format_defaults(struct format_tree *ft, struct client *c, struct session *s,
6963
    struct winlink *wl, struct window_pane *wp)
6964
0
{
6965
0
  struct paste_buffer *pb;
6966
6967
0
  if (c != NULL && c->name != NULL)
6968
0
    log_debug("%s: c=%s", __func__, c->name);
6969
0
  else
6970
0
    log_debug("%s: c=none", __func__);
6971
0
  if (s != NULL)
6972
0
    log_debug("%s: s=$%u", __func__, s->id);
6973
0
  else
6974
0
    log_debug("%s: s=none", __func__);
6975
0
  if (wl != NULL)
6976
0
    log_debug("%s: wl=%u", __func__, wl->idx);
6977
0
  else
6978
0
    log_debug("%s: wl=none", __func__);
6979
0
  if (wp != NULL)
6980
0
    log_debug("%s: wp=%%%u", __func__, wp->id);
6981
0
  else
6982
0
    log_debug("%s: wp=none", __func__);
6983
6984
0
  if (c != NULL && s != NULL && c->session != s)
6985
0
    log_debug("%s: session does not match", __func__);
6986
6987
0
  if (wp != NULL)
6988
0
    ft->type = FORMAT_TYPE_PANE;
6989
0
  else if (wl != NULL)
6990
0
    ft->type = FORMAT_TYPE_WINDOW;
6991
0
  else if (s != NULL)
6992
0
    ft->type = FORMAT_TYPE_SESSION;
6993
0
  else
6994
0
    ft->type = FORMAT_TYPE_UNKNOWN;
6995
6996
0
  if (s == NULL && c != NULL)
6997
0
    s = c->session;
6998
0
  if (wl == NULL && s != NULL)
6999
0
    wl = s->curw;
7000
0
  if (wp == NULL && wl != NULL)
7001
0
    wp = wl->window->active;
7002
7003
0
  if (c != NULL)
7004
0
    format_defaults_client(ft, c);
7005
0
  if (s != NULL)
7006
0
    format_defaults_session(ft, s);
7007
0
  if (wl != NULL)
7008
0
    format_defaults_winlink(ft, wl);
7009
0
  if (wp != NULL)
7010
0
    format_defaults_pane(ft, wp);
7011
7012
0
  pb = paste_get_top(NULL);
7013
0
  if (pb != NULL)
7014
0
    format_defaults_paste_buffer(ft, pb);
7015
0
}
7016
7017
/* Set default format keys for a session. */
7018
static void
7019
format_defaults_session(struct format_tree *ft, struct session *s)
7020
0
{
7021
0
  ft->s = s;
7022
0
}
7023
7024
/* Set default format keys for a client. */
7025
static void
7026
format_defaults_client(struct format_tree *ft, struct client *c)
7027
0
{
7028
0
  if (ft->s == NULL)
7029
0
    ft->s = c->session;
7030
0
  ft->c = c;
7031
0
}
7032
7033
/* Set default format keys for a window. */
7034
void
7035
format_defaults_window(struct format_tree *ft, struct window *w)
7036
0
{
7037
0
  ft->w = w;
7038
0
}
7039
7040
/* Set default format keys for a winlink. */
7041
static void
7042
format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
7043
0
{
7044
0
  if (ft->w == NULL)
7045
0
    format_defaults_window(ft, wl->window);
7046
0
  ft->wl = wl;
7047
0
}
7048
7049
/* Set default format keys for a window pane. */
7050
void
7051
format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
7052
0
{
7053
0
  struct window_mode_entry  *wme;
7054
7055
0
  if (ft->w == NULL)
7056
0
    format_defaults_window(ft, wp->window);
7057
0
  ft->wp = wp;
7058
7059
0
  wme = TAILQ_FIRST(&wp->modes);
7060
0
  if (wme != NULL && wme->mode->formats != NULL)
7061
0
    wme->mode->formats(wme, ft);
7062
0
}
7063
7064
/* Set default format keys for paste buffer. */
7065
void
7066
format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
7067
0
{
7068
0
  ft->pb = pb;
7069
0
}
7070
7071
static int
7072
format_is_word_separator(const char *ws, const struct grid_cell *gc)
7073
0
{
7074
0
  if (utf8_cstrhas(ws, &gc->data))
7075
0
    return (1);
7076
0
  if (gc->flags & GRID_FLAG_TAB)
7077
0
    return (1);
7078
0
  return gc->data.size == 1 && *gc->data.data == ' ';
7079
0
}
7080
7081
/* Return word at given coordinates. Caller frees. */
7082
char *
7083
format_grid_word(struct grid *gd, u_int x, u_int y)
7084
0
{
7085
0
  const struct grid_line  *gl;
7086
0
  struct grid_cell   gc;
7087
0
  const char    *ws;
7088
0
  struct utf8_data  *ud = NULL;
7089
0
  u_int      end;
7090
0
  size_t       size = 0;
7091
0
  int      found = 0;
7092
0
  char      *s = NULL;
7093
7094
0
  ws = options_get_string(global_s_options, "word-separators");
7095
7096
0
  for (;;) {
7097
0
    grid_get_cell(gd, x, y, &gc);
7098
0
    if ((~gc.flags & GRID_FLAG_PADDING) &&
7099
0
        format_is_word_separator(ws, &gc)) {
7100
0
      found = 1;
7101
0
      break;
7102
0
    }
7103
7104
0
    if (x == 0) {
7105
0
      if (y == 0)
7106
0
        break;
7107
0
      gl = grid_peek_line(gd, y - 1);
7108
0
      if (~gl->flags & GRID_LINE_WRAPPED)
7109
0
        break;
7110
0
      y--;
7111
0
      x = grid_line_length(gd, y);
7112
0
      if (x == 0)
7113
0
        break;
7114
0
    }
7115
0
    x--;
7116
0
  }
7117
0
  for (;;) {
7118
0
    if (found) {
7119
0
      end = grid_line_length(gd, y);
7120
0
      if (end == 0 || x == end - 1) {
7121
0
        if (y == gd->hsize + gd->sy - 1)
7122
0
          break;
7123
0
        gl = grid_peek_line(gd, y);
7124
0
        if (~gl->flags & GRID_LINE_WRAPPED)
7125
0
          break;
7126
0
        y++;
7127
0
        x = 0;
7128
0
      } else
7129
0
        x++;
7130
0
    }
7131
0
    found = 1;
7132
7133
0
    grid_get_cell(gd, x, y, &gc);
7134
0
    if (gc.flags & GRID_FLAG_PADDING)
7135
0
      continue;
7136
0
    if (format_is_word_separator(ws, &gc))
7137
0
      break;
7138
7139
0
    ud = xreallocarray(ud, size + 2, sizeof *ud);
7140
0
    memcpy(&ud[size++], &gc.data, sizeof *ud);
7141
0
  }
7142
0
  if (size != 0) {
7143
0
    ud[size].size = 0;
7144
0
    s = utf8_tocstr(ud);
7145
0
    free(ud);
7146
0
  }
7147
0
  return (s);
7148
0
}
7149
7150
/* Return line at given coordinates. Caller frees. */
7151
char *
7152
format_grid_line(struct grid *gd, u_int y)
7153
0
{
7154
0
  struct grid_cell   gc;
7155
0
  struct utf8_data  *ud = NULL;
7156
0
  u_int      x;
7157
0
  size_t       size = 0;
7158
0
  char      *s = NULL;
7159
7160
0
  for (x = 0; x < grid_line_length(gd, y); x++) {
7161
0
    grid_get_cell(gd, x, y, &gc);
7162
0
    if (gc.flags & GRID_FLAG_PADDING)
7163
0
      continue;
7164
7165
0
    ud = xreallocarray(ud, size + 2, sizeof *ud);
7166
0
    if (gc.flags & GRID_FLAG_TAB)
7167
0
      utf8_set(&ud[size++], '\t');
7168
0
    else
7169
0
      memcpy(&ud[size++], &gc.data, sizeof *ud);
7170
0
  }
7171
0
  if (size != 0) {
7172
0
    ud[size].size = 0;
7173
0
    s = utf8_tocstr(ud);
7174
0
    free(ud);
7175
0
  }
7176
0
  return (s);
7177
0
}
7178
7179
/* Return hyperlink at given coordinates. Caller frees. */
7180
char *
7181
format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
7182
0
{
7183
0
  const char    *uri;
7184
0
  struct grid_cell   gc;
7185
7186
0
  for (;;) {
7187
0
    grid_get_cell(gd, x, y, &gc);
7188
0
    if (~gc.flags & GRID_FLAG_PADDING)
7189
0
      break;
7190
0
    if (x == 0)
7191
0
      return (NULL);
7192
0
    x--;
7193
0
  }
7194
0
  if (s->hyperlinks == NULL || gc.link == 0)
7195
0
    return (NULL);
7196
0
  if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
7197
0
    return (NULL);
7198
0
  return (xstrdup(uri));
7199
0
}