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