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