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