1 /* $OpenBSD: format.c,v 1.259 2020/06/23 05:23:26 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/wait.h> 21 22 #include <ctype.h> 23 #include <errno.h> 24 #include <fnmatch.h> 25 #include <libgen.h> 26 #include <math.h> 27 #include <regex.h> 28 #include <stdarg.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <unistd.h> 33 34 #include "tmux.h" 35 36 /* 37 * Build a list of key-value pairs and use them to expand #{key} entries in a 38 * string. 39 */ 40 41 static char *format_job_get(struct format_tree *, const char *); 42 static void format_job_timer(int, short, void *); 43 44 static int format_replace(struct format_tree *, const char *, size_t, 45 char **, size_t *, size_t *); 46 static void format_defaults_session(struct format_tree *, 47 struct session *); 48 static void format_defaults_client(struct format_tree *, struct client *); 49 static void format_defaults_winlink(struct format_tree *, struct winlink *); 50 51 /* Entry in format job tree. */ 52 struct format_job { 53 struct client *client; 54 u_int tag; 55 const char *cmd; 56 const char *expanded; 57 58 time_t last; 59 char *out; 60 int updated; 61 62 struct job *job; 63 int status; 64 65 RB_ENTRY(format_job) entry; 66 }; 67 68 /* Format job tree. */ 69 static struct event format_job_event; 70 static int format_job_cmp(struct format_job *, struct format_job *); 71 static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); 72 RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp); 73 74 /* Format job tree comparison function. */ 75 static int 76 format_job_cmp(struct format_job *fj1, struct format_job *fj2) 77 { 78 if (fj1->tag < fj2->tag) 79 return (-1); 80 if (fj1->tag > fj2->tag) 81 return (1); 82 return (strcmp(fj1->cmd, fj2->cmd)); 83 } 84 85 /* Format modifiers. */ 86 #define FORMAT_TIMESTRING 0x1 87 #define FORMAT_BASENAME 0x2 88 #define FORMAT_DIRNAME 0x4 89 #define FORMAT_QUOTE 0x8 90 #define FORMAT_LITERAL 0x10 91 #define FORMAT_EXPAND 0x20 92 #define FORMAT_EXPANDTIME 0x40 93 #define FORMAT_SESSIONS 0x80 94 #define FORMAT_WINDOWS 0x100 95 #define FORMAT_PANES 0x200 96 #define FORMAT_PRETTY 0x400 97 98 /* Limit on recursion. */ 99 #define FORMAT_LOOP_LIMIT 10 100 101 /* Entry in format tree. */ 102 struct format_entry { 103 char *key; 104 char *value; 105 time_t t; 106 format_cb cb; 107 RB_ENTRY(format_entry) entry; 108 }; 109 110 /* Format entry tree. */ 111 struct format_tree { 112 struct client *c; 113 struct session *s; 114 struct winlink *wl; 115 struct window *w; 116 struct window_pane *wp; 117 118 struct cmdq_item *item; 119 struct client *client; 120 int flags; 121 u_int tag; 122 time_t time; 123 u_int loop; 124 125 struct mouse_event m; 126 127 RB_HEAD(format_entry_tree, format_entry) tree; 128 }; 129 static int format_entry_cmp(struct format_entry *, struct format_entry *); 130 RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); 131 132 /* Format modifier. */ 133 struct format_modifier { 134 char modifier[3]; 135 u_int size; 136 137 char **argv; 138 int argc; 139 }; 140 141 /* Format entry tree comparison function. */ 142 static int 143 format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) 144 { 145 return (strcmp(fe1->key, fe2->key)); 146 } 147 148 /* Single-character uppercase aliases. */ 149 static const char *format_upper[] = { 150 NULL, /* A */ 151 NULL, /* B */ 152 NULL, /* C */ 153 "pane_id", /* D */ 154 NULL, /* E */ 155 "window_flags", /* F */ 156 NULL, /* G */ 157 "host", /* H */ 158 "window_index", /* I */ 159 NULL, /* J */ 160 NULL, /* K */ 161 NULL, /* L */ 162 NULL, /* M */ 163 NULL, /* N */ 164 NULL, /* O */ 165 "pane_index", /* P */ 166 NULL, /* Q */ 167 NULL, /* R */ 168 "session_name", /* S */ 169 "pane_title", /* T */ 170 NULL, /* U */ 171 NULL, /* V */ 172 "window_name", /* W */ 173 NULL, /* X */ 174 NULL, /* Y */ 175 NULL /* Z */ 176 }; 177 178 /* Single-character lowercase aliases. */ 179 static const char *format_lower[] = { 180 NULL, /* a */ 181 NULL, /* b */ 182 NULL, /* c */ 183 NULL, /* d */ 184 NULL, /* e */ 185 NULL, /* f */ 186 NULL, /* g */ 187 "host_short", /* h */ 188 NULL, /* i */ 189 NULL, /* j */ 190 NULL, /* k */ 191 NULL, /* l */ 192 NULL, /* m */ 193 NULL, /* n */ 194 NULL, /* o */ 195 NULL, /* p */ 196 NULL, /* q */ 197 NULL, /* r */ 198 NULL, /* s */ 199 NULL, /* t */ 200 NULL, /* u */ 201 NULL, /* v */ 202 NULL, /* w */ 203 NULL, /* x */ 204 NULL, /* y */ 205 NULL /* z */ 206 }; 207 208 /* Is logging enabled? */ 209 static inline int 210 format_logging(struct format_tree *ft) 211 { 212 return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE)); 213 } 214 215 /* Log a message if verbose. */ 216 static void printflike(3, 4) 217 format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) 218 { 219 va_list ap; 220 char *s; 221 static const char spaces[] = " "; 222 223 if (!format_logging(ft)) 224 return; 225 226 va_start(ap, fmt); 227 xvasprintf(&s, fmt, ap); 228 va_end(ap); 229 230 log_debug("%s: %s", from, s); 231 if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) 232 cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s); 233 234 free(s); 235 } 236 #define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) 237 238 /* Format job update callback. */ 239 static void 240 format_job_update(struct job *job) 241 { 242 struct format_job *fj = job_get_data(job); 243 struct evbuffer *evb = job_get_event(job)->input; 244 char *line = NULL, *next; 245 time_t t; 246 247 while ((next = evbuffer_readline(evb)) != NULL) { 248 free(line); 249 line = next; 250 } 251 if (line == NULL) 252 return; 253 fj->updated = 1; 254 255 free(fj->out); 256 fj->out = line; 257 258 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out); 259 260 t = time(NULL); 261 if (fj->status && fj->last != t) { 262 if (fj->client != NULL) 263 server_status_client(fj->client); 264 fj->last = t; 265 } 266 } 267 268 /* Format job complete callback. */ 269 static void 270 format_job_complete(struct job *job) 271 { 272 struct format_job *fj = job_get_data(job); 273 struct evbuffer *evb = job_get_event(job)->input; 274 char *line, *buf; 275 size_t len; 276 277 fj->job = NULL; 278 279 buf = NULL; 280 if ((line = evbuffer_readline(evb)) == NULL) { 281 len = EVBUFFER_LENGTH(evb); 282 buf = xmalloc(len + 1); 283 if (len != 0) 284 memcpy(buf, EVBUFFER_DATA(evb), len); 285 buf[len] = '\0'; 286 } else 287 buf = line; 288 289 log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf); 290 291 if (*buf != '\0' || !fj->updated) { 292 free(fj->out); 293 fj->out = buf; 294 } else 295 free(buf); 296 297 if (fj->status) { 298 if (fj->client != NULL) 299 server_status_client(fj->client); 300 fj->status = 0; 301 } 302 } 303 304 /* Find a job. */ 305 static char * 306 format_job_get(struct format_tree *ft, const char *cmd) 307 { 308 struct format_job_tree *jobs; 309 struct format_job fj0, *fj; 310 time_t t; 311 char *expanded; 312 int force; 313 314 if (ft->client == NULL) 315 jobs = &format_jobs; 316 else if (ft->client->jobs != NULL) 317 jobs = ft->client->jobs; 318 else { 319 jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs); 320 RB_INIT(jobs); 321 } 322 323 fj0.tag = ft->tag; 324 fj0.cmd = cmd; 325 if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) { 326 fj = xcalloc(1, sizeof *fj); 327 fj->client = ft->client; 328 fj->tag = ft->tag; 329 fj->cmd = xstrdup(cmd); 330 fj->expanded = NULL; 331 332 xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); 333 334 RB_INSERT(format_job_tree, jobs, fj); 335 } 336 337 expanded = format_expand(ft, cmd); 338 if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { 339 free((void *)fj->expanded); 340 fj->expanded = xstrdup(expanded); 341 force = 1; 342 } else 343 force = (ft->flags & FORMAT_FORCE); 344 345 t = time(NULL); 346 if (force && fj->job != NULL) 347 job_free(fj->job); 348 if (force || (fj->job == NULL && fj->last != t)) { 349 fj->job = job_run(expanded, NULL, 350 server_client_get_cwd(ft->client, NULL), format_job_update, 351 format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1); 352 if (fj->job == NULL) { 353 free(fj->out); 354 xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); 355 } 356 fj->last = t; 357 fj->updated = 0; 358 } 359 360 if (ft->flags & FORMAT_STATUS) 361 fj->status = 1; 362 363 free(expanded); 364 return (format_expand(ft, fj->out)); 365 } 366 367 /* Remove old jobs. */ 368 static void 369 format_job_tidy(struct format_job_tree *jobs, int force) 370 { 371 struct format_job *fj, *fj1; 372 time_t now; 373 374 now = time(NULL); 375 RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) { 376 if (!force && (fj->last > now || now - fj->last < 3600)) 377 continue; 378 RB_REMOVE(format_job_tree, jobs, fj); 379 380 log_debug("%s: %s", __func__, fj->cmd); 381 382 if (fj->job != NULL) 383 job_free(fj->job); 384 385 free((void *)fj->expanded); 386 free((void *)fj->cmd); 387 free(fj->out); 388 389 free(fj); 390 } 391 } 392 393 /* Remove old jobs for client. */ 394 void 395 format_lost_client(struct client *c) 396 { 397 if (c->jobs != NULL) 398 format_job_tidy(c->jobs, 1); 399 free(c->jobs); 400 } 401 402 /* Remove old jobs periodically. */ 403 static void 404 format_job_timer(__unused int fd, __unused short events, __unused void *arg) 405 { 406 struct client *c; 407 struct timeval tv = { .tv_sec = 60 }; 408 409 format_job_tidy(&format_jobs, 0); 410 TAILQ_FOREACH(c, &clients, entry) { 411 if (c->jobs != NULL) 412 format_job_tidy(c->jobs, 0); 413 } 414 415 evtimer_del(&format_job_event); 416 evtimer_add(&format_job_event, &tv); 417 } 418 419 /* Callback for host. */ 420 static char * 421 format_cb_host(__unused struct format_tree *ft) 422 { 423 char host[HOST_NAME_MAX + 1]; 424 425 if (gethostname(host, sizeof host) != 0) 426 return (xstrdup("")); 427 return (xstrdup(host)); 428 } 429 430 /* Callback for host_short. */ 431 static char * 432 format_cb_host_short(__unused struct format_tree *ft) 433 { 434 char host[HOST_NAME_MAX + 1], *cp; 435 436 if (gethostname(host, sizeof host) != 0) 437 return (xstrdup("")); 438 if ((cp = strchr(host, '.')) != NULL) 439 *cp = '\0'; 440 return (xstrdup(host)); 441 } 442 443 /* Callback for pid. */ 444 static char * 445 format_cb_pid(__unused struct format_tree *ft) 446 { 447 char *value; 448 449 xasprintf(&value, "%ld", (long)getpid()); 450 return (value); 451 } 452 453 /* Callback for session_attached_list. */ 454 static char * 455 format_cb_session_attached_list(struct format_tree *ft) 456 { 457 struct session *s = ft->s; 458 struct client *loop; 459 struct evbuffer *buffer; 460 int size; 461 char *value = NULL; 462 463 if (s == NULL) 464 return (NULL); 465 466 buffer = evbuffer_new(); 467 if (buffer == NULL) 468 fatalx("out of memory"); 469 470 TAILQ_FOREACH(loop, &clients, entry) { 471 if (loop->session == s) { 472 if (EVBUFFER_LENGTH(buffer) > 0) 473 evbuffer_add(buffer, ",", 1); 474 evbuffer_add_printf(buffer, "%s", loop->name); 475 } 476 } 477 478 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 479 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 480 evbuffer_free(buffer); 481 return (value); 482 } 483 484 /* Callback for session_alerts. */ 485 static char * 486 format_cb_session_alerts(struct format_tree *ft) 487 { 488 struct session *s = ft->s; 489 struct winlink *wl; 490 char alerts[1024], tmp[16]; 491 492 if (s == NULL) 493 return (NULL); 494 495 *alerts = '\0'; 496 RB_FOREACH(wl, winlinks, &s->windows) { 497 if ((wl->flags & WINLINK_ALERTFLAGS) == 0) 498 continue; 499 xsnprintf(tmp, sizeof tmp, "%u", wl->idx); 500 501 if (*alerts != '\0') 502 strlcat(alerts, ",", sizeof alerts); 503 strlcat(alerts, tmp, sizeof alerts); 504 if (wl->flags & WINLINK_ACTIVITY) 505 strlcat(alerts, "#", sizeof alerts); 506 if (wl->flags & WINLINK_BELL) 507 strlcat(alerts, "!", sizeof alerts); 508 if (wl->flags & WINLINK_SILENCE) 509 strlcat(alerts, "~", sizeof alerts); 510 } 511 return (xstrdup(alerts)); 512 } 513 514 /* Callback for session_stack. */ 515 static char * 516 format_cb_session_stack(struct format_tree *ft) 517 { 518 struct session *s = ft->s; 519 struct winlink *wl; 520 char result[1024], tmp[16]; 521 522 if (s == NULL) 523 return (NULL); 524 525 xsnprintf(result, sizeof result, "%u", s->curw->idx); 526 TAILQ_FOREACH(wl, &s->lastw, sentry) { 527 xsnprintf(tmp, sizeof tmp, "%u", wl->idx); 528 529 if (*result != '\0') 530 strlcat(result, ",", sizeof result); 531 strlcat(result, tmp, sizeof result); 532 } 533 return (xstrdup(result)); 534 } 535 536 /* Callback for window_stack_index. */ 537 static char * 538 format_cb_window_stack_index(struct format_tree *ft) 539 { 540 struct session *s = ft->wl->session; 541 struct winlink *wl; 542 u_int idx; 543 char *value = NULL; 544 545 idx = 0; 546 TAILQ_FOREACH(wl, &s->lastw, sentry) { 547 idx++; 548 if (wl == ft->wl) 549 break; 550 } 551 if (wl == NULL) 552 return (xstrdup("0")); 553 xasprintf(&value, "%u", idx); 554 return (value); 555 } 556 557 /* Callback for window_linked_sessions_list. */ 558 static char * 559 format_cb_window_linked_sessions_list(struct format_tree *ft) 560 { 561 struct window *w = ft->wl->window; 562 struct winlink *wl; 563 struct evbuffer *buffer; 564 int size; 565 char *value = NULL; 566 567 buffer = evbuffer_new(); 568 if (buffer == NULL) 569 fatalx("out of memory"); 570 571 TAILQ_FOREACH(wl, &w->winlinks, wentry) { 572 if (EVBUFFER_LENGTH(buffer) > 0) 573 evbuffer_add(buffer, ",", 1); 574 evbuffer_add_printf(buffer, "%s", wl->session->name); 575 } 576 577 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 578 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 579 evbuffer_free(buffer); 580 return (value); 581 } 582 583 /* Callback for window_active_sessions. */ 584 static char * 585 format_cb_window_active_sessions(struct format_tree *ft) 586 { 587 struct window *w = ft->wl->window; 588 struct winlink *wl; 589 u_int n = 0; 590 char *value; 591 592 TAILQ_FOREACH(wl, &w->winlinks, wentry) { 593 if (wl->session->curw == wl) 594 n++; 595 } 596 597 xasprintf(&value, "%u", n); 598 return (value); 599 } 600 601 /* Callback for window_active_sessions_list. */ 602 static char * 603 format_cb_window_active_sessions_list(struct format_tree *ft) 604 { 605 struct window *w = ft->wl->window; 606 struct winlink *wl; 607 struct evbuffer *buffer; 608 int size; 609 char *value = NULL; 610 611 buffer = evbuffer_new(); 612 if (buffer == NULL) 613 fatalx("out of memory"); 614 615 TAILQ_FOREACH(wl, &w->winlinks, wentry) { 616 if (wl->session->curw == wl) { 617 if (EVBUFFER_LENGTH(buffer) > 0) 618 evbuffer_add(buffer, ",", 1); 619 evbuffer_add_printf(buffer, "%s", wl->session->name); 620 } 621 } 622 623 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 624 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 625 evbuffer_free(buffer); 626 return (value); 627 } 628 629 /* Callback for window_active_clients. */ 630 static char * 631 format_cb_window_active_clients(struct format_tree *ft) 632 { 633 struct window *w = ft->wl->window; 634 struct client *loop; 635 struct session *client_session; 636 u_int n = 0; 637 char *value; 638 639 TAILQ_FOREACH(loop, &clients, entry) { 640 client_session = loop->session; 641 if (client_session == NULL) 642 continue; 643 644 if (w == client_session->curw->window) 645 n++; 646 } 647 648 xasprintf(&value, "%u", n); 649 return (value); 650 } 651 652 /* Callback for window_active_clients_list. */ 653 static char * 654 format_cb_window_active_clients_list(struct format_tree *ft) 655 { 656 struct window *w = ft->wl->window; 657 struct client *loop; 658 struct session *client_session; 659 struct evbuffer *buffer; 660 int size; 661 char *value = NULL; 662 663 buffer = evbuffer_new(); 664 if (buffer == NULL) 665 fatalx("out of memory"); 666 667 TAILQ_FOREACH(loop, &clients, entry) { 668 client_session = loop->session; 669 if (client_session == NULL) 670 continue; 671 672 if (w == client_session->curw->window) { 673 if (EVBUFFER_LENGTH(buffer) > 0) 674 evbuffer_add(buffer, ",", 1); 675 evbuffer_add_printf(buffer, "%s", loop->name); 676 } 677 } 678 679 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 680 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 681 evbuffer_free(buffer); 682 return (value); 683 } 684 685 /* Callback for window_layout. */ 686 static char * 687 format_cb_window_layout(struct format_tree *ft) 688 { 689 struct window *w = ft->w; 690 691 if (w == NULL) 692 return (NULL); 693 694 if (w->saved_layout_root != NULL) 695 return (layout_dump(w->saved_layout_root)); 696 return (layout_dump(w->layout_root)); 697 } 698 699 /* Callback for window_visible_layout. */ 700 static char * 701 format_cb_window_visible_layout(struct format_tree *ft) 702 { 703 struct window *w = ft->w; 704 705 if (w == NULL) 706 return (NULL); 707 708 return (layout_dump(w->layout_root)); 709 } 710 711 /* Callback for pane_start_command. */ 712 static char * 713 format_cb_start_command(struct format_tree *ft) 714 { 715 struct window_pane *wp = ft->wp; 716 717 if (wp == NULL) 718 return (NULL); 719 720 return (cmd_stringify_argv(wp->argc, wp->argv)); 721 } 722 723 /* Callback for pane_current_command. */ 724 static char * 725 format_cb_current_command(struct format_tree *ft) 726 { 727 struct window_pane *wp = ft->wp; 728 char *cmd, *value; 729 730 if (wp == NULL || wp->shell == NULL) 731 return (NULL); 732 733 cmd = get_proc_name(wp->fd, wp->tty); 734 if (cmd == NULL || *cmd == '\0') { 735 free(cmd); 736 cmd = cmd_stringify_argv(wp->argc, wp->argv); 737 if (cmd == NULL || *cmd == '\0') { 738 free(cmd); 739 cmd = xstrdup(wp->shell); 740 } 741 } 742 value = parse_window_name(cmd); 743 free(cmd); 744 return (value); 745 } 746 747 /* Callback for pane_current_path. */ 748 static char * 749 format_cb_current_path(struct format_tree *ft) 750 { 751 struct window_pane *wp = ft->wp; 752 char *cwd; 753 754 if (wp == NULL) 755 return (NULL); 756 757 cwd = get_proc_cwd(wp->fd); 758 if (cwd == NULL) 759 return (NULL); 760 return (xstrdup(cwd)); 761 } 762 763 /* Callback for history_bytes. */ 764 static char * 765 format_cb_history_bytes(struct format_tree *ft) 766 { 767 struct window_pane *wp = ft->wp; 768 struct grid *gd; 769 struct grid_line *gl; 770 size_t size = 0; 771 u_int i; 772 char *value; 773 774 if (wp == NULL) 775 return (NULL); 776 gd = wp->base.grid; 777 778 for (i = 0; i < gd->hsize + gd->sy; i++) { 779 gl = grid_get_line(gd, i); 780 size += gl->cellsize * sizeof *gl->celldata; 781 size += gl->extdsize * sizeof *gl->extddata; 782 } 783 size += (gd->hsize + gd->sy) * sizeof *gl; 784 785 xasprintf(&value, "%zu", size); 786 return (value); 787 } 788 789 /* Callback for history_all_bytes. */ 790 static char * 791 format_cb_history_all_bytes(struct format_tree *ft) 792 { 793 struct window_pane *wp = ft->wp; 794 struct grid *gd; 795 struct grid_line *gl; 796 u_int i, lines, cells = 0, extended_cells = 0; 797 char *value; 798 799 if (wp == NULL) 800 return (NULL); 801 gd = wp->base.grid; 802 803 lines = gd->hsize + gd->sy; 804 for (i = 0; i < lines; i++) { 805 gl = grid_get_line(gd, i); 806 cells += gl->cellsize; 807 extended_cells += gl->extdsize; 808 } 809 810 xasprintf(&value, "%u,%zu,%u,%zu,%u,%zu", lines, 811 lines * sizeof *gl, cells, cells * sizeof *gl->celldata, 812 extended_cells, extended_cells * sizeof *gl->extddata); 813 return (value); 814 } 815 816 /* Callback for pane_tabs. */ 817 static char * 818 format_cb_pane_tabs(struct format_tree *ft) 819 { 820 struct window_pane *wp = ft->wp; 821 struct evbuffer *buffer; 822 u_int i; 823 int size; 824 char *value = NULL; 825 826 if (wp == NULL) 827 return (NULL); 828 829 buffer = evbuffer_new(); 830 if (buffer == NULL) 831 fatalx("out of memory"); 832 for (i = 0; i < wp->base.grid->sx; i++) { 833 if (!bit_test(wp->base.tabs, i)) 834 continue; 835 836 if (EVBUFFER_LENGTH(buffer) > 0) 837 evbuffer_add(buffer, ",", 1); 838 evbuffer_add_printf(buffer, "%u", i); 839 } 840 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 841 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 842 evbuffer_free(buffer); 843 return (value); 844 } 845 846 /* Callback for session_group_list. */ 847 static char * 848 format_cb_session_group_list(struct format_tree *ft) 849 { 850 struct session *s = ft->s; 851 struct session_group *sg; 852 struct session *loop; 853 struct evbuffer *buffer; 854 int size; 855 char *value = NULL; 856 857 if (s == NULL) 858 return (NULL); 859 sg = session_group_contains(s); 860 if (sg == NULL) 861 return (NULL); 862 863 buffer = evbuffer_new(); 864 if (buffer == NULL) 865 fatalx("out of memory"); 866 867 TAILQ_FOREACH(loop, &sg->sessions, gentry) { 868 if (EVBUFFER_LENGTH(buffer) > 0) 869 evbuffer_add(buffer, ",", 1); 870 evbuffer_add_printf(buffer, "%s", loop->name); 871 } 872 873 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 874 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 875 evbuffer_free(buffer); 876 return (value); 877 } 878 879 /* Callback for session_group_attached_list. */ 880 static char * 881 format_cb_session_group_attached_list(struct format_tree *ft) 882 { 883 struct session *s = ft->s, *client_session, *session_loop; 884 struct session_group *sg; 885 struct client *loop; 886 struct evbuffer *buffer; 887 int size; 888 char *value = NULL; 889 890 if (s == NULL) 891 return (NULL); 892 sg = session_group_contains(s); 893 if (sg == NULL) 894 return (NULL); 895 896 buffer = evbuffer_new(); 897 if (buffer == NULL) 898 fatalx("out of memory"); 899 900 TAILQ_FOREACH(loop, &clients, entry) { 901 client_session = loop->session; 902 if (client_session == NULL) 903 continue; 904 TAILQ_FOREACH(session_loop, &sg->sessions, gentry) { 905 if (session_loop == client_session){ 906 if (EVBUFFER_LENGTH(buffer) > 0) 907 evbuffer_add(buffer, ",", 1); 908 evbuffer_add_printf(buffer, "%s", loop->name); 909 } 910 } 911 } 912 913 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 914 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 915 evbuffer_free(buffer); 916 return (value); 917 } 918 919 /* Callback for pane_in_mode. */ 920 static char * 921 format_cb_pane_in_mode(struct format_tree *ft) 922 { 923 struct window_pane *wp = ft->wp; 924 u_int n = 0; 925 struct window_mode_entry *wme; 926 char *value; 927 928 if (wp == NULL) 929 return (NULL); 930 931 TAILQ_FOREACH(wme, &wp->modes, entry) 932 n++; 933 xasprintf(&value, "%u", n); 934 return (value); 935 } 936 937 /* Callback for pane_at_top. */ 938 static char * 939 format_cb_pane_at_top(struct format_tree *ft) 940 { 941 struct window_pane *wp = ft->wp; 942 struct window *w; 943 int status, flag; 944 char *value; 945 946 if (wp == NULL) 947 return (NULL); 948 w = wp->window; 949 950 status = options_get_number(w->options, "pane-border-status"); 951 if (status == PANE_STATUS_TOP) 952 flag = (wp->yoff == 1); 953 else 954 flag = (wp->yoff == 0); 955 xasprintf(&value, "%d", flag); 956 return (value); 957 } 958 959 /* Callback for pane_at_bottom. */ 960 static char * 961 format_cb_pane_at_bottom(struct format_tree *ft) 962 { 963 struct window_pane *wp = ft->wp; 964 struct window *w; 965 int status, flag; 966 char *value; 967 968 if (wp == NULL) 969 return (NULL); 970 w = wp->window; 971 972 status = options_get_number(w->options, "pane-border-status"); 973 if (status == PANE_STATUS_BOTTOM) 974 flag = (wp->yoff + wp->sy == w->sy - 1); 975 else 976 flag = (wp->yoff + wp->sy == w->sy); 977 xasprintf(&value, "%d", flag); 978 return (value); 979 } 980 981 /* Callback for cursor_character. */ 982 static char * 983 format_cb_cursor_character(struct format_tree *ft) 984 { 985 struct window_pane *wp = ft->wp; 986 struct grid_cell gc; 987 char *value = NULL; 988 989 if (wp == NULL) 990 return (NULL); 991 992 grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc); 993 if (~gc.flags & GRID_FLAG_PADDING) 994 xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data); 995 return (value); 996 } 997 998 /* Return word at given coordinates. Caller frees. */ 999 char * 1000 format_grid_word(struct grid *gd, u_int x, u_int y) 1001 { 1002 const struct grid_line *gl; 1003 struct grid_cell gc; 1004 const char *ws; 1005 struct utf8_data *ud = NULL; 1006 u_int end; 1007 size_t size = 0; 1008 int found = 0; 1009 char *s = NULL; 1010 1011 ws = options_get_string(global_s_options, "word-separators"); 1012 1013 for (;;) { 1014 grid_get_cell(gd, x, y, &gc); 1015 if (gc.flags & GRID_FLAG_PADDING) 1016 break; 1017 if (utf8_cstrhas(ws, &gc.data)) { 1018 found = 1; 1019 break; 1020 } 1021 1022 if (x == 0) { 1023 if (y == 0) 1024 break; 1025 gl = grid_peek_line(gd, y - 1); 1026 if (~gl->flags & GRID_LINE_WRAPPED) 1027 break; 1028 y--; 1029 x = grid_line_length(gd, y); 1030 if (x == 0) 1031 break; 1032 } 1033 x--; 1034 } 1035 for (;;) { 1036 if (found) { 1037 end = grid_line_length(gd, y); 1038 if (end == 0 || x == end - 1) { 1039 if (y == gd->hsize + gd->sy - 1) 1040 break; 1041 gl = grid_peek_line(gd, y); 1042 if (~gl->flags & GRID_LINE_WRAPPED) 1043 break; 1044 y++; 1045 x = 0; 1046 } else 1047 x++; 1048 } 1049 found = 1; 1050 1051 grid_get_cell(gd, x, y, &gc); 1052 if (gc.flags & GRID_FLAG_PADDING) 1053 break; 1054 if (utf8_cstrhas(ws, &gc.data)) 1055 break; 1056 1057 ud = xreallocarray(ud, size + 2, sizeof *ud); 1058 memcpy(&ud[size++], &gc.data, sizeof *ud); 1059 } 1060 if (size != 0) { 1061 ud[size].size = 0; 1062 s = utf8_tocstr(ud); 1063 free(ud); 1064 } 1065 return (s); 1066 } 1067 1068 /* Callback for mouse_word. */ 1069 static char * 1070 format_cb_mouse_word(struct format_tree *ft) 1071 { 1072 struct window_pane *wp; 1073 struct grid *gd; 1074 u_int x, y; 1075 char *s; 1076 1077 if (!ft->m.valid) 1078 return (NULL); 1079 wp = cmd_mouse_pane(&ft->m, NULL, NULL); 1080 if (wp == NULL) 1081 return (NULL); 1082 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) 1083 return (NULL); 1084 1085 if (!TAILQ_EMPTY(&wp->modes)) { 1086 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || 1087 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) 1088 return (s = window_copy_get_word(wp, x, y)); 1089 return (NULL); 1090 } 1091 gd = wp->base.grid; 1092 return (format_grid_word(gd, x, gd->hsize + y)); 1093 } 1094 1095 /* Return line at given coordinates. Caller frees. */ 1096 char * 1097 format_grid_line(struct grid *gd, u_int y) 1098 { 1099 struct grid_cell gc; 1100 struct utf8_data *ud = NULL; 1101 u_int x; 1102 size_t size = 0; 1103 char *s = NULL; 1104 1105 for (x = 0; x < grid_line_length(gd, y); x++) { 1106 grid_get_cell(gd, x, y, &gc); 1107 if (gc.flags & GRID_FLAG_PADDING) 1108 break; 1109 1110 ud = xreallocarray(ud, size + 2, sizeof *ud); 1111 memcpy(&ud[size++], &gc.data, sizeof *ud); 1112 } 1113 if (size != 0) { 1114 ud[size].size = 0; 1115 s = utf8_tocstr(ud); 1116 free(ud); 1117 } 1118 return (s); 1119 } 1120 1121 /* Callback for mouse_line. */ 1122 static char * 1123 format_cb_mouse_line(struct format_tree *ft) 1124 { 1125 struct window_pane *wp; 1126 struct grid *gd; 1127 u_int x, y; 1128 1129 if (!ft->m.valid) 1130 return (NULL); 1131 wp = cmd_mouse_pane(&ft->m, NULL, NULL); 1132 if (wp == NULL) 1133 return (NULL); 1134 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) 1135 return (NULL); 1136 1137 if (!TAILQ_EMPTY(&wp->modes)) { 1138 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || 1139 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) 1140 return (window_copy_get_line(wp, y)); 1141 return (NULL); 1142 } 1143 gd = wp->base.grid; 1144 return (format_grid_line(gd, gd->hsize + y)); 1145 } 1146 1147 /* Merge one format tree into another. */ 1148 void 1149 format_merge(struct format_tree *ft, struct format_tree *from) 1150 { 1151 struct format_entry *fe; 1152 1153 RB_FOREACH(fe, format_entry_tree, &from->tree) { 1154 if (fe->value != NULL) 1155 format_add(ft, fe->key, "%s", fe->value); 1156 } 1157 } 1158 1159 /* Get format pane. */ 1160 struct window_pane * 1161 format_get_pane(struct format_tree *ft) 1162 { 1163 return (ft->wp); 1164 } 1165 1166 /* Add item bits to tree. */ 1167 static void 1168 format_create_add_item(struct format_tree *ft, struct cmdq_item *item) 1169 { 1170 struct key_event *event = cmdq_get_event(item); 1171 struct mouse_event *m = &event->m; 1172 struct window_pane *wp; 1173 u_int x, y; 1174 1175 cmdq_merge_formats(item, ft); 1176 1177 if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { 1178 format_add(ft, "mouse_pane", "%%%u", wp->id); 1179 if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { 1180 format_add(ft, "mouse_x", "%u", x); 1181 format_add(ft, "mouse_y", "%u", y); 1182 format_add_cb(ft, "mouse_word", format_cb_mouse_word); 1183 format_add_cb(ft, "mouse_line", format_cb_mouse_line); 1184 } 1185 } 1186 memcpy(&ft->m, m, sizeof ft->m); 1187 } 1188 1189 /* Create a new tree. */ 1190 struct format_tree * 1191 format_create(struct client *c, struct cmdq_item *item, int tag, int flags) 1192 { 1193 struct format_tree *ft; 1194 const struct window_mode **wm; 1195 char tmp[64]; 1196 1197 if (!event_initialized(&format_job_event)) { 1198 evtimer_set(&format_job_event, format_job_timer, NULL); 1199 format_job_timer(-1, 0, NULL); 1200 } 1201 1202 ft = xcalloc(1, sizeof *ft); 1203 RB_INIT(&ft->tree); 1204 1205 if (c != NULL) { 1206 ft->client = c; 1207 ft->client->references++; 1208 } 1209 ft->item = item; 1210 1211 ft->tag = tag; 1212 ft->flags = flags; 1213 ft->time = time(NULL); 1214 1215 format_add(ft, "version", "%s", getversion()); 1216 format_add_cb(ft, "host", format_cb_host); 1217 format_add_cb(ft, "host_short", format_cb_host_short); 1218 format_add_cb(ft, "pid", format_cb_pid); 1219 format_add(ft, "socket_path", "%s", socket_path); 1220 format_add_tv(ft, "start_time", &start_time); 1221 1222 for (wm = all_window_modes; *wm != NULL; wm++) { 1223 if ((*wm)->default_format != NULL) { 1224 xsnprintf(tmp, sizeof tmp, "%s_format", (*wm)->name); 1225 tmp[strcspn(tmp, "-")] = '_'; 1226 format_add(ft, tmp, "%s", (*wm)->default_format); 1227 } 1228 } 1229 1230 if (item != NULL) 1231 format_create_add_item(ft, item); 1232 1233 return (ft); 1234 } 1235 1236 /* Free a tree. */ 1237 void 1238 format_free(struct format_tree *ft) 1239 { 1240 struct format_entry *fe, *fe1; 1241 1242 RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { 1243 RB_REMOVE(format_entry_tree, &ft->tree, fe); 1244 free(fe->value); 1245 free(fe->key); 1246 free(fe); 1247 } 1248 1249 if (ft->client != NULL) 1250 server_client_unref(ft->client); 1251 free(ft); 1252 } 1253 1254 /* Walk each format. */ 1255 void 1256 format_each(struct format_tree *ft, void (*cb)(const char *, const char *, 1257 void *), void *arg) 1258 { 1259 struct format_entry *fe; 1260 char s[64]; 1261 1262 RB_FOREACH(fe, format_entry_tree, &ft->tree) { 1263 if (fe->t != 0) { 1264 xsnprintf(s, sizeof s, "%lld", (long long)fe->t); 1265 cb(fe->key, s, arg); 1266 } else { 1267 if (fe->value == NULL && fe->cb != NULL) { 1268 fe->value = fe->cb(ft); 1269 if (fe->value == NULL) 1270 fe->value = xstrdup(""); 1271 } 1272 cb(fe->key, fe->value, arg); 1273 } 1274 } 1275 } 1276 1277 /* Add a key-value pair. */ 1278 void 1279 format_add(struct format_tree *ft, const char *key, const char *fmt, ...) 1280 { 1281 struct format_entry *fe; 1282 struct format_entry *fe_now; 1283 va_list ap; 1284 1285 fe = xmalloc(sizeof *fe); 1286 fe->key = xstrdup(key); 1287 1288 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1289 if (fe_now != NULL) { 1290 free(fe->key); 1291 free(fe); 1292 free(fe_now->value); 1293 fe = fe_now; 1294 } 1295 1296 fe->cb = NULL; 1297 fe->t = 0; 1298 1299 va_start(ap, fmt); 1300 xvasprintf(&fe->value, fmt, ap); 1301 va_end(ap); 1302 } 1303 1304 /* Add a key and time. */ 1305 void 1306 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) 1307 { 1308 struct format_entry *fe, *fe_now; 1309 1310 fe = xmalloc(sizeof *fe); 1311 fe->key = xstrdup(key); 1312 1313 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1314 if (fe_now != NULL) { 1315 free(fe->key); 1316 free(fe); 1317 free(fe_now->value); 1318 fe = fe_now; 1319 } 1320 1321 fe->cb = NULL; 1322 fe->t = tv->tv_sec; 1323 1324 fe->value = NULL; 1325 } 1326 1327 /* Add a key and function. */ 1328 void 1329 format_add_cb(struct format_tree *ft, const char *key, format_cb cb) 1330 { 1331 struct format_entry *fe; 1332 struct format_entry *fe_now; 1333 1334 fe = xmalloc(sizeof *fe); 1335 fe->key = xstrdup(key); 1336 1337 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1338 if (fe_now != NULL) { 1339 free(fe->key); 1340 free(fe); 1341 free(fe_now->value); 1342 fe = fe_now; 1343 } 1344 1345 fe->cb = cb; 1346 fe->t = 0; 1347 1348 fe->value = NULL; 1349 } 1350 1351 /* Quote special characters in string. */ 1352 static char * 1353 format_quote(const char *s) 1354 { 1355 const char *cp; 1356 char *out, *at; 1357 1358 at = out = xmalloc(strlen(s) * 2 + 1); 1359 for (cp = s; *cp != '\0'; cp++) { 1360 if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL) 1361 *at++ = '\\'; 1362 *at++ = *cp; 1363 } 1364 *at = '\0'; 1365 return (out); 1366 } 1367 1368 /* Make a prettier time. */ 1369 static char * 1370 format_pretty_time(time_t t) 1371 { 1372 struct tm now_tm, tm; 1373 time_t now, age; 1374 char s[6]; 1375 int m; 1376 1377 time(&now); 1378 if (now < t) 1379 now = t; 1380 age = now - t; 1381 1382 localtime_r(&now, &now_tm); 1383 localtime_r(&t, &tm); 1384 1385 /* Last 24 hours. */ 1386 if (age < 24 * 3600) { 1387 strftime(s, sizeof s, "%H:%M", &tm); 1388 return (xstrdup(s)); 1389 } 1390 1391 /* This month or last 28 days. */ 1392 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) || 1393 age < 28 * 24 * 3600) { 1394 strftime(s, sizeof s, "%a%d", &tm); 1395 return (xstrdup(s)); 1396 } 1397 1398 /* Last 12 months. */ 1399 if (now_tm.tm_mon == 0) 1400 m = 11; 1401 else 1402 m = now_tm.tm_mon - 1; 1403 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) || 1404 (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) { 1405 strftime(s, sizeof s, "%d%b", &tm); 1406 return (xstrdup(s)); 1407 } 1408 1409 /* Older than that. */ 1410 strftime(s, sizeof s, "%h%y", &tm); 1411 return (xstrdup(s)); 1412 } 1413 1414 /* Find a format entry. */ 1415 static char * 1416 format_find(struct format_tree *ft, const char *key, int modifiers, 1417 const char *time_format) 1418 { 1419 struct format_entry *fe, fe_find; 1420 struct environ_entry *envent; 1421 struct options_entry *o; 1422 int idx; 1423 char *found = NULL, *saved, s[512]; 1424 const char *errstr; 1425 time_t t = 0; 1426 struct tm tm; 1427 1428 o = options_parse_get(global_options, key, &idx, 0); 1429 if (o == NULL && ft->wp != NULL) 1430 o = options_parse_get(ft->wp->options, key, &idx, 0); 1431 if (o == NULL && ft->w != NULL) 1432 o = options_parse_get(ft->w->options, key, &idx, 0); 1433 if (o == NULL) 1434 o = options_parse_get(global_w_options, key, &idx, 0); 1435 if (o == NULL && ft->s != NULL) 1436 o = options_parse_get(ft->s->options, key, &idx, 0); 1437 if (o == NULL) 1438 o = options_parse_get(global_s_options, key, &idx, 0); 1439 if (o != NULL) { 1440 found = options_to_string(o, idx, 1); 1441 goto found; 1442 } 1443 1444 fe_find.key = (char *)key; 1445 fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); 1446 if (fe != NULL) { 1447 if (fe->t != 0) { 1448 t = fe->t; 1449 goto found; 1450 } 1451 if (fe->value == NULL && fe->cb != NULL) { 1452 fe->value = fe->cb(ft); 1453 if (fe->value == NULL) 1454 fe->value = xstrdup(""); 1455 } 1456 found = xstrdup(fe->value); 1457 goto found; 1458 } 1459 1460 if (~modifiers & FORMAT_TIMESTRING) { 1461 envent = NULL; 1462 if (ft->s != NULL) 1463 envent = environ_find(ft->s->environ, key); 1464 if (envent == NULL) 1465 envent = environ_find(global_environ, key); 1466 if (envent != NULL && envent->value != NULL) { 1467 found = xstrdup(envent->value); 1468 goto found; 1469 } 1470 } 1471 1472 return (NULL); 1473 1474 found: 1475 if (modifiers & FORMAT_TIMESTRING) { 1476 if (t == 0 && found != NULL) { 1477 t = strtonum(found, 0, INT64_MAX, &errstr); 1478 if (errstr != NULL) 1479 t = 0; 1480 free(found); 1481 } 1482 if (t == 0) 1483 return (NULL); 1484 if (modifiers & FORMAT_PRETTY) 1485 found = format_pretty_time(t); 1486 else { 1487 if (time_format != NULL) { 1488 localtime_r(&t, &tm); 1489 strftime(s, sizeof s, time_format, &tm); 1490 } else { 1491 ctime_r(&t, s); 1492 s[strcspn(s, "\n")] = '\0'; 1493 } 1494 found = xstrdup(s); 1495 } 1496 return (found); 1497 } 1498 1499 if (t != 0) 1500 xasprintf(&found, "%lld", (long long)t); 1501 else if (found == NULL) 1502 return (NULL); 1503 if (modifiers & FORMAT_BASENAME) { 1504 saved = found; 1505 found = xstrdup(basename(saved)); 1506 free(saved); 1507 } 1508 if (modifiers & FORMAT_DIRNAME) { 1509 saved = found; 1510 found = xstrdup(dirname(saved)); 1511 free(saved); 1512 } 1513 if (modifiers & FORMAT_QUOTE) { 1514 saved = found; 1515 found = xstrdup(format_quote(saved)); 1516 free(saved); 1517 } 1518 return (found); 1519 } 1520 1521 /* Remove escaped characters from string. */ 1522 static char * 1523 format_strip(const char *s) 1524 { 1525 char *out, *cp; 1526 int brackets = 0; 1527 1528 cp = out = xmalloc(strlen(s) + 1); 1529 for (; *s != '\0'; s++) { 1530 if (*s == '#' && s[1] == '{') 1531 brackets++; 1532 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { 1533 if (brackets != 0) 1534 *cp++ = *s; 1535 continue; 1536 } 1537 if (*s == '}') 1538 brackets--; 1539 *cp++ = *s; 1540 } 1541 *cp = '\0'; 1542 return (out); 1543 } 1544 1545 /* Skip until end. */ 1546 const char * 1547 format_skip(const char *s, const char *end) 1548 { 1549 int brackets = 0; 1550 1551 for (; *s != '\0'; s++) { 1552 if (*s == '#' && s[1] == '{') 1553 brackets++; 1554 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { 1555 s++; 1556 continue; 1557 } 1558 if (*s == '}') 1559 brackets--; 1560 if (strchr(end, *s) != NULL && brackets == 0) 1561 break; 1562 } 1563 if (*s == '\0') 1564 return (NULL); 1565 return (s); 1566 } 1567 1568 /* Return left and right alternatives separated by commas. */ 1569 static int 1570 format_choose(struct format_tree *ft, const char *s, char **left, char **right, 1571 int expand) 1572 { 1573 const char *cp; 1574 char *left0, *right0; 1575 1576 cp = format_skip(s, ","); 1577 if (cp == NULL) 1578 return (-1); 1579 left0 = xstrndup(s, cp - s); 1580 right0 = xstrdup(cp + 1); 1581 1582 if (expand) { 1583 *left = format_expand(ft, left0); 1584 free(left0); 1585 *right = format_expand(ft, right0); 1586 free(right0); 1587 } else { 1588 *left = left0; 1589 *right = right0; 1590 } 1591 return (0); 1592 } 1593 1594 /* Is this true? */ 1595 int 1596 format_true(const char *s) 1597 { 1598 if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0')) 1599 return (1); 1600 return (0); 1601 } 1602 1603 /* Check if modifier end. */ 1604 static int 1605 format_is_end(char c) 1606 { 1607 return (c == ';' || c == ':'); 1608 } 1609 1610 /* Add to modifier list. */ 1611 static void 1612 format_add_modifier(struct format_modifier **list, u_int *count, 1613 const char *c, size_t n, char **argv, int argc) 1614 { 1615 struct format_modifier *fm; 1616 1617 *list = xreallocarray(*list, (*count) + 1, sizeof **list); 1618 fm = &(*list)[(*count)++]; 1619 1620 memcpy(fm->modifier, c, n); 1621 fm->modifier[n] = '\0'; 1622 fm->size = n; 1623 1624 fm->argv = argv; 1625 fm->argc = argc; 1626 } 1627 1628 /* Free modifier list. */ 1629 static void 1630 format_free_modifiers(struct format_modifier *list, u_int count) 1631 { 1632 u_int i; 1633 1634 for (i = 0; i < count; i++) 1635 cmd_free_argv(list[i].argc, list[i].argv); 1636 free(list); 1637 } 1638 1639 /* Build modifier list. */ 1640 static struct format_modifier * 1641 format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) 1642 { 1643 const char *cp = *s, *end; 1644 struct format_modifier *list = NULL; 1645 char c, last[] = "X;:", **argv, *value; 1646 int argc; 1647 1648 /* 1649 * Modifiers are a ; separated list of the forms: 1650 * l,m,C,b,d,t,q,E,T,S,W,P,<,> 1651 * =a 1652 * =/a 1653 * =/a/ 1654 * s/a/b/ 1655 * s/a/b 1656 * ||,&&,!=,==,<=,>= 1657 */ 1658 1659 *count = 0; 1660 1661 while (*cp != '\0' && *cp != ':') { 1662 /* Skip any separator character. */ 1663 if (*cp == ';') 1664 cp++; 1665 1666 /* Check single character modifiers with no arguments. */ 1667 if (strchr("lbdqETSWP<>", cp[0]) != NULL && 1668 format_is_end(cp[1])) { 1669 format_add_modifier(&list, count, cp, 1, NULL, 0); 1670 cp++; 1671 continue; 1672 } 1673 1674 /* Then try double character with no arguments. */ 1675 if ((memcmp("||", cp, 2) == 0 || 1676 memcmp("&&", cp, 2) == 0 || 1677 memcmp("!=", cp, 2) == 0 || 1678 memcmp("==", cp, 2) == 0 || 1679 memcmp("<=", cp, 2) == 0 || 1680 memcmp(">=", cp, 2) == 0) && 1681 format_is_end(cp[2])) { 1682 format_add_modifier(&list, count, cp, 2, NULL, 0); 1683 cp += 2; 1684 continue; 1685 } 1686 1687 /* Now try single character with arguments. */ 1688 if (strchr("mCst=pe", cp[0]) == NULL) 1689 break; 1690 c = cp[0]; 1691 1692 /* No arguments provided. */ 1693 if (format_is_end(cp[1])) { 1694 format_add_modifier(&list, count, cp, 1, NULL, 0); 1695 cp++; 1696 continue; 1697 } 1698 argv = NULL; 1699 argc = 0; 1700 1701 /* Single argument with no wrapper character. */ 1702 if (!ispunct(cp[1]) || cp[1] == '-') { 1703 end = format_skip(cp + 1, ":;"); 1704 if (end == NULL) 1705 break; 1706 1707 argv = xcalloc(1, sizeof *argv); 1708 value = xstrndup(cp + 1, end - (cp + 1)); 1709 argv[0] = format_expand(ft, value); 1710 free(value); 1711 argc = 1; 1712 1713 format_add_modifier(&list, count, &c, 1, argv, argc); 1714 cp = end; 1715 continue; 1716 } 1717 1718 /* Multiple arguments with a wrapper character. */ 1719 last[0] = cp[1]; 1720 cp++; 1721 do { 1722 if (cp[0] == last[0] && format_is_end(cp[1])) { 1723 cp++; 1724 break; 1725 } 1726 end = format_skip(cp + 1, last); 1727 if (end == NULL) 1728 break; 1729 cp++; 1730 1731 argv = xreallocarray (argv, argc + 1, sizeof *argv); 1732 value = xstrndup(cp, end - cp); 1733 argv[argc++] = format_expand(ft, value); 1734 free(value); 1735 1736 cp = end; 1737 } while (!format_is_end(cp[0])); 1738 format_add_modifier(&list, count, &c, 1, argv, argc); 1739 } 1740 if (*cp != ':') { 1741 format_free_modifiers(list, *count); 1742 *count = 0; 1743 return (NULL); 1744 } 1745 *s = cp + 1; 1746 return (list); 1747 } 1748 1749 /* Match against an fnmatch(3) pattern or regular expression. */ 1750 static char * 1751 format_match(struct format_modifier *fm, const char *pattern, const char *text) 1752 { 1753 const char *s = ""; 1754 regex_t r; 1755 int flags = 0; 1756 1757 if (fm->argc >= 1) 1758 s = fm->argv[0]; 1759 if (strchr(s, 'r') == NULL) { 1760 if (strchr(s, 'i') != NULL) 1761 flags |= FNM_CASEFOLD; 1762 if (fnmatch(pattern, text, flags) != 0) 1763 return (xstrdup("0")); 1764 } else { 1765 flags = REG_EXTENDED|REG_NOSUB; 1766 if (strchr(s, 'i') != NULL) 1767 flags |= REG_ICASE; 1768 if (regcomp(&r, pattern, flags) != 0) 1769 return (xstrdup("0")); 1770 if (regexec(&r, text, 0, NULL, 0) != 0) { 1771 regfree(&r); 1772 return (xstrdup("0")); 1773 } 1774 regfree(&r); 1775 } 1776 return (xstrdup("1")); 1777 } 1778 1779 /* Perform substitution in string. */ 1780 static char * 1781 format_sub(struct format_modifier *fm, const char *text, const char *pattern, 1782 const char *with) 1783 { 1784 char *value; 1785 int flags = REG_EXTENDED; 1786 1787 if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) 1788 flags |= REG_ICASE; 1789 value = regsub(pattern, with, text, flags); 1790 if (value == NULL) 1791 return (xstrdup(text)); 1792 return (value); 1793 } 1794 1795 /* Search inside pane. */ 1796 static char * 1797 format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) 1798 { 1799 int ignore = 0, regex = 0; 1800 char *value; 1801 1802 if (fm->argc >= 1) { 1803 if (strchr(fm->argv[0], 'i') != NULL) 1804 ignore = 1; 1805 if (strchr(fm->argv[0], 'r') != NULL) 1806 regex = 1; 1807 } 1808 xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore)); 1809 return (value); 1810 } 1811 1812 /* Loop over sessions. */ 1813 static char * 1814 format_loop_sessions(struct format_tree *ft, const char *fmt) 1815 { 1816 struct client *c = ft->client; 1817 struct cmdq_item *item = ft->item; 1818 struct format_tree *nft; 1819 char *expanded, *value; 1820 size_t valuelen; 1821 struct session *s; 1822 1823 value = xcalloc(1, 1); 1824 valuelen = 1; 1825 1826 RB_FOREACH(s, sessions, &sessions) { 1827 format_log(ft, "session loop: $%u", s->id); 1828 nft = format_create(c, item, FORMAT_NONE, ft->flags); 1829 nft->loop = ft->loop; 1830 format_defaults(nft, ft->c, s, NULL, NULL); 1831 expanded = format_expand(nft, fmt); 1832 format_free(nft); 1833 1834 valuelen += strlen(expanded); 1835 value = xrealloc(value, valuelen); 1836 1837 strlcat(value, expanded, valuelen); 1838 free(expanded); 1839 } 1840 1841 return (value); 1842 } 1843 1844 /* Loop over windows. */ 1845 static char * 1846 format_loop_windows(struct format_tree *ft, const char *fmt) 1847 { 1848 struct client *c = ft->client; 1849 struct cmdq_item *item = ft->item; 1850 struct format_tree *nft; 1851 char *all, *active, *use, *expanded, *value; 1852 size_t valuelen; 1853 struct winlink *wl; 1854 struct window *w; 1855 1856 if (ft->s == NULL) { 1857 format_log(ft, "window loop but no session"); 1858 return (NULL); 1859 } 1860 1861 if (format_choose(ft, fmt, &all, &active, 0) != 0) { 1862 all = xstrdup(fmt); 1863 active = NULL; 1864 } 1865 1866 value = xcalloc(1, 1); 1867 valuelen = 1; 1868 1869 RB_FOREACH(wl, winlinks, &ft->s->windows) { 1870 w = wl->window; 1871 format_log(ft, "window loop: %u @%u", wl->idx, w->id); 1872 if (active != NULL && wl == ft->s->curw) 1873 use = active; 1874 else 1875 use = all; 1876 nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); 1877 nft->loop = ft->loop; 1878 format_defaults(nft, ft->c, ft->s, wl, NULL); 1879 expanded = format_expand(nft, use); 1880 format_free(nft); 1881 1882 valuelen += strlen(expanded); 1883 value = xrealloc(value, valuelen); 1884 1885 strlcat(value, expanded, valuelen); 1886 free(expanded); 1887 } 1888 1889 free(active); 1890 free(all); 1891 1892 return (value); 1893 } 1894 1895 /* Loop over panes. */ 1896 static char * 1897 format_loop_panes(struct format_tree *ft, const char *fmt) 1898 { 1899 struct client *c = ft->client; 1900 struct cmdq_item *item = ft->item; 1901 struct format_tree *nft; 1902 char *all, *active, *use, *expanded, *value; 1903 size_t valuelen; 1904 struct window_pane *wp; 1905 1906 if (ft->w == NULL) { 1907 format_log(ft, "pane loop but no window"); 1908 return (NULL); 1909 } 1910 1911 if (format_choose(ft, fmt, &all, &active, 0) != 0) { 1912 all = xstrdup(fmt); 1913 active = NULL; 1914 } 1915 1916 value = xcalloc(1, 1); 1917 valuelen = 1; 1918 1919 TAILQ_FOREACH(wp, &ft->w->panes, entry) { 1920 format_log(ft, "pane loop: %%%u", wp->id); 1921 if (active != NULL && wp == ft->w->active) 1922 use = active; 1923 else 1924 use = all; 1925 nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); 1926 nft->loop = ft->loop; 1927 format_defaults(nft, ft->c, ft->s, ft->wl, wp); 1928 expanded = format_expand(nft, use); 1929 format_free(nft); 1930 1931 valuelen += strlen(expanded); 1932 value = xrealloc(value, valuelen); 1933 1934 strlcat(value, expanded, valuelen); 1935 free(expanded); 1936 } 1937 1938 free(active); 1939 free(all); 1940 1941 return (value); 1942 } 1943 1944 static char * 1945 format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, 1946 const char *copy) 1947 { 1948 int argc = mexp->argc; 1949 const char *errstr; 1950 char *endch, *value, *left = NULL, *right = NULL; 1951 int use_fp = 0; 1952 u_int prec = 0; 1953 double mleft, mright, result; 1954 enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; 1955 1956 if (strcmp(mexp->argv[0], "+") == 0) 1957 operator = ADD; 1958 else if (strcmp(mexp->argv[0], "-") == 0) 1959 operator = SUBTRACT; 1960 else if (strcmp(mexp->argv[0], "*") == 0) 1961 operator = MULTIPLY; 1962 else if (strcmp(mexp->argv[0], "/") == 0) 1963 operator = DIVIDE; 1964 else if (strcmp(mexp->argv[0], "%") == 0 || 1965 strcmp(mexp->argv[0], "m") == 0) 1966 operator = MODULUS; 1967 else { 1968 format_log(ft, "expression has no valid operator: '%s'", 1969 mexp->argv[0]); 1970 goto fail; 1971 } 1972 1973 /* The second argument may be flags. */ 1974 if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) { 1975 use_fp = 1; 1976 prec = 2; 1977 } 1978 1979 /* The third argument may be precision. */ 1980 if (argc >= 3) { 1981 prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); 1982 if (errstr != NULL) { 1983 format_log (ft, "expression precision %s: %s", errstr, 1984 mexp->argv[2]); 1985 goto fail; 1986 } 1987 } 1988 1989 if (format_choose(ft, copy, &left, &right, 1) != 0) { 1990 format_log(ft, "expression syntax error"); 1991 goto fail; 1992 } 1993 1994 mleft = strtod(left, &endch); 1995 if (*endch != '\0') { 1996 format_log(ft, "expression left side is invalid: %s", left); 1997 goto fail; 1998 } 1999 2000 mright = strtod(right, &endch); 2001 if (*endch != '\0') { 2002 format_log(ft, "expression right side is invalid: %s", right); 2003 goto fail; 2004 } 2005 2006 if (!use_fp) { 2007 mleft = (long long)mleft; 2008 mright = (long long)mright; 2009 } 2010 format_log(ft, "expression left side is: %.*f", prec, mleft); 2011 format_log(ft, "expression right side is: %.*f", prec, mright); 2012 2013 switch (operator) { 2014 case ADD: 2015 result = mleft + mright; 2016 break; 2017 case SUBTRACT: 2018 result = mleft - mright; 2019 break; 2020 case MULTIPLY: 2021 result = mleft * mright; 2022 break; 2023 case DIVIDE: 2024 result = mleft / mright; 2025 break; 2026 case MODULUS: 2027 result = fmod(mleft, mright); 2028 break; 2029 } 2030 if (use_fp) 2031 xasprintf(&value, "%.*f", prec, result); 2032 else 2033 xasprintf(&value, "%.*f", prec, (double)(long long)result); 2034 format_log(ft, "expression result is %s", value); 2035 2036 free(right); 2037 free(left); 2038 return (value); 2039 2040 fail: 2041 free(right); 2042 free(left); 2043 return (NULL); 2044 } 2045 2046 /* Replace a key. */ 2047 static int 2048 format_replace(struct format_tree *ft, const char *key, size_t keylen, 2049 char **buf, size_t *len, size_t *off) 2050 { 2051 struct window_pane *wp = ft->wp; 2052 const char *errptr, *copy, *cp, *marker = NULL; 2053 const char *time_format = NULL; 2054 char *copy0, *condition, *found, *new; 2055 char *value, *left, *right; 2056 size_t valuelen; 2057 int modifiers = 0, limit = 0, width = 0, j; 2058 struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; 2059 struct format_modifier **sub = NULL, *mexp = NULL; 2060 u_int i, count, nsub = 0; 2061 2062 /* Make a copy of the key. */ 2063 copy = copy0 = xstrndup(key, keylen); 2064 2065 /* Process modifier list. */ 2066 list = format_build_modifiers(ft, ©, &count); 2067 for (i = 0; i < count; i++) { 2068 fm = &list[i]; 2069 if (format_logging(ft)) { 2070 format_log(ft, "modifier %u is %s", i, fm->modifier); 2071 for (j = 0; j < fm->argc; j++) { 2072 format_log(ft, "modifier %u argument %d: %s", i, 2073 j, fm->argv[j]); 2074 } 2075 } 2076 if (fm->size == 1) { 2077 switch (fm->modifier[0]) { 2078 case 'm': 2079 case '<': 2080 case '>': 2081 cmp = fm; 2082 break; 2083 case 'C': 2084 search = fm; 2085 break; 2086 case 's': 2087 if (fm->argc < 2) 2088 break; 2089 sub = xreallocarray (sub, nsub + 1, 2090 sizeof *sub); 2091 sub[nsub++] = fm; 2092 break; 2093 case '=': 2094 if (fm->argc < 1) 2095 break; 2096 limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, 2097 &errptr); 2098 if (errptr != NULL) 2099 limit = 0; 2100 if (fm->argc >= 2 && fm->argv[1] != NULL) 2101 marker = fm->argv[1]; 2102 break; 2103 case 'p': 2104 if (fm->argc < 1) 2105 break; 2106 width = strtonum(fm->argv[0], INT_MIN, INT_MAX, 2107 &errptr); 2108 if (errptr != NULL) 2109 width = 0; 2110 break; 2111 case 'e': 2112 if (fm->argc < 1 || fm->argc > 3) 2113 break; 2114 mexp = fm; 2115 break; 2116 case 'l': 2117 modifiers |= FORMAT_LITERAL; 2118 break; 2119 case 'b': 2120 modifiers |= FORMAT_BASENAME; 2121 break; 2122 case 'd': 2123 modifiers |= FORMAT_DIRNAME; 2124 break; 2125 case 't': 2126 modifiers |= FORMAT_TIMESTRING; 2127 if (fm->argc < 1) 2128 break; 2129 if (strchr(fm->argv[0], 'p') != NULL) 2130 modifiers |= FORMAT_PRETTY; 2131 else if (fm->argc >= 2 && 2132 strchr(fm->argv[0], 'f') != NULL) 2133 time_format = format_strip(fm->argv[1]); 2134 break; 2135 case 'q': 2136 modifiers |= FORMAT_QUOTE; 2137 break; 2138 case 'E': 2139 modifiers |= FORMAT_EXPAND; 2140 break; 2141 case 'T': 2142 modifiers |= FORMAT_EXPANDTIME; 2143 break; 2144 case 'S': 2145 modifiers |= FORMAT_SESSIONS; 2146 break; 2147 case 'W': 2148 modifiers |= FORMAT_WINDOWS; 2149 break; 2150 case 'P': 2151 modifiers |= FORMAT_PANES; 2152 break; 2153 } 2154 } else if (fm->size == 2) { 2155 if (strcmp(fm->modifier, "||") == 0 || 2156 strcmp(fm->modifier, "&&") == 0 || 2157 strcmp(fm->modifier, "==") == 0 || 2158 strcmp(fm->modifier, "!=") == 0 || 2159 strcmp(fm->modifier, ">=") == 0 || 2160 strcmp(fm->modifier, "<=") == 0) 2161 cmp = fm; 2162 } 2163 } 2164 2165 /* Is this a literal string? */ 2166 if (modifiers & FORMAT_LITERAL) { 2167 value = xstrdup(copy); 2168 goto done; 2169 } 2170 2171 /* Is this a loop, comparison or condition? */ 2172 if (modifiers & FORMAT_SESSIONS) { 2173 value = format_loop_sessions(ft, copy); 2174 if (value == NULL) 2175 goto fail; 2176 } else if (modifiers & FORMAT_WINDOWS) { 2177 value = format_loop_windows(ft, copy); 2178 if (value == NULL) 2179 goto fail; 2180 } else if (modifiers & FORMAT_PANES) { 2181 value = format_loop_panes(ft, copy); 2182 if (value == NULL) 2183 goto fail; 2184 } else if (search != NULL) { 2185 /* Search in pane. */ 2186 new = format_expand(ft, copy); 2187 if (wp == NULL) { 2188 format_log(ft, "search '%s' but no pane", new); 2189 value = xstrdup("0"); 2190 } else { 2191 format_log(ft, "search '%s' pane %%%u", new, wp->id); 2192 value = format_search(fm, wp, new); 2193 } 2194 free(new); 2195 } else if (cmp != NULL) { 2196 /* Comparison of left and right. */ 2197 if (format_choose(ft, copy, &left, &right, 1) != 0) { 2198 format_log(ft, "compare %s syntax error: %s", 2199 cmp->modifier, copy); 2200 goto fail; 2201 } 2202 format_log(ft, "compare %s left is: %s", cmp->modifier, left); 2203 format_log(ft, "compare %s right is: %s", cmp->modifier, right); 2204 2205 if (strcmp(cmp->modifier, "||") == 0) { 2206 if (format_true(left) || format_true(right)) 2207 value = xstrdup("1"); 2208 else 2209 value = xstrdup("0"); 2210 } else if (strcmp(cmp->modifier, "&&") == 0) { 2211 if (format_true(left) && format_true(right)) 2212 value = xstrdup("1"); 2213 else 2214 value = xstrdup("0"); 2215 } else if (strcmp(cmp->modifier, "==") == 0) { 2216 if (strcmp(left, right) == 0) 2217 value = xstrdup("1"); 2218 else 2219 value = xstrdup("0"); 2220 } else if (strcmp(cmp->modifier, "!=") == 0) { 2221 if (strcmp(left, right) != 0) 2222 value = xstrdup("1"); 2223 else 2224 value = xstrdup("0"); 2225 } else if (strcmp(cmp->modifier, "<") == 0) { 2226 if (strcmp(left, right) < 0) 2227 value = xstrdup("1"); 2228 else 2229 value = xstrdup("0"); 2230 } else if (strcmp(cmp->modifier, ">") == 0) { 2231 if (strcmp(left, right) > 0) 2232 value = xstrdup("1"); 2233 else 2234 value = xstrdup("0"); 2235 } else if (strcmp(cmp->modifier, "<=") == 0) { 2236 if (strcmp(left, right) <= 0) 2237 value = xstrdup("1"); 2238 else 2239 value = xstrdup("0"); 2240 } else if (strcmp(cmp->modifier, ">=") == 0) { 2241 if (strcmp(left, right) >= 0) 2242 value = xstrdup("1"); 2243 else 2244 value = xstrdup("0"); 2245 } else if (strcmp(cmp->modifier, "m") == 0) 2246 value = format_match(cmp, left, right); 2247 2248 free(right); 2249 free(left); 2250 } else if (*copy == '?') { 2251 /* Conditional: check first and choose second or third. */ 2252 cp = format_skip(copy + 1, ","); 2253 if (cp == NULL) { 2254 format_log(ft, "condition syntax error: %s", copy + 1); 2255 goto fail; 2256 } 2257 condition = xstrndup(copy + 1, cp - (copy + 1)); 2258 format_log(ft, "condition is: %s", condition); 2259 2260 found = format_find(ft, condition, modifiers, time_format); 2261 if (found == NULL) { 2262 /* 2263 * If the condition not found, try to expand it. If 2264 * the expansion doesn't have any effect, then assume 2265 * false. 2266 */ 2267 found = format_expand(ft, condition); 2268 if (strcmp(found, condition) == 0) { 2269 free(found); 2270 found = xstrdup(""); 2271 format_log(ft, "condition '%s' found: %s", 2272 condition, found); 2273 } else { 2274 format_log(ft, 2275 "condition '%s' not found; assuming false", 2276 condition); 2277 } 2278 } else 2279 format_log(ft, "condition '%s' found", condition); 2280 2281 if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { 2282 format_log(ft, "condition '%s' syntax error: %s", 2283 condition, cp + 1); 2284 free(found); 2285 goto fail; 2286 } 2287 if (format_true(found)) { 2288 format_log(ft, "condition '%s' is true", condition); 2289 value = format_expand(ft, left); 2290 } else { 2291 format_log(ft, "condition '%s' is false", condition); 2292 value = format_expand(ft, right); 2293 } 2294 free(right); 2295 free(left); 2296 2297 free(condition); 2298 free(found); 2299 } else if (mexp != NULL) { 2300 value = format_replace_expression(mexp, ft, copy); 2301 if (value == NULL) 2302 value = xstrdup(""); 2303 } else { 2304 /* Neither: look up directly. */ 2305 value = format_find(ft, copy, modifiers, time_format); 2306 if (value == NULL) { 2307 format_log(ft, "format '%s' not found", copy); 2308 value = xstrdup(""); 2309 } else 2310 format_log(ft, "format '%s' found: %s", copy, value); 2311 } 2312 2313 done: 2314 /* Expand again if required. */ 2315 if (modifiers & FORMAT_EXPAND) { 2316 new = format_expand(ft, value); 2317 free(value); 2318 value = new; 2319 } 2320 else if (modifiers & FORMAT_EXPANDTIME) { 2321 new = format_expand_time(ft, value); 2322 free(value); 2323 value = new; 2324 } 2325 2326 /* Perform substitution if any. */ 2327 for (i = 0; i < nsub; i++) { 2328 left = format_expand(ft, sub[i]->argv[0]); 2329 right = format_expand(ft, sub[i]->argv[1]); 2330 new = format_sub(sub[i], value, left, right); 2331 format_log(ft, "substitute '%s' to '%s': %s", left, right, new); 2332 free(value); 2333 value = new; 2334 free(right); 2335 free(left); 2336 } 2337 2338 /* Truncate the value if needed. */ 2339 if (limit > 0) { 2340 new = format_trim_left(value, limit); 2341 if (marker != NULL && strcmp(new, value) != 0) { 2342 free(value); 2343 xasprintf(&value, "%s%s", new, marker); 2344 } else { 2345 free(value); 2346 value = new; 2347 } 2348 format_log(ft, "applied length limit %d: %s", limit, value); 2349 } else if (limit < 0) { 2350 new = format_trim_right(value, -limit); 2351 if (marker != NULL && strcmp(new, value) != 0) { 2352 free(value); 2353 xasprintf(&value, "%s%s", marker, new); 2354 } else { 2355 free(value); 2356 value = new; 2357 } 2358 format_log(ft, "applied length limit %d: %s", limit, value); 2359 } 2360 2361 /* Pad the value if needed. */ 2362 if (width > 0) { 2363 new = utf8_padcstr(value, width); 2364 free(value); 2365 value = new; 2366 format_log(ft, "applied padding width %d: %s", width, value); 2367 } else if (width < 0) { 2368 new = utf8_rpadcstr(value, -width); 2369 free(value); 2370 value = new; 2371 format_log(ft, "applied padding width %d: %s", width, value); 2372 } 2373 2374 /* Expand the buffer and copy in the value. */ 2375 valuelen = strlen(value); 2376 while (*len - *off < valuelen + 1) { 2377 *buf = xreallocarray(*buf, 2, *len); 2378 *len *= 2; 2379 } 2380 memcpy(*buf + *off, value, valuelen); 2381 *off += valuelen; 2382 2383 format_log(ft, "replaced '%s' with '%s'", copy0, value); 2384 free(value); 2385 2386 free(sub); 2387 format_free_modifiers(list, count); 2388 free(copy0); 2389 return (0); 2390 2391 fail: 2392 format_log(ft, "failed %s", copy0); 2393 2394 free(sub); 2395 format_free_modifiers(list, count); 2396 free(copy0); 2397 return (-1); 2398 } 2399 2400 /* Expand keys in a template. */ 2401 static char * 2402 format_expand1(struct format_tree *ft, const char *fmt, int time) 2403 { 2404 char *buf, *out, *name; 2405 const char *ptr, *s; 2406 size_t off, len, n, outlen; 2407 int ch, brackets; 2408 struct tm *tm; 2409 char expanded[8192]; 2410 2411 if (fmt == NULL || *fmt == '\0') 2412 return (xstrdup("")); 2413 2414 if (ft->loop == FORMAT_LOOP_LIMIT) 2415 return (xstrdup("")); 2416 ft->loop++; 2417 2418 format_log(ft, "expanding format: %s", fmt); 2419 2420 if (time) { 2421 tm = localtime(&ft->time); 2422 if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { 2423 format_log(ft, "format is too long"); 2424 return (xstrdup("")); 2425 } 2426 if (format_logging(ft) && strcmp(expanded, fmt) != 0) 2427 format_log(ft, "after time expanded: %s", expanded); 2428 fmt = expanded; 2429 } 2430 2431 len = 64; 2432 buf = xmalloc(len); 2433 off = 0; 2434 2435 while (*fmt != '\0') { 2436 if (*fmt != '#') { 2437 while (len - off < 2) { 2438 buf = xreallocarray(buf, 2, len); 2439 len *= 2; 2440 } 2441 buf[off++] = *fmt++; 2442 continue; 2443 } 2444 fmt++; 2445 2446 ch = (u_char)*fmt++; 2447 switch (ch) { 2448 case '(': 2449 brackets = 1; 2450 for (ptr = fmt; *ptr != '\0'; ptr++) { 2451 if (*ptr == '(') 2452 brackets++; 2453 if (*ptr == ')' && --brackets == 0) 2454 break; 2455 } 2456 if (*ptr != ')' || brackets != 0) 2457 break; 2458 n = ptr - fmt; 2459 2460 name = xstrndup(fmt, n); 2461 format_log(ft, "found #(): %s", name); 2462 2463 if (ft->flags & FORMAT_NOJOBS) { 2464 out = xstrdup(""); 2465 format_log(ft, "#() is disabled"); 2466 } else { 2467 out = format_job_get(ft, name); 2468 format_log(ft, "#() result: %s", out); 2469 } 2470 free(name); 2471 2472 outlen = strlen(out); 2473 while (len - off < outlen + 1) { 2474 buf = xreallocarray(buf, 2, len); 2475 len *= 2; 2476 } 2477 memcpy(buf + off, out, outlen); 2478 off += outlen; 2479 2480 free(out); 2481 2482 fmt += n + 1; 2483 continue; 2484 case '{': 2485 ptr = format_skip((char *)fmt - 2, "}"); 2486 if (ptr == NULL) 2487 break; 2488 n = ptr - fmt; 2489 2490 format_log(ft, "found #{}: %.*s", (int)n, fmt); 2491 if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) 2492 break; 2493 fmt += n + 1; 2494 continue; 2495 case '}': 2496 case '#': 2497 case ',': 2498 format_log(ft, "found #%c", ch); 2499 while (len - off < 2) { 2500 buf = xreallocarray(buf, 2, len); 2501 len *= 2; 2502 } 2503 buf[off++] = ch; 2504 continue; 2505 default: 2506 s = NULL; 2507 if (ch >= 'A' && ch <= 'Z') 2508 s = format_upper[ch - 'A']; 2509 else if (ch >= 'a' && ch <= 'z') 2510 s = format_lower[ch - 'a']; 2511 if (s == NULL) { 2512 while (len - off < 3) { 2513 buf = xreallocarray(buf, 2, len); 2514 len *= 2; 2515 } 2516 buf[off++] = '#'; 2517 buf[off++] = ch; 2518 continue; 2519 } 2520 n = strlen(s); 2521 format_log(ft, "found #%c: %s", ch, s); 2522 if (format_replace(ft, s, n, &buf, &len, &off) != 0) 2523 break; 2524 continue; 2525 } 2526 2527 break; 2528 } 2529 buf[off] = '\0'; 2530 2531 format_log(ft, "result is: %s", buf); 2532 ft->loop--; 2533 2534 return (buf); 2535 } 2536 2537 /* Expand keys in a template, passing through strftime first. */ 2538 char * 2539 format_expand_time(struct format_tree *ft, const char *fmt) 2540 { 2541 return (format_expand1(ft, fmt, 1)); 2542 } 2543 2544 /* Expand keys in a template. */ 2545 char * 2546 format_expand(struct format_tree *ft, const char *fmt) 2547 { 2548 return (format_expand1(ft, fmt, 0)); 2549 } 2550 2551 /* Expand a single string. */ 2552 char * 2553 format_single(struct cmdq_item *item, const char *fmt, struct client *c, 2554 struct session *s, struct winlink *wl, struct window_pane *wp) 2555 { 2556 struct format_tree *ft; 2557 char *expanded; 2558 2559 ft = format_create_defaults(item, c, s, wl, wp); 2560 expanded = format_expand(ft, fmt); 2561 format_free(ft); 2562 return (expanded); 2563 } 2564 2565 /* Expand a single string using state. */ 2566 char * 2567 format_single_from_state(struct cmdq_item *item, const char *fmt, 2568 struct client *c, struct cmd_find_state *fs) 2569 { 2570 return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp)); 2571 } 2572 2573 /* Expand a single string using target. */ 2574 char * 2575 format_single_from_target(struct cmdq_item *item, const char *fmt) 2576 { 2577 struct client *tc = cmdq_get_target_client(item); 2578 2579 return (format_single_from_state(item, fmt, tc, cmdq_get_target(item))); 2580 } 2581 2582 /* Create and add defaults. */ 2583 struct format_tree * 2584 format_create_defaults(struct cmdq_item *item, struct client *c, 2585 struct session *s, struct winlink *wl, struct window_pane *wp) 2586 { 2587 struct format_tree *ft; 2588 2589 if (item != NULL) 2590 ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); 2591 else 2592 ft = format_create(NULL, item, FORMAT_NONE, 0); 2593 format_defaults(ft, c, s, wl, wp); 2594 return (ft); 2595 } 2596 2597 /* Create and add defaults using state. */ 2598 struct format_tree * 2599 format_create_from_state(struct cmdq_item *item, struct client *c, 2600 struct cmd_find_state *fs) 2601 { 2602 return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp)); 2603 } 2604 2605 /* Create and add defaults using target. */ 2606 struct format_tree * 2607 format_create_from_target(struct cmdq_item *item) 2608 { 2609 struct client *tc = cmdq_get_target_client(item); 2610 2611 return (format_create_from_state(item, tc, cmdq_get_target(item))); 2612 } 2613 2614 /* Set defaults for any of arguments that are not NULL. */ 2615 void 2616 format_defaults(struct format_tree *ft, struct client *c, struct session *s, 2617 struct winlink *wl, struct window_pane *wp) 2618 { 2619 struct paste_buffer *pb; 2620 2621 if (c != NULL && c->name != NULL) 2622 log_debug("%s: c=%s", __func__, c->name); 2623 else 2624 log_debug("%s: c=none", __func__); 2625 if (s != NULL) 2626 log_debug("%s: s=$%u", __func__, s->id); 2627 else 2628 log_debug("%s: s=none", __func__); 2629 if (wl != NULL) 2630 log_debug("%s: wl=%u", __func__, wl->idx); 2631 else 2632 log_debug("%s: wl=none", __func__); 2633 if (wp != NULL) 2634 log_debug("%s: wp=%%%u", __func__, wp->id); 2635 else 2636 log_debug("%s: wp=none", __func__); 2637 2638 if (c != NULL && s != NULL && c->session != s) 2639 log_debug("%s: session does not match", __func__); 2640 2641 format_add(ft, "session_format", "%d", s != NULL); 2642 format_add(ft, "window_format", "%d", wl != NULL); 2643 format_add(ft, "pane_format", "%d", wp != NULL); 2644 2645 if (s == NULL && c != NULL) 2646 s = c->session; 2647 if (wl == NULL && s != NULL) 2648 wl = s->curw; 2649 if (wp == NULL && wl != NULL) 2650 wp = wl->window->active; 2651 2652 if (c != NULL) 2653 format_defaults_client(ft, c); 2654 if (s != NULL) 2655 format_defaults_session(ft, s); 2656 if (wl != NULL) 2657 format_defaults_winlink(ft, wl); 2658 if (wp != NULL) 2659 format_defaults_pane(ft, wp); 2660 2661 pb = paste_get_top (NULL); 2662 if (pb != NULL) 2663 format_defaults_paste_buffer(ft, pb); 2664 } 2665 2666 /* Set default format keys for a session. */ 2667 static void 2668 format_defaults_session(struct format_tree *ft, struct session *s) 2669 { 2670 struct session_group *sg; 2671 2672 ft->s = s; 2673 2674 format_add(ft, "session_name", "%s", s->name); 2675 format_add(ft, "session_path", "%s", s->cwd); 2676 format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); 2677 format_add(ft, "session_id", "$%u", s->id); 2678 2679 sg = session_group_contains(s); 2680 format_add(ft, "session_grouped", "%d", sg != NULL); 2681 if (sg != NULL) { 2682 format_add(ft, "session_group", "%s", sg->name); 2683 format_add(ft, "session_group_size", "%u", 2684 session_group_count (sg)); 2685 format_add(ft, "session_group_attached", "%u", 2686 session_group_attached_count (sg)); 2687 format_add(ft, "session_group_many_attached", "%u", 2688 session_group_attached_count (sg) > 1); 2689 format_add_cb(ft, "session_group_list", 2690 format_cb_session_group_list); 2691 format_add_cb(ft, "session_group_attached_list", 2692 format_cb_session_group_attached_list); 2693 } 2694 2695 format_add_tv(ft, "session_created", &s->creation_time); 2696 format_add_tv(ft, "session_last_attached", &s->last_attached_time); 2697 format_add_tv(ft, "session_activity", &s->activity_time); 2698 2699 format_add(ft, "session_attached", "%u", s->attached); 2700 format_add(ft, "session_many_attached", "%d", s->attached > 1); 2701 format_add_cb(ft, "session_attached_list", 2702 format_cb_session_attached_list); 2703 2704 format_add_cb(ft, "session_alerts", format_cb_session_alerts); 2705 format_add_cb(ft, "session_stack", format_cb_session_stack); 2706 2707 if (server_check_marked() && marked_pane.s == s) 2708 format_add(ft, "session_marked", "1"); 2709 else 2710 format_add(ft, "session_marked", "0"); 2711 } 2712 2713 /* Set default format keys for a client. */ 2714 static void 2715 format_defaults_client(struct format_tree *ft, struct client *c) 2716 { 2717 struct session *s; 2718 const char *name; 2719 struct tty *tty = &c->tty; 2720 2721 if (ft->s == NULL) 2722 ft->s = c->session; 2723 ft->c = c; 2724 2725 format_add(ft, "client_name", "%s", c->name); 2726 format_add(ft, "client_pid", "%ld", (long) c->pid); 2727 format_add(ft, "client_height", "%u", tty->sy); 2728 format_add(ft, "client_width", "%u", tty->sx); 2729 format_add(ft, "client_cell_width", "%u", tty->xpixel); 2730 format_add(ft, "client_cell_height", "%u", tty->ypixel); 2731 format_add(ft, "client_tty", "%s", c->ttyname); 2732 format_add(ft, "client_control_mode", "%d", 2733 !!(c->flags & CLIENT_CONTROL)); 2734 2735 format_add(ft, "client_termname", "%s", c->term_name); 2736 format_add(ft, "client_termfeatures", "%s", 2737 tty_get_features(c->term_features)); 2738 if (c->term_type != NULL) 2739 format_add(ft, "client_termtype", "%s", c->term_type); 2740 2741 format_add_tv(ft, "client_created", &c->creation_time); 2742 format_add_tv(ft, "client_activity", &c->activity_time); 2743 2744 format_add(ft, "client_written", "%zu", c->written); 2745 format_add(ft, "client_discarded", "%zu", c->discarded); 2746 2747 name = server_client_get_key_table(c); 2748 if (strcmp(c->keytable->name, name) == 0) 2749 format_add(ft, "client_prefix", "%d", 0); 2750 else 2751 format_add(ft, "client_prefix", "%d", 1); 2752 format_add(ft, "client_key_table", "%s", c->keytable->name); 2753 2754 if (c->flags & CLIENT_UTF8) 2755 format_add(ft, "client_utf8", "%d", 1); 2756 else 2757 format_add(ft, "client_utf8", "%d", 0); 2758 if (c->flags & CLIENT_READONLY) 2759 format_add(ft, "client_readonly", "%d", 1); 2760 else 2761 format_add(ft, "client_readonly", "%d", 0); 2762 format_add(ft, "client_flags", "%s", server_client_get_flags(c)); 2763 2764 s = c->session; 2765 if (s != NULL) 2766 format_add(ft, "client_session", "%s", s->name); 2767 s = c->last_session; 2768 if (s != NULL && session_alive(s)) 2769 format_add(ft, "client_last_session", "%s", s->name); 2770 } 2771 2772 /* Set default format keys for a window. */ 2773 void 2774 format_defaults_window(struct format_tree *ft, struct window *w) 2775 { 2776 ft->w = w; 2777 2778 format_add_tv(ft, "window_activity", &w->activity_time); 2779 format_add(ft, "window_id", "@%u", w->id); 2780 format_add(ft, "window_name", "%s", w->name); 2781 format_add(ft, "window_width", "%u", w->sx); 2782 format_add(ft, "window_height", "%u", w->sy); 2783 format_add(ft, "window_cell_width", "%u", w->xpixel); 2784 format_add(ft, "window_cell_height", "%u", w->ypixel); 2785 format_add_cb(ft, "window_layout", format_cb_window_layout); 2786 format_add_cb(ft, "window_visible_layout", 2787 format_cb_window_visible_layout); 2788 format_add(ft, "window_panes", "%u", window_count_panes(w)); 2789 format_add(ft, "window_zoomed_flag", "%d", 2790 !!(w->flags & WINDOW_ZOOMED)); 2791 } 2792 2793 /* Set default format keys for a winlink. */ 2794 static void 2795 format_defaults_winlink(struct format_tree *ft, struct winlink *wl) 2796 { 2797 struct client *c = ft->c; 2798 struct session *s = wl->session; 2799 struct window *w = wl->window; 2800 int flag; 2801 u_int ox, oy, sx, sy; 2802 2803 if (ft->w == NULL) 2804 format_defaults_window(ft, w); 2805 ft->wl = wl; 2806 2807 if (c != NULL) { 2808 flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); 2809 format_add(ft, "window_bigger", "%d", flag); 2810 if (flag) { 2811 format_add(ft, "window_offset_x", "%u", ox); 2812 format_add(ft, "window_offset_y", "%u", oy); 2813 } 2814 } 2815 2816 format_add(ft, "window_index", "%d", wl->idx); 2817 format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); 2818 format_add(ft, "window_flags", "%s", window_printable_flags(wl)); 2819 format_add(ft, "window_active", "%d", wl == s->curw); 2820 format_add_cb(ft, "window_active_sessions", 2821 format_cb_window_active_sessions); 2822 format_add_cb(ft, "window_active_sessions_list", 2823 format_cb_window_active_sessions_list); 2824 format_add_cb(ft, "window_active_clients", 2825 format_cb_window_active_clients); 2826 format_add_cb(ft, "window_active_clients_list", 2827 format_cb_window_active_clients_list); 2828 2829 format_add(ft, "window_start_flag", "%d", 2830 !!(wl == RB_MIN(winlinks, &s->windows))); 2831 format_add(ft, "window_end_flag", "%d", 2832 !!(wl == RB_MAX(winlinks, &s->windows))); 2833 2834 if (server_check_marked() && marked_pane.wl == wl) 2835 format_add(ft, "window_marked_flag", "1"); 2836 else 2837 format_add(ft, "window_marked_flag", "0"); 2838 2839 format_add(ft, "window_bell_flag", "%d", 2840 !!(wl->flags & WINLINK_BELL)); 2841 format_add(ft, "window_activity_flag", "%d", 2842 !!(wl->flags & WINLINK_ACTIVITY)); 2843 format_add(ft, "window_silence_flag", "%d", 2844 !!(wl->flags & WINLINK_SILENCE)); 2845 format_add(ft, "window_last_flag", "%d", 2846 !!(wl == TAILQ_FIRST(&s->lastw))); 2847 format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); 2848 2849 format_add_cb(ft, "window_linked_sessions_list", 2850 format_cb_window_linked_sessions_list); 2851 format_add(ft, "window_linked_sessions", "%u", 2852 wl->window->references); 2853 } 2854 2855 /* Set default format keys for a window pane. */ 2856 void 2857 format_defaults_pane(struct format_tree *ft, struct window_pane *wp) 2858 { 2859 struct window *w = wp->window; 2860 struct grid *gd = wp->base.grid; 2861 int status = wp->status; 2862 u_int idx; 2863 struct window_mode_entry *wme; 2864 2865 if (ft->w == NULL) 2866 format_defaults_window(ft, w); 2867 ft->wp = wp; 2868 2869 format_add(ft, "history_size", "%u", gd->hsize); 2870 format_add(ft, "history_limit", "%u", gd->hlimit); 2871 format_add_cb(ft, "history_bytes", format_cb_history_bytes); 2872 format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes); 2873 2874 format_add(ft, "pane_written", "%zu", wp->written); 2875 format_add(ft, "pane_skipped", "%zu", wp->skipped); 2876 2877 if (window_pane_index(wp, &idx) != 0) 2878 fatalx("index not found"); 2879 format_add(ft, "pane_index", "%u", idx); 2880 2881 format_add(ft, "pane_width", "%u", wp->sx); 2882 format_add(ft, "pane_height", "%u", wp->sy); 2883 format_add(ft, "pane_title", "%s", wp->base.title); 2884 if (wp->base.path != NULL) 2885 format_add(ft, "pane_path", "%s", wp->base.path); 2886 format_add(ft, "pane_id", "%%%u", wp->id); 2887 format_add(ft, "pane_active", "%d", wp == w->active); 2888 format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); 2889 format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1); 2890 2891 if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status)) 2892 format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); 2893 if (~wp->flags & PANE_EMPTY) 2894 format_add(ft, "pane_dead", "%d", wp->fd == -1); 2895 else 2896 format_add(ft, "pane_dead", "0"); 2897 2898 if (server_check_marked() && marked_pane.wp == wp) 2899 format_add(ft, "pane_marked", "1"); 2900 else 2901 format_add(ft, "pane_marked", "0"); 2902 format_add(ft, "pane_marked_set", "%d", server_check_marked()); 2903 2904 format_add(ft, "pane_left", "%u", wp->xoff); 2905 format_add(ft, "pane_top", "%u", wp->yoff); 2906 format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); 2907 format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); 2908 format_add(ft, "pane_at_left", "%d", wp->xoff == 0); 2909 format_add_cb(ft, "pane_at_top", format_cb_pane_at_top); 2910 format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); 2911 format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom); 2912 2913 wme = TAILQ_FIRST(&wp->modes); 2914 if (wme != NULL) { 2915 format_add(ft, "pane_mode", "%s", wme->mode->name); 2916 if (wme->mode->formats != NULL) 2917 wme->mode->formats(wme, ft); 2918 } 2919 format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode); 2920 2921 format_add(ft, "pane_synchronized", "%d", 2922 !!options_get_number(w->options, "synchronize-panes")); 2923 if (wp->searchstr != NULL) 2924 format_add(ft, "pane_search_string", "%s", wp->searchstr); 2925 2926 format_add(ft, "pane_tty", "%s", wp->tty); 2927 format_add(ft, "pane_pid", "%ld", (long) wp->pid); 2928 format_add_cb(ft, "pane_start_command", format_cb_start_command); 2929 format_add_cb(ft, "pane_current_command", format_cb_current_command); 2930 format_add_cb(ft, "pane_current_path", format_cb_current_path); 2931 2932 format_add(ft, "cursor_x", "%u", wp->base.cx); 2933 format_add(ft, "cursor_y", "%u", wp->base.cy); 2934 format_add_cb(ft, "cursor_character", format_cb_cursor_character); 2935 2936 format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); 2937 format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); 2938 2939 format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL); 2940 if (wp->base.saved_cx != UINT_MAX) 2941 format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx); 2942 if (wp->base.saved_cy != UINT_MAX) 2943 format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy); 2944 2945 format_add(ft, "cursor_flag", "%d", 2946 !!(wp->base.mode & MODE_CURSOR)); 2947 format_add(ft, "insert_flag", "%d", 2948 !!(wp->base.mode & MODE_INSERT)); 2949 format_add(ft, "keypad_cursor_flag", "%d", 2950 !!(wp->base.mode & MODE_KCURSOR)); 2951 format_add(ft, "keypad_flag", "%d", 2952 !!(wp->base.mode & MODE_KKEYPAD)); 2953 format_add(ft, "wrap_flag", "%d", 2954 !!(wp->base.mode & MODE_WRAP)); 2955 format_add(ft, "origin_flag", "%d", 2956 !!(wp->base.mode & MODE_ORIGIN)); 2957 2958 format_add(ft, "mouse_any_flag", "%d", 2959 !!(wp->base.mode & ALL_MOUSE_MODES)); 2960 format_add(ft, "mouse_standard_flag", "%d", 2961 !!(wp->base.mode & MODE_MOUSE_STANDARD)); 2962 format_add(ft, "mouse_button_flag", "%d", 2963 !!(wp->base.mode & MODE_MOUSE_BUTTON)); 2964 format_add(ft, "mouse_all_flag", "%d", 2965 !!(wp->base.mode & MODE_MOUSE_ALL)); 2966 format_add(ft, "mouse_utf8_flag", "%d", 2967 !!(wp->base.mode & MODE_MOUSE_UTF8)); 2968 format_add(ft, "mouse_sgr_flag", "%d", 2969 !!(wp->base.mode & MODE_MOUSE_SGR)); 2970 2971 format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); 2972 } 2973 2974 /* Set default format keys for paste buffer. */ 2975 void 2976 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) 2977 { 2978 struct timeval tv; 2979 size_t size; 2980 char *s; 2981 2982 timerclear(&tv); 2983 tv.tv_sec = paste_buffer_created(pb); 2984 paste_buffer_data(pb, &size); 2985 2986 format_add(ft, "buffer_size", "%zu", size); 2987 format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); 2988 format_add_tv(ft, "buffer_created", &tv); 2989 2990 s = paste_make_sample(pb); 2991 format_add(ft, "buffer_sample", "%s", s); 2992 free(s); 2993 } 2994