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>
31 extern const struct cmd_entry cmd_attach_session_entry
;
32 extern const struct cmd_entry cmd_bind_key_entry
;
33 extern const struct cmd_entry cmd_break_pane_entry
;
34 extern const struct cmd_entry cmd_capture_pane_entry
;
35 extern const struct cmd_entry cmd_choose_buffer_entry
;
36 extern const struct cmd_entry cmd_choose_client_entry
;
37 extern const struct cmd_entry cmd_choose_tree_entry
;
38 extern const struct cmd_entry cmd_clear_history_entry
;
39 extern const struct cmd_entry cmd_clear_prompt_history_entry
;
40 extern const struct cmd_entry cmd_clock_mode_entry
;
41 extern const struct cmd_entry cmd_command_prompt_entry
;
42 extern const struct cmd_entry cmd_confirm_before_entry
;
43 extern const struct cmd_entry cmd_copy_mode_entry
;
44 extern const struct cmd_entry cmd_customize_mode_entry
;
45 extern const struct cmd_entry cmd_delete_buffer_entry
;
46 extern const struct cmd_entry cmd_detach_client_entry
;
47 extern const struct cmd_entry cmd_display_menu_entry
;
48 extern const struct cmd_entry cmd_display_message_entry
;
49 extern const struct cmd_entry cmd_display_popup_entry
;
50 extern const struct cmd_entry cmd_display_panes_entry
;
51 extern const struct cmd_entry cmd_down_pane_entry
;
52 extern const struct cmd_entry cmd_find_window_entry
;
53 extern const struct cmd_entry cmd_has_session_entry
;
54 extern const struct cmd_entry cmd_if_shell_entry
;
55 extern const struct cmd_entry cmd_join_pane_entry
;
56 extern const struct cmd_entry cmd_kill_pane_entry
;
57 extern const struct cmd_entry cmd_kill_server_entry
;
58 extern const struct cmd_entry cmd_kill_session_entry
;
59 extern const struct cmd_entry cmd_kill_window_entry
;
60 extern const struct cmd_entry cmd_last_pane_entry
;
61 extern const struct cmd_entry cmd_last_window_entry
;
62 extern const struct cmd_entry cmd_link_window_entry
;
63 extern const struct cmd_entry cmd_list_buffers_entry
;
64 extern const struct cmd_entry cmd_list_clients_entry
;
65 extern const struct cmd_entry cmd_list_commands_entry
;
66 extern const struct cmd_entry cmd_list_keys_entry
;
67 extern const struct cmd_entry cmd_list_panes_entry
;
68 extern const struct cmd_entry cmd_list_sessions_entry
;
69 extern const struct cmd_entry cmd_list_windows_entry
;
70 extern const struct cmd_entry cmd_load_buffer_entry
;
71 extern const struct cmd_entry cmd_lock_client_entry
;
72 extern const struct cmd_entry cmd_lock_server_entry
;
73 extern const struct cmd_entry cmd_lock_session_entry
;
74 extern const struct cmd_entry cmd_move_pane_entry
;
75 extern const struct cmd_entry cmd_move_window_entry
;
76 extern const struct cmd_entry cmd_new_session_entry
;
77 extern const struct cmd_entry cmd_new_window_entry
;
78 extern const struct cmd_entry cmd_next_layout_entry
;
79 extern const struct cmd_entry cmd_next_window_entry
;
80 extern const struct cmd_entry cmd_paste_buffer_entry
;
81 extern const struct cmd_entry cmd_pipe_pane_entry
;
82 extern const struct cmd_entry cmd_previous_layout_entry
;
83 extern const struct cmd_entry cmd_previous_window_entry
;
84 extern const struct cmd_entry cmd_refresh_client_entry
;
85 extern const struct cmd_entry cmd_rename_session_entry
;
86 extern const struct cmd_entry cmd_rename_window_entry
;
87 extern const struct cmd_entry cmd_resize_pane_entry
;
88 extern const struct cmd_entry cmd_resize_window_entry
;
89 extern const struct cmd_entry cmd_respawn_pane_entry
;
90 extern const struct cmd_entry cmd_respawn_window_entry
;
91 extern const struct cmd_entry cmd_rotate_window_entry
;
92 extern const struct cmd_entry cmd_run_shell_entry
;
93 extern const struct cmd_entry cmd_save_buffer_entry
;
94 extern const struct cmd_entry cmd_select_layout_entry
;
95 extern const struct cmd_entry cmd_select_pane_entry
;
96 extern const struct cmd_entry cmd_select_window_entry
;
97 extern const struct cmd_entry cmd_send_keys_entry
;
98 extern const struct cmd_entry cmd_send_prefix_entry
;
99 extern const struct cmd_entry cmd_server_access_entry
;
100 extern const struct cmd_entry cmd_set_buffer_entry
;
101 extern const struct cmd_entry cmd_set_environment_entry
;
102 extern const struct cmd_entry cmd_set_hook_entry
;
103 extern const struct cmd_entry cmd_set_option_entry
;
104 extern const struct cmd_entry cmd_set_window_option_entry
;
105 extern const struct cmd_entry cmd_show_buffer_entry
;
106 extern const struct cmd_entry cmd_show_environment_entry
;
107 extern const struct cmd_entry cmd_show_hooks_entry
;
108 extern const struct cmd_entry cmd_show_messages_entry
;
109 extern const struct cmd_entry cmd_show_options_entry
;
110 extern const struct cmd_entry cmd_show_prompt_history_entry
;
111 extern const struct cmd_entry cmd_show_window_options_entry
;
112 extern const struct cmd_entry cmd_source_file_entry
;
113 extern const struct cmd_entry cmd_split_window_entry
;
114 extern const struct cmd_entry cmd_start_server_entry
;
115 extern const struct cmd_entry cmd_suspend_client_entry
;
116 extern const struct cmd_entry cmd_swap_pane_entry
;
117 extern const struct cmd_entry cmd_swap_window_entry
;
118 extern const struct cmd_entry cmd_switch_client_entry
;
119 extern const struct cmd_entry cmd_unbind_key_entry
;
120 extern const struct cmd_entry cmd_unlink_window_entry
;
121 extern const struct cmd_entry cmd_up_pane_entry
;
122 extern const struct cmd_entry cmd_wait_for_entry
;
124 const struct cmd_entry
*cmd_table
[] = {
125 &cmd_attach_session_entry
,
127 &cmd_break_pane_entry
,
128 &cmd_capture_pane_entry
,
129 &cmd_choose_buffer_entry
,
130 &cmd_choose_client_entry
,
131 &cmd_choose_tree_entry
,
132 &cmd_clear_history_entry
,
133 &cmd_clear_prompt_history_entry
,
134 &cmd_clock_mode_entry
,
135 &cmd_command_prompt_entry
,
136 &cmd_confirm_before_entry
,
137 &cmd_copy_mode_entry
,
138 &cmd_customize_mode_entry
,
139 &cmd_delete_buffer_entry
,
140 &cmd_detach_client_entry
,
141 &cmd_display_menu_entry
,
142 &cmd_display_message_entry
,
143 &cmd_display_popup_entry
,
144 &cmd_display_panes_entry
,
145 &cmd_find_window_entry
,
146 &cmd_has_session_entry
,
148 &cmd_join_pane_entry
,
149 &cmd_kill_pane_entry
,
150 &cmd_kill_server_entry
,
151 &cmd_kill_session_entry
,
152 &cmd_kill_window_entry
,
153 &cmd_last_pane_entry
,
154 &cmd_last_window_entry
,
155 &cmd_link_window_entry
,
156 &cmd_list_buffers_entry
,
157 &cmd_list_clients_entry
,
158 &cmd_list_commands_entry
,
159 &cmd_list_keys_entry
,
160 &cmd_list_panes_entry
,
161 &cmd_list_sessions_entry
,
162 &cmd_list_windows_entry
,
163 &cmd_load_buffer_entry
,
164 &cmd_lock_client_entry
,
165 &cmd_lock_server_entry
,
166 &cmd_lock_session_entry
,
167 &cmd_move_pane_entry
,
168 &cmd_move_window_entry
,
169 &cmd_new_session_entry
,
170 &cmd_new_window_entry
,
171 &cmd_next_layout_entry
,
172 &cmd_next_window_entry
,
173 &cmd_paste_buffer_entry
,
174 &cmd_pipe_pane_entry
,
175 &cmd_previous_layout_entry
,
176 &cmd_previous_window_entry
,
177 &cmd_refresh_client_entry
,
178 &cmd_rename_session_entry
,
179 &cmd_rename_window_entry
,
180 &cmd_resize_pane_entry
,
181 &cmd_resize_window_entry
,
182 &cmd_respawn_pane_entry
,
183 &cmd_respawn_window_entry
,
184 &cmd_rotate_window_entry
,
185 &cmd_run_shell_entry
,
186 &cmd_save_buffer_entry
,
187 &cmd_select_layout_entry
,
188 &cmd_select_pane_entry
,
189 &cmd_select_window_entry
,
190 &cmd_send_keys_entry
,
191 &cmd_send_prefix_entry
,
192 &cmd_server_access_entry
,
193 &cmd_set_buffer_entry
,
194 &cmd_set_environment_entry
,
196 &cmd_set_option_entry
,
197 &cmd_set_window_option_entry
,
198 &cmd_show_buffer_entry
,
199 &cmd_show_environment_entry
,
200 &cmd_show_hooks_entry
,
201 &cmd_show_messages_entry
,
202 &cmd_show_options_entry
,
203 &cmd_show_prompt_history_entry
,
204 &cmd_show_window_options_entry
,
205 &cmd_source_file_entry
,
206 &cmd_split_window_entry
,
207 &cmd_start_server_entry
,
208 &cmd_suspend_client_entry
,
209 &cmd_swap_pane_entry
,
210 &cmd_swap_window_entry
,
211 &cmd_switch_client_entry
,
212 &cmd_unbind_key_entry
,
213 &cmd_unlink_window_entry
,
218 /* Instance of a command. */
220 const struct cmd_entry
*entry
;
227 TAILQ_ENTRY(cmd
) qentry
;
229 TAILQ_HEAD(cmds
, cmd
);
231 /* Next group number for new command list. */
232 static u_int cmd_list_next_group
= 1;
234 /* Log an argument vector. */
235 void printflike(3, 4)
236 cmd_log_argv(int argc
, char **argv
, const char *fmt
, ...)
243 xvasprintf(&prefix
, fmt
, ap
);
246 for (i
= 0; i
< argc
; i
++)
247 log_debug("%s: argv[%d]=%s", prefix
, i
, argv
[i
]);
251 /* Prepend to an argument vector. */
253 cmd_prepend_argv(int *argc
, char ***argv
, const char *arg
)
258 new_argv
= xreallocarray(NULL
, (*argc
) + 1, sizeof *new_argv
);
259 new_argv
[0] = xstrdup(arg
);
260 for (i
= 0; i
< *argc
; i
++)
261 new_argv
[1 + i
] = (*argv
)[i
];
268 /* Append to an argument vector. */
270 cmd_append_argv(int *argc
, char ***argv
, const char *arg
)
272 *argv
= xreallocarray(*argv
, (*argc
) + 1, sizeof **argv
);
273 (*argv
)[(*argc
)++] = xstrdup(arg
);
276 /* Pack an argument vector up into a buffer. */
278 cmd_pack_argv(int argc
, char **argv
, char *buf
, size_t len
)
285 cmd_log_argv(argc
, argv
, "%s", __func__
);
288 for (i
= 0; i
< argc
; i
++) {
289 if (strlcpy(buf
, argv
[i
], len
) >= len
)
291 arglen
= strlen(argv
[i
]) + 1;
299 /* Unpack an argument vector from a packed buffer. */
301 cmd_unpack_argv(char *buf
, size_t len
, int argc
, char ***argv
)
308 *argv
= xcalloc(argc
, sizeof **argv
);
311 for (i
= 0; i
< argc
; i
++) {
313 cmd_free_argv(argc
, *argv
);
317 arglen
= strlen(buf
) + 1;
318 (*argv
)[i
] = xstrdup(buf
);
323 cmd_log_argv(argc
, *argv
, "%s", __func__
);
328 /* Copy an argument vector, ensuring it is terminated by NULL. */
330 cmd_copy_argv(int argc
, char **argv
)
337 new_argv
= xcalloc(argc
+ 1, sizeof *new_argv
);
338 for (i
= 0; i
< argc
; i
++) {
340 new_argv
[i
] = xstrdup(argv
[i
]);
345 /* Free an argument vector. */
347 cmd_free_argv(int argc
, char **argv
)
353 for (i
= 0; i
< argc
; i
++)
358 /* Convert argument vector to a string. */
360 cmd_stringify_argv(int argc
, char **argv
)
362 char *buf
= NULL
, *s
;
367 return (xstrdup(""));
369 for (i
= 0; i
< argc
; i
++) {
370 s
= args_escape(argv
[i
]);
371 log_debug("%s: %u %s = %s", __func__
, i
, argv
[i
], s
);
373 len
+= strlen(s
) + 1;
374 buf
= xrealloc(buf
, len
);
379 strlcat(buf
, " ", len
);
380 strlcat(buf
, s
, len
);
387 /* Get entry for command. */
388 const struct cmd_entry
*
389 cmd_get_entry(struct cmd
*cmd
)
394 /* Get arguments for command. */
396 cmd_get_args(struct cmd
*cmd
)
401 /* Get group for command. */
403 cmd_get_group(struct cmd
*cmd
)
408 /* Get file and line for command. */
410 cmd_get_source(struct cmd
*cmd
, const char **file
, u_int
*line
)
418 /* Look for an alias for a command. */
420 cmd_get_alias(const char *name
)
422 struct options_entry
*o
;
423 struct options_array_item
*a
;
424 union options_value
*ov
;
428 o
= options_get_only(global_options
, "command-alias");
431 wanted
= strlen(name
);
433 a
= options_array_first(o
);
435 ov
= options_array_item_value(a
);
437 equals
= strchr(ov
->string
, '=');
438 if (equals
!= NULL
) {
439 n
= equals
- ov
->string
;
440 if (n
== wanted
&& strncmp(name
, ov
->string
, n
) == 0)
441 return (xstrdup(equals
+ 1));
444 a
= options_array_next(a
);
449 /* Look up a command entry by name. */
450 static const struct cmd_entry
*
451 cmd_find(const char *name
, char **cause
)
453 const struct cmd_entry
**loop
, *entry
, *found
= NULL
;
458 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
460 if (entry
->alias
!= NULL
&& strcmp(entry
->alias
, name
) == 0) {
466 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
472 if (strcmp(entry
->name
, name
) == 0)
478 xasprintf(cause
, "unknown command: %s", name
);
485 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
487 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
489 if (strlcat(s
, entry
->name
, sizeof s
) >= sizeof s
)
491 if (strlcat(s
, ", ", sizeof s
) >= sizeof s
)
494 s
[strlen(s
) - 2] = '\0';
495 xasprintf(cause
, "ambiguous command: %s, could be: %s", name
, s
);
499 /* Parse a single command from an argument vector. */
501 cmd_parse(struct args_value
*values
, u_int count
, const char *file
, u_int line
,
504 const struct cmd_entry
*entry
;
509 if (count
== 0 || values
[0].type
!= ARGS_STRING
) {
510 xasprintf(cause
, "no command");
513 entry
= cmd_find(values
[0].string
, cause
);
517 args
= args_parse(&entry
->args
, values
, count
, &error
);
518 if (args
== NULL
&& error
== NULL
) {
519 xasprintf(cause
, "usage: %s %s", entry
->name
, entry
->usage
);
523 xasprintf(cause
, "command %s: %s", entry
->name
, error
);
528 cmd
= xcalloc(1, sizeof *cmd
);
533 cmd
->file
= xstrdup(file
);
539 /* Free a command. */
541 cmd_free(struct cmd
*cmd
)
545 args_free(cmd
->args
);
549 /* Copy a command. */
551 cmd_copy(struct cmd
*cmd
, int argc
, char **argv
)
555 new_cmd
= xcalloc(1, sizeof *new_cmd
);
556 new_cmd
->entry
= cmd
->entry
;
557 new_cmd
->args
= args_copy(cmd
->args
, argc
, argv
);
559 if (cmd
->file
!= NULL
)
560 new_cmd
->file
= xstrdup(cmd
->file
);
561 new_cmd
->line
= cmd
->line
;
566 /* Get a command as a string. */
568 cmd_print(struct cmd
*cmd
)
572 s
= args_print(cmd
->args
);
574 xasprintf(&out
, "%s %s", cmd
->entry
->name
, s
);
576 out
= xstrdup(cmd
->entry
->name
);
582 /* Create a new command list. */
586 struct cmd_list
*cmdlist
;
588 cmdlist
= xcalloc(1, sizeof *cmdlist
);
589 cmdlist
->references
= 1;
590 cmdlist
->group
= cmd_list_next_group
++;
591 cmdlist
->list
= xcalloc(1, sizeof *cmdlist
->list
);
592 TAILQ_INIT(cmdlist
->list
);
596 /* Append a command to a command list. */
598 cmd_list_append(struct cmd_list
*cmdlist
, struct cmd
*cmd
)
600 cmd
->group
= cmdlist
->group
;
601 TAILQ_INSERT_TAIL(cmdlist
->list
, cmd
, qentry
);
604 /* Append all commands from one list to another. */
606 cmd_list_append_all(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
610 TAILQ_FOREACH(cmd
, from
->list
, qentry
)
611 cmd
->group
= cmdlist
->group
;
612 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
615 /* Move all commands from one command list to another. */
617 cmd_list_move(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
619 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
620 cmdlist
->group
= cmd_list_next_group
++;
623 /* Free a command list. */
625 cmd_list_free(struct cmd_list
*cmdlist
)
627 struct cmd
*cmd
, *cmd1
;
629 if (--cmdlist
->references
!= 0)
632 TAILQ_FOREACH_SAFE(cmd
, cmdlist
->list
, qentry
, cmd1
) {
633 TAILQ_REMOVE(cmdlist
->list
, cmd
, qentry
);
640 /* Copy a command list, expanding %s in arguments. */
642 cmd_list_copy(struct cmd_list
*cmdlist
, int argc
, char **argv
)
645 struct cmd_list
*new_cmdlist
;
647 u_int group
= cmdlist
->group
;
650 s
= cmd_list_print(cmdlist
, 0);
651 log_debug("%s: %s", __func__
, s
);
654 new_cmdlist
= cmd_list_new();
655 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
656 if (cmd
->group
!= group
) {
657 new_cmdlist
->group
= cmd_list_next_group
++;
660 new_cmd
= cmd_copy(cmd
, argc
, argv
);
661 cmd_list_append(new_cmdlist
, new_cmd
);
664 s
= cmd_list_print(new_cmdlist
, 0);
665 log_debug("%s: %s", __func__
, s
);
668 return (new_cmdlist
);
671 /* Get a command list as a string. */
673 cmd_list_print(struct cmd_list
*cmdlist
, int escaped
)
675 struct cmd
*cmd
, *next
;
680 buf
= xcalloc(1, len
);
682 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
683 this = cmd_print(cmd
);
685 len
+= strlen(this) + 6;
686 buf
= xrealloc(buf
, len
);
688 strlcat(buf
, this, len
);
690 next
= TAILQ_NEXT(cmd
, qentry
);
692 if (cmd
->group
!= next
->group
) {
694 strlcat(buf
, " \\;\\; ", len
);
696 strlcat(buf
, " ;; ", len
);
699 strlcat(buf
, " \\; ", len
);
701 strlcat(buf
, " ; ", len
);
711 /* Get first command in list. */
713 cmd_list_first(struct cmd_list
*cmdlist
)
715 return (TAILQ_FIRST(cmdlist
->list
));
718 /* Get next command in list. */
720 cmd_list_next(struct cmd
*cmd
)
722 return (TAILQ_NEXT(cmd
, qentry
));
725 /* Do all of the commands in this command list have this flag? */
727 cmd_list_all_have(struct cmd_list
*cmdlist
, int flag
)
731 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
732 if (~cmd
->entry
->flags
& flag
)
738 /* Do any of the commands in this command list have this flag? */
740 cmd_list_any_have(struct cmd_list
*cmdlist
, int flag
)
744 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
745 if (cmd
->entry
->flags
& flag
)
751 /* Adjust current mouse position for a pane. */
753 cmd_mouse_at(struct window_pane
*wp
, struct mouse_event
*m
, u_int
*xp
,
765 log_debug("%s: x=%u, y=%u%s", __func__
, x
, y
, last
? " (last)" : "");
767 if (m
->statusat
== 0 && y
>= m
->statuslines
)
770 if (x
< wp
->xoff
|| x
>= wp
->xoff
+ wp
->sx
)
772 if (y
< wp
->yoff
|| y
>= wp
->yoff
+ wp
->sy
)
782 /* Get current mouse window if any. */
784 cmd_mouse_window(struct mouse_event
*m
, struct session
**sp
)
792 if (m
->s
== -1 || (s
= session_find_by_id(m
->s
)) == NULL
)
797 if ((w
= window_find_by_id(m
->w
)) == NULL
)
799 wl
= winlink_find_by_window(&s
->windows
, w
);
806 /* Get current mouse pane if any. */
808 cmd_mouse_pane(struct mouse_event
*m
, struct session
**sp
,
809 struct winlink
**wlp
)
812 struct window_pane
*wp
;
814 if ((wl
= cmd_mouse_window(m
, sp
)) == NULL
)
816 if ((wp
= window_pane_find_by_id(m
->wp
)) == NULL
)
818 if (!window_has_pane(wl
->window
, wp
))
826 /* Replace the first %% or %idx in template by s. */
828 cmd_template_replace(const char *template, const char *s
, int idx
)
831 const char *ptr
, *cp
, quote
[] = "\"\\$;~";
832 int replaced
, quoted
;
835 if (strchr(template, '%') == NULL
)
836 return (xstrdup(template));
844 while (*ptr
!= '\0') {
845 switch (ch
= *ptr
++) {
847 if (*ptr
< '1' || *ptr
> '9' || *ptr
- '0' != idx
) {
848 if (*ptr
!= '%' || replaced
)
854 quoted
= (*ptr
== '%');
858 buf
= xrealloc(buf
, len
+ (strlen(s
) * 3) + 1);
859 for (cp
= s
; *cp
!= '\0'; cp
++) {
860 if (quoted
&& strchr(quote
, *cp
) != NULL
)
867 buf
= xrealloc(buf
, len
+ 2);