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