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