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