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