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_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
;
121 const struct cmd_entry
*cmd_table
[] = {
122 &cmd_attach_session_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
,
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
,
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
,
215 /* Instance of a command. */
217 const struct cmd_entry
*entry
;
224 TAILQ_ENTRY(cmd
) qentry
;
226 TAILQ_HEAD(cmds
, cmd
);
228 /* Next group number for new command list. */
229 static u_int cmd_list_next_group
= 1;
231 /* Log an argument vector. */
232 void printflike(3, 4)
233 cmd_log_argv(int argc
, char **argv
, const char *fmt
, ...)
240 xvasprintf(&prefix
, fmt
, ap
);
243 for (i
= 0; i
< argc
; i
++)
244 log_debug("%s: argv[%d]=%s", prefix
, i
, argv
[i
]);
248 /* Prepend to an argument vector. */
250 cmd_prepend_argv(int *argc
, char ***argv
, const char *arg
)
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
];
265 /* Append to an argument vector. */
267 cmd_append_argv(int *argc
, char ***argv
, const char *arg
)
269 *argv
= xreallocarray(*argv
, (*argc
) + 1, sizeof **argv
);
270 (*argv
)[(*argc
)++] = xstrdup(arg
);
273 /* Pack an argument vector up into a buffer. */
275 cmd_pack_argv(int argc
, char **argv
, char *buf
, size_t len
)
282 cmd_log_argv(argc
, argv
, "%s", __func__
);
285 for (i
= 0; i
< argc
; i
++) {
286 if (strlcpy(buf
, argv
[i
], len
) >= len
)
288 arglen
= strlen(argv
[i
]) + 1;
296 /* Unpack an argument vector from a packed buffer. */
298 cmd_unpack_argv(char *buf
, size_t len
, int argc
, char ***argv
)
305 *argv
= xcalloc(argc
, sizeof **argv
);
308 for (i
= 0; i
< argc
; i
++) {
310 cmd_free_argv(argc
, *argv
);
314 arglen
= strlen(buf
) + 1;
315 (*argv
)[i
] = xstrdup(buf
);
320 cmd_log_argv(argc
, *argv
, "%s", __func__
);
325 /* Copy an argument vector, ensuring it is terminated by NULL. */
327 cmd_copy_argv(int argc
, char **argv
)
334 new_argv
= xcalloc(argc
+ 1, sizeof *new_argv
);
335 for (i
= 0; i
< argc
; i
++) {
337 new_argv
[i
] = xstrdup(argv
[i
]);
342 /* Free an argument vector. */
344 cmd_free_argv(int argc
, char **argv
)
350 for (i
= 0; i
< argc
; i
++)
355 /* Convert argument vector to a string. */
357 cmd_stringify_argv(int argc
, char **argv
)
359 char *buf
= NULL
, *s
;
364 return (xstrdup(""));
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
);
370 len
+= strlen(s
) + 1;
371 buf
= xrealloc(buf
, len
);
376 strlcat(buf
, " ", len
);
377 strlcat(buf
, s
, len
);
384 /* Get entry for command. */
385 const struct cmd_entry
*
386 cmd_get_entry(struct cmd
*cmd
)
391 /* Get arguments for command. */
393 cmd_get_args(struct cmd
*cmd
)
398 /* Get group for command. */
400 cmd_get_group(struct cmd
*cmd
)
405 /* Get file and line for command. */
407 cmd_get_source(struct cmd
*cmd
, const char **file
, u_int
*line
)
415 /* Look for an alias for a command. */
417 cmd_get_alias(const char *name
)
419 struct options_entry
*o
;
420 struct options_array_item
*a
;
421 union options_value
*ov
;
425 o
= options_get_only(global_options
, "command-alias");
428 wanted
= strlen(name
);
430 a
= options_array_first(o
);
432 ov
= options_array_item_value(a
);
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));
441 a
= options_array_next(a
);
446 /* Look up a command entry by name. */
447 static const struct cmd_entry
*
448 cmd_find(const char *name
, char **cause
)
450 const struct cmd_entry
**loop
, *entry
, *found
= NULL
;
455 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
457 if (entry
->alias
!= NULL
&& strcmp(entry
->alias
, name
) == 0) {
463 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
469 if (strcmp(entry
->name
, name
) == 0)
475 xasprintf(cause
, "unknown command: %s", name
);
482 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
484 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
486 if (strlcat(s
, entry
->name
, sizeof s
) >= sizeof s
)
488 if (strlcat(s
, ", ", sizeof s
) >= sizeof s
)
491 s
[strlen(s
) - 2] = '\0';
492 xasprintf(cause
, "ambiguous command: %s, could be: %s", name
, s
);
496 /* Parse a single command from an argument vector. */
498 cmd_parse(struct args_value
*values
, u_int count
, const char *file
, u_int line
,
501 const struct cmd_entry
*entry
;
506 if (count
== 0 || values
[0].type
!= ARGS_STRING
) {
507 xasprintf(cause
, "no command");
510 entry
= cmd_find(values
[0].string
, cause
);
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
);
520 xasprintf(cause
, "command %s: %s", entry
->name
, error
);
525 cmd
= xcalloc(1, sizeof *cmd
);
530 cmd
->file
= xstrdup(file
);
536 /* Free a command. */
538 cmd_free(struct cmd
*cmd
)
542 args_free(cmd
->args
);
546 /* Copy a command. */
548 cmd_copy(struct cmd
*cmd
, int argc
, char **argv
)
552 new_cmd
= xcalloc(1, sizeof *new_cmd
);
553 new_cmd
->entry
= cmd
->entry
;
554 new_cmd
->args
= args_copy(cmd
->args
, argc
, argv
);
556 if (cmd
->file
!= NULL
)
557 new_cmd
->file
= xstrdup(cmd
->file
);
558 new_cmd
->line
= cmd
->line
;
563 /* Get a command as a string. */
565 cmd_print(struct cmd
*cmd
)
569 s
= args_print(cmd
->args
);
571 xasprintf(&out
, "%s %s", cmd
->entry
->name
, s
);
573 out
= xstrdup(cmd
->entry
->name
);
579 /* Create a new command list. */
583 struct cmd_list
*cmdlist
;
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
);
593 /* Append a command to a command list. */
595 cmd_list_append(struct cmd_list
*cmdlist
, struct cmd
*cmd
)
597 cmd
->group
= cmdlist
->group
;
598 TAILQ_INSERT_TAIL(cmdlist
->list
, cmd
, qentry
);
601 /* Append all commands from one list to another. */
603 cmd_list_append_all(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
607 TAILQ_FOREACH(cmd
, from
->list
, qentry
)
608 cmd
->group
= cmdlist
->group
;
609 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
612 /* Move all commands from one command list to another. */
614 cmd_list_move(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
616 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
617 cmdlist
->group
= cmd_list_next_group
++;
620 /* Free a command list. */
622 cmd_list_free(struct cmd_list
*cmdlist
)
624 struct cmd
*cmd
, *cmd1
;
626 if (--cmdlist
->references
!= 0)
629 TAILQ_FOREACH_SAFE(cmd
, cmdlist
->list
, qentry
, cmd1
) {
630 TAILQ_REMOVE(cmdlist
->list
, cmd
, qentry
);
637 /* Copy a command list, expanding %s in arguments. */
639 cmd_list_copy(struct cmd_list
*cmdlist
, int argc
, char **argv
)
642 struct cmd_list
*new_cmdlist
;
644 u_int group
= cmdlist
->group
;
647 s
= cmd_list_print(cmdlist
, 0);
648 log_debug("%s: %s", __func__
, s
);
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
++;
657 new_cmd
= cmd_copy(cmd
, argc
, argv
);
658 cmd_list_append(new_cmdlist
, new_cmd
);
661 s
= cmd_list_print(new_cmdlist
, 0);
662 log_debug("%s: %s", __func__
, s
);
665 return (new_cmdlist
);
668 /* Get a command list as a string. */
670 cmd_list_print(struct cmd_list
*cmdlist
, int escaped
)
672 struct cmd
*cmd
, *next
;
677 buf
= xcalloc(1, len
);
679 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
680 this = cmd_print(cmd
);
682 len
+= strlen(this) + 6;
683 buf
= xrealloc(buf
, len
);
685 strlcat(buf
, this, len
);
687 next
= TAILQ_NEXT(cmd
, qentry
);
689 if (cmd
->group
!= next
->group
) {
691 strlcat(buf
, " \\;\\; ", len
);
693 strlcat(buf
, " ;; ", len
);
696 strlcat(buf
, " \\; ", len
);
698 strlcat(buf
, " ; ", len
);
708 /* Get first command in list. */
710 cmd_list_first(struct cmd_list
*cmdlist
)
712 return (TAILQ_FIRST(cmdlist
->list
));
715 /* Get next command in list. */
717 cmd_list_next(struct cmd
*cmd
)
719 return (TAILQ_NEXT(cmd
, qentry
));
722 /* Do all of the commands in this command list have this flag? */
724 cmd_list_all_have(struct cmd_list
*cmdlist
, int flag
)
728 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
729 if (~cmd
->entry
->flags
& flag
)
735 /* Do any of the commands in this command list have this flag? */
737 cmd_list_any_have(struct cmd_list
*cmdlist
, int flag
)
741 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
742 if (cmd
->entry
->flags
& flag
)
748 /* Adjust current mouse position for a pane. */
750 cmd_mouse_at(struct window_pane
*wp
, struct mouse_event
*m
, u_int
*xp
,
762 log_debug("%s: x=%u, y=%u%s", __func__
, x
, y
, last
? " (last)" : "");
764 if (m
->statusat
== 0 && y
>= m
->statuslines
)
767 if (x
< wp
->xoff
|| x
>= wp
->xoff
+ wp
->sx
)
769 if (y
< wp
->yoff
|| y
>= wp
->yoff
+ wp
->sy
)
779 /* Get current mouse window if any. */
781 cmd_mouse_window(struct mouse_event
*m
, struct session
**sp
)
789 if (m
->s
== -1 || (s
= session_find_by_id(m
->s
)) == NULL
)
794 if ((w
= window_find_by_id(m
->w
)) == NULL
)
796 wl
= winlink_find_by_window(&s
->windows
, w
);
803 /* Get current mouse pane if any. */
805 cmd_mouse_pane(struct mouse_event
*m
, struct session
**sp
,
806 struct winlink
**wlp
)
809 struct window_pane
*wp
;
811 if ((wl
= cmd_mouse_window(m
, sp
)) == NULL
)
814 wp
= wl
->window
->active
;
816 if ((wp
= window_pane_find_by_id(m
->wp
)) == NULL
)
818 if (!window_has_pane(wl
->window
, wp
))
827 /* Replace the first %% or %idx in template by s. */
829 cmd_template_replace(const char *template, const char *s
, int idx
)
832 const char *ptr
, *cp
, quote
[] = "\"\\$;~";
833 int replaced
, quoted
;
836 if (strchr(template, '%') == NULL
)
837 return (xstrdup(template));
845 while (*ptr
!= '\0') {
846 switch (ch
= *ptr
++) {
848 if (*ptr
< '1' || *ptr
> '9' || *ptr
- '0' != idx
) {
849 if (*ptr
!= '%' || replaced
)
855 quoted
= (*ptr
== '%');
859 buf
= xrealloc(buf
, len
+ (strlen(s
) * 3) + 1);
860 for (cp
= s
; *cp
!= '\0'; cp
++) {
861 if (quoted
&& strchr(quote
, *cp
) != NULL
)
868 buf
= xrealloc(buf
, len
+ 2);