1 /* $OpenBSD: format.c,v 1.274 2021/02/15 09:39:37 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 pane_fg. */ 882 static char * 883 format_cb_pane_fg(struct format_tree *ft) 884 { 885 struct window_pane *wp = ft->wp; 886 struct grid_cell gc; 887 888 tty_default_colours(&gc, wp); 889 return (xstrdup(colour_tostring(gc.fg))); 890 } 891 892 /* Callback for pane_bg. */ 893 static char * 894 format_cb_pane_bg(struct format_tree *ft) 895 { 896 struct window_pane *wp = ft->wp; 897 struct grid_cell gc; 898 899 tty_default_colours(&gc, wp); 900 return (xstrdup(colour_tostring(gc.bg))); 901 } 902 903 /* Callback for session_group_list. */ 904 static char * 905 format_cb_session_group_list(struct format_tree *ft) 906 { 907 struct session *s = ft->s; 908 struct session_group *sg; 909 struct session *loop; 910 struct evbuffer *buffer; 911 int size; 912 char *value = NULL; 913 914 if (s == NULL) 915 return (NULL); 916 sg = session_group_contains(s); 917 if (sg == NULL) 918 return (NULL); 919 920 buffer = evbuffer_new(); 921 if (buffer == NULL) 922 fatalx("out of memory"); 923 924 TAILQ_FOREACH(loop, &sg->sessions, gentry) { 925 if (EVBUFFER_LENGTH(buffer) > 0) 926 evbuffer_add(buffer, ",", 1); 927 evbuffer_add_printf(buffer, "%s", loop->name); 928 } 929 930 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 931 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 932 evbuffer_free(buffer); 933 return (value); 934 } 935 936 /* Callback for session_group_attached_list. */ 937 static char * 938 format_cb_session_group_attached_list(struct format_tree *ft) 939 { 940 struct session *s = ft->s, *client_session, *session_loop; 941 struct session_group *sg; 942 struct client *loop; 943 struct evbuffer *buffer; 944 int size; 945 char *value = NULL; 946 947 if (s == NULL) 948 return (NULL); 949 sg = session_group_contains(s); 950 if (sg == NULL) 951 return (NULL); 952 953 buffer = evbuffer_new(); 954 if (buffer == NULL) 955 fatalx("out of memory"); 956 957 TAILQ_FOREACH(loop, &clients, entry) { 958 client_session = loop->session; 959 if (client_session == NULL) 960 continue; 961 TAILQ_FOREACH(session_loop, &sg->sessions, gentry) { 962 if (session_loop == client_session){ 963 if (EVBUFFER_LENGTH(buffer) > 0) 964 evbuffer_add(buffer, ",", 1); 965 evbuffer_add_printf(buffer, "%s", loop->name); 966 } 967 } 968 } 969 970 if ((size = EVBUFFER_LENGTH(buffer)) != 0) 971 xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer)); 972 evbuffer_free(buffer); 973 return (value); 974 } 975 976 /* Callback for pane_in_mode. */ 977 static char * 978 format_cb_pane_in_mode(struct format_tree *ft) 979 { 980 struct window_pane *wp = ft->wp; 981 u_int n = 0; 982 struct window_mode_entry *wme; 983 char *value; 984 985 if (wp == NULL) 986 return (NULL); 987 988 TAILQ_FOREACH(wme, &wp->modes, entry) 989 n++; 990 xasprintf(&value, "%u", n); 991 return (value); 992 } 993 994 /* Callback for pane_at_top. */ 995 static char * 996 format_cb_pane_at_top(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_TOP) 1009 flag = (wp->yoff == 1); 1010 else 1011 flag = (wp->yoff == 0); 1012 xasprintf(&value, "%d", flag); 1013 return (value); 1014 } 1015 1016 /* Callback for pane_at_bottom. */ 1017 static char * 1018 format_cb_pane_at_bottom(struct format_tree *ft) 1019 { 1020 struct window_pane *wp = ft->wp; 1021 struct window *w; 1022 int status, flag; 1023 char *value; 1024 1025 if (wp == NULL) 1026 return (NULL); 1027 w = wp->window; 1028 1029 status = options_get_number(w->options, "pane-border-status"); 1030 if (status == PANE_STATUS_BOTTOM) 1031 flag = (wp->yoff + wp->sy == w->sy - 1); 1032 else 1033 flag = (wp->yoff + wp->sy == w->sy); 1034 xasprintf(&value, "%d", flag); 1035 return (value); 1036 } 1037 1038 /* Callback for cursor_character. */ 1039 static char * 1040 format_cb_cursor_character(struct format_tree *ft) 1041 { 1042 struct window_pane *wp = ft->wp; 1043 struct grid_cell gc; 1044 char *value = NULL; 1045 1046 if (wp == NULL) 1047 return (NULL); 1048 1049 grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc); 1050 if (~gc.flags & GRID_FLAG_PADDING) 1051 xasprintf(&value, "%.*s", (int)gc.data.size, gc.data.data); 1052 return (value); 1053 } 1054 1055 /* Return word at given coordinates. Caller frees. */ 1056 char * 1057 format_grid_word(struct grid *gd, u_int x, u_int y) 1058 { 1059 const struct grid_line *gl; 1060 struct grid_cell gc; 1061 const char *ws; 1062 struct utf8_data *ud = NULL; 1063 u_int end; 1064 size_t size = 0; 1065 int found = 0; 1066 char *s = NULL; 1067 1068 ws = options_get_string(global_s_options, "word-separators"); 1069 1070 for (;;) { 1071 grid_get_cell(gd, x, y, &gc); 1072 if (gc.flags & GRID_FLAG_PADDING) 1073 break; 1074 if (utf8_cstrhas(ws, &gc.data)) { 1075 found = 1; 1076 break; 1077 } 1078 1079 if (x == 0) { 1080 if (y == 0) 1081 break; 1082 gl = grid_peek_line(gd, y - 1); 1083 if (~gl->flags & GRID_LINE_WRAPPED) 1084 break; 1085 y--; 1086 x = grid_line_length(gd, y); 1087 if (x == 0) 1088 break; 1089 } 1090 x--; 1091 } 1092 for (;;) { 1093 if (found) { 1094 end = grid_line_length(gd, y); 1095 if (end == 0 || x == end - 1) { 1096 if (y == gd->hsize + gd->sy - 1) 1097 break; 1098 gl = grid_peek_line(gd, y); 1099 if (~gl->flags & GRID_LINE_WRAPPED) 1100 break; 1101 y++; 1102 x = 0; 1103 } else 1104 x++; 1105 } 1106 found = 1; 1107 1108 grid_get_cell(gd, x, y, &gc); 1109 if (gc.flags & GRID_FLAG_PADDING) 1110 break; 1111 if (utf8_cstrhas(ws, &gc.data)) 1112 break; 1113 1114 ud = xreallocarray(ud, size + 2, sizeof *ud); 1115 memcpy(&ud[size++], &gc.data, sizeof *ud); 1116 } 1117 if (size != 0) { 1118 ud[size].size = 0; 1119 s = utf8_tocstr(ud); 1120 free(ud); 1121 } 1122 return (s); 1123 } 1124 1125 /* Callback for mouse_word. */ 1126 static char * 1127 format_cb_mouse_word(struct format_tree *ft) 1128 { 1129 struct window_pane *wp; 1130 struct grid *gd; 1131 u_int x, y; 1132 char *s; 1133 1134 if (!ft->m.valid) 1135 return (NULL); 1136 wp = cmd_mouse_pane(&ft->m, NULL, NULL); 1137 if (wp == NULL) 1138 return (NULL); 1139 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) 1140 return (NULL); 1141 1142 if (!TAILQ_EMPTY(&wp->modes)) { 1143 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || 1144 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) 1145 return (s = window_copy_get_word(wp, x, y)); 1146 return (NULL); 1147 } 1148 gd = wp->base.grid; 1149 return (format_grid_word(gd, x, gd->hsize + y)); 1150 } 1151 1152 /* Return line at given coordinates. Caller frees. */ 1153 char * 1154 format_grid_line(struct grid *gd, u_int y) 1155 { 1156 struct grid_cell gc; 1157 struct utf8_data *ud = NULL; 1158 u_int x; 1159 size_t size = 0; 1160 char *s = NULL; 1161 1162 for (x = 0; x < grid_line_length(gd, y); x++) { 1163 grid_get_cell(gd, x, y, &gc); 1164 if (gc.flags & GRID_FLAG_PADDING) 1165 break; 1166 1167 ud = xreallocarray(ud, size + 2, sizeof *ud); 1168 memcpy(&ud[size++], &gc.data, sizeof *ud); 1169 } 1170 if (size != 0) { 1171 ud[size].size = 0; 1172 s = utf8_tocstr(ud); 1173 free(ud); 1174 } 1175 return (s); 1176 } 1177 1178 /* Callback for mouse_line. */ 1179 static char * 1180 format_cb_mouse_line(struct format_tree *ft) 1181 { 1182 struct window_pane *wp; 1183 struct grid *gd; 1184 u_int x, y; 1185 1186 if (!ft->m.valid) 1187 return (NULL); 1188 wp = cmd_mouse_pane(&ft->m, NULL, NULL); 1189 if (wp == NULL) 1190 return (NULL); 1191 if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) 1192 return (NULL); 1193 1194 if (!TAILQ_EMPTY(&wp->modes)) { 1195 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || 1196 TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) 1197 return (window_copy_get_line(wp, y)); 1198 return (NULL); 1199 } 1200 gd = wp->base.grid; 1201 return (format_grid_line(gd, gd->hsize + y)); 1202 } 1203 1204 /* Merge one format tree into another. */ 1205 void 1206 format_merge(struct format_tree *ft, struct format_tree *from) 1207 { 1208 struct format_entry *fe; 1209 1210 RB_FOREACH(fe, format_entry_tree, &from->tree) { 1211 if (fe->value != NULL) 1212 format_add(ft, fe->key, "%s", fe->value); 1213 } 1214 } 1215 1216 /* Get format pane. */ 1217 struct window_pane * 1218 format_get_pane(struct format_tree *ft) 1219 { 1220 return (ft->wp); 1221 } 1222 1223 /* Add item bits to tree. */ 1224 static void 1225 format_create_add_item(struct format_tree *ft, struct cmdq_item *item) 1226 { 1227 struct key_event *event = cmdq_get_event(item); 1228 struct mouse_event *m = &event->m; 1229 struct window_pane *wp; 1230 u_int x, y; 1231 1232 cmdq_merge_formats(item, ft); 1233 1234 if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { 1235 format_add(ft, "mouse_pane", "%%%u", wp->id); 1236 if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { 1237 format_add(ft, "mouse_x", "%u", x); 1238 format_add(ft, "mouse_y", "%u", y); 1239 format_add_cb(ft, "mouse_word", format_cb_mouse_word); 1240 format_add_cb(ft, "mouse_line", format_cb_mouse_line); 1241 } 1242 } 1243 memcpy(&ft->m, m, sizeof ft->m); 1244 } 1245 1246 /* Create a new tree. */ 1247 struct format_tree * 1248 format_create(struct client *c, struct cmdq_item *item, int tag, int flags) 1249 { 1250 struct format_tree *ft; 1251 const struct window_mode **wm; 1252 char tmp[64]; 1253 1254 if (!event_initialized(&format_job_event)) { 1255 evtimer_set(&format_job_event, format_job_timer, NULL); 1256 format_job_timer(-1, 0, NULL); 1257 } 1258 1259 ft = xcalloc(1, sizeof *ft); 1260 RB_INIT(&ft->tree); 1261 1262 if (c != NULL) { 1263 ft->client = c; 1264 ft->client->references++; 1265 } 1266 ft->item = item; 1267 1268 ft->tag = tag; 1269 ft->flags = flags; 1270 1271 format_add(ft, "version", "%s", getversion()); 1272 format_add_cb(ft, "host", format_cb_host); 1273 format_add_cb(ft, "host_short", format_cb_host_short); 1274 format_add_cb(ft, "pid", format_cb_pid); 1275 format_add(ft, "socket_path", "%s", socket_path); 1276 format_add_tv(ft, "start_time", &start_time); 1277 1278 for (wm = all_window_modes; *wm != NULL; wm++) { 1279 if ((*wm)->default_format != NULL) { 1280 xsnprintf(tmp, sizeof tmp, "%s_format", (*wm)->name); 1281 tmp[strcspn(tmp, "-")] = '_'; 1282 format_add(ft, tmp, "%s", (*wm)->default_format); 1283 } 1284 } 1285 1286 if (item != NULL) 1287 format_create_add_item(ft, item); 1288 1289 return (ft); 1290 } 1291 1292 /* Free a tree. */ 1293 void 1294 format_free(struct format_tree *ft) 1295 { 1296 struct format_entry *fe, *fe1; 1297 1298 RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { 1299 RB_REMOVE(format_entry_tree, &ft->tree, fe); 1300 free(fe->value); 1301 free(fe->key); 1302 free(fe); 1303 } 1304 1305 if (ft->client != NULL) 1306 server_client_unref(ft->client); 1307 free(ft); 1308 } 1309 1310 /* Walk each format. */ 1311 void 1312 format_each(struct format_tree *ft, void (*cb)(const char *, const char *, 1313 void *), void *arg) 1314 { 1315 struct format_entry *fe; 1316 char s[64]; 1317 1318 RB_FOREACH(fe, format_entry_tree, &ft->tree) { 1319 if (fe->time != 0) { 1320 xsnprintf(s, sizeof s, "%lld", (long long)fe->time); 1321 cb(fe->key, s, arg); 1322 } else { 1323 if (fe->value == NULL && fe->cb != NULL) { 1324 fe->value = fe->cb(ft); 1325 if (fe->value == NULL) 1326 fe->value = xstrdup(""); 1327 } 1328 cb(fe->key, fe->value, arg); 1329 } 1330 } 1331 } 1332 1333 /* Add a key-value pair. */ 1334 void 1335 format_add(struct format_tree *ft, const char *key, const char *fmt, ...) 1336 { 1337 struct format_entry *fe; 1338 struct format_entry *fe_now; 1339 va_list ap; 1340 1341 fe = xmalloc(sizeof *fe); 1342 fe->key = xstrdup(key); 1343 1344 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1345 if (fe_now != NULL) { 1346 free(fe->key); 1347 free(fe); 1348 free(fe_now->value); 1349 fe = fe_now; 1350 } 1351 1352 fe->cb = NULL; 1353 fe->time = 0; 1354 1355 va_start(ap, fmt); 1356 xvasprintf(&fe->value, fmt, ap); 1357 va_end(ap); 1358 } 1359 1360 /* Add a key and time. */ 1361 void 1362 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) 1363 { 1364 struct format_entry *fe, *fe_now; 1365 1366 fe = xmalloc(sizeof *fe); 1367 fe->key = xstrdup(key); 1368 1369 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1370 if (fe_now != NULL) { 1371 free(fe->key); 1372 free(fe); 1373 free(fe_now->value); 1374 fe = fe_now; 1375 } 1376 1377 fe->cb = NULL; 1378 fe->time = tv->tv_sec; 1379 1380 fe->value = NULL; 1381 } 1382 1383 /* Add a key and function. */ 1384 void 1385 format_add_cb(struct format_tree *ft, const char *key, format_cb cb) 1386 { 1387 struct format_entry *fe; 1388 struct format_entry *fe_now; 1389 1390 fe = xmalloc(sizeof *fe); 1391 fe->key = xstrdup(key); 1392 1393 fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); 1394 if (fe_now != NULL) { 1395 free(fe->key); 1396 free(fe); 1397 free(fe_now->value); 1398 fe = fe_now; 1399 } 1400 1401 fe->cb = cb; 1402 fe->time = 0; 1403 1404 fe->value = NULL; 1405 } 1406 1407 /* Quote shell special characters in string. */ 1408 static char * 1409 format_quote_shell(const char *s) 1410 { 1411 const char *cp; 1412 char *out, *at; 1413 1414 at = out = xmalloc(strlen(s) * 2 + 1); 1415 for (cp = s; *cp != '\0'; cp++) { 1416 if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL) 1417 *at++ = '\\'; 1418 *at++ = *cp; 1419 } 1420 *at = '\0'; 1421 return (out); 1422 } 1423 1424 /* Quote #s in string. */ 1425 static char * 1426 format_quote_style(const char *s) 1427 { 1428 const char *cp; 1429 char *out, *at; 1430 1431 at = out = xmalloc(strlen(s) * 2 + 1); 1432 for (cp = s; *cp != '\0'; cp++) { 1433 if (*cp == '#') 1434 *at++ = '#'; 1435 *at++ = *cp; 1436 } 1437 *at = '\0'; 1438 return (out); 1439 } 1440 1441 /* Make a prettier time. */ 1442 static char * 1443 format_pretty_time(time_t t) 1444 { 1445 struct tm now_tm, tm; 1446 time_t now, age; 1447 char s[6]; 1448 1449 time(&now); 1450 if (now < t) 1451 now = t; 1452 age = now - t; 1453 1454 localtime_r(&now, &now_tm); 1455 localtime_r(&t, &tm); 1456 1457 /* Last 24 hours. */ 1458 if (age < 24 * 3600) { 1459 strftime(s, sizeof s, "%H:%M", &tm); 1460 return (xstrdup(s)); 1461 } 1462 1463 /* This month or last 28 days. */ 1464 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon == now_tm.tm_mon) || 1465 age < 28 * 24 * 3600) { 1466 strftime(s, sizeof s, "%a%d", &tm); 1467 return (xstrdup(s)); 1468 } 1469 1470 /* Last 12 months. */ 1471 if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) || 1472 (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) { 1473 strftime(s, sizeof s, "%d%b", &tm); 1474 return (xstrdup(s)); 1475 } 1476 1477 /* Older than that. */ 1478 strftime(s, sizeof s, "%h%y", &tm); 1479 return (xstrdup(s)); 1480 } 1481 1482 /* Find a format entry. */ 1483 static char * 1484 format_find(struct format_tree *ft, const char *key, int modifiers, 1485 const char *time_format) 1486 { 1487 struct format_entry *fe, fe_find; 1488 struct environ_entry *envent; 1489 struct options_entry *o; 1490 int idx; 1491 char *found = NULL, *saved, s[512]; 1492 const char *errstr; 1493 time_t t = 0; 1494 struct tm tm; 1495 1496 o = options_parse_get(global_options, key, &idx, 0); 1497 if (o == NULL && ft->wp != NULL) 1498 o = options_parse_get(ft->wp->options, key, &idx, 0); 1499 if (o == NULL && ft->w != NULL) 1500 o = options_parse_get(ft->w->options, key, &idx, 0); 1501 if (o == NULL) 1502 o = options_parse_get(global_w_options, key, &idx, 0); 1503 if (o == NULL && ft->s != NULL) 1504 o = options_parse_get(ft->s->options, key, &idx, 0); 1505 if (o == NULL) 1506 o = options_parse_get(global_s_options, key, &idx, 0); 1507 if (o != NULL) { 1508 found = options_to_string(o, idx, 1); 1509 goto found; 1510 } 1511 1512 fe_find.key = (char *)key; 1513 fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); 1514 if (fe != NULL) { 1515 if (fe->time != 0) { 1516 t = fe->time; 1517 goto found; 1518 } 1519 if (fe->value == NULL && fe->cb != NULL) { 1520 fe->value = fe->cb(ft); 1521 if (fe->value == NULL) 1522 fe->value = xstrdup(""); 1523 } 1524 found = xstrdup(fe->value); 1525 goto found; 1526 } 1527 1528 if (~modifiers & FORMAT_TIMESTRING) { 1529 envent = NULL; 1530 if (ft->s != NULL) 1531 envent = environ_find(ft->s->environ, key); 1532 if (envent == NULL) 1533 envent = environ_find(global_environ, key); 1534 if (envent != NULL && envent->value != NULL) { 1535 found = xstrdup(envent->value); 1536 goto found; 1537 } 1538 } 1539 1540 return (NULL); 1541 1542 found: 1543 if (modifiers & FORMAT_TIMESTRING) { 1544 if (t == 0 && found != NULL) { 1545 t = strtonum(found, 0, INT64_MAX, &errstr); 1546 if (errstr != NULL) 1547 t = 0; 1548 free(found); 1549 } 1550 if (t == 0) 1551 return (NULL); 1552 if (modifiers & FORMAT_PRETTY) 1553 found = format_pretty_time(t); 1554 else { 1555 if (time_format != NULL) { 1556 localtime_r(&t, &tm); 1557 strftime(s, sizeof s, time_format, &tm); 1558 } else { 1559 ctime_r(&t, s); 1560 s[strcspn(s, "\n")] = '\0'; 1561 } 1562 found = xstrdup(s); 1563 } 1564 return (found); 1565 } 1566 1567 if (t != 0) 1568 xasprintf(&found, "%lld", (long long)t); 1569 else if (found == NULL) 1570 return (NULL); 1571 if (modifiers & FORMAT_BASENAME) { 1572 saved = found; 1573 found = xstrdup(basename(saved)); 1574 free(saved); 1575 } 1576 if (modifiers & FORMAT_DIRNAME) { 1577 saved = found; 1578 found = xstrdup(dirname(saved)); 1579 free(saved); 1580 } 1581 if (modifiers & FORMAT_QUOTE_SHELL) { 1582 saved = found; 1583 found = xstrdup(format_quote_shell(saved)); 1584 free(saved); 1585 } 1586 if (modifiers & FORMAT_QUOTE_STYLE) { 1587 saved = found; 1588 found = xstrdup(format_quote_style(saved)); 1589 free(saved); 1590 } 1591 return (found); 1592 } 1593 1594 /* Remove escaped characters from string. */ 1595 static char * 1596 format_strip(const char *s) 1597 { 1598 char *out, *cp; 1599 int brackets = 0; 1600 1601 cp = out = xmalloc(strlen(s) + 1); 1602 for (; *s != '\0'; s++) { 1603 if (*s == '#' && s[1] == '{') 1604 brackets++; 1605 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { 1606 if (brackets != 0) 1607 *cp++ = *s; 1608 continue; 1609 } 1610 if (*s == '}') 1611 brackets--; 1612 *cp++ = *s; 1613 } 1614 *cp = '\0'; 1615 return (out); 1616 } 1617 1618 /* Skip until end. */ 1619 const char * 1620 format_skip(const char *s, const char *end) 1621 { 1622 int brackets = 0; 1623 1624 for (; *s != '\0'; s++) { 1625 if (*s == '#' && s[1] == '{') 1626 brackets++; 1627 if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { 1628 s++; 1629 continue; 1630 } 1631 if (*s == '}') 1632 brackets--; 1633 if (strchr(end, *s) != NULL && brackets == 0) 1634 break; 1635 } 1636 if (*s == '\0') 1637 return (NULL); 1638 return (s); 1639 } 1640 1641 /* Return left and right alternatives separated by commas. */ 1642 static int 1643 format_choose(struct format_expand_state *es, const char *s, char **left, 1644 char **right, int expand) 1645 { 1646 const char *cp; 1647 char *left0, *right0; 1648 1649 cp = format_skip(s, ","); 1650 if (cp == NULL) 1651 return (-1); 1652 left0 = xstrndup(s, cp - s); 1653 right0 = xstrdup(cp + 1); 1654 1655 if (expand) { 1656 *left = format_expand1(es, left0); 1657 free(left0); 1658 *right = format_expand1(es, right0); 1659 free(right0); 1660 } else { 1661 *left = left0; 1662 *right = right0; 1663 } 1664 return (0); 1665 } 1666 1667 /* Is this true? */ 1668 int 1669 format_true(const char *s) 1670 { 1671 if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0')) 1672 return (1); 1673 return (0); 1674 } 1675 1676 /* Check if modifier end. */ 1677 static int 1678 format_is_end(char c) 1679 { 1680 return (c == ';' || c == ':'); 1681 } 1682 1683 /* Add to modifier list. */ 1684 static void 1685 format_add_modifier(struct format_modifier **list, u_int *count, 1686 const char *c, size_t n, char **argv, int argc) 1687 { 1688 struct format_modifier *fm; 1689 1690 *list = xreallocarray(*list, (*count) + 1, sizeof **list); 1691 fm = &(*list)[(*count)++]; 1692 1693 memcpy(fm->modifier, c, n); 1694 fm->modifier[n] = '\0'; 1695 fm->size = n; 1696 1697 fm->argv = argv; 1698 fm->argc = argc; 1699 } 1700 1701 /* Free modifier list. */ 1702 static void 1703 format_free_modifiers(struct format_modifier *list, u_int count) 1704 { 1705 u_int i; 1706 1707 for (i = 0; i < count; i++) 1708 cmd_free_argv(list[i].argc, list[i].argv); 1709 free(list); 1710 } 1711 1712 /* Build modifier list. */ 1713 static struct format_modifier * 1714 format_build_modifiers(struct format_expand_state *es, const char **s, 1715 u_int *count) 1716 { 1717 const char *cp = *s, *end; 1718 struct format_modifier *list = NULL; 1719 char c, last[] = "X;:", **argv, *value; 1720 int argc; 1721 1722 /* 1723 * Modifiers are a ; separated list of the forms: 1724 * l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,> 1725 * =a 1726 * =/a 1727 * =/a/ 1728 * s/a/b/ 1729 * s/a/b 1730 * ||,&&,!=,==,<=,>= 1731 */ 1732 1733 *count = 0; 1734 1735 while (*cp != '\0' && *cp != ':') { 1736 /* Skip any separator character. */ 1737 if (*cp == ';') 1738 cp++; 1739 1740 /* Check single character modifiers with no arguments. */ 1741 if (strchr("lbdnwETSWP<>", cp[0]) != NULL && 1742 format_is_end(cp[1])) { 1743 format_add_modifier(&list, count, cp, 1, NULL, 0); 1744 cp++; 1745 continue; 1746 } 1747 1748 /* Then try double character with no arguments. */ 1749 if ((memcmp("||", cp, 2) == 0 || 1750 memcmp("&&", cp, 2) == 0 || 1751 memcmp("!=", cp, 2) == 0 || 1752 memcmp("==", cp, 2) == 0 || 1753 memcmp("<=", cp, 2) == 0 || 1754 memcmp(">=", cp, 2) == 0) && 1755 format_is_end(cp[2])) { 1756 format_add_modifier(&list, count, cp, 2, NULL, 0); 1757 cp += 2; 1758 continue; 1759 } 1760 1761 /* Now try single character with arguments. */ 1762 if (strchr("mCNst=peq", cp[0]) == NULL) 1763 break; 1764 c = cp[0]; 1765 1766 /* No arguments provided. */ 1767 if (format_is_end(cp[1])) { 1768 format_add_modifier(&list, count, cp, 1, NULL, 0); 1769 cp++; 1770 continue; 1771 } 1772 argv = NULL; 1773 argc = 0; 1774 1775 /* Single argument with no wrapper character. */ 1776 if (!ispunct(cp[1]) || cp[1] == '-') { 1777 end = format_skip(cp + 1, ":;"); 1778 if (end == NULL) 1779 break; 1780 1781 argv = xcalloc(1, sizeof *argv); 1782 value = xstrndup(cp + 1, end - (cp + 1)); 1783 argv[0] = format_expand1(es, value); 1784 free(value); 1785 argc = 1; 1786 1787 format_add_modifier(&list, count, &c, 1, argv, argc); 1788 cp = end; 1789 continue; 1790 } 1791 1792 /* Multiple arguments with a wrapper character. */ 1793 last[0] = cp[1]; 1794 cp++; 1795 do { 1796 if (cp[0] == last[0] && format_is_end(cp[1])) { 1797 cp++; 1798 break; 1799 } 1800 end = format_skip(cp + 1, last); 1801 if (end == NULL) 1802 break; 1803 cp++; 1804 1805 argv = xreallocarray (argv, argc + 1, sizeof *argv); 1806 value = xstrndup(cp, end - cp); 1807 argv[argc++] = format_expand1(es, value); 1808 free(value); 1809 1810 cp = end; 1811 } while (!format_is_end(cp[0])); 1812 format_add_modifier(&list, count, &c, 1, argv, argc); 1813 } 1814 if (*cp != ':') { 1815 format_free_modifiers(list, *count); 1816 *count = 0; 1817 return (NULL); 1818 } 1819 *s = cp + 1; 1820 return (list); 1821 } 1822 1823 /* Match against an fnmatch(3) pattern or regular expression. */ 1824 static char * 1825 format_match(struct format_modifier *fm, const char *pattern, const char *text) 1826 { 1827 const char *s = ""; 1828 regex_t r; 1829 int flags = 0; 1830 1831 if (fm->argc >= 1) 1832 s = fm->argv[0]; 1833 if (strchr(s, 'r') == NULL) { 1834 if (strchr(s, 'i') != NULL) 1835 flags |= FNM_CASEFOLD; 1836 if (fnmatch(pattern, text, flags) != 0) 1837 return (xstrdup("0")); 1838 } else { 1839 flags = REG_EXTENDED|REG_NOSUB; 1840 if (strchr(s, 'i') != NULL) 1841 flags |= REG_ICASE; 1842 if (regcomp(&r, pattern, flags) != 0) 1843 return (xstrdup("0")); 1844 if (regexec(&r, text, 0, NULL, 0) != 0) { 1845 regfree(&r); 1846 return (xstrdup("0")); 1847 } 1848 regfree(&r); 1849 } 1850 return (xstrdup("1")); 1851 } 1852 1853 /* Perform substitution in string. */ 1854 static char * 1855 format_sub(struct format_modifier *fm, const char *text, const char *pattern, 1856 const char *with) 1857 { 1858 char *value; 1859 int flags = REG_EXTENDED; 1860 1861 if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) 1862 flags |= REG_ICASE; 1863 value = regsub(pattern, with, text, flags); 1864 if (value == NULL) 1865 return (xstrdup(text)); 1866 return (value); 1867 } 1868 1869 /* Search inside pane. */ 1870 static char * 1871 format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) 1872 { 1873 int ignore = 0, regex = 0; 1874 char *value; 1875 1876 if (fm->argc >= 1) { 1877 if (strchr(fm->argv[0], 'i') != NULL) 1878 ignore = 1; 1879 if (strchr(fm->argv[0], 'r') != NULL) 1880 regex = 1; 1881 } 1882 xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore)); 1883 return (value); 1884 } 1885 1886 /* Does session name exist? */ 1887 static char * 1888 format_session_name(struct format_expand_state *es, const char *fmt) 1889 { 1890 char *name; 1891 struct session *s; 1892 1893 name = format_expand1(es, fmt); 1894 RB_FOREACH(s, sessions, &sessions) { 1895 if (strcmp(s->name, name) == 0) { 1896 free(name); 1897 return (xstrdup("1")); 1898 } 1899 } 1900 free(name); 1901 return (xstrdup("0")); 1902 } 1903 1904 /* Loop over sessions. */ 1905 static char * 1906 format_loop_sessions(struct format_expand_state *es, const char *fmt) 1907 { 1908 struct format_tree *ft = es->ft; 1909 struct client *c = ft->client; 1910 struct cmdq_item *item = ft->item; 1911 struct format_tree *nft; 1912 struct format_expand_state next; 1913 char *expanded, *value; 1914 size_t valuelen; 1915 struct session *s; 1916 1917 value = xcalloc(1, 1); 1918 valuelen = 1; 1919 1920 RB_FOREACH(s, sessions, &sessions) { 1921 format_log(es, "session loop: $%u", s->id); 1922 nft = format_create(c, item, FORMAT_NONE, ft->flags); 1923 format_defaults(nft, ft->c, s, NULL, NULL); 1924 format_copy_state(&next, es, 0); 1925 next.ft = nft; 1926 expanded = format_expand1(&next, fmt); 1927 format_free(next.ft); 1928 1929 valuelen += strlen(expanded); 1930 value = xrealloc(value, valuelen); 1931 1932 strlcat(value, expanded, valuelen); 1933 free(expanded); 1934 } 1935 1936 return (value); 1937 } 1938 1939 /* Does window name exist? */ 1940 static char * 1941 format_window_name(struct format_expand_state *es, const char *fmt) 1942 { 1943 struct format_tree *ft = es->ft; 1944 char *name; 1945 struct winlink *wl; 1946 1947 if (ft->s == NULL) { 1948 format_log(es, "window name but no session"); 1949 return (NULL); 1950 } 1951 1952 name = format_expand1(es, fmt); 1953 RB_FOREACH(wl, winlinks, &ft->s->windows) { 1954 if (strcmp(wl->window->name, name) == 0) { 1955 free(name); 1956 return (xstrdup("1")); 1957 } 1958 } 1959 free(name); 1960 return (xstrdup("0")); 1961 } 1962 1963 /* Loop over windows. */ 1964 static char * 1965 format_loop_windows(struct format_expand_state *es, const char *fmt) 1966 { 1967 struct format_tree *ft = es->ft; 1968 struct client *c = ft->client; 1969 struct cmdq_item *item = ft->item; 1970 struct format_tree *nft; 1971 struct format_expand_state next; 1972 char *all, *active, *use, *expanded, *value; 1973 size_t valuelen; 1974 struct winlink *wl; 1975 struct window *w; 1976 1977 if (ft->s == NULL) { 1978 format_log(es, "window loop but no session"); 1979 return (NULL); 1980 } 1981 1982 if (format_choose(es, fmt, &all, &active, 0) != 0) { 1983 all = xstrdup(fmt); 1984 active = NULL; 1985 } 1986 1987 value = xcalloc(1, 1); 1988 valuelen = 1; 1989 1990 RB_FOREACH(wl, winlinks, &ft->s->windows) { 1991 w = wl->window; 1992 format_log(es, "window loop: %u @%u", wl->idx, w->id); 1993 if (active != NULL && wl == ft->s->curw) 1994 use = active; 1995 else 1996 use = all; 1997 nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); 1998 format_defaults(nft, ft->c, ft->s, wl, NULL); 1999 format_copy_state(&next, es, 0); 2000 next.ft = nft; 2001 expanded = format_expand1(&next, use); 2002 format_free(nft); 2003 2004 valuelen += strlen(expanded); 2005 value = xrealloc(value, valuelen); 2006 2007 strlcat(value, expanded, valuelen); 2008 free(expanded); 2009 } 2010 2011 free(active); 2012 free(all); 2013 2014 return (value); 2015 } 2016 2017 /* Loop over panes. */ 2018 static char * 2019 format_loop_panes(struct format_expand_state *es, const char *fmt) 2020 { 2021 struct format_tree *ft = es->ft; 2022 struct client *c = ft->client; 2023 struct cmdq_item *item = ft->item; 2024 struct format_tree *nft; 2025 struct format_expand_state next; 2026 char *all, *active, *use, *expanded, *value; 2027 size_t valuelen; 2028 struct window_pane *wp; 2029 2030 if (ft->w == NULL) { 2031 format_log(es, "pane loop but no window"); 2032 return (NULL); 2033 } 2034 2035 if (format_choose(es, fmt, &all, &active, 0) != 0) { 2036 all = xstrdup(fmt); 2037 active = NULL; 2038 } 2039 2040 value = xcalloc(1, 1); 2041 valuelen = 1; 2042 2043 TAILQ_FOREACH(wp, &ft->w->panes, entry) { 2044 format_log(es, "pane loop: %%%u", wp->id); 2045 if (active != NULL && wp == ft->w->active) 2046 use = active; 2047 else 2048 use = all; 2049 nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); 2050 format_defaults(nft, ft->c, ft->s, ft->wl, wp); 2051 format_copy_state(&next, es, 0); 2052 next.ft = nft; 2053 expanded = format_expand1(&next, use); 2054 format_free(nft); 2055 2056 valuelen += strlen(expanded); 2057 value = xrealloc(value, valuelen); 2058 2059 strlcat(value, expanded, valuelen); 2060 free(expanded); 2061 } 2062 2063 free(active); 2064 free(all); 2065 2066 return (value); 2067 } 2068 2069 static char * 2070 format_replace_expression(struct format_modifier *mexp, 2071 struct format_expand_state *es, const char *copy) 2072 { 2073 int argc = mexp->argc; 2074 const char *errstr; 2075 char *endch, *value, *left = NULL, *right = NULL; 2076 int use_fp = 0; 2077 u_int prec = 0; 2078 double mleft, mright, result; 2079 enum { ADD, 2080 SUBTRACT, 2081 MULTIPLY, 2082 DIVIDE, 2083 MODULUS, 2084 EQUAL, 2085 NOT_EQUAL, 2086 GREATER_THAN, 2087 GREATER_THAN_EQUAL, 2088 LESS_THAN, 2089 LESS_THAN_EQUAL } operator; 2090 2091 if (strcmp(mexp->argv[0], "+") == 0) 2092 operator = ADD; 2093 else if (strcmp(mexp->argv[0], "-") == 0) 2094 operator = SUBTRACT; 2095 else if (strcmp(mexp->argv[0], "*") == 0) 2096 operator = MULTIPLY; 2097 else if (strcmp(mexp->argv[0], "/") == 0) 2098 operator = DIVIDE; 2099 else if (strcmp(mexp->argv[0], "%") == 0 || 2100 strcmp(mexp->argv[0], "m") == 0) 2101 operator = MODULUS; 2102 else if (strcmp(mexp->argv[0], "==") == 0) 2103 operator = EQUAL; 2104 else if (strcmp(mexp->argv[0], "!=") == 0) 2105 operator = NOT_EQUAL; 2106 else if (strcmp(mexp->argv[0], ">") == 0) 2107 operator = GREATER_THAN; 2108 else if (strcmp(mexp->argv[0], "<") == 0) 2109 operator = LESS_THAN; 2110 else if (strcmp(mexp->argv[0], ">=") == 0) 2111 operator = GREATER_THAN_EQUAL; 2112 else if (strcmp(mexp->argv[0], "<=") == 0) 2113 operator = LESS_THAN_EQUAL; 2114 else { 2115 format_log(es, "expression has no valid operator: '%s'", 2116 mexp->argv[0]); 2117 goto fail; 2118 } 2119 2120 /* The second argument may be flags. */ 2121 if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) { 2122 use_fp = 1; 2123 prec = 2; 2124 } 2125 2126 /* The third argument may be precision. */ 2127 if (argc >= 3) { 2128 prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); 2129 if (errstr != NULL) { 2130 format_log(es, "expression precision %s: %s", errstr, 2131 mexp->argv[2]); 2132 goto fail; 2133 } 2134 } 2135 2136 if (format_choose(es, copy, &left, &right, 1) != 0) { 2137 format_log(es, "expression syntax error"); 2138 goto fail; 2139 } 2140 2141 mleft = strtod(left, &endch); 2142 if (*endch != '\0') { 2143 format_log(es, "expression left side is invalid: %s", left); 2144 goto fail; 2145 } 2146 2147 mright = strtod(right, &endch); 2148 if (*endch != '\0') { 2149 format_log(es, "expression right side is invalid: %s", right); 2150 goto fail; 2151 } 2152 2153 if (!use_fp) { 2154 mleft = (long long)mleft; 2155 mright = (long long)mright; 2156 } 2157 format_log(es, "expression left side is: %.*f", prec, mleft); 2158 format_log(es, "expression right side is: %.*f", prec, mright); 2159 2160 switch (operator) { 2161 case ADD: 2162 result = mleft + mright; 2163 break; 2164 case SUBTRACT: 2165 result = mleft - mright; 2166 break; 2167 case MULTIPLY: 2168 result = mleft * mright; 2169 break; 2170 case DIVIDE: 2171 result = mleft / mright; 2172 break; 2173 case MODULUS: 2174 result = fmod(mleft, mright); 2175 break; 2176 case EQUAL: 2177 result = fabs(mleft - mright) < 1e-9; 2178 break; 2179 case NOT_EQUAL: 2180 result = fabs(mleft - mright) > 1e-9; 2181 break; 2182 case GREATER_THAN: 2183 result = (mleft > mright); 2184 break; 2185 case GREATER_THAN_EQUAL: 2186 result = (mleft >= mright); 2187 break; 2188 case LESS_THAN: 2189 result = (mleft < mright); 2190 break; 2191 case LESS_THAN_EQUAL: 2192 result = (mleft > mright); 2193 break; 2194 } 2195 if (use_fp) 2196 xasprintf(&value, "%.*f", prec, result); 2197 else 2198 xasprintf(&value, "%.*f", prec, (double)(long long)result); 2199 format_log(es, "expression result is %s", value); 2200 2201 free(right); 2202 free(left); 2203 return (value); 2204 2205 fail: 2206 free(right); 2207 free(left); 2208 return (NULL); 2209 } 2210 2211 /* Replace a key. */ 2212 static int 2213 format_replace(struct format_expand_state *es, const char *key, size_t keylen, 2214 char **buf, size_t *len, size_t *off) 2215 { 2216 struct format_tree *ft = es->ft; 2217 struct window_pane *wp = ft->wp; 2218 const char *errptr, *copy, *cp, *marker = NULL; 2219 const char *time_format = NULL; 2220 char *copy0, *condition, *found, *new; 2221 char *value, *left, *right; 2222 size_t valuelen; 2223 int modifiers = 0, limit = 0, width = 0; 2224 int j; 2225 struct format_modifier *list, *cmp = NULL, *search = NULL; 2226 struct format_modifier **sub = NULL, *mexp = NULL, *fm; 2227 u_int i, count, nsub = 0; 2228 struct format_expand_state next; 2229 2230 /* Make a copy of the key. */ 2231 copy = copy0 = xstrndup(key, keylen); 2232 2233 /* Process modifier list. */ 2234 list = format_build_modifiers(es, ©, &count); 2235 for (i = 0; i < count; i++) { 2236 fm = &list[i]; 2237 if (format_logging(ft)) { 2238 format_log(es, "modifier %u is %s", i, fm->modifier); 2239 for (j = 0; j < fm->argc; j++) { 2240 format_log(es, "modifier %u argument %d: %s", i, 2241 j, fm->argv[j]); 2242 } 2243 } 2244 if (fm->size == 1) { 2245 switch (fm->modifier[0]) { 2246 case 'm': 2247 case '<': 2248 case '>': 2249 cmp = fm; 2250 break; 2251 case 'C': 2252 search = fm; 2253 break; 2254 case 's': 2255 if (fm->argc < 2) 2256 break; 2257 sub = xreallocarray (sub, nsub + 1, 2258 sizeof *sub); 2259 sub[nsub++] = fm; 2260 break; 2261 case '=': 2262 if (fm->argc < 1) 2263 break; 2264 limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, 2265 &errptr); 2266 if (errptr != NULL) 2267 limit = 0; 2268 if (fm->argc >= 2 && fm->argv[1] != NULL) 2269 marker = fm->argv[1]; 2270 break; 2271 case 'p': 2272 if (fm->argc < 1) 2273 break; 2274 width = strtonum(fm->argv[0], INT_MIN, INT_MAX, 2275 &errptr); 2276 if (errptr != NULL) 2277 width = 0; 2278 break; 2279 case 'w': 2280 modifiers |= FORMAT_WIDTH; 2281 break; 2282 case 'e': 2283 if (fm->argc < 1 || fm->argc > 3) 2284 break; 2285 mexp = fm; 2286 break; 2287 case 'l': 2288 modifiers |= FORMAT_LITERAL; 2289 break; 2290 case 'b': 2291 modifiers |= FORMAT_BASENAME; 2292 break; 2293 case 'd': 2294 modifiers |= FORMAT_DIRNAME; 2295 break; 2296 case 'n': 2297 modifiers |= FORMAT_LENGTH; 2298 break; 2299 case 't': 2300 modifiers |= FORMAT_TIMESTRING; 2301 if (fm->argc < 1) 2302 break; 2303 if (strchr(fm->argv[0], 'p') != NULL) 2304 modifiers |= FORMAT_PRETTY; 2305 else if (fm->argc >= 2 && 2306 strchr(fm->argv[0], 'f') != NULL) 2307 time_format = format_strip(fm->argv[1]); 2308 break; 2309 case 'q': 2310 if (fm->argc < 1) 2311 modifiers |= FORMAT_QUOTE_SHELL; 2312 else if (strchr(fm->argv[0], 'e') != NULL || 2313 strchr(fm->argv[0], 'h') != NULL) 2314 modifiers |= FORMAT_QUOTE_STYLE; 2315 break; 2316 case 'E': 2317 modifiers |= FORMAT_EXPAND; 2318 break; 2319 case 'T': 2320 modifiers |= FORMAT_EXPANDTIME; 2321 break; 2322 case 'N': 2323 if (fm->argc < 1 || 2324 strchr(fm->argv[0], 'w') != NULL) 2325 modifiers |= FORMAT_WINDOW_NAME; 2326 else if (strchr(fm->argv[0], 's') != NULL) 2327 modifiers |= FORMAT_SESSION_NAME; 2328 break; 2329 case 'S': 2330 modifiers |= FORMAT_SESSIONS; 2331 break; 2332 case 'W': 2333 modifiers |= FORMAT_WINDOWS; 2334 break; 2335 case 'P': 2336 modifiers |= FORMAT_PANES; 2337 break; 2338 } 2339 } else if (fm->size == 2) { 2340 if (strcmp(fm->modifier, "||") == 0 || 2341 strcmp(fm->modifier, "&&") == 0 || 2342 strcmp(fm->modifier, "==") == 0 || 2343 strcmp(fm->modifier, "!=") == 0 || 2344 strcmp(fm->modifier, ">=") == 0 || 2345 strcmp(fm->modifier, "<=") == 0) 2346 cmp = fm; 2347 } 2348 } 2349 2350 /* Is this a literal string? */ 2351 if (modifiers & FORMAT_LITERAL) { 2352 value = xstrdup(copy); 2353 goto done; 2354 } 2355 2356 /* Is this a loop, comparison or condition? */ 2357 if (modifiers & FORMAT_SESSIONS) { 2358 value = format_loop_sessions(es, copy); 2359 if (value == NULL) 2360 goto fail; 2361 } else if (modifiers & FORMAT_WINDOWS) { 2362 value = format_loop_windows(es, copy); 2363 if (value == NULL) 2364 goto fail; 2365 } else if (modifiers & FORMAT_PANES) { 2366 value = format_loop_panes(es, copy); 2367 if (value == NULL) 2368 goto fail; 2369 } else if (modifiers & FORMAT_WINDOW_NAME) { 2370 value = format_window_name(es, copy); 2371 if (value == NULL) 2372 goto fail; 2373 } else if (modifiers & FORMAT_SESSION_NAME) { 2374 value = format_session_name(es, copy); 2375 if (value == NULL) 2376 goto fail; 2377 } else if (search != NULL) { 2378 /* Search in pane. */ 2379 new = format_expand1(es, copy); 2380 if (wp == NULL) { 2381 format_log(es, "search '%s' but no pane", new); 2382 value = xstrdup("0"); 2383 } else { 2384 format_log(es, "search '%s' pane %%%u", new, wp->id); 2385 value = format_search(fm, wp, new); 2386 } 2387 free(new); 2388 } else if (cmp != NULL) { 2389 /* Comparison of left and right. */ 2390 if (format_choose(es, copy, &left, &right, 1) != 0) { 2391 format_log(es, "compare %s syntax error: %s", 2392 cmp->modifier, copy); 2393 goto fail; 2394 } 2395 format_log(es, "compare %s left is: %s", cmp->modifier, left); 2396 format_log(es, "compare %s right is: %s", cmp->modifier, right); 2397 2398 if (strcmp(cmp->modifier, "||") == 0) { 2399 if (format_true(left) || format_true(right)) 2400 value = xstrdup("1"); 2401 else 2402 value = xstrdup("0"); 2403 } else if (strcmp(cmp->modifier, "&&") == 0) { 2404 if (format_true(left) && format_true(right)) 2405 value = xstrdup("1"); 2406 else 2407 value = xstrdup("0"); 2408 } else if (strcmp(cmp->modifier, "==") == 0) { 2409 if (strcmp(left, right) == 0) 2410 value = xstrdup("1"); 2411 else 2412 value = xstrdup("0"); 2413 } else if (strcmp(cmp->modifier, "!=") == 0) { 2414 if (strcmp(left, right) != 0) 2415 value = xstrdup("1"); 2416 else 2417 value = xstrdup("0"); 2418 } else if (strcmp(cmp->modifier, "<") == 0) { 2419 if (strcmp(left, right) < 0) 2420 value = xstrdup("1"); 2421 else 2422 value = xstrdup("0"); 2423 } else if (strcmp(cmp->modifier, ">") == 0) { 2424 if (strcmp(left, right) > 0) 2425 value = xstrdup("1"); 2426 else 2427 value = xstrdup("0"); 2428 } else if (strcmp(cmp->modifier, "<=") == 0) { 2429 if (strcmp(left, right) <= 0) 2430 value = xstrdup("1"); 2431 else 2432 value = xstrdup("0"); 2433 } else if (strcmp(cmp->modifier, ">=") == 0) { 2434 if (strcmp(left, right) >= 0) 2435 value = xstrdup("1"); 2436 else 2437 value = xstrdup("0"); 2438 } else if (strcmp(cmp->modifier, "m") == 0) 2439 value = format_match(cmp, left, right); 2440 2441 free(right); 2442 free(left); 2443 } else if (*copy == '?') { 2444 /* Conditional: check first and choose second or third. */ 2445 cp = format_skip(copy + 1, ","); 2446 if (cp == NULL) { 2447 format_log(es, "condition syntax error: %s", copy + 1); 2448 goto fail; 2449 } 2450 condition = xstrndup(copy + 1, cp - (copy + 1)); 2451 format_log(es, "condition is: %s", condition); 2452 2453 found = format_find(ft, condition, modifiers, time_format); 2454 if (found == NULL) { 2455 /* 2456 * If the condition not found, try to expand it. If 2457 * the expansion doesn't have any effect, then assume 2458 * false. 2459 */ 2460 found = format_expand1(es, condition); 2461 if (strcmp(found, condition) == 0) { 2462 free(found); 2463 found = xstrdup(""); 2464 format_log(es, "condition '%s' found: %s", 2465 condition, found); 2466 } else { 2467 format_log(es, 2468 "condition '%s' not found; assuming false", 2469 condition); 2470 } 2471 } else 2472 format_log(es, "condition '%s' found", condition); 2473 2474 if (format_choose(es, cp + 1, &left, &right, 0) != 0) { 2475 format_log(es, "condition '%s' syntax error: %s", 2476 condition, cp + 1); 2477 free(found); 2478 goto fail; 2479 } 2480 if (format_true(found)) { 2481 format_log(es, "condition '%s' is true", condition); 2482 value = format_expand1(es, left); 2483 } else { 2484 format_log(es, "condition '%s' is false", condition); 2485 value = format_expand1(es, right); 2486 } 2487 free(right); 2488 free(left); 2489 2490 free(condition); 2491 free(found); 2492 } else if (mexp != NULL) { 2493 value = format_replace_expression(mexp, es, copy); 2494 if (value == NULL) 2495 value = xstrdup(""); 2496 } else { 2497 if (strstr(copy, "#{") != 0) { 2498 format_log(es, "expanding inner format '%s'", copy); 2499 value = format_expand1(es, copy); 2500 } else { 2501 value = format_find(ft, copy, modifiers, time_format); 2502 if (value == NULL) { 2503 format_log(es, "format '%s' not found", copy); 2504 value = xstrdup(""); 2505 } else { 2506 format_log(es, "format '%s' found: %s", copy, 2507 value); 2508 } 2509 } 2510 } 2511 2512 done: 2513 /* Expand again if required. */ 2514 if (modifiers & FORMAT_EXPAND) { 2515 new = format_expand1(es, value); 2516 free(value); 2517 value = new; 2518 } else if (modifiers & FORMAT_EXPANDTIME) { 2519 format_copy_state(&next, es, FORMAT_EXPAND_TIME); 2520 new = format_expand1(&next, value); 2521 free(value); 2522 value = new; 2523 } 2524 2525 /* Perform substitution if any. */ 2526 for (i = 0; i < nsub; i++) { 2527 left = format_expand1(es, sub[i]->argv[0]); 2528 right = format_expand1(es, sub[i]->argv[1]); 2529 new = format_sub(sub[i], value, left, right); 2530 format_log(es, "substitute '%s' to '%s': %s", left, right, new); 2531 free(value); 2532 value = new; 2533 free(right); 2534 free(left); 2535 } 2536 2537 /* Truncate the value if needed. */ 2538 if (limit > 0) { 2539 new = format_trim_left(value, limit); 2540 if (marker != NULL && strcmp(new, value) != 0) { 2541 free(value); 2542 xasprintf(&value, "%s%s", new, marker); 2543 } else { 2544 free(value); 2545 value = new; 2546 } 2547 format_log(es, "applied length limit %d: %s", limit, value); 2548 } else if (limit < 0) { 2549 new = format_trim_right(value, -limit); 2550 if (marker != NULL && strcmp(new, value) != 0) { 2551 free(value); 2552 xasprintf(&value, "%s%s", marker, new); 2553 } else { 2554 free(value); 2555 value = new; 2556 } 2557 format_log(es, "applied length limit %d: %s", limit, value); 2558 } 2559 2560 /* Pad the value if needed. */ 2561 if (width > 0) { 2562 new = utf8_padcstr(value, width); 2563 free(value); 2564 value = new; 2565 format_log(es, "applied padding width %d: %s", width, value); 2566 } else if (width < 0) { 2567 new = utf8_rpadcstr(value, -width); 2568 free(value); 2569 value = new; 2570 format_log(es, "applied padding width %d: %s", width, value); 2571 } 2572 2573 /* Replace with the length or width if needed. */ 2574 if (modifiers & FORMAT_LENGTH) { 2575 xasprintf(&new, "%zu", strlen(value)); 2576 free(value); 2577 value = new; 2578 format_log(es, "replacing with length: %s", new); 2579 } 2580 if (modifiers & FORMAT_WIDTH) { 2581 xasprintf(&new, "%u", format_width(value)); 2582 free(value); 2583 value = new; 2584 format_log(es, "replacing with width: %s", new); 2585 } 2586 2587 /* Expand the buffer and copy in the value. */ 2588 valuelen = strlen(value); 2589 while (*len - *off < valuelen + 1) { 2590 *buf = xreallocarray(*buf, 2, *len); 2591 *len *= 2; 2592 } 2593 memcpy(*buf + *off, value, valuelen); 2594 *off += valuelen; 2595 2596 format_log(es, "replaced '%s' with '%s'", copy0, value); 2597 free(value); 2598 2599 free(sub); 2600 format_free_modifiers(list, count); 2601 free(copy0); 2602 return (0); 2603 2604 fail: 2605 format_log(es, "failed %s", copy0); 2606 2607 free(sub); 2608 format_free_modifiers(list, count); 2609 free(copy0); 2610 return (-1); 2611 } 2612 2613 /* Expand keys in a template. */ 2614 static char * 2615 format_expand1(struct format_expand_state *es, const char *fmt) 2616 { 2617 struct format_tree *ft = es->ft; 2618 char *buf, *out, *name; 2619 const char *ptr, *s; 2620 size_t off, len, n, outlen; 2621 int ch, brackets; 2622 struct tm *tm; 2623 char expanded[8192]; 2624 2625 if (fmt == NULL || *fmt == '\0') 2626 return (xstrdup("")); 2627 2628 if (es->loop == FORMAT_LOOP_LIMIT) 2629 return (xstrdup("")); 2630 es->loop++; 2631 2632 format_log(es, "expanding format: %s", fmt); 2633 2634 if (es->flags & FORMAT_EXPAND_TIME) { 2635 if (es->time == 0) 2636 es->time = time(NULL); 2637 tm = localtime(&es->time); 2638 if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { 2639 format_log(es, "format is too long"); 2640 return (xstrdup("")); 2641 } 2642 if (format_logging(ft) && strcmp(expanded, fmt) != 0) 2643 format_log(es, "after time expanded: %s", expanded); 2644 fmt = expanded; 2645 } 2646 2647 len = 64; 2648 buf = xmalloc(len); 2649 off = 0; 2650 2651 while (*fmt != '\0') { 2652 if (*fmt != '#') { 2653 while (len - off < 2) { 2654 buf = xreallocarray(buf, 2, len); 2655 len *= 2; 2656 } 2657 buf[off++] = *fmt++; 2658 continue; 2659 } 2660 fmt++; 2661 2662 ch = (u_char)*fmt++; 2663 switch (ch) { 2664 case '(': 2665 brackets = 1; 2666 for (ptr = fmt; *ptr != '\0'; ptr++) { 2667 if (*ptr == '(') 2668 brackets++; 2669 if (*ptr == ')' && --brackets == 0) 2670 break; 2671 } 2672 if (*ptr != ')' || brackets != 0) 2673 break; 2674 n = ptr - fmt; 2675 2676 name = xstrndup(fmt, n); 2677 format_log(es, "found #(): %s", name); 2678 2679 if ((ft->flags & FORMAT_NOJOBS) || 2680 (es->flags & FORMAT_EXPAND_NOJOBS)) { 2681 out = xstrdup(""); 2682 format_log(es, "#() is disabled"); 2683 } else { 2684 out = format_job_get(es, name); 2685 format_log(es, "#() result: %s", out); 2686 } 2687 free(name); 2688 2689 outlen = strlen(out); 2690 while (len - off < outlen + 1) { 2691 buf = xreallocarray(buf, 2, len); 2692 len *= 2; 2693 } 2694 memcpy(buf + off, out, outlen); 2695 off += outlen; 2696 2697 free(out); 2698 2699 fmt += n + 1; 2700 continue; 2701 case '{': 2702 ptr = format_skip((char *)fmt - 2, "}"); 2703 if (ptr == NULL) 2704 break; 2705 n = ptr - fmt; 2706 2707 format_log(es, "found #{}: %.*s", (int)n, fmt); 2708 if (format_replace(es, fmt, n, &buf, &len, &off) != 0) 2709 break; 2710 fmt += n + 1; 2711 continue; 2712 case '#': 2713 /* 2714 * If ##[ (with two or more #s), then it is a style and 2715 * can be left for format_draw to handle. 2716 */ 2717 ptr = fmt; 2718 n = 2; 2719 while (*ptr == '#') { 2720 ptr++; 2721 n++; 2722 } 2723 if (*ptr == '[') { 2724 format_log(es, "found #*%zu[", n); 2725 while (len - off < n + 2) { 2726 buf = xreallocarray(buf, 2, len); 2727 len *= 2; 2728 } 2729 memcpy(buf + off, fmt - 2, n + 1); 2730 off += n + 1; 2731 fmt = ptr + 1; 2732 continue; 2733 } 2734 /* FALLTHROUGH */ 2735 case '}': 2736 case ',': 2737 format_log(es, "found #%c", ch); 2738 while (len - off < 2) { 2739 buf = xreallocarray(buf, 2, len); 2740 len *= 2; 2741 } 2742 buf[off++] = ch; 2743 continue; 2744 default: 2745 s = NULL; 2746 if (ch >= 'A' && ch <= 'Z') 2747 s = format_upper[ch - 'A']; 2748 else if (ch >= 'a' && ch <= 'z') 2749 s = format_lower[ch - 'a']; 2750 if (s == NULL) { 2751 while (len - off < 3) { 2752 buf = xreallocarray(buf, 2, len); 2753 len *= 2; 2754 } 2755 buf[off++] = '#'; 2756 buf[off++] = ch; 2757 continue; 2758 } 2759 n = strlen(s); 2760 format_log(es, "found #%c: %s", ch, s); 2761 if (format_replace(es, s, n, &buf, &len, &off) != 0) 2762 break; 2763 continue; 2764 } 2765 2766 break; 2767 } 2768 buf[off] = '\0'; 2769 2770 format_log(es, "result is: %s", buf); 2771 es->loop--; 2772 2773 return (buf); 2774 } 2775 2776 /* Expand keys in a template, passing through strftime first. */ 2777 char * 2778 format_expand_time(struct format_tree *ft, const char *fmt) 2779 { 2780 struct format_expand_state es; 2781 2782 memset(&es, 0, sizeof es); 2783 es.ft = ft; 2784 es.flags = FORMAT_EXPAND_TIME; 2785 return (format_expand1(&es, fmt)); 2786 } 2787 2788 /* Expand keys in a template. */ 2789 char * 2790 format_expand(struct format_tree *ft, const char *fmt) 2791 { 2792 struct format_expand_state es; 2793 2794 memset(&es, 0, sizeof es); 2795 es.ft = ft; 2796 es.flags = 0; 2797 return (format_expand1(&es, fmt)); 2798 } 2799 2800 /* Expand a single string. */ 2801 char * 2802 format_single(struct cmdq_item *item, const char *fmt, struct client *c, 2803 struct session *s, struct winlink *wl, struct window_pane *wp) 2804 { 2805 struct format_tree *ft; 2806 char *expanded; 2807 2808 ft = format_create_defaults(item, c, s, wl, wp); 2809 expanded = format_expand(ft, fmt); 2810 format_free(ft); 2811 return (expanded); 2812 } 2813 2814 /* Expand a single string using state. */ 2815 char * 2816 format_single_from_state(struct cmdq_item *item, const char *fmt, 2817 struct client *c, struct cmd_find_state *fs) 2818 { 2819 return (format_single(item, fmt, c, fs->s, fs->wl, fs->wp)); 2820 } 2821 2822 /* Expand a single string using target. */ 2823 char * 2824 format_single_from_target(struct cmdq_item *item, const char *fmt) 2825 { 2826 struct client *tc = cmdq_get_target_client(item); 2827 2828 return (format_single_from_state(item, fmt, tc, cmdq_get_target(item))); 2829 } 2830 2831 /* Create and add defaults. */ 2832 struct format_tree * 2833 format_create_defaults(struct cmdq_item *item, struct client *c, 2834 struct session *s, struct winlink *wl, struct window_pane *wp) 2835 { 2836 struct format_tree *ft; 2837 2838 if (item != NULL) 2839 ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); 2840 else 2841 ft = format_create(NULL, item, FORMAT_NONE, 0); 2842 format_defaults(ft, c, s, wl, wp); 2843 return (ft); 2844 } 2845 2846 /* Create and add defaults using state. */ 2847 struct format_tree * 2848 format_create_from_state(struct cmdq_item *item, struct client *c, 2849 struct cmd_find_state *fs) 2850 { 2851 return (format_create_defaults(item, c, fs->s, fs->wl, fs->wp)); 2852 } 2853 2854 /* Create and add defaults using target. */ 2855 struct format_tree * 2856 format_create_from_target(struct cmdq_item *item) 2857 { 2858 struct client *tc = cmdq_get_target_client(item); 2859 2860 return (format_create_from_state(item, tc, cmdq_get_target(item))); 2861 } 2862 2863 /* Set defaults for any of arguments that are not NULL. */ 2864 void 2865 format_defaults(struct format_tree *ft, struct client *c, struct session *s, 2866 struct winlink *wl, struct window_pane *wp) 2867 { 2868 struct paste_buffer *pb; 2869 2870 if (c != NULL && c->name != NULL) 2871 log_debug("%s: c=%s", __func__, c->name); 2872 else 2873 log_debug("%s: c=none", __func__); 2874 if (s != NULL) 2875 log_debug("%s: s=$%u", __func__, s->id); 2876 else 2877 log_debug("%s: s=none", __func__); 2878 if (wl != NULL) 2879 log_debug("%s: wl=%u", __func__, wl->idx); 2880 else 2881 log_debug("%s: wl=none", __func__); 2882 if (wp != NULL) 2883 log_debug("%s: wp=%%%u", __func__, wp->id); 2884 else 2885 log_debug("%s: wp=none", __func__); 2886 2887 if (c != NULL && s != NULL && c->session != s) 2888 log_debug("%s: session does not match", __func__); 2889 2890 format_add(ft, "session_format", "%d", s != NULL); 2891 format_add(ft, "window_format", "%d", wl != NULL); 2892 format_add(ft, "pane_format", "%d", wp != NULL); 2893 2894 if (s == NULL && c != NULL) 2895 s = c->session; 2896 if (wl == NULL && s != NULL) 2897 wl = s->curw; 2898 if (wp == NULL && wl != NULL) 2899 wp = wl->window->active; 2900 2901 if (c != NULL) 2902 format_defaults_client(ft, c); 2903 if (s != NULL) 2904 format_defaults_session(ft, s); 2905 if (wl != NULL) 2906 format_defaults_winlink(ft, wl); 2907 if (wp != NULL) 2908 format_defaults_pane(ft, wp); 2909 2910 pb = paste_get_top (NULL); 2911 if (pb != NULL) 2912 format_defaults_paste_buffer(ft, pb); 2913 } 2914 2915 /* Set default format keys for a session. */ 2916 static void 2917 format_defaults_session(struct format_tree *ft, struct session *s) 2918 { 2919 struct session_group *sg; 2920 2921 ft->s = s; 2922 2923 format_add(ft, "session_name", "%s", s->name); 2924 format_add(ft, "session_path", "%s", s->cwd); 2925 format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); 2926 format_add(ft, "session_id", "$%u", s->id); 2927 2928 sg = session_group_contains(s); 2929 format_add(ft, "session_grouped", "%d", sg != NULL); 2930 if (sg != NULL) { 2931 format_add(ft, "session_group", "%s", sg->name); 2932 format_add(ft, "session_group_size", "%u", 2933 session_group_count (sg)); 2934 format_add(ft, "session_group_attached", "%u", 2935 session_group_attached_count (sg)); 2936 format_add(ft, "session_group_many_attached", "%u", 2937 session_group_attached_count (sg) > 1); 2938 format_add_cb(ft, "session_group_list", 2939 format_cb_session_group_list); 2940 format_add_cb(ft, "session_group_attached_list", 2941 format_cb_session_group_attached_list); 2942 } 2943 2944 format_add_tv(ft, "session_created", &s->creation_time); 2945 format_add_tv(ft, "session_last_attached", &s->last_attached_time); 2946 format_add_tv(ft, "session_activity", &s->activity_time); 2947 2948 format_add(ft, "session_attached", "%u", s->attached); 2949 format_add(ft, "session_many_attached", "%d", s->attached > 1); 2950 format_add_cb(ft, "session_attached_list", 2951 format_cb_session_attached_list); 2952 2953 format_add_cb(ft, "session_alerts", format_cb_session_alerts); 2954 format_add_cb(ft, "session_stack", format_cb_session_stack); 2955 2956 if (server_check_marked() && marked_pane.s == s) 2957 format_add(ft, "session_marked", "1"); 2958 else 2959 format_add(ft, "session_marked", "0"); 2960 } 2961 2962 /* Set default format keys for a client. */ 2963 static void 2964 format_defaults_client(struct format_tree *ft, struct client *c) 2965 { 2966 struct session *s; 2967 const char *name; 2968 struct tty *tty = &c->tty; 2969 2970 if (ft->s == NULL) 2971 ft->s = c->session; 2972 ft->c = c; 2973 2974 format_add(ft, "client_name", "%s", c->name); 2975 format_add(ft, "client_pid", "%ld", (long) c->pid); 2976 format_add(ft, "client_height", "%u", tty->sy); 2977 format_add(ft, "client_width", "%u", tty->sx); 2978 format_add(ft, "client_cell_width", "%u", tty->xpixel); 2979 format_add(ft, "client_cell_height", "%u", tty->ypixel); 2980 format_add(ft, "client_tty", "%s", c->ttyname); 2981 format_add(ft, "client_control_mode", "%d", 2982 !!(c->flags & CLIENT_CONTROL)); 2983 2984 format_add(ft, "client_termname", "%s", c->term_name); 2985 format_add(ft, "client_termfeatures", "%s", 2986 tty_get_features(c->term_features)); 2987 if (c->term_type != NULL) 2988 format_add(ft, "client_termtype", "%s", c->term_type); 2989 2990 format_add_tv(ft, "client_created", &c->creation_time); 2991 format_add_tv(ft, "client_activity", &c->activity_time); 2992 2993 format_add(ft, "client_written", "%zu", c->written); 2994 format_add(ft, "client_discarded", "%zu", c->discarded); 2995 2996 name = server_client_get_key_table(c); 2997 if (strcmp(c->keytable->name, name) == 0) 2998 format_add(ft, "client_prefix", "%d", 0); 2999 else 3000 format_add(ft, "client_prefix", "%d", 1); 3001 format_add(ft, "client_key_table", "%s", c->keytable->name); 3002 3003 if (c->flags & CLIENT_UTF8) 3004 format_add(ft, "client_utf8", "%d", 1); 3005 else 3006 format_add(ft, "client_utf8", "%d", 0); 3007 if (c->flags & CLIENT_READONLY) 3008 format_add(ft, "client_readonly", "%d", 1); 3009 else 3010 format_add(ft, "client_readonly", "%d", 0); 3011 format_add(ft, "client_flags", "%s", server_client_get_flags(c)); 3012 3013 s = c->session; 3014 if (s != NULL) 3015 format_add(ft, "client_session", "%s", s->name); 3016 s = c->last_session; 3017 if (s != NULL && session_alive(s)) 3018 format_add(ft, "client_last_session", "%s", s->name); 3019 } 3020 3021 /* Set default format keys for a window. */ 3022 void 3023 format_defaults_window(struct format_tree *ft, struct window *w) 3024 { 3025 ft->w = w; 3026 3027 format_add_tv(ft, "window_activity", &w->activity_time); 3028 format_add(ft, "window_id", "@%u", w->id); 3029 format_add(ft, "window_name", "%s", w->name); 3030 format_add(ft, "window_width", "%u", w->sx); 3031 format_add(ft, "window_height", "%u", w->sy); 3032 format_add(ft, "window_cell_width", "%u", w->xpixel); 3033 format_add(ft, "window_cell_height", "%u", w->ypixel); 3034 format_add_cb(ft, "window_layout", format_cb_window_layout); 3035 format_add_cb(ft, "window_visible_layout", 3036 format_cb_window_visible_layout); 3037 format_add(ft, "window_panes", "%u", window_count_panes(w)); 3038 format_add(ft, "window_zoomed_flag", "%d", 3039 !!(w->flags & WINDOW_ZOOMED)); 3040 } 3041 3042 /* Set default format keys for a winlink. */ 3043 static void 3044 format_defaults_winlink(struct format_tree *ft, struct winlink *wl) 3045 { 3046 struct client *c = ft->c; 3047 struct session *s = wl->session; 3048 struct window *w = wl->window; 3049 int flag; 3050 u_int ox, oy, sx, sy; 3051 3052 if (ft->w == NULL) 3053 format_defaults_window(ft, w); 3054 ft->wl = wl; 3055 3056 if (c != NULL) { 3057 flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); 3058 format_add(ft, "window_bigger", "%d", flag); 3059 if (flag) { 3060 format_add(ft, "window_offset_x", "%u", ox); 3061 format_add(ft, "window_offset_y", "%u", oy); 3062 } 3063 } 3064 3065 format_add(ft, "window_index", "%d", wl->idx); 3066 format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); 3067 format_add(ft, "window_flags", "%s", window_printable_flags(wl, 1)); 3068 format_add(ft, "window_raw_flags", "%s", window_printable_flags(wl, 0)); 3069 format_add(ft, "window_active", "%d", wl == s->curw); 3070 format_add_cb(ft, "window_active_sessions", 3071 format_cb_window_active_sessions); 3072 format_add_cb(ft, "window_active_sessions_list", 3073 format_cb_window_active_sessions_list); 3074 format_add_cb(ft, "window_active_clients", 3075 format_cb_window_active_clients); 3076 format_add_cb(ft, "window_active_clients_list", 3077 format_cb_window_active_clients_list); 3078 3079 format_add(ft, "window_start_flag", "%d", 3080 !!(wl == RB_MIN(winlinks, &s->windows))); 3081 format_add(ft, "window_end_flag", "%d", 3082 !!(wl == RB_MAX(winlinks, &s->windows))); 3083 3084 if (server_check_marked() && marked_pane.wl == wl) 3085 format_add(ft, "window_marked_flag", "1"); 3086 else 3087 format_add(ft, "window_marked_flag", "0"); 3088 3089 format_add(ft, "window_bell_flag", "%d", 3090 !!(wl->flags & WINLINK_BELL)); 3091 format_add(ft, "window_activity_flag", "%d", 3092 !!(wl->flags & WINLINK_ACTIVITY)); 3093 format_add(ft, "window_silence_flag", "%d", 3094 !!(wl->flags & WINLINK_SILENCE)); 3095 format_add(ft, "window_last_flag", "%d", 3096 !!(wl == TAILQ_FIRST(&s->lastw))); 3097 format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); 3098 3099 format_add_cb(ft, "window_linked_sessions_list", 3100 format_cb_window_linked_sessions_list); 3101 format_add(ft, "window_linked_sessions", "%u", 3102 wl->window->references); 3103 } 3104 3105 /* Set default format keys for a window pane. */ 3106 void 3107 format_defaults_pane(struct format_tree *ft, struct window_pane *wp) 3108 { 3109 struct window *w = wp->window; 3110 struct grid *gd = wp->base.grid; 3111 int status = wp->status; 3112 u_int idx; 3113 struct window_mode_entry *wme; 3114 3115 if (ft->w == NULL) 3116 format_defaults_window(ft, w); 3117 ft->wp = wp; 3118 3119 format_add(ft, "history_size", "%u", gd->hsize); 3120 format_add(ft, "history_limit", "%u", gd->hlimit); 3121 format_add_cb(ft, "history_bytes", format_cb_history_bytes); 3122 format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes); 3123 3124 if (window_pane_index(wp, &idx) != 0) 3125 fatalx("index not found"); 3126 format_add(ft, "pane_index", "%u", idx); 3127 3128 format_add(ft, "pane_width", "%u", wp->sx); 3129 format_add(ft, "pane_height", "%u", wp->sy); 3130 format_add(ft, "pane_title", "%s", wp->base.title); 3131 if (wp->base.path != NULL) 3132 format_add(ft, "pane_path", "%s", wp->base.path); 3133 format_add(ft, "pane_id", "%%%u", wp->id); 3134 format_add(ft, "pane_active", "%d", wp == w->active); 3135 format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); 3136 format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1); 3137 3138 if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status)) 3139 format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); 3140 if (~wp->flags & PANE_EMPTY) 3141 format_add(ft, "pane_dead", "%d", wp->fd == -1); 3142 else 3143 format_add(ft, "pane_dead", "0"); 3144 format_add(ft, "pane_last", "%d", wp == w->last); 3145 3146 if (server_check_marked() && marked_pane.wp == wp) 3147 format_add(ft, "pane_marked", "1"); 3148 else 3149 format_add(ft, "pane_marked", "0"); 3150 format_add(ft, "pane_marked_set", "%d", server_check_marked()); 3151 3152 format_add(ft, "pane_left", "%u", wp->xoff); 3153 format_add(ft, "pane_top", "%u", wp->yoff); 3154 format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); 3155 format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); 3156 format_add(ft, "pane_at_left", "%d", wp->xoff == 0); 3157 format_add_cb(ft, "pane_at_top", format_cb_pane_at_top); 3158 format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); 3159 format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom); 3160 3161 wme = TAILQ_FIRST(&wp->modes); 3162 if (wme != NULL) { 3163 format_add(ft, "pane_mode", "%s", wme->mode->name); 3164 if (wme->mode->formats != NULL) 3165 wme->mode->formats(wme, ft); 3166 } 3167 format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode); 3168 3169 format_add(ft, "pane_synchronized", "%d", 3170 !!options_get_number(wp->options, "synchronize-panes")); 3171 if (wp->searchstr != NULL) 3172 format_add(ft, "pane_search_string", "%s", wp->searchstr); 3173 3174 format_add(ft, "pane_tty", "%s", wp->tty); 3175 format_add(ft, "pane_pid", "%ld", (long) wp->pid); 3176 format_add_cb(ft, "pane_start_command", format_cb_start_command); 3177 format_add_cb(ft, "pane_current_command", format_cb_current_command); 3178 format_add_cb(ft, "pane_current_path", format_cb_current_path); 3179 3180 format_add(ft, "cursor_x", "%u", wp->base.cx); 3181 format_add(ft, "cursor_y", "%u", wp->base.cy); 3182 format_add_cb(ft, "cursor_character", format_cb_cursor_character); 3183 3184 format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); 3185 format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); 3186 3187 format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL); 3188 if (wp->base.saved_cx != UINT_MAX) 3189 format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx); 3190 if (wp->base.saved_cy != UINT_MAX) 3191 format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy); 3192 3193 format_add(ft, "cursor_flag", "%d", 3194 !!(wp->base.mode & MODE_CURSOR)); 3195 format_add(ft, "insert_flag", "%d", 3196 !!(wp->base.mode & MODE_INSERT)); 3197 format_add(ft, "keypad_cursor_flag", "%d", 3198 !!(wp->base.mode & MODE_KCURSOR)); 3199 format_add(ft, "keypad_flag", "%d", 3200 !!(wp->base.mode & MODE_KKEYPAD)); 3201 format_add(ft, "wrap_flag", "%d", 3202 !!(wp->base.mode & MODE_WRAP)); 3203 format_add(ft, "origin_flag", "%d", 3204 !!(wp->base.mode & MODE_ORIGIN)); 3205 3206 format_add(ft, "mouse_any_flag", "%d", 3207 !!(wp->base.mode & ALL_MOUSE_MODES)); 3208 format_add(ft, "mouse_standard_flag", "%d", 3209 !!(wp->base.mode & MODE_MOUSE_STANDARD)); 3210 format_add(ft, "mouse_button_flag", "%d", 3211 !!(wp->base.mode & MODE_MOUSE_BUTTON)); 3212 format_add(ft, "mouse_all_flag", "%d", 3213 !!(wp->base.mode & MODE_MOUSE_ALL)); 3214 format_add(ft, "mouse_utf8_flag", "%d", 3215 !!(wp->base.mode & MODE_MOUSE_UTF8)); 3216 format_add(ft, "mouse_sgr_flag", "%d", 3217 !!(wp->base.mode & MODE_MOUSE_SGR)); 3218 3219 format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); 3220 format_add_cb(ft, "pane_fg", format_cb_pane_fg); 3221 format_add_cb(ft, "pane_bg", format_cb_pane_bg); 3222 } 3223 3224 /* Set default format keys for paste buffer. */ 3225 void 3226 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) 3227 { 3228 struct timeval tv; 3229 size_t size; 3230 char *s; 3231 3232 timerclear(&tv); 3233 tv.tv_sec = paste_buffer_created(pb); 3234 paste_buffer_data(pb, &size); 3235 3236 format_add(ft, "buffer_size", "%zu", size); 3237 format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); 3238 format_add_tv(ft, "buffer_created", &tv); 3239 3240 s = paste_make_sample(pb); 3241 format_add(ft, "buffer_sample", "%s", s); 3242 free(s); 3243 } 3244