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_clear_prompt_history_entry;
39 extern const struct cmd_entry cmd_clock_mode_entry;
40 extern const struct cmd_entry cmd_command_prompt_entry;
41 extern const struct cmd_entry cmd_confirm_before_entry;
42 extern const struct cmd_entry cmd_copy_mode_entry;
43 extern const struct cmd_entry cmd_customize_mode_entry;
44 extern const struct cmd_entry cmd_delete_buffer_entry;
45 extern const struct cmd_entry cmd_detach_client_entry;
46 extern const struct cmd_entry cmd_display_menu_entry;
47 extern const struct cmd_entry cmd_display_message_entry;
48 extern const struct cmd_entry cmd_display_popup_entry;
49 extern const struct cmd_entry cmd_display_panes_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_server_access_entry;
98 extern const struct cmd_entry cmd_set_buffer_entry;
99 extern const struct cmd_entry cmd_set_environment_entry;
100 extern const struct cmd_entry cmd_set_hook_entry;
101 extern const struct cmd_entry cmd_set_option_entry;
102 extern const struct cmd_entry cmd_set_window_option_entry;
103 extern const struct cmd_entry cmd_show_buffer_entry;
104 extern const struct cmd_entry cmd_show_environment_entry;
105 extern const struct cmd_entry cmd_show_hooks_entry;
106 extern const struct cmd_entry cmd_show_messages_entry;
107 extern const struct cmd_entry cmd_show_options_entry;
108 extern const struct cmd_entry cmd_show_prompt_history_entry;
109 extern const struct cmd_entry cmd_show_window_options_entry;
110 extern const struct cmd_entry cmd_source_file_entry;
111 extern const struct cmd_entry cmd_split_window_entry;
112 extern const struct cmd_entry cmd_start_server_entry;
113 extern const struct cmd_entry cmd_suspend_client_entry;
114 extern const struct cmd_entry cmd_swap_pane_entry;
115 extern const struct cmd_entry cmd_swap_window_entry;
116 extern const struct cmd_entry cmd_switch_client_entry;
117 extern const struct cmd_entry cmd_unbind_key_entry;
118 extern const struct cmd_entry cmd_unlink_window_entry;
119 extern const struct cmd_entry cmd_wait_for_entry;
120 
121 const struct cmd_entry *cmd_table[] = {
122           &cmd_attach_session_entry,
123           &cmd_bind_key_entry,
124           &cmd_break_pane_entry,
125           &cmd_capture_pane_entry,
126           &cmd_choose_buffer_entry,
127           &cmd_choose_client_entry,
128           &cmd_choose_tree_entry,
129           &cmd_clear_history_entry,
130           &cmd_clear_prompt_history_entry,
131           &cmd_clock_mode_entry,
132           &cmd_command_prompt_entry,
133           &cmd_confirm_before_entry,
134           &cmd_copy_mode_entry,
135           &cmd_customize_mode_entry,
136           &cmd_delete_buffer_entry,
137           &cmd_detach_client_entry,
138           &cmd_display_menu_entry,
139           &cmd_display_message_entry,
140           &cmd_display_popup_entry,
141           &cmd_display_panes_entry,
142           &cmd_find_window_entry,
143           &cmd_has_session_entry,
144           &cmd_if_shell_entry,
145           &cmd_join_pane_entry,
146           &cmd_kill_pane_entry,
147           &cmd_kill_server_entry,
148           &cmd_kill_session_entry,
149           &cmd_kill_window_entry,
150           &cmd_last_pane_entry,
151           &cmd_last_window_entry,
152           &cmd_link_window_entry,
153           &cmd_list_buffers_entry,
154           &cmd_list_clients_entry,
155           &cmd_list_commands_entry,
156           &cmd_list_keys_entry,
157           &cmd_list_panes_entry,
158           &cmd_list_sessions_entry,
159           &cmd_list_windows_entry,
160           &cmd_load_buffer_entry,
161           &cmd_lock_client_entry,
162           &cmd_lock_server_entry,
163           &cmd_lock_session_entry,
164           &cmd_move_pane_entry,
165           &cmd_move_window_entry,
166           &cmd_new_session_entry,
167           &cmd_new_window_entry,
168           &cmd_next_layout_entry,
169           &cmd_next_window_entry,
170           &cmd_paste_buffer_entry,
171           &cmd_pipe_pane_entry,
172           &cmd_previous_layout_entry,
173           &cmd_previous_window_entry,
174           &cmd_refresh_client_entry,
175           &cmd_rename_session_entry,
176           &cmd_rename_window_entry,
177           &cmd_resize_pane_entry,
178           &cmd_resize_window_entry,
179           &cmd_respawn_pane_entry,
180           &cmd_respawn_window_entry,
181           &cmd_rotate_window_entry,
182           &cmd_run_shell_entry,
183           &cmd_save_buffer_entry,
184           &cmd_select_layout_entry,
185           &cmd_select_pane_entry,
186           &cmd_select_window_entry,
187           &cmd_send_keys_entry,
188           &cmd_send_prefix_entry,
189           &cmd_server_access_entry,
190           &cmd_set_buffer_entry,
191           &cmd_set_environment_entry,
192           &cmd_set_hook_entry,
193           &cmd_set_option_entry,
194           &cmd_set_window_option_entry,
195           &cmd_show_buffer_entry,
196           &cmd_show_environment_entry,
197           &cmd_show_hooks_entry,
198           &cmd_show_messages_entry,
199           &cmd_show_options_entry,
200           &cmd_show_prompt_history_entry,
201           &cmd_show_window_options_entry,
202           &cmd_source_file_entry,
203           &cmd_split_window_entry,
204           &cmd_start_server_entry,
205           &cmd_suspend_client_entry,
206           &cmd_swap_pane_entry,
207           &cmd_swap_window_entry,
208           &cmd_switch_client_entry,
209           &cmd_unbind_key_entry,
210           &cmd_unlink_window_entry,
211           &cmd_wait_for_entry,
212           NULL
213 };
214 
215 /* Instance of a command. */
216 struct cmd {
217           const struct cmd_entry         *entry;
218           struct args                    *args;
219           u_int                           group;
220 
221           char                           *file;
222           u_int                           line;
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,const char * arg)250 cmd_prepend_argv(int *argc, char ***argv, const 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,const char * arg)267 cmd_append_argv(int *argc, char ***argv, const 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(struct args_value * values,u_int count,const char * file,u_int line,char ** cause)498 cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
499     char **cause)
500 {
501           const struct cmd_entry        *entry;
502           struct cmd                    *cmd;
503           struct args                   *args;
504           char                          *error = NULL;
505 
506           if (count == 0 || values[0].type != ARGS_STRING) {
507                     xasprintf(cause, "no command");
508                     return (NULL);
509           }
510           entry = cmd_find(values[0].string, cause);
511           if (entry == NULL)
512                     return (NULL);
513 
514           args = args_parse(&entry->args, values, count, &error);
515           if (args == NULL && error == NULL) {
516                     xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
517                     return (NULL);
518           }
519           if (args == NULL) {
520                     xasprintf(cause, "command %s: %s", entry->name, error);
521                     free(error);
522                     return (NULL);
523           }
524 
525           cmd = xcalloc(1, sizeof *cmd);
526           cmd->entry = entry;
527           cmd->args = args;
528 
529           if (file != NULL)
530                     cmd->file = xstrdup(file);
531           cmd->line = line;
532 
533           return (cmd);
534 }
535 
536 /* Free a command. */
537 void
cmd_free(struct cmd * cmd)538 cmd_free(struct cmd *cmd)
539 {
540           free(cmd->file);
541 
542           args_free(cmd->args);
543           free(cmd);
544 }
545 
546 /* Copy a command. */
547 struct cmd *
cmd_copy(struct cmd * cmd,int argc,char ** argv)548 cmd_copy(struct cmd *cmd, int argc, char **argv)
549 {
550           struct cmd          *new_cmd;
551 
552           new_cmd = xcalloc(1, sizeof *new_cmd);
553           new_cmd->entry = cmd->entry;
554           new_cmd->args = args_copy(cmd->args, argc, argv);
555 
556           if (cmd->file != NULL)
557                     new_cmd->file = xstrdup(cmd->file);
558           new_cmd->line = cmd->line;
559 
560           return (new_cmd);
561 }
562 
563 /* Get a command as a string. */
564 char *
cmd_print(struct cmd * cmd)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 *
cmd_list_new(void)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
cmd_list_append(struct cmd_list * cmdlist,struct cmd * cmd)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 /* Append all commands from one list to another.  */
602 void
cmd_list_append_all(struct cmd_list * cmdlist,struct cmd_list * from)603 cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from)
604 {
605           struct cmd          *cmd;
606 
607           TAILQ_FOREACH(cmd, from->list, qentry)
608                     cmd->group = cmdlist->group;
609           TAILQ_CONCAT(cmdlist->list, from->list, qentry);
610 }
611 
612 /* Move all commands from one command list to another. */
613 void
cmd_list_move(struct cmd_list * cmdlist,struct cmd_list * from)614 cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
615 {
616           TAILQ_CONCAT(cmdlist->list, from->list, qentry);
617           cmdlist->group = cmd_list_next_group++;
618 }
619 
620 /* Free a command list. */
621 void
cmd_list_free(struct cmd_list * cmdlist)622 cmd_list_free(struct cmd_list *cmdlist)
623 {
624           struct cmd          *cmd, *cmd1;
625 
626           if (--cmdlist->references != 0)
627                     return;
628 
629           TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
630                     TAILQ_REMOVE(cmdlist->list, cmd, qentry);
631                     cmd_free(cmd);
632           }
633           free(cmdlist->list);
634           free(cmdlist);
635 }
636 
637 /* Copy a command list, expanding %s in arguments. */
638 struct cmd_list *
cmd_list_copy(struct cmd_list * cmdlist,int argc,char ** argv)639 cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
640 {
641           struct cmd          *cmd;
642           struct cmd_list     *new_cmdlist;
643           struct cmd          *new_cmd;
644           u_int                group = cmdlist->group;
645           char                *s;
646 
647           s = cmd_list_print(cmdlist, 0);
648           log_debug("%s: %s", __func__, s);
649           free(s);
650 
651           new_cmdlist = cmd_list_new();
652           TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
653                     if (cmd->group != group) {
654                               new_cmdlist->group = cmd_list_next_group++;
655                               group = cmd->group;
656                     }
657                     new_cmd = cmd_copy(cmd, argc, argv);
658                     cmd_list_append(new_cmdlist, new_cmd);
659           }
660 
661           s = cmd_list_print(new_cmdlist, 0);
662           log_debug("%s: %s", __func__, s);
663           free(s);
664 
665           return (new_cmdlist);
666 }
667 
668 /* Get a command list as a string. */
669 char *
cmd_list_print(struct cmd_list * cmdlist,int escaped)670 cmd_list_print(struct cmd_list *cmdlist, int escaped)
671 {
672           struct cmd          *cmd, *next;
673           char                *buf, *this;
674           size_t               len;
675 
676           len = 1;
677           buf = xcalloc(1, len);
678 
679           TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
680                     this = cmd_print(cmd);
681 
682                     len += strlen(this) + 6;
683                     buf = xrealloc(buf, len);
684 
685                     strlcat(buf, this, len);
686 
687                     next = TAILQ_NEXT(cmd, qentry);
688                     if (next != NULL) {
689                               if (cmd->group != next->group) {
690                                         if (escaped)
691                                                   strlcat(buf, " \\;\\; ", len);
692                                         else
693                                                   strlcat(buf, " ;; ", len);
694                               } else {
695                                         if (escaped)
696                                                   strlcat(buf, " \\; ", len);
697                                         else
698                                                   strlcat(buf, " ; ", len);
699                               }
700                     }
701 
702                     free(this);
703           }
704 
705           return (buf);
706 }
707 
708 /* Get first command in list. */
709 struct cmd *
cmd_list_first(struct cmd_list * cmdlist)710 cmd_list_first(struct cmd_list *cmdlist)
711 {
712           return (TAILQ_FIRST(cmdlist->list));
713 }
714 
715 /* Get next command in list. */
716 struct cmd *
cmd_list_next(struct cmd * cmd)717 cmd_list_next(struct cmd *cmd)
718 {
719           return (TAILQ_NEXT(cmd, qentry));
720 }
721 
722 /* Do all of the commands in this command list have this flag? */
723 int
cmd_list_all_have(struct cmd_list * cmdlist,int flag)724 cmd_list_all_have(struct cmd_list *cmdlist, int flag)
725 {
726           struct cmd          *cmd;
727 
728           TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
729                     if (~cmd->entry->flags & flag)
730                               return (0);
731           }
732           return (1);
733 }
734 
735 /* Do any of the commands in this command list have this flag? */
736 int
cmd_list_any_have(struct cmd_list * cmdlist,int flag)737 cmd_list_any_have(struct cmd_list *cmdlist, int flag)
738 {
739           struct cmd          *cmd;
740 
741           TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
742                     if (cmd->entry->flags & flag)
743                               return (1);
744           }
745           return (0);
746 }
747 
748 /* Adjust current mouse position for a pane. */
749 int
cmd_mouse_at(struct window_pane * wp,struct mouse_event * m,u_int * xp,u_int * yp,int last)750 cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
751     u_int *yp, int last)
752 {
753           u_int     x, y;
754 
755           if (last) {
756                     x = m->lx + m->ox;
757                     y = m->ly + m->oy;
758           } else {
759                     x = m->x + m->ox;
760                     y = m->y + m->oy;
761           }
762           log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
763 
764           if (m->statusat == 0 && y >= m->statuslines)
765                     y -= m->statuslines;
766 
767           if (x < wp->xoff || x >= wp->xoff + wp->sx)
768                     return (-1);
769           if (y < wp->yoff || y >= wp->yoff + wp->sy)
770                     return (-1);
771 
772           if (xp != NULL)
773                     *xp = x - wp->xoff;
774           if (yp != NULL)
775                     *yp = y - wp->yoff;
776           return (0);
777 }
778 
779 /* Get current mouse window if any. */
780 struct winlink *
cmd_mouse_window(struct mouse_event * m,struct session ** sp)781 cmd_mouse_window(struct mouse_event *m, struct session **sp)
782 {
783           struct session      *s;
784           struct window       *w;
785           struct winlink      *wl;
786 
787           if (!m->valid)
788                     return (NULL);
789           if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL)
790                     return (NULL);
791           if (m->w == -1)
792                     wl = s->curw;
793           else {
794                     if ((w = window_find_by_id(m->w)) == NULL)
795                               return (NULL);
796                     wl = winlink_find_by_window(&s->windows, w);
797           }
798           if (sp != NULL)
799                     *sp = s;
800           return (wl);
801 }
802 
803 /* Get current mouse pane if any. */
804 struct window_pane *
cmd_mouse_pane(struct mouse_event * m,struct session ** sp,struct winlink ** wlp)805 cmd_mouse_pane(struct mouse_event *m, struct session **sp,
806     struct winlink **wlp)
807 {
808           struct winlink                *wl;
809           struct window_pane            *wp;
810 
811           if ((wl = cmd_mouse_window(m, sp)) == NULL)
812                     return (NULL);
813           if (m->wp == -1)
814                     wp = wl->window->active;
815           else {
816                     if ((wp = window_pane_find_by_id(m->wp)) == NULL)
817                               return (NULL);
818                     if (!window_has_pane(wl->window, wp))
819                               return (NULL);
820           }
821 
822           if (wlp != NULL)
823                     *wlp = wl;
824           return (wp);
825 }
826 
827 /* Replace the first %% or %idx in template by s. */
828 char *
cmd_template_replace(const char * template,const char * s,int idx)829 cmd_template_replace(const char *template, const char *s, int idx)
830 {
831           char                 ch, *buf;
832           const char          *ptr, *cp, quote[] = "\"\\$;~";
833           int                  replaced, quoted;
834           size_t               len;
835 
836           if (strchr(template, '%') == NULL)
837                     return (xstrdup(template));
838 
839           buf = xmalloc(1);
840           *buf = '\0';
841           len = 0;
842           replaced = 0;
843 
844           ptr = template;
845           while (*ptr != '\0') {
846                     switch (ch = *ptr++) {
847                     case '%':
848                               if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
849                                         if (*ptr != '%' || replaced)
850                                                   break;
851                                         replaced = 1;
852                               }
853                               ptr++;
854 
855                               quoted = (*ptr == '%');
856                               if (quoted)
857                                         ptr++;
858 
859                               buf = xrealloc(buf, len + (strlen(s) * 3) + 1);
860                               for (cp = s; *cp != '\0'; cp++) {
861                                         if (quoted && strchr(quote, *cp) != NULL)
862                                                   buf[len++] = '\\';
863                                         buf[len++] = *cp;
864                               }
865                               buf[len] = '\0';
866                               continue;
867                     }
868                     buf = xrealloc(buf, len + 2);
869                     buf[len++] = ch;
870                     buf[len] = '\0';
871           }
872 
873           return (buf);
874 }
875