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_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
;
122 const struct cmd_entry
*cmd_table
[] = {
123 &cmd_attach_session_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
,
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
,
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
,
216 /* Instance of a command. */
218 const struct cmd_entry
*entry
;
225 TAILQ_ENTRY(cmd
) qentry
;
227 TAILQ_HEAD(cmds
, cmd
);
229 /* Next group number for new command list. */
230 static u_int cmd_list_next_group
= 1;
232 /* Log an argument vector. */
233 void printflike(3, 4)
234 cmd_log_argv(int argc
, char **argv
, const char *fmt
, ...)
241 xvasprintf(&prefix
, fmt
, ap
);
244 for (i
= 0; i
< argc
; i
++)
245 log_debug("%s: argv[%d]=%s", prefix
, i
, argv
[i
]);
249 /* Prepend to an argument vector. */
251 cmd_prepend_argv(int *argc
, char ***argv
, const char *arg
)
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
];
266 /* Append to an argument vector. */
268 cmd_append_argv(int *argc
, char ***argv
, const char *arg
)
270 *argv
= xreallocarray(*argv
, (*argc
) + 1, sizeof **argv
);
271 (*argv
)[(*argc
)++] = xstrdup(arg
);
274 /* Pack an argument vector up into a buffer. */
276 cmd_pack_argv(int argc
, char **argv
, char *buf
, size_t len
)
283 cmd_log_argv(argc
, argv
, "%s", __func__
);
286 for (i
= 0; i
< argc
; i
++) {
287 if (strlcpy(buf
, argv
[i
], len
) >= len
)
289 arglen
= strlen(argv
[i
]) + 1;
297 /* Unpack an argument vector from a packed buffer. */
299 cmd_unpack_argv(char *buf
, size_t len
, int argc
, char ***argv
)
306 *argv
= xcalloc(argc
, sizeof **argv
);
309 for (i
= 0; i
< argc
; i
++) {
311 cmd_free_argv(argc
, *argv
);
315 arglen
= strlen(buf
) + 1;
316 (*argv
)[i
] = xstrdup(buf
);
321 cmd_log_argv(argc
, *argv
, "%s", __func__
);
326 /* Copy an argument vector, ensuring it is terminated by NULL. */
328 cmd_copy_argv(int argc
, char **argv
)
335 new_argv
= xcalloc(argc
+ 1, sizeof *new_argv
);
336 for (i
= 0; i
< argc
; i
++) {
338 new_argv
[i
] = xstrdup(argv
[i
]);
343 /* Free an argument vector. */
345 cmd_free_argv(int argc
, char **argv
)
351 for (i
= 0; i
< argc
; i
++)
356 /* Convert argument vector to a string. */
358 cmd_stringify_argv(int argc
, char **argv
)
360 char *buf
= NULL
, *s
;
365 return (xstrdup(""));
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
);
371 len
+= strlen(s
) + 1;
372 buf
= xrealloc(buf
, len
);
377 strlcat(buf
, " ", len
);
378 strlcat(buf
, s
, len
);
385 /* Get entry for command. */
386 const struct cmd_entry
*
387 cmd_get_entry(struct cmd
*cmd
)
392 /* Get arguments for command. */
394 cmd_get_args(struct cmd
*cmd
)
399 /* Get group for command. */
401 cmd_get_group(struct cmd
*cmd
)
406 /* Get file and line for command. */
408 cmd_get_source(struct cmd
*cmd
, const char **file
, u_int
*line
)
416 /* Look for an alias for a command. */
418 cmd_get_alias(const char *name
)
420 struct options_entry
*o
;
421 struct options_array_item
*a
;
422 union options_value
*ov
;
426 o
= options_get_only(global_options
, "command-alias");
429 wanted
= strlen(name
);
431 a
= options_array_first(o
);
433 ov
= options_array_item_value(a
);
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));
442 a
= options_array_next(a
);
447 /* Look up a command entry by name. */
448 static const struct cmd_entry
*
449 cmd_find(const char *name
, char **cause
)
451 const struct cmd_entry
**loop
, *entry
, *found
= NULL
;
456 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
458 if (entry
->alias
!= NULL
&& strcmp(entry
->alias
, name
) == 0) {
464 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
470 if (strcmp(entry
->name
, name
) == 0)
476 xasprintf(cause
, "unknown command: %s", name
);
483 for (loop
= cmd_table
; *loop
!= NULL
; loop
++) {
485 if (strncmp(entry
->name
, name
, strlen(name
)) != 0)
487 if (strlcat(s
, entry
->name
, sizeof s
) >= sizeof s
)
489 if (strlcat(s
, ", ", sizeof s
) >= sizeof s
)
492 s
[strlen(s
) - 2] = '\0';
493 xasprintf(cause
, "ambiguous command: %s, could be: %s", name
, s
);
497 /* Parse a single command from an argument vector. */
499 cmd_parse(struct args_value
*values
, u_int count
, const char *file
, u_int line
,
502 const struct cmd_entry
*entry
;
507 if (count
== 0 || values
[0].type
!= ARGS_STRING
) {
508 xasprintf(cause
, "no command");
511 entry
= cmd_find(values
[0].string
, cause
);
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
);
521 xasprintf(cause
, "command %s: %s", entry
->name
, error
);
526 cmd
= xcalloc(1, sizeof *cmd
);
531 cmd
->file
= xstrdup(file
);
537 /* Free a command. */
539 cmd_free(struct cmd
*cmd
)
543 args_free(cmd
->args
);
547 /* Copy a command. */
549 cmd_copy(struct cmd
*cmd
, int argc
, char **argv
)
553 new_cmd
= xcalloc(1, sizeof *new_cmd
);
554 new_cmd
->entry
= cmd
->entry
;
555 new_cmd
->args
= args_copy(cmd
->args
, argc
, argv
);
557 if (cmd
->file
!= NULL
)
558 new_cmd
->file
= xstrdup(cmd
->file
);
559 new_cmd
->line
= cmd
->line
;
564 /* Get a command as a string. */
566 cmd_print(struct cmd
*cmd
)
570 s
= args_print(cmd
->args
);
572 xasprintf(&out
, "%s %s", cmd
->entry
->name
, s
);
574 out
= xstrdup(cmd
->entry
->name
);
580 /* Create a new command list. */
584 struct cmd_list
*cmdlist
;
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
);
594 /* Append a command to a command list. */
596 cmd_list_append(struct cmd_list
*cmdlist
, struct cmd
*cmd
)
598 cmd
->group
= cmdlist
->group
;
599 TAILQ_INSERT_TAIL(cmdlist
->list
, cmd
, qentry
);
602 /* Append all commands from one list to another. */
604 cmd_list_append_all(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
608 TAILQ_FOREACH(cmd
, from
->list
, qentry
)
609 cmd
->group
= cmdlist
->group
;
610 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
613 /* Move all commands from one command list to another. */
615 cmd_list_move(struct cmd_list
*cmdlist
, struct cmd_list
*from
)
617 TAILQ_CONCAT(cmdlist
->list
, from
->list
, qentry
);
618 cmdlist
->group
= cmd_list_next_group
++;
621 /* Free a command list. */
623 cmd_list_free(struct cmd_list
*cmdlist
)
625 struct cmd
*cmd
, *cmd1
;
627 if (--cmdlist
->references
!= 0)
630 TAILQ_FOREACH_SAFE(cmd
, cmdlist
->list
, qentry
, cmd1
) {
631 TAILQ_REMOVE(cmdlist
->list
, cmd
, qentry
);
638 /* Copy a command list, expanding %s in arguments. */
640 cmd_list_copy(struct cmd_list
*cmdlist
, int argc
, char **argv
)
643 struct cmd_list
*new_cmdlist
;
645 u_int group
= cmdlist
->group
;
648 s
= cmd_list_print(cmdlist
, 0);
649 log_debug("%s: %s", __func__
, s
);
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
++;
658 new_cmd
= cmd_copy(cmd
, argc
, argv
);
659 cmd_list_append(new_cmdlist
, new_cmd
);
662 s
= cmd_list_print(new_cmdlist
, 0);
663 log_debug("%s: %s", __func__
, s
);
666 return (new_cmdlist
);
669 /* Get a command list as a string. */
671 cmd_list_print(struct cmd_list
*cmdlist
, int escaped
)
673 struct cmd
*cmd
, *next
;
678 buf
= xcalloc(1, len
);
680 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
681 this = cmd_print(cmd
);
683 len
+= strlen(this) + 6;
684 buf
= xrealloc(buf
, len
);
686 strlcat(buf
, this, len
);
688 next
= TAILQ_NEXT(cmd
, qentry
);
690 if (cmd
->group
!= next
->group
) {
692 strlcat(buf
, " \\;\\; ", len
);
694 strlcat(buf
, " ;; ", len
);
697 strlcat(buf
, " \\; ", len
);
699 strlcat(buf
, " ; ", len
);
709 /* Get first command in list. */
711 cmd_list_first(struct cmd_list
*cmdlist
)
713 return (TAILQ_FIRST(cmdlist
->list
));
716 /* Get next command in list. */
718 cmd_list_next(struct cmd
*cmd
)
720 return (TAILQ_NEXT(cmd
, qentry
));
723 /* Do all of the commands in this command list have this flag? */
725 cmd_list_all_have(struct cmd_list
*cmdlist
, int flag
)
729 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
730 if (~cmd
->entry
->flags
& flag
)
736 /* Do any of the commands in this command list have this flag? */
738 cmd_list_any_have(struct cmd_list
*cmdlist
, int flag
)
742 TAILQ_FOREACH(cmd
, cmdlist
->list
, qentry
) {
743 if (cmd
->entry
->flags
& flag
)
749 /* Adjust current mouse position for a pane. */
751 cmd_mouse_at(struct window_pane
*wp
, struct mouse_event
*m
, u_int
*xp
,
763 log_debug("%s: x=%u, y=%u%s", __func__
, x
, y
, last
? " (last)" : "");
765 if (m
->statusat
== 0 && y
>= m
->statuslines
)
768 if (x
< wp
->xoff
|| x
>= wp
->xoff
+ wp
->sx
)
770 if (y
< wp
->yoff
|| y
>= wp
->yoff
+ wp
->sy
)
780 /* Get current mouse window if any. */
782 cmd_mouse_window(struct mouse_event
*m
, struct session
**sp
)
790 if (m
->s
== -1 || (s
= session_find_by_id(m
->s
)) == NULL
)
795 if ((w
= window_find_by_id(m
->w
)) == NULL
)
797 wl
= winlink_find_by_window(&s
->windows
, w
);
804 /* Get current mouse pane if any. */
806 cmd_mouse_pane(struct mouse_event
*m
, struct session
**sp
,
807 struct winlink
**wlp
)
810 struct window_pane
*wp
;
812 if ((wl
= cmd_mouse_window(m
, sp
)) == NULL
)
815 wp
= wl
->window
->active
;
817 if ((wp
= window_pane_find_by_id(m
->wp
)) == NULL
)
819 if (!window_has_pane(wl
->window
, wp
))
828 /* Replace the first %% or %idx in template by s. */
830 cmd_template_replace(const char *template, const char *s
, int idx
)
833 const char *ptr
, *cp
, quote
[] = "\"\\$;~";
834 int replaced
, quoted
;
837 if (strchr(template, '%') == NULL
)
838 return (xstrdup(template));
846 while (*ptr
!= '\0') {
847 switch (ch
= *ptr
++) {
849 if (*ptr
< '1' || *ptr
> '9' || *ptr
- '0' != idx
) {
850 if (*ptr
!= '%' || replaced
)
856 quoted
= (*ptr
== '%');
860 buf
= xrealloc(buf
, len
+ (strlen(s
) * 3) + 1);
861 for (cp
= s
; *cp
!= '\0'; cp
++) {
862 if (quoted
&& strchr(quote
, *cp
) != NULL
)
869 buf
= xrealloc(buf
, len
+ 2);