Coverage Report

Created: 2026-04-12 06:58

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