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