1 /* $OpenBSD: cmd.c,v 1.164 2021/06/10 07:50:03 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 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/time.h> 21 22 #include <fnmatch.h> 23 #include <paths.h> 24 #include <pwd.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "tmux.h" 30 31 extern const struct cmd_entry cmd_attach_session_entry; 32 extern const struct cmd_entry cmd_bind_key_entry; 33 extern const struct cmd_entry cmd_break_pane_entry; 34 extern const struct cmd_entry cmd_capture_pane_entry; 35 extern const struct cmd_entry cmd_choose_buffer_entry; 36 extern const struct cmd_entry cmd_choose_client_entry; 37 extern const struct cmd_entry cmd_choose_tree_entry; 38 extern const struct cmd_entry cmd_clear_history_entry; 39 extern const struct cmd_entry cmd_clear_prompt_history_entry; 40 extern const struct cmd_entry cmd_clock_mode_entry; 41 extern const struct cmd_entry cmd_command_prompt_entry; 42 extern const struct cmd_entry cmd_confirm_before_entry; 43 extern const struct cmd_entry cmd_copy_mode_entry; 44 extern const struct cmd_entry cmd_customize_mode_entry; 45 extern const struct cmd_entry cmd_delete_buffer_entry; 46 extern const struct cmd_entry cmd_detach_client_entry; 47 extern const struct cmd_entry cmd_display_menu_entry; 48 extern const struct cmd_entry cmd_display_message_entry; 49 extern const struct cmd_entry cmd_display_popup_entry; 50 extern const struct cmd_entry cmd_display_panes_entry; 51 extern const struct cmd_entry cmd_down_pane_entry; 52 extern const struct cmd_entry cmd_find_window_entry; 53 extern const struct cmd_entry cmd_has_session_entry; 54 extern const struct cmd_entry cmd_if_shell_entry; 55 extern const struct cmd_entry cmd_join_pane_entry; 56 extern const struct cmd_entry cmd_kill_pane_entry; 57 extern const struct cmd_entry cmd_kill_server_entry; 58 extern const struct cmd_entry cmd_kill_session_entry; 59 extern const struct cmd_entry cmd_kill_window_entry; 60 extern const struct cmd_entry cmd_last_pane_entry; 61 extern const struct cmd_entry cmd_last_window_entry; 62 extern const struct cmd_entry cmd_link_window_entry; 63 extern const struct cmd_entry cmd_list_buffers_entry; 64 extern const struct cmd_entry cmd_list_clients_entry; 65 extern const struct cmd_entry cmd_list_commands_entry; 66 extern const struct cmd_entry cmd_list_keys_entry; 67 extern const struct cmd_entry cmd_list_panes_entry; 68 extern const struct cmd_entry cmd_list_sessions_entry; 69 extern const struct cmd_entry cmd_list_windows_entry; 70 extern const struct cmd_entry cmd_load_buffer_entry; 71 extern const struct cmd_entry cmd_lock_client_entry; 72 extern const struct cmd_entry cmd_lock_server_entry; 73 extern const struct cmd_entry cmd_lock_session_entry; 74 extern const struct cmd_entry cmd_move_pane_entry; 75 extern const struct cmd_entry cmd_move_window_entry; 76 extern const struct cmd_entry cmd_new_session_entry; 77 extern const struct cmd_entry cmd_new_window_entry; 78 extern const struct cmd_entry cmd_next_layout_entry; 79 extern const struct cmd_entry cmd_next_window_entry; 80 extern const struct cmd_entry cmd_paste_buffer_entry; 81 extern const struct cmd_entry cmd_pipe_pane_entry; 82 extern const struct cmd_entry cmd_previous_layout_entry; 83 extern const struct cmd_entry cmd_previous_window_entry; 84 extern const struct cmd_entry cmd_refresh_client_entry; 85 extern const struct cmd_entry cmd_rename_session_entry; 86 extern const struct cmd_entry cmd_rename_window_entry; 87 extern const struct cmd_entry cmd_resize_pane_entry; 88 extern const struct cmd_entry cmd_resize_window_entry; 89 extern const struct cmd_entry cmd_respawn_pane_entry; 90 extern const struct cmd_entry cmd_respawn_window_entry; 91 extern const struct cmd_entry cmd_rotate_window_entry; 92 extern const struct cmd_entry cmd_run_shell_entry; 93 extern const struct cmd_entry cmd_save_buffer_entry; 94 extern const struct cmd_entry cmd_select_layout_entry; 95 extern const struct cmd_entry cmd_select_pane_entry; 96 extern const struct cmd_entry cmd_select_window_entry; 97 extern const struct cmd_entry cmd_send_keys_entry; 98 extern const struct cmd_entry cmd_send_prefix_entry; 99 extern const struct cmd_entry cmd_set_buffer_entry; 100 extern const struct cmd_entry cmd_set_environment_entry; 101 extern const struct cmd_entry cmd_set_hook_entry; 102 extern const struct cmd_entry cmd_set_option_entry; 103 extern const struct cmd_entry cmd_set_window_option_entry; 104 extern const struct cmd_entry cmd_show_buffer_entry; 105 extern const struct cmd_entry cmd_show_environment_entry; 106 extern const struct cmd_entry cmd_show_hooks_entry; 107 extern const struct cmd_entry cmd_show_messages_entry; 108 extern const struct cmd_entry cmd_show_options_entry; 109 extern const struct cmd_entry cmd_show_prompt_history_entry; 110 extern const struct cmd_entry cmd_show_window_options_entry; 111 extern const struct cmd_entry cmd_source_file_entry; 112 extern const struct cmd_entry cmd_split_window_entry; 113 extern const struct cmd_entry cmd_start_server_entry; 114 extern const struct cmd_entry cmd_suspend_client_entry; 115 extern const struct cmd_entry cmd_swap_pane_entry; 116 extern const struct cmd_entry cmd_swap_window_entry; 117 extern const struct cmd_entry cmd_switch_client_entry; 118 extern const struct cmd_entry cmd_unbind_key_entry; 119 extern const struct cmd_entry cmd_unlink_window_entry; 120 extern const struct cmd_entry cmd_up_pane_entry; 121 extern const struct cmd_entry cmd_wait_for_entry; 122 123 const struct cmd_entry *cmd_table[] = { 124 &cmd_attach_session_entry, 125 &cmd_bind_key_entry, 126 &cmd_break_pane_entry, 127 &cmd_capture_pane_entry, 128 &cmd_choose_buffer_entry, 129 &cmd_choose_client_entry, 130 &cmd_choose_tree_entry, 131 &cmd_clear_history_entry, 132 &cmd_clear_prompt_history_entry, 133 &cmd_clock_mode_entry, 134 &cmd_command_prompt_entry, 135 &cmd_confirm_before_entry, 136 &cmd_copy_mode_entry, 137 &cmd_customize_mode_entry, 138 &cmd_delete_buffer_entry, 139 &cmd_detach_client_entry, 140 &cmd_display_menu_entry, 141 &cmd_display_message_entry, 142 &cmd_display_popup_entry, 143 &cmd_display_panes_entry, 144 &cmd_find_window_entry, 145 &cmd_has_session_entry, 146 &cmd_if_shell_entry, 147 &cmd_join_pane_entry, 148 &cmd_kill_pane_entry, 149 &cmd_kill_server_entry, 150 &cmd_kill_session_entry, 151 &cmd_kill_window_entry, 152 &cmd_last_pane_entry, 153 &cmd_last_window_entry, 154 &cmd_link_window_entry, 155 &cmd_list_buffers_entry, 156 &cmd_list_clients_entry, 157 &cmd_list_commands_entry, 158 &cmd_list_keys_entry, 159 &cmd_list_panes_entry, 160 &cmd_list_sessions_entry, 161 &cmd_list_windows_entry, 162 &cmd_load_buffer_entry, 163 &cmd_lock_client_entry, 164 &cmd_lock_server_entry, 165 &cmd_lock_session_entry, 166 &cmd_move_pane_entry, 167 &cmd_move_window_entry, 168 &cmd_new_session_entry, 169 &cmd_new_window_entry, 170 &cmd_next_layout_entry, 171 &cmd_next_window_entry, 172 &cmd_paste_buffer_entry, 173 &cmd_pipe_pane_entry, 174 &cmd_previous_layout_entry, 175 &cmd_previous_window_entry, 176 &cmd_refresh_client_entry, 177 &cmd_rename_session_entry, 178 &cmd_rename_window_entry, 179 &cmd_resize_pane_entry, 180 &cmd_resize_window_entry, 181 &cmd_respawn_pane_entry, 182 &cmd_respawn_window_entry, 183 &cmd_rotate_window_entry, 184 &cmd_run_shell_entry, 185 &cmd_save_buffer_entry, 186 &cmd_select_layout_entry, 187 &cmd_select_pane_entry, 188 &cmd_select_window_entry, 189 &cmd_send_keys_entry, 190 &cmd_send_prefix_entry, 191 &cmd_set_buffer_entry, 192 &cmd_set_environment_entry, 193 &cmd_set_hook_entry, 194 &cmd_set_option_entry, 195 &cmd_set_window_option_entry, 196 &cmd_show_buffer_entry, 197 &cmd_show_environment_entry, 198 &cmd_show_hooks_entry, 199 &cmd_show_messages_entry, 200 &cmd_show_options_entry, 201 &cmd_show_prompt_history_entry, 202 &cmd_show_window_options_entry, 203 &cmd_source_file_entry, 204 &cmd_split_window_entry, 205 &cmd_start_server_entry, 206 &cmd_suspend_client_entry, 207 &cmd_swap_pane_entry, 208 &cmd_swap_window_entry, 209 &cmd_switch_client_entry, 210 &cmd_unbind_key_entry, 211 &cmd_unlink_window_entry, 212 &cmd_wait_for_entry, 213 NULL 214 }; 215 216 /* Instance of a command. */ 217 struct cmd { 218 const struct cmd_entry *entry; 219 struct args *args; 220 u_int group; 221 222 char *file; 223 u_int line; 224 225 char *alias; 226 int argc; 227 char **argv; 228 229 TAILQ_ENTRY(cmd) qentry; 230 }; 231 TAILQ_HEAD(cmds, cmd); 232 233 /* Next group number for new command list. */ 234 static u_int cmd_list_next_group = 1; 235 236 /* Log an argument vector. */ 237 void printflike(3, 4) 238 cmd_log_argv(int argc, char **argv, const char *fmt, ...) 239 { 240 char *prefix; 241 va_list ap; 242 int i; 243 244 va_start(ap, fmt); 245 xvasprintf(&prefix, fmt, ap); 246 va_end(ap); 247 248 for (i = 0; i < argc; i++) 249 log_debug("%s: argv[%d]=%s", prefix, i, argv[i]); 250 free(prefix); 251 } 252 253 /* Prepend to an argument vector. */ 254 void 255 cmd_prepend_argv(int *argc, char ***argv, char *arg) 256 { 257 char **new_argv; 258 int i; 259 260 new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); 261 new_argv[0] = xstrdup(arg); 262 for (i = 0; i < *argc; i++) 263 new_argv[1 + i] = (*argv)[i]; 264 265 free(*argv); 266 *argv = new_argv; 267 (*argc)++; 268 } 269 270 /* Append to an argument vector. */ 271 void 272 cmd_append_argv(int *argc, char ***argv, char *arg) 273 { 274 *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); 275 (*argv)[(*argc)++] = xstrdup(arg); 276 } 277 278 /* Pack an argument vector up into a buffer. */ 279 int 280 cmd_pack_argv(int argc, char **argv, char *buf, size_t len) 281 { 282 size_t arglen; 283 int i; 284 285 if (argc == 0) 286 return (0); 287 cmd_log_argv(argc, argv, "%s", __func__); 288 289 *buf = '\0'; 290 for (i = 0; i < argc; i++) { 291 if (strlcpy(buf, argv[i], len) >= len) 292 return (-1); 293 arglen = strlen(argv[i]) + 1; 294 buf += arglen; 295 len -= arglen; 296 } 297 298 return (0); 299 } 300 301 /* Unpack an argument vector from a packed buffer. */ 302 int 303 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) 304 { 305 int i; 306 size_t arglen; 307 308 if (argc == 0) 309 return (0); 310 *argv = xcalloc(argc, sizeof **argv); 311 312 buf[len - 1] = '\0'; 313 for (i = 0; i < argc; i++) { 314 if (len == 0) { 315 cmd_free_argv(argc, *argv); 316 return (-1); 317 } 318 319 arglen = strlen(buf) + 1; 320 (*argv)[i] = xstrdup(buf); 321 322 buf += arglen; 323 len -= arglen; 324 } 325 cmd_log_argv(argc, *argv, "%s", __func__); 326 327 return (0); 328 } 329 330 /* Copy an argument vector, ensuring it is terminated by NULL. */ 331 char ** 332 cmd_copy_argv(int argc, char **argv) 333 { 334 char **new_argv; 335 int i; 336 337 if (argc == 0) 338 return (NULL); 339 new_argv = xcalloc(argc + 1, sizeof *new_argv); 340 for (i = 0; i < argc; i++) { 341 if (argv[i] != NULL) 342 new_argv[i] = xstrdup(argv[i]); 343 } 344 return (new_argv); 345 } 346 347 /* Free an argument vector. */ 348 void 349 cmd_free_argv(int argc, char **argv) 350 { 351 int i; 352 353 if (argc == 0) 354 return; 355 for (i = 0; i < argc; i++) 356 free(argv[i]); 357 free(argv); 358 } 359 360 /* Convert argument vector to a string. */ 361 char * 362 cmd_stringify_argv(int argc, char **argv) 363 { 364 char *buf = NULL, *s; 365 size_t len = 0; 366 int i; 367 368 if (argc == 0) 369 return (xstrdup("")); 370 371 for (i = 0; i < argc; i++) { 372 s = args_escape(argv[i]); 373 log_debug("%s: %u %s = %s", __func__, i, argv[i], s); 374 375 len += strlen(s) + 1; 376 buf = xrealloc(buf, len); 377 378 if (i == 0) 379 *buf = '\0'; 380 else 381 strlcat(buf, " ", len); 382 strlcat(buf, s, len); 383 384 free(s); 385 } 386 return (buf); 387 } 388 389 /* Get entry for command. */ 390 const struct cmd_entry * 391 cmd_get_entry(struct cmd *cmd) 392 { 393 return (cmd->entry); 394 } 395 396 /* Get arguments for command. */ 397 struct args * 398 cmd_get_args(struct cmd *cmd) 399 { 400 return (cmd->args); 401 } 402 403 /* Get group for command. */ 404 u_int 405 cmd_get_group(struct cmd *cmd) 406 { 407 return (cmd->group); 408 } 409 410 /* Get file and line for command. */ 411 void 412 cmd_get_source(struct cmd *cmd, const char **file, u_int *line) 413 { 414 if (file != NULL) 415 *file = cmd->file; 416 if (line != NULL) 417 *line = cmd->line; 418 } 419 420 /* Look for an alias for a command. */ 421 char * 422 cmd_get_alias(const char *name) 423 { 424 struct options_entry *o; 425 struct options_array_item *a; 426 union options_value *ov; 427 size_t wanted, n; 428 const char *equals; 429 430 o = options_get_only(global_options, "command-alias"); 431 if (o == NULL) 432 return (NULL); 433 wanted = strlen(name); 434 435 a = options_array_first(o); 436 while (a != NULL) { 437 ov = options_array_item_value(a); 438 439 equals = strchr(ov->string, '='); 440 if (equals != NULL) { 441 n = equals - ov->string; 442 if (n == wanted && strncmp(name, ov->string, n) == 0) 443 return (xstrdup(equals + 1)); 444 } 445 446 a = options_array_next(a); 447 } 448 return (NULL); 449 } 450 451 /* Look up a command entry by name. */ 452 static const struct cmd_entry * 453 cmd_find(const char *name, char **cause) 454 { 455 const struct cmd_entry **loop, *entry, *found = NULL; 456 int ambiguous; 457 char s[8192]; 458 459 ambiguous = 0; 460 for (loop = cmd_table; *loop != NULL; loop++) { 461 entry = *loop; 462 if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { 463 ambiguous = 0; 464 found = entry; 465 break; 466 } 467 468 if (strncmp(entry->name, name, strlen(name)) != 0) 469 continue; 470 if (found != NULL) 471 ambiguous = 1; 472 found = entry; 473 474 if (strcmp(entry->name, name) == 0) 475 break; 476 } 477 if (ambiguous) 478 goto ambiguous; 479 if (found == NULL) { 480 xasprintf(cause, "unknown command: %s", name); 481 return (NULL); 482 } 483 return (found); 484 485 ambiguous: 486 *s = '\0'; 487 for (loop = cmd_table; *loop != NULL; loop++) { 488 entry = *loop; 489 if (strncmp(entry->name, name, strlen(name)) != 0) 490 continue; 491 if (strlcat(s, entry->name, sizeof s) >= sizeof s) 492 break; 493 if (strlcat(s, ", ", sizeof s) >= sizeof s) 494 break; 495 } 496 s[strlen(s) - 2] = '\0'; 497 xasprintf(cause, "ambiguous command: %s, could be: %s", name, s); 498 return (NULL); 499 } 500 501 /* Parse a single command from an argument vector. */ 502 struct cmd * 503 cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) 504 { 505 const struct cmd_entry *entry; 506 const char *name; 507 struct cmd *cmd; 508 struct args *args; 509 510 if (argc == 0) { 511 xasprintf(cause, "no command"); 512 return (NULL); 513 } 514 name = argv[0]; 515 516 entry = cmd_find(name, cause); 517 if (entry == NULL) 518 return (NULL); 519 cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); 520 521 args = args_parse(entry->args.template, argc, argv); 522 if (args == NULL) 523 goto usage; 524 if (entry->args.lower != -1 && args->argc < entry->args.lower) 525 goto usage; 526 if (entry->args.upper != -1 && args->argc > entry->args.upper) 527 goto usage; 528 529 cmd = xcalloc(1, sizeof *cmd); 530 cmd->entry = entry; 531 cmd->args = args; 532 533 if (file != NULL) 534 cmd->file = xstrdup(file); 535 cmd->line = line; 536 537 cmd->alias = NULL; 538 cmd->argc = argc; 539 cmd->argv = cmd_copy_argv(argc, argv); 540 541 return (cmd); 542 543 usage: 544 if (args != NULL) 545 args_free(args); 546 xasprintf(cause, "usage: %s %s", entry->name, entry->usage); 547 return (NULL); 548 } 549 550 /* Free a command. */ 551 void 552 cmd_free(struct cmd *cmd) 553 { 554 free(cmd->alias); 555 cmd_free_argv(cmd->argc, cmd->argv); 556 557 free(cmd->file); 558 559 args_free(cmd->args); 560 free(cmd); 561 } 562 563 /* Get a command as a string. */ 564 char * 565 cmd_print(struct cmd *cmd) 566 { 567 char *out, *s; 568 569 s = args_print(cmd->args); 570 if (*s != '\0') 571 xasprintf(&out, "%s %s", cmd->entry->name, s); 572 else 573 out = xstrdup(cmd->entry->name); 574 free(s); 575 576 return (out); 577 } 578 579 /* Create a new command list. */ 580 struct cmd_list * 581 cmd_list_new(void) 582 { 583 struct cmd_list *cmdlist; 584 585 cmdlist = xcalloc(1, sizeof *cmdlist); 586 cmdlist->references = 1; 587 cmdlist->group = cmd_list_next_group++; 588 cmdlist->list = xcalloc(1, sizeof *cmdlist->list); 589 TAILQ_INIT(cmdlist->list); 590 return (cmdlist); 591 } 592 593 /* Append a command to a command list. */ 594 void 595 cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) 596 { 597 cmd->group = cmdlist->group; 598 TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); 599 } 600 601 /* Move all commands from one command list to another */ 602 void 603 cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) 604 { 605 TAILQ_CONCAT(cmdlist->list, from->list, qentry); 606 cmdlist->group = cmd_list_next_group++; 607 } 608 609 /* Free a command list. */ 610 void 611 cmd_list_free(struct cmd_list *cmdlist) 612 { 613 struct cmd *cmd, *cmd1; 614 615 if (--cmdlist->references != 0) 616 return; 617 618 TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) { 619 TAILQ_REMOVE(cmdlist->list, cmd, qentry); 620 cmd_free(cmd); 621 } 622 free(cmdlist->list); 623 free(cmdlist); 624 } 625 626 /* Get a command list as a string. */ 627 char * 628 cmd_list_print(struct cmd_list *cmdlist, int escaped) 629 { 630 struct cmd *cmd, *next; 631 char *buf, *this; 632 size_t len; 633 634 len = 1; 635 buf = xcalloc(1, len); 636 637 TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 638 this = cmd_print(cmd); 639 640 len += strlen(this) + 6; 641 buf = xrealloc(buf, len); 642 643 strlcat(buf, this, len); 644 645 next = TAILQ_NEXT(cmd, qentry); 646 if (next != NULL) { 647 if (cmd->group != next->group) { 648 if (escaped) 649 strlcat(buf, " \\;\\; ", len); 650 else 651 strlcat(buf, " ;; ", len); 652 } else { 653 if (escaped) 654 strlcat(buf, " \\; ", len); 655 else 656 strlcat(buf, " ; ", len); 657 } 658 } 659 660 free(this); 661 } 662 663 return (buf); 664 } 665 666 /* Get first command in list. */ 667 struct cmd * 668 cmd_list_first(struct cmd_list *cmdlist) 669 { 670 return (TAILQ_FIRST(cmdlist->list)); 671 } 672 673 /* Get next command in list. */ 674 struct cmd * 675 cmd_list_next(struct cmd *cmd) 676 { 677 return (TAILQ_NEXT(cmd, qentry)); 678 } 679 680 /* Do all of the commands in this command list have this flag? */ 681 int 682 cmd_list_all_have(struct cmd_list *cmdlist, int flag) 683 { 684 struct cmd *cmd; 685 686 TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 687 if (~cmd->entry->flags & flag) 688 return (0); 689 } 690 return (1); 691 } 692 693 /* Do any of the commands in this command list have this flag? */ 694 int 695 cmd_list_any_have(struct cmd_list *cmdlist, int flag) 696 { 697 struct cmd *cmd; 698 699 TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 700 if (cmd->entry->flags & flag) 701 return (1); 702 } 703 return (0); 704 } 705 706 /* Adjust current mouse position for a pane. */ 707 int 708 cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, 709 u_int *yp, int last) 710 { 711 u_int x, y; 712 713 if (last) { 714 x = m->lx + m->ox; 715 y = m->ly + m->oy; 716 } else { 717 x = m->x + m->ox; 718 y = m->y + m->oy; 719 } 720 log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); 721 722 if (m->statusat == 0 && y >= m->statuslines) 723 y -= m->statuslines; 724 725 if (x < wp->xoff || x >= wp->xoff + wp->sx) 726 return (-1); 727 if (y < wp->yoff || y >= wp->yoff + wp->sy) 728 return (-1); 729 730 if (xp != NULL) 731 *xp = x - wp->xoff; 732 if (yp != NULL) 733 *yp = y - wp->yoff; 734 return (0); 735 } 736 737 /* Get current mouse window if any. */ 738 struct winlink * 739 cmd_mouse_window(struct mouse_event *m, struct session **sp) 740 { 741 struct session *s; 742 struct window *w; 743 struct winlink *wl; 744 745 if (!m->valid) 746 return (NULL); 747 if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) 748 return (NULL); 749 if (m->w == -1) 750 wl = s->curw; 751 else { 752 if ((w = window_find_by_id(m->w)) == NULL) 753 return (NULL); 754 wl = winlink_find_by_window(&s->windows, w); 755 } 756 if (sp != NULL) 757 *sp = s; 758 return (wl); 759 } 760 761 /* Get current mouse pane if any. */ 762 struct window_pane * 763 cmd_mouse_pane(struct mouse_event *m, struct session **sp, 764 struct winlink **wlp) 765 { 766 struct winlink *wl; 767 struct window_pane *wp; 768 769 if ((wl = cmd_mouse_window(m, sp)) == NULL) 770 return (NULL); 771 if ((wp = window_pane_find_by_id(m->wp)) == NULL) 772 return (NULL); 773 if (!window_has_pane(wl->window, wp)) 774 return (NULL); 775 776 if (wlp != NULL) 777 *wlp = wl; 778 return (wp); 779 } 780 781 /* Replace the first %% or %idx in template by s. */ 782 char * 783 cmd_template_replace(const char *template, const char *s, int idx) 784 { 785 char ch, *buf; 786 const char *ptr, *cp, quote[] = "\"\\$;~"; 787 int replaced, quoted; 788 size_t len; 789 790 if (strchr(template, '%') == NULL) 791 return (xstrdup(template)); 792 793 buf = xmalloc(1); 794 *buf = '\0'; 795 len = 0; 796 replaced = 0; 797 798 ptr = template; 799 while (*ptr != '\0') { 800 switch (ch = *ptr++) { 801 case '%': 802 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { 803 if (*ptr != '%' || replaced) 804 break; 805 replaced = 1; 806 } 807 ptr++; 808 809 quoted = (*ptr == '%'); 810 if (quoted) 811 ptr++; 812 813 buf = xrealloc(buf, len + (strlen(s) * 3) + 1); 814 for (cp = s; *cp != '\0'; cp++) { 815 if (quoted && strchr(quote, *cp) != NULL) 816 buf[len++] = '\\'; 817 buf[len++] = *cp; 818 } 819 buf[len] = '\0'; 820 continue; 821 } 822 buf = xrealloc(buf, len + 2); 823 buf[len++] = ch; 824 buf[len] = '\0'; 825 } 826 827 return (buf); 828 } 829