4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
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_down_pane_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_up_pane_entry
;
121 extern const struct cmd_entry cmd_wait_for_entry
;
123 const struct cmd_entry
*cmd_table
[] = {
124 &cmd_attach_session_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
,
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_server_access_entry
,
192 &cmd_set_buffer_entry
,
193 &cmd_set_environment_entry
,
195 &cmd_set_option_entry
,
196 &cmd_set_window_option_entry
,
197 &cmd_show_buffer_entry
,
198 &cmd_show_environment_entry
,
199 &cmd_show_hooks_entry
,
200 &cmd_show_messages_entry
,
201 &cmd_show_options_entry
,
202 &cmd_show_prompt_history_entry
,
203 &cmd_show_window_options_entry
,
204 &cmd_source_file_entry
,
205 &cmd_split_window_entry
,
206 &cmd_start_server_entry
,
207 &cmd_suspend_client_entry
,
208 &cmd_swap_pane_entry
,
209 &cmd_swap_window_entry
,
210 &cmd_switch_client_entry
,
211 &cmd_unbind_key_entry
,
212 &cmd_unlink_window_entry
,
217 /* Instance of a command. */
219 const struct cmd_entry
*entry
;
226 TAILQ_ENTRY(cmd
) qentry
;
228 TAILQ_HEAD(cmds
, cmd
);
230 /* Next group number for new command list. */
231 static u_int cmd_list_next_group
= 1;
233 /* Log an argument vector. */
234 void printflike(3, 4)
235 cmd_log_argv(int argc
, char **argv
, const char *fmt
, ...)
242 xvasprintf(&prefix
, fmt
, ap
);
245 for (i
= 0; i
< argc
; i
++)
246 log_debug("%s: argv[%d]=%s", prefix
, i
, argv
[i
]);
250 /* Prepend to an argument vector. */
252 cmd_prepend_argv(int *argc
, char ***argv
, const char *arg
)
257 new_argv
= xreallocarray(NULL
, (*argc
) + 1, sizeof *new_argv
);
258 new_argv
[0] = xstrdup(arg
);
259 for (i
= 0; i
< *argc
; i
++)
260 new_argv
[1 + i
] = (*argv
)[i
];
267 /* Append to an argument vector. */
269 cmd_append_argv(int *argc
, char ***argv
, const char *arg
)
271 *argv
= xreallocarray(*argv
, (*argc
) + 1, sizeof **argv
);
272 (*argv
)[(*argc
)++] = xstrdup(arg
);
275 /* Pack an argument vector up into a buffer. */
277 cmd_pack_argv(int argc
, char **argv
, char *buf
, size_t len
)
284 cmd_log_argv(argc
, argv
, "%s", __func__
);
287 for (i
= 0; i
< argc
; i
++) {
288 if (strlcpy(buf
, argv
[i
], len
) >= len
)
290 arglen
= strlen(argv
[i
]) + 1;
298 /* Unpack an argument vector from a packed buffer. */
300 cmd_unpack_argv(char *buf
, size_t len
, int argc
, char ***argv
)
307 *argv
= xcalloc(argc
, sizeof **argv
);
310 for (i
= 0; i
< argc
; i
++) {
312 cmd_free_argv(argc
, *argv
);
316 arglen
= strlen(buf
) + 1;
317 (*argv
)[i
] = xstrdup(buf
);
322 cmd_log_argv(argc
, *argv
, "%s", __func__
);
327 /* Copy an argument vector, ensuring it is terminated by NULL. */
329 cmd_copy_argv(int argc
, char **argv
)
336 new_argv
= xcalloc(argc
+ 1, sizeof *new_argv
);
337 for (i
= 0; i
< argc
; i
++) {
339 new_argv
[i
] = xstrdup(argv
[i
]);
344 /* Free an argument vector. */
346 cmd_free_argv(int argc
, char **argv
)
352 for (i
= 0; i
< argc
; i
++)
357 /* Convert argument vector to a string. */
359 cmd_stringify_argv(int argc
, char **argv
)
361 char *buf
= NULL
, *s
;
366 return (xstrdup(""));
368 for (i
= 0; i
< argc
; i
++) {
369 s
= args_escape(argv
[i
]);
370 log_debug("%s: %u %s = %s", __func__
, i
, argv
[i
], s
);
372 len
+= strlen(s
) + 1;
373 buf
= xrealloc(buf
, len
);
378 strlcat(buf
, " ", len
);
379 strlcat(buf
, s
, len
);
386 /* Get entry for command. */
387 const struct cmd_entry
*
388 cmd_get_entry(struct cmd
*cmd
)
393 /* Get arguments for command. */
395 cmd_get_args(struct cmd
*cmd
)
400 /* Get group for command. */
402 cmd_get_group(struct cmd
*cmd
)
407 /* Get file and line for command. */
409 cmd_get_source(struct cmd
*cmd
, const char **file
, u_int
*line
)
417 /* Look for an alias for a command. */
419 cmd_get_alias(const char *name
)
421 struct options_entry
*o
;
422 struct options_array_item
*a
;
423 union options_value
*ov
;
427 o
= options_get_only(global_options
, "command-alias");
430 wanted
= strlen(name
);
432 a
= options_array_first(o
);
434 ov
= options_array_item_value(a
);
436 equals
= strchr(ov
->string
, '=');
437 if (equals
!= NULL
) {
438 n
= equals
- ov
->string
;
439 if (n
== wanted
&& strncmp(name
, ov
->string
, n
) == 0)
440 return (xstrdup(equals
+ 1));
443 a
= options_array_next(a
);
448 /* Look up a command entry by name. */
449 static const struct cmd_entry
*
450 cmd_find(const char *name
, char **cause
)
452 const struct cmd_entry
**loop
, *entry
, *found
= NULL
;
457 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
459 if (entry
->alias
!= NULL
&& strcmp(entry
->alias
, name
) == 0) {
465 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
471 if (strcmp(entry
->name
, name
) == 0)
477 xasprintf(cause
, "unknown command: %s", name
);
484 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
486 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
488 if (strlcat(s
, entry
->name
, sizeof s
) >= sizeof s
)
490 if (strlcat(s
, ", ", sizeof s
) >= sizeof s
)
493 s
[strlen(s
) - 2] = '\0';
494 xasprintf(cause
, "ambiguous command: %s, could be: %s", name
, s
);
498 /* Parse a single command from an argument vector. */
500 cmd_parse(struct args_value
*values
, u_int count
, const char *file
, u_int line
,
503 const struct cmd_entry
*entry
;
508 if (count
== 0 || values
[0].type
!= ARGS_STRING
) {
509 xasprintf(cause
, "no command");
512 entry
= cmd_find(values
[0].string
, cause
);
516 args
= args_parse(&entry
->args
, values
, count
, &error
);
517 if (args
== NULL
&& error
== NULL
) {
518 xasprintf(cause
, "usage: %s %s", entry
->name
, entry
->usage
);
522 xasprintf(cause
, "command %s: %s", entry
->name
, error
);
527 cmd
= xcalloc(1, sizeof *cmd
);
532 cmd
->file
= xstrdup(file
);
538 /* Free a command. */
540 cmd_free(struct cmd
*cmd
)
544 args_free(cmd
->args
);
548 /* Copy a command. */
550 cmd_copy(struct cmd
*cmd
, int argc
, char **argv
)
554 new_cmd
= xcalloc(1, sizeof *new_cmd
);
555 new_cmd
->entry
= cmd
->entry
;
556 new_cmd
->args
= args_copy(cmd
->args
, argc
, argv
);
558 if (cmd
->file
!= NULL
)
559 new_cmd
->file
= xstrdup(cmd
->file
);
560 new_cmd
->line
= cmd
->line
;
565 /* Get a command as a string. */
567 cmd_print(struct cmd
*cmd
)
571 s
= args_print(cmd
->args
);
573 xasprintf(&out
, "%s %s", cmd
->entry
->name
, s
);
575 out
= xstrdup(cmd
->entry
->name
);
581 /* Create a new command list. */
585 struct cmd_list
*cmdlist
;
587 cmdlist
= xcalloc(1, sizeof *cmdlist
);
588 cmdlist
->references
= 1;
589 cmdlist
->group
= cmd_list_next_group
++;
590 cmdlist
->list
= xcalloc(1, sizeof *cmdlist
->list
);
591 TAILQ_INIT(cmdlist
->list
);
595 /* Append a command to a command list. */
597 cmd_list_append(struct cmd_list
*cmdlist
, struct cmd
*cmd
)
599 cmd
->group
= cmdlist
->group
;
600 TAILQ_INSERT_TAIL(cmdlist
->list
, cmd
, qentry
);
603 /* Append all commands from one list to another. */
605 cmd_list_append_all(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
609 TAILQ_FOREACH(cmd
, from
->list
, qentry
)
610 cmd
->group
= cmdlist
->group
;
611 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
614 /* Move all commands from one command list to another. */
616 cmd_list_move(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
618 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
619 cmdlist
->group
= cmd_list_next_group
++;
622 /* Free a command list. */
624 cmd_list_free(struct cmd_list
*cmdlist
)
626 struct cmd
*cmd
, *cmd1
;
628 if (--cmdlist
->references
!= 0)
631 TAILQ_FOREACH_SAFE(cmd
, cmdlist
->list
, qentry
, cmd1
) {
632 TAILQ_REMOVE(cmdlist
->list
, cmd
, qentry
);
639 /* Copy a command list, expanding %s in arguments. */
641 cmd_list_copy(struct cmd_list
*cmdlist
, int argc
, char **argv
)
644 struct cmd_list
*new_cmdlist
;
646 u_int group
= cmdlist
->group
;
649 s
= cmd_list_print(cmdlist
, 0);
650 log_debug("%s: %s", __func__
, s
);
653 new_cmdlist
= cmd_list_new();
654 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
655 if (cmd
->group
!= group
) {
656 new_cmdlist
->group
= cmd_list_next_group
++;
659 new_cmd
= cmd_copy(cmd
, argc
, argv
);
660 cmd_list_append(new_cmdlist
, new_cmd
);
663 s
= cmd_list_print(new_cmdlist
, 0);
664 log_debug("%s: %s", __func__
, s
);
667 return (new_cmdlist
);
670 /* Get a command list as a string. */
672 cmd_list_print(struct cmd_list
*cmdlist
, int escaped
)
674 struct cmd
*cmd
, *next
;
679 buf
= xcalloc(1, len
);
681 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
682 this = cmd_print(cmd
);
684 len
+= strlen(this) + 6;
685 buf
= xrealloc(buf
, len
);
687 strlcat(buf
, this, len
);
689 next
= TAILQ_NEXT(cmd
, qentry
);
691 if (cmd
->group
!= next
->group
) {
693 strlcat(buf
, " \\;\\; ", len
);
695 strlcat(buf
, " ;; ", len
);
698 strlcat(buf
, " \\; ", len
);
700 strlcat(buf
, " ; ", len
);
710 /* Get first command in list. */
712 cmd_list_first(struct cmd_list
*cmdlist
)
714 return (TAILQ_FIRST(cmdlist
->list
));
717 /* Get next command in list. */
719 cmd_list_next(struct cmd
*cmd
)
721 return (TAILQ_NEXT(cmd
, qentry
));
724 /* Do all of the commands in this command list have this flag? */
726 cmd_list_all_have(struct cmd_list
*cmdlist
, int flag
)
730 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
731 if (~cmd
->entry
->flags
& flag
)
737 /* Do any of the commands in this command list have this flag? */
739 cmd_list_any_have(struct cmd_list
*cmdlist
, int flag
)
743 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
744 if (cmd
->entry
->flags
& flag
)
750 /* Adjust current mouse position for a pane. */
752 cmd_mouse_at(struct window_pane
*wp
, struct mouse_event
*m
, u_int
*xp
,
764 log_debug("%s: x=%u, y=%u%s", __func__
, x
, y
, last
? " (last)" : "");
766 if (m
->statusat
== 0 && y
>= m
->statuslines
)
769 if (x
< wp
->xoff
|| x
>= wp
->xoff
+ wp
->sx
)
771 if (y
< wp
->yoff
|| y
>= wp
->yoff
+ wp
->sy
)
781 /* Get current mouse window if any. */
783 cmd_mouse_window(struct mouse_event
*m
, struct session
**sp
)
791 if (m
->s
== -1 || (s
= session_find_by_id(m
->s
)) == NULL
)
796 if ((w
= window_find_by_id(m
->w
)) == NULL
)
798 wl
= winlink_find_by_window(&s
->windows
, w
);
805 /* Get current mouse pane if any. */
807 cmd_mouse_pane(struct mouse_event
*m
, struct session
**sp
,
808 struct winlink
**wlp
)
811 struct window_pane
*wp
;
813 if ((wl
= cmd_mouse_window(m
, sp
)) == NULL
)
815 if ((wp
= window_pane_find_by_id(m
->wp
)) == NULL
)
817 if (!window_has_pane(wl
->window
, wp
))
825 /* Replace the first %% or %idx in template by s. */
827 cmd_template_replace(const char *template, const char *s
, int idx
)
830 const char *ptr
, *cp
, quote
[] = "\"\\$;~";
831 int replaced
, quoted
;
834 if (strchr(template, '%') == NULL
)
835 return (xstrdup(template));
843 while (*ptr
!= '\0') {
844 switch (ch
= *ptr
++) {
846 if (*ptr
< '1' || *ptr
> '9' || *ptr
- '0' != idx
) {
847 if (*ptr
!= '%' || replaced
)
853 quoted
= (*ptr
== '%');
857 buf
= xrealloc(buf
, len
+ (strlen(s
) * 3) + 1);
858 for (cp
= s
; *cp
!= '\0'; cp
++) {
859 if (quoted
&& strchr(quote
, *cp
) != NULL
)
866 buf
= xrealloc(buf
, len
+ 2);