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