Line | Count | Source |
1 | | /* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */ |
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_pane_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_switch_mode_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_wait_for_entry; |
122 | | |
123 | | const struct cmd_entry *cmd_table[] = { |
124 | | &cmd_attach_session_entry, |
125 | | &cmd_bind_key_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, |
146 | | &cmd_if_shell_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_pane_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, |
195 | | &cmd_set_hook_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_switch_mode_entry, |
213 | | &cmd_unbind_key_entry, |
214 | | &cmd_unlink_window_entry, |
215 | | &cmd_wait_for_entry, |
216 | | NULL |
217 | | }; |
218 | | |
219 | | /* Instance of a command. */ |
220 | | struct cmd { |
221 | | const struct cmd_entry *entry; |
222 | | struct args *args; |
223 | | u_int group; |
224 | | |
225 | | char *file; |
226 | | u_int line; |
227 | | int parse_flags; |
228 | | |
229 | | TAILQ_ENTRY(cmd) qentry; |
230 | | }; |
231 | | TAILQ_HEAD(cmds, cmd); |
232 | | |
233 | | /* Next group number for new command list. */ |
234 | | static u_int cmd_list_next_group = 1; |
235 | | |
236 | | /* Log an argument vector. */ |
237 | | void printflike(3, 4) |
238 | | cmd_log_argv(int argc, char **argv, const char *fmt, ...) |
239 | 0 | { |
240 | 0 | char *prefix; |
241 | 0 | va_list ap; |
242 | 0 | int i; |
243 | |
|
244 | 0 | va_start(ap, fmt); |
245 | 0 | xvasprintf(&prefix, fmt, ap); |
246 | 0 | va_end(ap); |
247 | |
|
248 | 0 | for (i = 0; i < argc; i++) |
249 | 0 | log_debug("%s: argv[%d]=%s", prefix, i, argv[i]); |
250 | 0 | free(prefix); |
251 | 0 | } |
252 | | |
253 | | /* Prepend to an argument vector. */ |
254 | | void |
255 | | cmd_prepend_argv(int *argc, char ***argv, const char *arg) |
256 | 0 | { |
257 | 0 | char **new_argv; |
258 | 0 | int i; |
259 | |
|
260 | 0 | new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); |
261 | 0 | new_argv[0] = xstrdup(arg); |
262 | 0 | for (i = 0; i < *argc; i++) |
263 | 0 | new_argv[1 + i] = (*argv)[i]; |
264 | |
|
265 | 0 | free(*argv); |
266 | 0 | *argv = new_argv; |
267 | 0 | (*argc)++; |
268 | 0 | } |
269 | | |
270 | | /* Append to an argument vector. */ |
271 | | void |
272 | | cmd_append_argv(int *argc, char ***argv, const char *arg) |
273 | 0 | { |
274 | 0 | *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); |
275 | 0 | (*argv)[(*argc)++] = xstrdup(arg); |
276 | 0 | } |
277 | | |
278 | | /* Pack an argument vector up into a buffer. */ |
279 | | int |
280 | | cmd_pack_argv(int argc, char **argv, char *buf, size_t len) |
281 | 0 | { |
282 | 0 | size_t arglen; |
283 | 0 | int i; |
284 | |
|
285 | 0 | if (argc == 0) |
286 | 0 | return (0); |
287 | 0 | cmd_log_argv(argc, argv, "%s", __func__); |
288 | |
|
289 | 0 | *buf = '\0'; |
290 | 0 | for (i = 0; i < argc; i++) { |
291 | 0 | if (strlcpy(buf, argv[i], len) >= len) |
292 | 0 | return (-1); |
293 | 0 | arglen = strlen(argv[i]) + 1; |
294 | 0 | buf += arglen; |
295 | 0 | len -= arglen; |
296 | 0 | } |
297 | | |
298 | 0 | return (0); |
299 | 0 | } |
300 | | |
301 | | /* Unpack an argument vector from a packed buffer. */ |
302 | | int |
303 | | cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) |
304 | 0 | { |
305 | 0 | int i; |
306 | 0 | size_t arglen; |
307 | |
|
308 | 0 | if (argc == 0) |
309 | 0 | return (0); |
310 | 0 | if (argc < 0 || argc > 1000) |
311 | 0 | return (-1); |
312 | 0 | *argv = xcalloc(argc, sizeof **argv); |
313 | |
|
314 | 0 | buf[len - 1] = '\0'; |
315 | 0 | for (i = 0; i < argc; i++) { |
316 | 0 | if (len == 0) { |
317 | 0 | cmd_free_argv(argc, *argv); |
318 | 0 | return (-1); |
319 | 0 | } |
320 | | |
321 | 0 | arglen = strlen(buf) + 1; |
322 | 0 | (*argv)[i] = xstrdup(buf); |
323 | |
|
324 | 0 | buf += arglen; |
325 | 0 | len -= arglen; |
326 | 0 | } |
327 | 0 | cmd_log_argv(argc, *argv, "%s", __func__); |
328 | |
|
329 | 0 | return (0); |
330 | 0 | } |
331 | | |
332 | | /* Copy an argument vector, ensuring it is terminated by NULL. */ |
333 | | char ** |
334 | | cmd_copy_argv(int argc, char **argv) |
335 | 0 | { |
336 | 0 | char **new_argv; |
337 | 0 | int i; |
338 | |
|
339 | 0 | if (argc == 0) |
340 | 0 | return (NULL); |
341 | 0 | new_argv = xcalloc(argc + 1, sizeof *new_argv); |
342 | 0 | for (i = 0; i < argc; i++) { |
343 | 0 | if (argv[i] != NULL) |
344 | 0 | new_argv[i] = xstrdup(argv[i]); |
345 | 0 | } |
346 | 0 | return (new_argv); |
347 | 0 | } |
348 | | |
349 | | /* Free an argument vector. */ |
350 | | void |
351 | | cmd_free_argv(int argc, char **argv) |
352 | 966k | { |
353 | 966k | int i; |
354 | | |
355 | 966k | if (argc == 0) |
356 | 301k | return; |
357 | 1.57M | for (i = 0; i < argc; i++) |
358 | 912k | free(argv[i]); |
359 | 665k | free(argv); |
360 | 665k | } |
361 | | |
362 | | /* Convert argument vector to a string. */ |
363 | | char * |
364 | | cmd_stringify_argv(int argc, char **argv) |
365 | 0 | { |
366 | 0 | char *buf = NULL, *s; |
367 | 0 | size_t len = 0; |
368 | 0 | int i; |
369 | |
|
370 | 0 | if (argc == 0) |
371 | 0 | return (xstrdup("")); |
372 | | |
373 | 0 | for (i = 0; i < argc; i++) { |
374 | 0 | s = args_escape(argv[i]); |
375 | 0 | log_debug("%s: %u %s = %s", __func__, i, argv[i], s); |
376 | |
|
377 | 0 | len += strlen(s) + 1; |
378 | 0 | buf = xrealloc(buf, len); |
379 | |
|
380 | 0 | if (i == 0) |
381 | 0 | *buf = '\0'; |
382 | 0 | else |
383 | 0 | strlcat(buf, " ", len); |
384 | 0 | strlcat(buf, s, len); |
385 | |
|
386 | 0 | free(s); |
387 | 0 | } |
388 | 0 | return (buf); |
389 | 0 | } |
390 | | |
391 | | /* Get entry for command. */ |
392 | | const struct cmd_entry * |
393 | | cmd_get_entry(struct cmd *cmd) |
394 | 0 | { |
395 | 0 | return (cmd->entry); |
396 | 0 | } |
397 | | |
398 | | /* Get arguments for command. */ |
399 | | struct args * |
400 | | cmd_get_args(struct cmd *cmd) |
401 | 0 | { |
402 | 0 | return (cmd->args); |
403 | 0 | } |
404 | | |
405 | | /* Get group for command. */ |
406 | | u_int |
407 | | cmd_get_group(struct cmd *cmd) |
408 | 0 | { |
409 | 0 | return (cmd->group); |
410 | 0 | } |
411 | | |
412 | | /* Get file and line for command. */ |
413 | | void |
414 | | cmd_get_source(struct cmd *cmd, const char **file, u_int *line) |
415 | 0 | { |
416 | 0 | if (file != NULL) |
417 | 0 | *file = cmd->file; |
418 | 0 | if (line != NULL) |
419 | 0 | *line = cmd->line; |
420 | 0 | } |
421 | | |
422 | | /* Get parse flags for command. */ |
423 | | int |
424 | | cmd_get_parse_flags(struct cmd *cmd) |
425 | 0 | { |
426 | 0 | return (cmd->parse_flags); |
427 | 0 | } |
428 | | |
429 | | /* Look for an alias for a command. */ |
430 | | char * |
431 | | cmd_get_alias(const char *name) |
432 | 1.62k | { |
433 | 1.62k | struct options_entry *o; |
434 | 1.62k | struct options_array_item *a; |
435 | 1.62k | union options_value *ov; |
436 | 1.62k | size_t wanted, n; |
437 | 1.62k | const char *equals; |
438 | | |
439 | 1.62k | o = options_get_only(global_options, "command-alias"); |
440 | 1.62k | if (o == NULL) |
441 | 0 | return (NULL); |
442 | 1.62k | wanted = strlen(name); |
443 | | |
444 | 1.62k | a = options_array_first(o); |
445 | 10.9k | while (a != NULL) { |
446 | 9.48k | ov = options_array_item_value(a); |
447 | | |
448 | 9.48k | equals = strchr(ov->string, '='); |
449 | 9.48k | if (equals != NULL) { |
450 | 9.48k | n = equals - ov->string; |
451 | 9.48k | if (n == wanted && strncmp(name, ov->string, n) == 0) |
452 | 128 | return (xstrdup(equals + 1)); |
453 | 9.48k | } |
454 | | |
455 | 9.36k | a = options_array_next(a); |
456 | 9.36k | } |
457 | 1.49k | return (NULL); |
458 | 1.62k | } |
459 | | |
460 | | /* Look up a command entry by name. */ |
461 | | const struct cmd_entry * |
462 | | cmd_find(const char *name, char **cause) |
463 | 1.62k | { |
464 | 1.62k | const struct cmd_entry **loop, *entry, *found = NULL; |
465 | 1.62k | int ambiguous; |
466 | 1.62k | char s[8192]; |
467 | | |
468 | 1.62k | ambiguous = 0; |
469 | 113k | for (loop = cmd_table; *loop != NULL; loop++) { |
470 | 113k | entry = *loop; |
471 | 113k | if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { |
472 | 1.31k | ambiguous = 0; |
473 | 1.31k | found = entry; |
474 | 1.31k | break; |
475 | 1.31k | } |
476 | | |
477 | 112k | if (strncmp(entry->name, name, strlen(name)) != 0) |
478 | 108k | continue; |
479 | 3.76k | if (found != NULL) |
480 | 2.32k | ambiguous = 1; |
481 | 3.76k | found = entry; |
482 | | |
483 | 3.76k | if (strcmp(entry->name, name) == 0) |
484 | 136 | break; |
485 | 3.76k | } |
486 | 1.62k | if (ambiguous) |
487 | 1 | goto ambiguous; |
488 | 1.62k | if (found == NULL) { |
489 | 25 | xasprintf(cause, "unknown command: %s", name); |
490 | 25 | return (NULL); |
491 | 25 | } |
492 | 1.59k | return (found); |
493 | | |
494 | 1 | ambiguous: |
495 | 1 | *s = '\0'; |
496 | 93 | for (loop = cmd_table; *loop != NULL; loop++) { |
497 | 92 | entry = *loop; |
498 | 92 | if (strncmp(entry->name, name, strlen(name)) != 0) |
499 | 90 | continue; |
500 | 2 | if (strlcat(s, entry->name, sizeof s) >= sizeof s) |
501 | 0 | break; |
502 | 2 | if (strlcat(s, ", ", sizeof s) >= sizeof s) |
503 | 0 | break; |
504 | 2 | } |
505 | 1 | s[strlen(s) - 2] = '\0'; |
506 | 1 | xasprintf(cause, "ambiguous command: %s, could be: %s", name, s); |
507 | 1 | return (NULL); |
508 | 1.62k | } |
509 | | |
510 | | /* Parse a single command from an argument vector. */ |
511 | | struct cmd * |
512 | | cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, |
513 | | int parse_flags, char **cause) |
514 | 1.62k | { |
515 | 1.62k | const struct cmd_entry *entry; |
516 | 1.62k | struct cmd *cmd; |
517 | 1.62k | struct args *args; |
518 | 1.62k | char *error = NULL; |
519 | | |
520 | 1.62k | if (count == 0 || values[0].type != ARGS_STRING) { |
521 | 0 | xasprintf(cause, "no command"); |
522 | 0 | return (NULL); |
523 | 0 | } |
524 | 1.62k | entry = cmd_find(values[0].string, cause); |
525 | 1.62k | if (entry == NULL) |
526 | 26 | return (NULL); |
527 | | |
528 | 1.59k | args = args_parse(&entry->args, values, count, &error); |
529 | 1.59k | if (args == NULL && error == NULL) { |
530 | 0 | xasprintf(cause, "usage: %s %s", entry->name, entry->usage); |
531 | 0 | return (NULL); |
532 | 0 | } |
533 | 1.59k | if (args == NULL) { |
534 | 17 | xasprintf(cause, "command %s: %s", entry->name, error); |
535 | 17 | free(error); |
536 | 17 | return (NULL); |
537 | 17 | } |
538 | | |
539 | 1.57k | cmd = xcalloc(1, sizeof *cmd); |
540 | 1.57k | cmd->entry = entry; |
541 | 1.57k | cmd->args = args; |
542 | 1.57k | cmd->parse_flags = parse_flags; |
543 | | |
544 | 1.57k | if (file != NULL) |
545 | 0 | cmd->file = xstrdup(file); |
546 | 1.57k | cmd->line = line; |
547 | | |
548 | 1.57k | return (cmd); |
549 | 1.59k | } |
550 | | |
551 | | /* Free a command. */ |
552 | | void |
553 | | cmd_free(struct cmd *cmd) |
554 | 1.57k | { |
555 | 1.57k | free(cmd->file); |
556 | | |
557 | 1.57k | args_free(cmd->args); |
558 | 1.57k | free(cmd); |
559 | 1.57k | } |
560 | | |
561 | | /* Copy a command. */ |
562 | | struct cmd * |
563 | | cmd_copy(struct cmd *cmd, int argc, char **argv) |
564 | 0 | { |
565 | 0 | struct cmd *new_cmd; |
566 | |
|
567 | 0 | new_cmd = xcalloc(1, sizeof *new_cmd); |
568 | 0 | new_cmd->entry = cmd->entry; |
569 | 0 | new_cmd->args = args_copy(cmd->args, argc, argv); |
570 | |
|
571 | 0 | if (cmd->file != NULL) |
572 | 0 | new_cmd->file = xstrdup(cmd->file); |
573 | 0 | new_cmd->line = cmd->line; |
574 | |
|
575 | 0 | return (new_cmd); |
576 | 0 | } |
577 | | |
578 | | /* Get a command as a string. */ |
579 | | char * |
580 | | cmd_print(struct cmd *cmd) |
581 | 1.27k | { |
582 | 1.27k | char *out, *s; |
583 | | |
584 | 1.27k | s = args_print(cmd->args); |
585 | 1.27k | if (*s != '\0') |
586 | 869 | xasprintf(&out, "%s %s", cmd->entry->name, s); |
587 | 409 | else |
588 | 409 | out = xstrdup(cmd->entry->name); |
589 | 1.27k | free(s); |
590 | | |
591 | 1.27k | return (out); |
592 | 1.27k | } |
593 | | |
594 | | /* Create a new command list. */ |
595 | | struct cmd_list * |
596 | | cmd_list_new(void) |
597 | 3.50k | { |
598 | 3.50k | struct cmd_list *cmdlist; |
599 | | |
600 | 3.50k | cmdlist = xcalloc(1, sizeof *cmdlist); |
601 | 3.50k | cmdlist->references = 1; |
602 | 3.50k | cmdlist->group = cmd_list_next_group++; |
603 | 3.50k | cmdlist->list = xcalloc(1, sizeof *cmdlist->list); |
604 | 3.50k | TAILQ_INIT(cmdlist->list); |
605 | 3.50k | return (cmdlist); |
606 | 3.50k | } |
607 | | |
608 | | /* Append a command to a command list. */ |
609 | | void |
610 | | cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) |
611 | 1.57k | { |
612 | 1.57k | cmd->group = cmdlist->group; |
613 | 1.57k | TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); |
614 | 1.57k | } |
615 | | |
616 | | /* Append all commands from one list to another. */ |
617 | | void |
618 | | cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from) |
619 | 1.70k | { |
620 | 1.70k | struct cmd *cmd; |
621 | | |
622 | 1.70k | TAILQ_FOREACH(cmd, from->list, qentry) |
623 | 1.70k | cmd->group = cmdlist->group; |
624 | 1.70k | TAILQ_CONCAT(cmdlist->list, from->list, qentry); |
625 | 1.70k | } |
626 | | |
627 | | /* Move all commands from one command list to another. */ |
628 | | void |
629 | | cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) |
630 | 1.52k | { |
631 | 1.52k | TAILQ_CONCAT(cmdlist->list, from->list, qentry); |
632 | 1.52k | cmdlist->group = cmd_list_next_group++; |
633 | 1.52k | } |
634 | | |
635 | | /* Free a command list. */ |
636 | | void |
637 | | cmd_list_free(struct cmd_list *cmdlist) |
638 | 3.63k | { |
639 | 3.63k | struct cmd *cmd, *cmd1; |
640 | | |
641 | 3.63k | if (--cmdlist->references != 0) |
642 | 139 | return; |
643 | | |
644 | 3.49k | TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) { |
645 | 1.57k | TAILQ_REMOVE(cmdlist->list, cmd, qentry); |
646 | 1.57k | cmd_free(cmd); |
647 | 1.57k | } |
648 | 3.49k | free(cmdlist->list); |
649 | 3.49k | free(cmdlist); |
650 | 3.49k | } |
651 | | |
652 | | /* Copy a command list, expanding %s in arguments. */ |
653 | | struct cmd_list * |
654 | | cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv) |
655 | 0 | { |
656 | 0 | struct cmd *cmd; |
657 | 0 | struct cmd_list *new_cmdlist; |
658 | 0 | struct cmd *new_cmd; |
659 | 0 | u_int group = cmdlist->group; |
660 | 0 | char *s; |
661 | |
|
662 | 0 | s = cmd_list_print(cmdlist, 0); |
663 | 0 | log_debug("%s: %s", __func__, s); |
664 | 0 | free(s); |
665 | |
|
666 | 0 | new_cmdlist = cmd_list_new(); |
667 | 0 | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { |
668 | 0 | if (cmd->group != group) { |
669 | 0 | new_cmdlist->group = cmd_list_next_group++; |
670 | 0 | group = cmd->group; |
671 | 0 | } |
672 | 0 | new_cmd = cmd_copy(cmd, argc, argv); |
673 | 0 | cmd_list_append(new_cmdlist, new_cmd); |
674 | 0 | } |
675 | |
|
676 | 0 | s = cmd_list_print(new_cmdlist, 0); |
677 | 0 | log_debug("%s: %s", __func__, s); |
678 | 0 | free(s); |
679 | |
|
680 | 0 | return (new_cmdlist); |
681 | 0 | } |
682 | | |
683 | | /* Get a command list as a string. */ |
684 | | char * |
685 | | cmd_list_print(const struct cmd_list *cmdlist, int flags) |
686 | 879 | { |
687 | 879 | struct cmd *cmd, *next; |
688 | 879 | char *buf, *this; |
689 | 879 | size_t len; |
690 | 879 | const char *separator; |
691 | 879 | int escaped = flags & CMD_LIST_PRINT_ESCAPED; |
692 | 879 | int no_groups = flags & CMD_LIST_PRINT_NO_GROUPS; |
693 | 879 | const char *single_separator = escaped ? " \\; " : " ; "; |
694 | 879 | const char *double_separator = escaped ? " \\;\\; " : " ;; "; |
695 | | |
696 | 879 | len = 1; |
697 | 879 | buf = xcalloc(1, len); |
698 | | |
699 | 1.27k | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { |
700 | 1.27k | this = cmd_print(cmd); |
701 | | |
702 | 1.27k | len += strlen(this) + 6; |
703 | 1.27k | buf = xrealloc(buf, len); |
704 | | |
705 | 1.27k | strlcat(buf, this, len); |
706 | | |
707 | 1.27k | next = TAILQ_NEXT(cmd, qentry); |
708 | 1.27k | if (next != NULL) { |
709 | 665 | if (!no_groups && cmd->group != next->group) |
710 | 665 | separator = double_separator; |
711 | 0 | else |
712 | 0 | separator = single_separator; |
713 | 665 | strlcat(buf, separator, len); |
714 | 665 | } |
715 | | |
716 | 1.27k | free(this); |
717 | 1.27k | } |
718 | | |
719 | 879 | return (buf); |
720 | 879 | } |
721 | | |
722 | | /* Get first command in list. */ |
723 | | struct cmd * |
724 | | cmd_list_first(struct cmd_list *cmdlist) |
725 | 0 | { |
726 | 0 | return (TAILQ_FIRST(cmdlist->list)); |
727 | 0 | } |
728 | | |
729 | | /* Get next command in list. */ |
730 | | struct cmd * |
731 | | cmd_list_next(struct cmd *cmd) |
732 | 0 | { |
733 | 0 | return (TAILQ_NEXT(cmd, qentry)); |
734 | 0 | } |
735 | | |
736 | | /* Do all of the commands in this command list have this flag? */ |
737 | | int |
738 | | cmd_list_all_have(struct cmd_list *cmdlist, int flag) |
739 | 0 | { |
740 | 0 | struct cmd *cmd; |
741 | |
|
742 | 0 | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { |
743 | 0 | if (~cmd->entry->flags & flag) |
744 | 0 | return (0); |
745 | 0 | } |
746 | 0 | return (1); |
747 | 0 | } |
748 | | |
749 | | /* Do any of the commands in this command list have this flag? */ |
750 | | int |
751 | | cmd_list_any_have(struct cmd_list *cmdlist, int flag) |
752 | 0 | { |
753 | 0 | struct cmd *cmd; |
754 | |
|
755 | 0 | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { |
756 | 0 | if (cmd->entry->flags & flag) |
757 | 0 | return (1); |
758 | 0 | } |
759 | 0 | return (0); |
760 | 0 | } |
761 | | |
762 | | /* Adjust current mouse position for a pane. */ |
763 | | int |
764 | | cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, |
765 | | u_int *yp, int last) |
766 | 0 | { |
767 | 0 | u_int x, y; |
768 | |
|
769 | 0 | if (last) { |
770 | 0 | x = m->lx + m->ox; |
771 | 0 | y = m->ly + m->oy; |
772 | 0 | } else { |
773 | 0 | x = m->x + m->ox; |
774 | 0 | y = m->y + m->oy; |
775 | 0 | } |
776 | 0 | log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); |
777 | |
|
778 | 0 | if (m->statusat == 0 && y >= m->statuslines) |
779 | 0 | y -= m->statuslines; |
780 | |
|
781 | 0 | if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx) |
782 | 0 | return (-1); |
783 | 0 | if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy) |
784 | 0 | return (-1); |
785 | | |
786 | 0 | if (xp != NULL) |
787 | 0 | *xp = x - wp->xoff; |
788 | 0 | if (yp != NULL) |
789 | 0 | *yp = y - wp->yoff; |
790 | 0 | return (0); |
791 | 0 | } |
792 | | |
793 | | /* Get current mouse window if any. */ |
794 | | struct winlink * |
795 | | cmd_mouse_window(struct mouse_event *m, struct session **sp) |
796 | 0 | { |
797 | 0 | struct session *s; |
798 | 0 | struct window *w; |
799 | 0 | struct winlink *wl; |
800 | |
|
801 | 0 | if (!m->valid) |
802 | 0 | return (NULL); |
803 | 0 | if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) |
804 | 0 | return (NULL); |
805 | 0 | if (m->w == -1) |
806 | 0 | wl = s->curw; |
807 | 0 | else { |
808 | 0 | if ((w = window_find_by_id(m->w)) == NULL) |
809 | 0 | return (NULL); |
810 | 0 | wl = winlink_find_by_window(&s->windows, w); |
811 | 0 | } |
812 | 0 | if (sp != NULL) |
813 | 0 | *sp = s; |
814 | 0 | return (wl); |
815 | 0 | } |
816 | | |
817 | | /* Get current mouse pane if any. */ |
818 | | struct window_pane * |
819 | | cmd_mouse_pane(struct mouse_event *m, struct session **sp, |
820 | | struct winlink **wlp) |
821 | 0 | { |
822 | 0 | struct winlink *wl; |
823 | 0 | struct window_pane *wp; |
824 | |
|
825 | 0 | if ((wl = cmd_mouse_window(m, sp)) == NULL) |
826 | 0 | return (NULL); |
827 | 0 | if (m->wp == -1) |
828 | 0 | wp = wl->window->active; |
829 | 0 | else { |
830 | 0 | if ((wp = window_pane_find_by_id(m->wp)) == NULL) |
831 | 0 | return (NULL); |
832 | 0 | if (!window_has_pane(wl->window, wp)) |
833 | 0 | return (NULL); |
834 | 0 | } |
835 | 0 | if (wl->window->modal != NULL && wp != wl->window->modal) |
836 | 0 | return (NULL); |
837 | | |
838 | 0 | if (wlp != NULL) |
839 | 0 | *wlp = wl; |
840 | 0 | return (wp); |
841 | 0 | } |
842 | | |
843 | | /* |
844 | | * Replace the first %% or any %idx in template by s. %% is intended for use in |
845 | | * single quotes, so ' is escaped. %%% and %idx% are for double quotes, so a |
846 | | * list of special characters is escaped. %idx is left unescaped. |
847 | | */ |
848 | | char * |
849 | | cmd_template_replace(const char *template, const char *s, int idx) |
850 | 0 | { |
851 | 0 | char ch, *buf; |
852 | 0 | const char *ptr, *cp, dquote[] = "\"\\$;~"; |
853 | 0 | int replaced; |
854 | 0 | size_t len, slen; |
855 | 0 | enum { NQ, SQ, DQ } quote; |
856 | |
|
857 | 0 | if (strchr(template, '%') == NULL) |
858 | 0 | return (xstrdup(template)); |
859 | | |
860 | 0 | buf = xmalloc(1); |
861 | 0 | *buf = '\0'; |
862 | 0 | len = 0; |
863 | 0 | replaced = 0; |
864 | |
|
865 | 0 | ptr = template; |
866 | 0 | while (*ptr != '\0') { |
867 | 0 | switch (ch = *ptr++) { |
868 | 0 | case '%': |
869 | 0 | if (*ptr >= '1' && *ptr <= '9' && *ptr - '0' == idx) { |
870 | 0 | ptr++; |
871 | 0 | quote = NQ; |
872 | 0 | if (*ptr == '%') { |
873 | 0 | quote = DQ; |
874 | 0 | ptr++; |
875 | 0 | } |
876 | 0 | } else { |
877 | 0 | if (*ptr != '%' || replaced) |
878 | 0 | break; |
879 | 0 | replaced = 1; |
880 | 0 | ptr++; |
881 | 0 | quote = SQ; |
882 | 0 | if (*ptr == '%') { |
883 | 0 | quote = DQ; |
884 | 0 | ptr++; |
885 | 0 | } |
886 | 0 | } |
887 | | |
888 | 0 | slen = strlen(s); |
889 | 0 | if (slen >= SIZE_MAX / 4 || |
890 | 0 | len > SIZE_MAX - (slen * 4) - 1) |
891 | 0 | fatalx("argument too long"); |
892 | 0 | buf = xrealloc(buf, len + (slen * 4) + 1); |
893 | 0 | for (cp = s; *cp != '\0'; cp++) { |
894 | 0 | if (quote == SQ && *cp == '\'') { |
895 | 0 | buf[len++] = '\''; |
896 | 0 | buf[len++] = '\\'; |
897 | 0 | buf[len++] = '\''; |
898 | 0 | buf[len++] = '\''; |
899 | 0 | continue; |
900 | 0 | } |
901 | 0 | if (quote == DQ && strchr(dquote, *cp) != NULL) |
902 | 0 | buf[len++] = '\\'; |
903 | 0 | buf[len++] = *cp; |
904 | 0 | } |
905 | 0 | buf[len] = '\0'; |
906 | 0 | continue; |
907 | 0 | } |
908 | 0 | if (len > SIZE_MAX - 2) |
909 | 0 | fatalx("argument too long"); |
910 | 0 | buf = xrealloc(buf, len + 2); |
911 | 0 | buf[len++] = ch; |
912 | 0 | buf[len] = '\0'; |
913 | 0 | } |
914 | | |
915 | 0 | log_debug("%s: %s -> %s", __func__, template, buf); |
916 | 0 | return (buf); |
917 | 0 | } |