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