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