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