xref: /minix3/external/bsd/tmux/dist/cmd.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek 
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek  *
6eda6f593SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek  *
10eda6f593SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek  */
18eda6f593SDavid van Moolenbroek 
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek #include <sys/time.h>
21eda6f593SDavid van Moolenbroek 
22eda6f593SDavid van Moolenbroek #include <fnmatch.h>
23*0a6a1f1dSLionel Sambuc #include <pwd.h>
24eda6f593SDavid van Moolenbroek #include <stdlib.h>
25eda6f593SDavid van Moolenbroek #include <string.h>
26eda6f593SDavid van Moolenbroek #include <unistd.h>
27eda6f593SDavid van Moolenbroek 
28eda6f593SDavid van Moolenbroek #include "tmux.h"
29eda6f593SDavid van Moolenbroek 
30eda6f593SDavid van Moolenbroek const struct cmd_entry *cmd_table[] = {
31eda6f593SDavid van Moolenbroek 	&cmd_attach_session_entry,
32eda6f593SDavid van Moolenbroek 	&cmd_bind_key_entry,
33eda6f593SDavid van Moolenbroek 	&cmd_break_pane_entry,
34eda6f593SDavid van Moolenbroek 	&cmd_capture_pane_entry,
35eda6f593SDavid van Moolenbroek 	&cmd_choose_buffer_entry,
36eda6f593SDavid van Moolenbroek 	&cmd_choose_client_entry,
37*0a6a1f1dSLionel Sambuc 	&cmd_choose_list_entry,
38eda6f593SDavid van Moolenbroek 	&cmd_choose_session_entry,
39*0a6a1f1dSLionel Sambuc 	&cmd_choose_tree_entry,
40eda6f593SDavid van Moolenbroek 	&cmd_choose_window_entry,
41eda6f593SDavid van Moolenbroek 	&cmd_clear_history_entry,
42eda6f593SDavid van Moolenbroek 	&cmd_clock_mode_entry,
43eda6f593SDavid van Moolenbroek 	&cmd_command_prompt_entry,
44eda6f593SDavid van Moolenbroek 	&cmd_confirm_before_entry,
45eda6f593SDavid van Moolenbroek 	&cmd_copy_mode_entry,
46eda6f593SDavid van Moolenbroek 	&cmd_delete_buffer_entry,
47eda6f593SDavid van Moolenbroek 	&cmd_detach_client_entry,
48eda6f593SDavid van Moolenbroek 	&cmd_display_message_entry,
49eda6f593SDavid van Moolenbroek 	&cmd_display_panes_entry,
50eda6f593SDavid van Moolenbroek 	&cmd_find_window_entry,
51eda6f593SDavid van Moolenbroek 	&cmd_has_session_entry,
52eda6f593SDavid van Moolenbroek 	&cmd_if_shell_entry,
53eda6f593SDavid van Moolenbroek 	&cmd_join_pane_entry,
54eda6f593SDavid van Moolenbroek 	&cmd_kill_pane_entry,
55eda6f593SDavid van Moolenbroek 	&cmd_kill_server_entry,
56eda6f593SDavid van Moolenbroek 	&cmd_kill_session_entry,
57eda6f593SDavid van Moolenbroek 	&cmd_kill_window_entry,
58eda6f593SDavid van Moolenbroek 	&cmd_last_pane_entry,
59eda6f593SDavid van Moolenbroek 	&cmd_last_window_entry,
60eda6f593SDavid van Moolenbroek 	&cmd_link_window_entry,
61eda6f593SDavid van Moolenbroek 	&cmd_list_buffers_entry,
62eda6f593SDavid van Moolenbroek 	&cmd_list_clients_entry,
63eda6f593SDavid van Moolenbroek 	&cmd_list_commands_entry,
64eda6f593SDavid van Moolenbroek 	&cmd_list_keys_entry,
65eda6f593SDavid van Moolenbroek 	&cmd_list_panes_entry,
66eda6f593SDavid van Moolenbroek 	&cmd_list_sessions_entry,
67eda6f593SDavid van Moolenbroek 	&cmd_list_windows_entry,
68eda6f593SDavid van Moolenbroek 	&cmd_load_buffer_entry,
69eda6f593SDavid van Moolenbroek 	&cmd_lock_client_entry,
70eda6f593SDavid van Moolenbroek 	&cmd_lock_server_entry,
71eda6f593SDavid van Moolenbroek 	&cmd_lock_session_entry,
72*0a6a1f1dSLionel Sambuc 	&cmd_move_pane_entry,
73eda6f593SDavid van Moolenbroek 	&cmd_move_window_entry,
74eda6f593SDavid van Moolenbroek 	&cmd_new_session_entry,
75eda6f593SDavid van Moolenbroek 	&cmd_new_window_entry,
76eda6f593SDavid van Moolenbroek 	&cmd_next_layout_entry,
77eda6f593SDavid van Moolenbroek 	&cmd_next_window_entry,
78eda6f593SDavid van Moolenbroek 	&cmd_paste_buffer_entry,
79eda6f593SDavid van Moolenbroek 	&cmd_pipe_pane_entry,
80eda6f593SDavid van Moolenbroek 	&cmd_previous_layout_entry,
81eda6f593SDavid van Moolenbroek 	&cmd_previous_window_entry,
82eda6f593SDavid van Moolenbroek 	&cmd_refresh_client_entry,
83eda6f593SDavid van Moolenbroek 	&cmd_rename_session_entry,
84eda6f593SDavid van Moolenbroek 	&cmd_rename_window_entry,
85eda6f593SDavid van Moolenbroek 	&cmd_resize_pane_entry,
86eda6f593SDavid van Moolenbroek 	&cmd_respawn_pane_entry,
87eda6f593SDavid van Moolenbroek 	&cmd_respawn_window_entry,
88eda6f593SDavid van Moolenbroek 	&cmd_rotate_window_entry,
89eda6f593SDavid van Moolenbroek 	&cmd_run_shell_entry,
90eda6f593SDavid van Moolenbroek 	&cmd_save_buffer_entry,
91eda6f593SDavid van Moolenbroek 	&cmd_select_layout_entry,
92eda6f593SDavid van Moolenbroek 	&cmd_select_pane_entry,
93eda6f593SDavid van Moolenbroek 	&cmd_select_window_entry,
94eda6f593SDavid van Moolenbroek 	&cmd_send_keys_entry,
95eda6f593SDavid van Moolenbroek 	&cmd_send_prefix_entry,
96eda6f593SDavid van Moolenbroek 	&cmd_server_info_entry,
97eda6f593SDavid van Moolenbroek 	&cmd_set_buffer_entry,
98eda6f593SDavid van Moolenbroek 	&cmd_set_environment_entry,
99eda6f593SDavid van Moolenbroek 	&cmd_set_option_entry,
100eda6f593SDavid van Moolenbroek 	&cmd_set_window_option_entry,
101eda6f593SDavid van Moolenbroek 	&cmd_show_buffer_entry,
102eda6f593SDavid van Moolenbroek 	&cmd_show_environment_entry,
103eda6f593SDavid van Moolenbroek 	&cmd_show_messages_entry,
104eda6f593SDavid van Moolenbroek 	&cmd_show_options_entry,
105eda6f593SDavid van Moolenbroek 	&cmd_show_window_options_entry,
106eda6f593SDavid van Moolenbroek 	&cmd_source_file_entry,
107eda6f593SDavid van Moolenbroek 	&cmd_split_window_entry,
108eda6f593SDavid van Moolenbroek 	&cmd_start_server_entry,
109eda6f593SDavid van Moolenbroek 	&cmd_suspend_client_entry,
110eda6f593SDavid van Moolenbroek 	&cmd_swap_pane_entry,
111eda6f593SDavid van Moolenbroek 	&cmd_swap_window_entry,
112eda6f593SDavid van Moolenbroek 	&cmd_switch_client_entry,
113eda6f593SDavid van Moolenbroek 	&cmd_unbind_key_entry,
114eda6f593SDavid van Moolenbroek 	&cmd_unlink_window_entry,
115*0a6a1f1dSLionel Sambuc 	&cmd_wait_for_entry,
116eda6f593SDavid van Moolenbroek 	NULL
117eda6f593SDavid van Moolenbroek };
118eda6f593SDavid van Moolenbroek 
119*0a6a1f1dSLionel Sambuc int		 cmd_session_better(struct session *, struct session *, int);
120eda6f593SDavid van Moolenbroek struct session	*cmd_choose_session_list(struct sessionslist *);
121eda6f593SDavid van Moolenbroek struct session	*cmd_choose_session(int);
122eda6f593SDavid van Moolenbroek struct client	*cmd_choose_client(struct clients *);
123eda6f593SDavid van Moolenbroek struct client	*cmd_lookup_client(const char *);
124eda6f593SDavid van Moolenbroek struct session	*cmd_lookup_session(const char *, int *);
125*0a6a1f1dSLionel Sambuc struct session	*cmd_lookup_session_id(const char *);
126eda6f593SDavid van Moolenbroek struct winlink	*cmd_lookup_window(struct session *, const char *, int *);
127eda6f593SDavid van Moolenbroek int		 cmd_lookup_index(struct session *, const char *, int *);
128*0a6a1f1dSLionel Sambuc struct winlink	*cmd_lookup_winlink_windowid(struct session *, const char *);
129*0a6a1f1dSLionel Sambuc struct session	*cmd_window_session(struct cmd_q *, struct window *,
130*0a6a1f1dSLionel Sambuc 		    struct winlink **);
131eda6f593SDavid van Moolenbroek struct winlink	*cmd_find_window_offset(const char *, struct session *, int *);
132eda6f593SDavid van Moolenbroek int		 cmd_find_index_offset(const char *, struct session *, int *);
133eda6f593SDavid van Moolenbroek struct window_pane *cmd_find_pane_offset(const char *, struct winlink *);
134eda6f593SDavid van Moolenbroek 
135eda6f593SDavid van Moolenbroek int
cmd_pack_argv(int argc,char ** argv,char * buf,size_t len)136eda6f593SDavid van Moolenbroek cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
137eda6f593SDavid van Moolenbroek {
138eda6f593SDavid van Moolenbroek 	size_t	arglen;
139eda6f593SDavid van Moolenbroek 	int	i;
140eda6f593SDavid van Moolenbroek 
141eda6f593SDavid van Moolenbroek 	*buf = '\0';
142eda6f593SDavid van Moolenbroek 	for (i = 0; i < argc; i++) {
143eda6f593SDavid van Moolenbroek 		if (strlcpy(buf, argv[i], len) >= len)
144eda6f593SDavid van Moolenbroek 			return (-1);
145eda6f593SDavid van Moolenbroek 		arglen = strlen(argv[i]) + 1;
146eda6f593SDavid van Moolenbroek 		buf += arglen;
147eda6f593SDavid van Moolenbroek 		len -= arglen;
148eda6f593SDavid van Moolenbroek 	}
149eda6f593SDavid van Moolenbroek 
150eda6f593SDavid van Moolenbroek 	return (0);
151eda6f593SDavid van Moolenbroek }
152eda6f593SDavid van Moolenbroek 
153eda6f593SDavid van Moolenbroek int
cmd_unpack_argv(char * buf,size_t len,int argc,char *** argv)154eda6f593SDavid van Moolenbroek cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
155eda6f593SDavid van Moolenbroek {
156eda6f593SDavid van Moolenbroek 	int	i;
157eda6f593SDavid van Moolenbroek 	size_t	arglen;
158eda6f593SDavid van Moolenbroek 
159eda6f593SDavid van Moolenbroek 	if (argc == 0)
160eda6f593SDavid van Moolenbroek 		return (0);
161eda6f593SDavid van Moolenbroek 	*argv = xcalloc(argc, sizeof **argv);
162eda6f593SDavid van Moolenbroek 
163eda6f593SDavid van Moolenbroek 	buf[len - 1] = '\0';
164eda6f593SDavid van Moolenbroek 	for (i = 0; i < argc; i++) {
165eda6f593SDavid van Moolenbroek 		if (len == 0) {
166eda6f593SDavid van Moolenbroek 			cmd_free_argv(argc, *argv);
167eda6f593SDavid van Moolenbroek 			return (-1);
168eda6f593SDavid van Moolenbroek 		}
169eda6f593SDavid van Moolenbroek 
170eda6f593SDavid van Moolenbroek 		arglen = strlen(buf) + 1;
171eda6f593SDavid van Moolenbroek 		(*argv)[i] = xstrdup(buf);
172eda6f593SDavid van Moolenbroek 		buf += arglen;
173eda6f593SDavid van Moolenbroek 		len -= arglen;
174eda6f593SDavid van Moolenbroek 	}
175eda6f593SDavid van Moolenbroek 
176eda6f593SDavid van Moolenbroek 	return (0);
177eda6f593SDavid van Moolenbroek }
178eda6f593SDavid van Moolenbroek 
179eda6f593SDavid van Moolenbroek char **
cmd_copy_argv(int argc,char * const * argv)180eda6f593SDavid van Moolenbroek cmd_copy_argv(int argc, char *const *argv)
181eda6f593SDavid van Moolenbroek {
182eda6f593SDavid van Moolenbroek 	char	**new_argv;
183eda6f593SDavid van Moolenbroek 	int	  i;
184eda6f593SDavid van Moolenbroek 
185eda6f593SDavid van Moolenbroek 	if (argc == 0)
186eda6f593SDavid van Moolenbroek 		return (NULL);
187eda6f593SDavid van Moolenbroek 	new_argv = xcalloc(argc, sizeof *new_argv);
188eda6f593SDavid van Moolenbroek 	for (i = 0; i < argc; i++) {
189eda6f593SDavid van Moolenbroek 		if (argv[i] != NULL)
190eda6f593SDavid van Moolenbroek 			new_argv[i] = xstrdup(argv[i]);
191eda6f593SDavid van Moolenbroek 	}
192eda6f593SDavid van Moolenbroek 	return (new_argv);
193eda6f593SDavid van Moolenbroek }
194eda6f593SDavid van Moolenbroek 
195eda6f593SDavid van Moolenbroek void
cmd_free_argv(int argc,char ** argv)196eda6f593SDavid van Moolenbroek cmd_free_argv(int argc, char **argv)
197eda6f593SDavid van Moolenbroek {
198eda6f593SDavid van Moolenbroek 	int	i;
199eda6f593SDavid van Moolenbroek 
200eda6f593SDavid van Moolenbroek 	if (argc == 0)
201eda6f593SDavid van Moolenbroek 		return;
202*0a6a1f1dSLionel Sambuc 	for (i = 0; i < argc; i++)
203*0a6a1f1dSLionel Sambuc 		free(argv[i]);
204*0a6a1f1dSLionel Sambuc 	free(argv);
205eda6f593SDavid van Moolenbroek }
206eda6f593SDavid van Moolenbroek 
207eda6f593SDavid van Moolenbroek struct cmd *
cmd_parse(int argc,char ** argv,const char * file,u_int line,char ** cause)208*0a6a1f1dSLionel Sambuc cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
209eda6f593SDavid van Moolenbroek {
210eda6f593SDavid van Moolenbroek 	const struct cmd_entry **entryp, *entry;
211eda6f593SDavid van Moolenbroek 	struct cmd		*cmd;
212eda6f593SDavid van Moolenbroek 	struct args		*args;
213eda6f593SDavid van Moolenbroek 	char			 s[BUFSIZ];
214eda6f593SDavid van Moolenbroek 	int			 ambiguous = 0;
215eda6f593SDavid van Moolenbroek 
216eda6f593SDavid van Moolenbroek 	*cause = NULL;
217eda6f593SDavid van Moolenbroek 	if (argc == 0) {
218eda6f593SDavid van Moolenbroek 		xasprintf(cause, "no command");
219eda6f593SDavid van Moolenbroek 		return (NULL);
220eda6f593SDavid van Moolenbroek 	}
221eda6f593SDavid van Moolenbroek 
222eda6f593SDavid van Moolenbroek 	entry = NULL;
223eda6f593SDavid van Moolenbroek 	for (entryp = cmd_table; *entryp != NULL; entryp++) {
224eda6f593SDavid van Moolenbroek 		if ((*entryp)->alias != NULL &&
225eda6f593SDavid van Moolenbroek 		    strcmp((*entryp)->alias, argv[0]) == 0) {
226eda6f593SDavid van Moolenbroek 			ambiguous = 0;
227eda6f593SDavid van Moolenbroek 			entry = *entryp;
228eda6f593SDavid van Moolenbroek 			break;
229eda6f593SDavid van Moolenbroek 		}
230eda6f593SDavid van Moolenbroek 
231eda6f593SDavid van Moolenbroek 		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
232eda6f593SDavid van Moolenbroek 			continue;
233eda6f593SDavid van Moolenbroek 		if (entry != NULL)
234eda6f593SDavid van Moolenbroek 			ambiguous = 1;
235eda6f593SDavid van Moolenbroek 		entry = *entryp;
236eda6f593SDavid van Moolenbroek 
237eda6f593SDavid van Moolenbroek 		/* Bail now if an exact match. */
238eda6f593SDavid van Moolenbroek 		if (strcmp(entry->name, argv[0]) == 0)
239eda6f593SDavid van Moolenbroek 			break;
240eda6f593SDavid van Moolenbroek 	}
241eda6f593SDavid van Moolenbroek 	if (ambiguous)
242eda6f593SDavid van Moolenbroek 		goto ambiguous;
243eda6f593SDavid van Moolenbroek 	if (entry == NULL) {
244eda6f593SDavid van Moolenbroek 		xasprintf(cause, "unknown command: %s", argv[0]);
245eda6f593SDavid van Moolenbroek 		return (NULL);
246eda6f593SDavid van Moolenbroek 	}
247eda6f593SDavid van Moolenbroek 
248eda6f593SDavid van Moolenbroek 	args = args_parse(entry->args_template, argc, argv);
249eda6f593SDavid van Moolenbroek 	if (args == NULL)
250eda6f593SDavid van Moolenbroek 		goto usage;
251eda6f593SDavid van Moolenbroek 	if (entry->args_lower != -1 && args->argc < entry->args_lower)
252eda6f593SDavid van Moolenbroek 		goto usage;
253eda6f593SDavid van Moolenbroek 	if (entry->args_upper != -1 && args->argc > entry->args_upper)
254eda6f593SDavid van Moolenbroek 		goto usage;
255eda6f593SDavid van Moolenbroek 
256*0a6a1f1dSLionel Sambuc 	cmd = xcalloc(1, sizeof *cmd);
257eda6f593SDavid van Moolenbroek 	cmd->entry = entry;
258eda6f593SDavid van Moolenbroek 	cmd->args = args;
259*0a6a1f1dSLionel Sambuc 
260*0a6a1f1dSLionel Sambuc 	if (file != NULL)
261*0a6a1f1dSLionel Sambuc 		cmd->file = xstrdup(file);
262*0a6a1f1dSLionel Sambuc 	cmd->line = line;
263*0a6a1f1dSLionel Sambuc 
264eda6f593SDavid van Moolenbroek 	return (cmd);
265eda6f593SDavid van Moolenbroek 
266eda6f593SDavid van Moolenbroek ambiguous:
267eda6f593SDavid van Moolenbroek 	*s = '\0';
268eda6f593SDavid van Moolenbroek 	for (entryp = cmd_table; *entryp != NULL; entryp++) {
269eda6f593SDavid van Moolenbroek 		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
270eda6f593SDavid van Moolenbroek 			continue;
271eda6f593SDavid van Moolenbroek 		if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
272eda6f593SDavid van Moolenbroek 			break;
273eda6f593SDavid van Moolenbroek 		if (strlcat(s, ", ", sizeof s) >= sizeof s)
274eda6f593SDavid van Moolenbroek 			break;
275eda6f593SDavid van Moolenbroek 	}
276eda6f593SDavid van Moolenbroek 	s[strlen(s) - 2] = '\0';
277eda6f593SDavid van Moolenbroek 	xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
278eda6f593SDavid van Moolenbroek 	return (NULL);
279eda6f593SDavid van Moolenbroek 
280eda6f593SDavid van Moolenbroek usage:
281eda6f593SDavid van Moolenbroek 	if (args != NULL)
282eda6f593SDavid van Moolenbroek 		args_free(args);
283eda6f593SDavid van Moolenbroek 	xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
284eda6f593SDavid van Moolenbroek 	return (NULL);
285eda6f593SDavid van Moolenbroek }
286eda6f593SDavid van Moolenbroek 
287eda6f593SDavid van Moolenbroek size_t
cmd_print(struct cmd * cmd,char * buf,size_t len)288eda6f593SDavid van Moolenbroek cmd_print(struct cmd *cmd, char *buf, size_t len)
289eda6f593SDavid van Moolenbroek {
290eda6f593SDavid van Moolenbroek 	size_t	off, used;
291eda6f593SDavid van Moolenbroek 
292eda6f593SDavid van Moolenbroek 	off = xsnprintf(buf, len, "%s ", cmd->entry->name);
293*0a6a1f1dSLionel Sambuc 	if (off + 1 < len) {
294*0a6a1f1dSLionel Sambuc 		used = args_print(cmd->args, buf + off, len - off - 1);
295eda6f593SDavid van Moolenbroek 		if (used == 0)
296*0a6a1f1dSLionel Sambuc 			off--;
297*0a6a1f1dSLionel Sambuc 		else
298eda6f593SDavid van Moolenbroek 			off += used;
299eda6f593SDavid van Moolenbroek 		buf[off] = '\0';
300eda6f593SDavid van Moolenbroek 	}
301eda6f593SDavid van Moolenbroek 	return (off);
302eda6f593SDavid van Moolenbroek }
303eda6f593SDavid van Moolenbroek 
304eda6f593SDavid van Moolenbroek /*
305eda6f593SDavid van Moolenbroek  * Figure out the current session. Use: 1) the current session, if the command
306eda6f593SDavid van Moolenbroek  * context has one; 2) the most recently used session containing the pty of the
307eda6f593SDavid van Moolenbroek  * calling client, if any; 3) the session specified in the TMUX variable from
308eda6f593SDavid van Moolenbroek  * the environment (as passed from the client); 4) the most recently used
309eda6f593SDavid van Moolenbroek  * session from all sessions.
310eda6f593SDavid van Moolenbroek  */
311eda6f593SDavid van Moolenbroek struct session *
cmd_current_session(struct cmd_q * cmdq,int prefer_unattached)312*0a6a1f1dSLionel Sambuc cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
313eda6f593SDavid van Moolenbroek {
314*0a6a1f1dSLionel Sambuc 	struct client		*c = cmdq->client;
315eda6f593SDavid van Moolenbroek 	struct session		*s;
316eda6f593SDavid van Moolenbroek 	struct sessionslist	 ss;
317eda6f593SDavid van Moolenbroek 	struct winlink		*wl;
318eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
319*0a6a1f1dSLionel Sambuc 	const char		*path;
320eda6f593SDavid van Moolenbroek 	int			 found;
321eda6f593SDavid van Moolenbroek 
322*0a6a1f1dSLionel Sambuc 	if (c != NULL && c->session != NULL)
323*0a6a1f1dSLionel Sambuc 		return (c->session);
324eda6f593SDavid van Moolenbroek 
325eda6f593SDavid van Moolenbroek 	/*
326*0a6a1f1dSLionel Sambuc 	 * If the name of the calling client's pty is known, build a list of
327*0a6a1f1dSLionel Sambuc 	 * the sessions that contain it and if any choose either the first or
328*0a6a1f1dSLionel Sambuc 	 * the newest.
329eda6f593SDavid van Moolenbroek 	 */
330*0a6a1f1dSLionel Sambuc 	path = c == NULL ? NULL : c->tty.path;
331*0a6a1f1dSLionel Sambuc 	if (path != NULL) {
332eda6f593SDavid van Moolenbroek 		ARRAY_INIT(&ss);
333eda6f593SDavid van Moolenbroek 		RB_FOREACH(s, sessions, &sessions) {
334eda6f593SDavid van Moolenbroek 			found = 0;
335eda6f593SDavid van Moolenbroek 			RB_FOREACH(wl, winlinks, &s->windows) {
336eda6f593SDavid van Moolenbroek 				TAILQ_FOREACH(wp, &wl->window->panes, entry) {
337*0a6a1f1dSLionel Sambuc 					if (strcmp(wp->tty, path) == 0) {
338eda6f593SDavid van Moolenbroek 						found = 1;
339eda6f593SDavid van Moolenbroek 						break;
340eda6f593SDavid van Moolenbroek 					}
341eda6f593SDavid van Moolenbroek 				}
342eda6f593SDavid van Moolenbroek 				if (found)
343eda6f593SDavid van Moolenbroek 					break;
344eda6f593SDavid van Moolenbroek 			}
345eda6f593SDavid van Moolenbroek 			if (found)
346eda6f593SDavid van Moolenbroek 				ARRAY_ADD(&ss, s);
347eda6f593SDavid van Moolenbroek 		}
348eda6f593SDavid van Moolenbroek 
349eda6f593SDavid van Moolenbroek 		s = cmd_choose_session_list(&ss);
350eda6f593SDavid van Moolenbroek 		ARRAY_FREE(&ss);
351eda6f593SDavid van Moolenbroek 		if (s != NULL)
352eda6f593SDavid van Moolenbroek 			return (s);
353eda6f593SDavid van Moolenbroek 	}
354eda6f593SDavid van Moolenbroek 
355*0a6a1f1dSLionel Sambuc 	return (cmd_choose_session(prefer_unattached));
356eda6f593SDavid van Moolenbroek }
357eda6f593SDavid van Moolenbroek 
358*0a6a1f1dSLionel Sambuc /* Is this session better? */
359*0a6a1f1dSLionel Sambuc int
cmd_session_better(struct session * s,struct session * best,int prefer_unattached)360*0a6a1f1dSLionel Sambuc cmd_session_better(struct session *s, struct session *best,
361*0a6a1f1dSLionel Sambuc     int prefer_unattached)
362*0a6a1f1dSLionel Sambuc {
363*0a6a1f1dSLionel Sambuc 	if (best == NULL)
364*0a6a1f1dSLionel Sambuc 		return (1);
365*0a6a1f1dSLionel Sambuc 	if (prefer_unattached) {
366*0a6a1f1dSLionel Sambuc 		if (!(best->flags & SESSION_UNATTACHED) &&
367*0a6a1f1dSLionel Sambuc 		    (s->flags & SESSION_UNATTACHED))
368*0a6a1f1dSLionel Sambuc 			return (1);
369*0a6a1f1dSLionel Sambuc 		else if ((best->flags & SESSION_UNATTACHED) &&
370*0a6a1f1dSLionel Sambuc 		    !(s->flags & SESSION_UNATTACHED))
371*0a6a1f1dSLionel Sambuc 			return (0);
372*0a6a1f1dSLionel Sambuc 	}
373*0a6a1f1dSLionel Sambuc 	return (timercmp(&s->activity_time, &best->activity_time, >));
374eda6f593SDavid van Moolenbroek }
375eda6f593SDavid van Moolenbroek 
376eda6f593SDavid van Moolenbroek /*
377eda6f593SDavid van Moolenbroek  * Find the most recently used session, preferring unattached if the flag is
378eda6f593SDavid van Moolenbroek  * set.
379eda6f593SDavid van Moolenbroek  */
380eda6f593SDavid van Moolenbroek struct session *
cmd_choose_session(int prefer_unattached)381eda6f593SDavid van Moolenbroek cmd_choose_session(int prefer_unattached)
382eda6f593SDavid van Moolenbroek {
383*0a6a1f1dSLionel Sambuc 	struct session	*s, *best;
384eda6f593SDavid van Moolenbroek 
385*0a6a1f1dSLionel Sambuc 	best = NULL;
386eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
387*0a6a1f1dSLionel Sambuc 		if (cmd_session_better(s, best, prefer_unattached))
388*0a6a1f1dSLionel Sambuc 			best = s;
389eda6f593SDavid van Moolenbroek 	}
390*0a6a1f1dSLionel Sambuc 	return (best);
391eda6f593SDavid van Moolenbroek }
392eda6f593SDavid van Moolenbroek 
393eda6f593SDavid van Moolenbroek /* Find the most recently used session from a list. */
394eda6f593SDavid van Moolenbroek struct session *
cmd_choose_session_list(struct sessionslist * ss)395eda6f593SDavid van Moolenbroek cmd_choose_session_list(struct sessionslist *ss)
396eda6f593SDavid van Moolenbroek {
397eda6f593SDavid van Moolenbroek 	struct session	*s, *sbest;
398eda6f593SDavid van Moolenbroek 	struct timeval	*tv = NULL;
399eda6f593SDavid van Moolenbroek 	u_int		 i;
400eda6f593SDavid van Moolenbroek 
401eda6f593SDavid van Moolenbroek 	sbest = NULL;
402eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(ss); i++) {
403eda6f593SDavid van Moolenbroek 		if ((s = ARRAY_ITEM(ss, i)) == NULL)
404eda6f593SDavid van Moolenbroek 			continue;
405eda6f593SDavid van Moolenbroek 
406eda6f593SDavid van Moolenbroek 		if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
407eda6f593SDavid van Moolenbroek 			sbest = s;
408eda6f593SDavid van Moolenbroek 			tv = &s->activity_time;
409eda6f593SDavid van Moolenbroek 		}
410eda6f593SDavid van Moolenbroek 	}
411eda6f593SDavid van Moolenbroek 
412eda6f593SDavid van Moolenbroek 	return (sbest);
413eda6f593SDavid van Moolenbroek }
414eda6f593SDavid van Moolenbroek 
415eda6f593SDavid van Moolenbroek /*
416eda6f593SDavid van Moolenbroek  * Find the current client. First try the current client if set, then pick the
417eda6f593SDavid van Moolenbroek  * most recently used of the clients attached to the current session if any,
418eda6f593SDavid van Moolenbroek  * then of all clients.
419eda6f593SDavid van Moolenbroek  */
420eda6f593SDavid van Moolenbroek struct client *
cmd_current_client(struct cmd_q * cmdq)421*0a6a1f1dSLionel Sambuc cmd_current_client(struct cmd_q *cmdq)
422eda6f593SDavid van Moolenbroek {
423eda6f593SDavid van Moolenbroek 	struct session		*s;
424eda6f593SDavid van Moolenbroek 	struct client		*c;
425eda6f593SDavid van Moolenbroek 	struct clients		 cc;
426eda6f593SDavid van Moolenbroek 	u_int			 i;
427eda6f593SDavid van Moolenbroek 
428*0a6a1f1dSLionel Sambuc 	if (cmdq->client != NULL && cmdq->client->session != NULL)
429*0a6a1f1dSLionel Sambuc 		return (cmdq->client);
430eda6f593SDavid van Moolenbroek 
431eda6f593SDavid van Moolenbroek 	/*
432eda6f593SDavid van Moolenbroek 	 * No current client set. Find the current session and return the
433eda6f593SDavid van Moolenbroek 	 * newest of its clients.
434eda6f593SDavid van Moolenbroek 	 */
435*0a6a1f1dSLionel Sambuc 	s = cmd_current_session(cmdq, 0);
436eda6f593SDavid van Moolenbroek 	if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
437eda6f593SDavid van Moolenbroek 		ARRAY_INIT(&cc);
438eda6f593SDavid van Moolenbroek 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
439eda6f593SDavid van Moolenbroek 			if ((c = ARRAY_ITEM(&clients, i)) == NULL)
440eda6f593SDavid van Moolenbroek 				continue;
441eda6f593SDavid van Moolenbroek 			if (s == c->session)
442eda6f593SDavid van Moolenbroek 				ARRAY_ADD(&cc, c);
443eda6f593SDavid van Moolenbroek 		}
444eda6f593SDavid van Moolenbroek 
445eda6f593SDavid van Moolenbroek 		c = cmd_choose_client(&cc);
446eda6f593SDavid van Moolenbroek 		ARRAY_FREE(&cc);
447eda6f593SDavid van Moolenbroek 		if (c != NULL)
448eda6f593SDavid van Moolenbroek 			return (c);
449eda6f593SDavid van Moolenbroek 	}
450eda6f593SDavid van Moolenbroek 
451eda6f593SDavid van Moolenbroek 	return (cmd_choose_client(&clients));
452eda6f593SDavid van Moolenbroek }
453eda6f593SDavid van Moolenbroek 
454eda6f593SDavid van Moolenbroek /* Choose the most recently used client from a list. */
455eda6f593SDavid van Moolenbroek struct client *
cmd_choose_client(struct clients * cc)456eda6f593SDavid van Moolenbroek cmd_choose_client(struct clients *cc)
457eda6f593SDavid van Moolenbroek {
458eda6f593SDavid van Moolenbroek 	struct client	*c, *cbest;
459eda6f593SDavid van Moolenbroek 	struct timeval	*tv = NULL;
460eda6f593SDavid van Moolenbroek 	u_int		 i;
461eda6f593SDavid van Moolenbroek 
462eda6f593SDavid van Moolenbroek 	cbest = NULL;
463eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(cc); i++) {
464eda6f593SDavid van Moolenbroek 		if ((c = ARRAY_ITEM(cc, i)) == NULL)
465eda6f593SDavid van Moolenbroek 			continue;
466eda6f593SDavid van Moolenbroek 		if (c->session == NULL)
467eda6f593SDavid van Moolenbroek 			continue;
468eda6f593SDavid van Moolenbroek 
469eda6f593SDavid van Moolenbroek 		if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
470eda6f593SDavid van Moolenbroek 			cbest = c;
471eda6f593SDavid van Moolenbroek 			tv = &c->activity_time;
472eda6f593SDavid van Moolenbroek 		}
473eda6f593SDavid van Moolenbroek 	}
474eda6f593SDavid van Moolenbroek 
475eda6f593SDavid van Moolenbroek 	return (cbest);
476eda6f593SDavid van Moolenbroek }
477eda6f593SDavid van Moolenbroek 
478eda6f593SDavid van Moolenbroek /* Find the target client or report an error and return NULL. */
479eda6f593SDavid van Moolenbroek struct client *
cmd_find_client(struct cmd_q * cmdq,const char * arg,int quiet)480*0a6a1f1dSLionel Sambuc cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet)
481eda6f593SDavid van Moolenbroek {
482eda6f593SDavid van Moolenbroek 	struct client	*c;
483eda6f593SDavid van Moolenbroek 	char		*tmparg;
484eda6f593SDavid van Moolenbroek 	size_t		 arglen;
485eda6f593SDavid van Moolenbroek 
486eda6f593SDavid van Moolenbroek 	/* A NULL argument means the current client. */
487*0a6a1f1dSLionel Sambuc 	if (arg == NULL) {
488*0a6a1f1dSLionel Sambuc 		c = cmd_current_client(cmdq);
489*0a6a1f1dSLionel Sambuc 		if (c == NULL && !quiet)
490*0a6a1f1dSLionel Sambuc 			cmdq_error(cmdq, "no clients");
491*0a6a1f1dSLionel Sambuc 		return (c);
492*0a6a1f1dSLionel Sambuc 	}
493eda6f593SDavid van Moolenbroek 	tmparg = xstrdup(arg);
494eda6f593SDavid van Moolenbroek 
495eda6f593SDavid van Moolenbroek 	/* Trim a single trailing colon if any. */
496eda6f593SDavid van Moolenbroek 	arglen = strlen(tmparg);
497eda6f593SDavid van Moolenbroek 	if (arglen != 0 && tmparg[arglen - 1] == ':')
498eda6f593SDavid van Moolenbroek 		tmparg[arglen - 1] = '\0';
499eda6f593SDavid van Moolenbroek 
500eda6f593SDavid van Moolenbroek 	/* Find the client, if any. */
501eda6f593SDavid van Moolenbroek 	c = cmd_lookup_client(tmparg);
502eda6f593SDavid van Moolenbroek 
503eda6f593SDavid van Moolenbroek 	/* If no client found, report an error. */
504*0a6a1f1dSLionel Sambuc 	if (c == NULL && !quiet)
505*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "client not found: %s", tmparg);
506eda6f593SDavid van Moolenbroek 
507*0a6a1f1dSLionel Sambuc 	free(tmparg);
508eda6f593SDavid van Moolenbroek 	return (c);
509eda6f593SDavid van Moolenbroek }
510eda6f593SDavid van Moolenbroek 
511eda6f593SDavid van Moolenbroek /*
512eda6f593SDavid van Moolenbroek  * Lookup a client by device path. Either of a full match and a match without a
513eda6f593SDavid van Moolenbroek  * leading _PATH_DEV ("/dev/") is accepted.
514eda6f593SDavid van Moolenbroek  */
515eda6f593SDavid van Moolenbroek struct client *
cmd_lookup_client(const char * name)516eda6f593SDavid van Moolenbroek cmd_lookup_client(const char *name)
517eda6f593SDavid van Moolenbroek {
518eda6f593SDavid van Moolenbroek 	struct client	*c;
519eda6f593SDavid van Moolenbroek 	const char	*path;
520eda6f593SDavid van Moolenbroek 	u_int		 i;
521eda6f593SDavid van Moolenbroek 
522eda6f593SDavid van Moolenbroek 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
523eda6f593SDavid van Moolenbroek 		c = ARRAY_ITEM(&clients, i);
524*0a6a1f1dSLionel Sambuc 		if (c == NULL || c->session == NULL || c->tty.path == NULL)
525eda6f593SDavid van Moolenbroek 			continue;
526eda6f593SDavid van Moolenbroek 		path = c->tty.path;
527eda6f593SDavid van Moolenbroek 
528eda6f593SDavid van Moolenbroek 		/* Check for exact matches. */
529eda6f593SDavid van Moolenbroek 		if (strcmp(name, path) == 0)
530eda6f593SDavid van Moolenbroek 			return (c);
531eda6f593SDavid van Moolenbroek 
532eda6f593SDavid van Moolenbroek 		/* Check without leading /dev if present. */
533eda6f593SDavid van Moolenbroek 		if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
534eda6f593SDavid van Moolenbroek 			continue;
535eda6f593SDavid van Moolenbroek 		if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
536eda6f593SDavid van Moolenbroek 			return (c);
537eda6f593SDavid van Moolenbroek 	}
538eda6f593SDavid van Moolenbroek 
539eda6f593SDavid van Moolenbroek 	return (NULL);
540eda6f593SDavid van Moolenbroek }
541eda6f593SDavid van Moolenbroek 
542*0a6a1f1dSLionel Sambuc /* Find the target session or report an error and return NULL. */
543*0a6a1f1dSLionel Sambuc struct session *
cmd_lookup_session_id(const char * arg)544*0a6a1f1dSLionel Sambuc cmd_lookup_session_id(const char *arg)
545*0a6a1f1dSLionel Sambuc {
546*0a6a1f1dSLionel Sambuc 	char	*endptr;
547*0a6a1f1dSLionel Sambuc 	long	 id;
548*0a6a1f1dSLionel Sambuc 
549*0a6a1f1dSLionel Sambuc 	if (arg[0] != '$')
550*0a6a1f1dSLionel Sambuc 		return (NULL);
551*0a6a1f1dSLionel Sambuc 	id = strtol(arg + 1, &endptr, 10);
552*0a6a1f1dSLionel Sambuc 	if (arg[1] != '\0' && *endptr == '\0')
553*0a6a1f1dSLionel Sambuc 		return (session_find_by_id(id));
554*0a6a1f1dSLionel Sambuc 	return (NULL);
555*0a6a1f1dSLionel Sambuc }
556*0a6a1f1dSLionel Sambuc 
557eda6f593SDavid van Moolenbroek /* Lookup a session by name. If no session is found, NULL is returned. */
558eda6f593SDavid van Moolenbroek struct session *
cmd_lookup_session(const char * name,int * ambiguous)559eda6f593SDavid van Moolenbroek cmd_lookup_session(const char *name, int *ambiguous)
560eda6f593SDavid van Moolenbroek {
561eda6f593SDavid van Moolenbroek 	struct session	*s, *sfound;
562eda6f593SDavid van Moolenbroek 
563eda6f593SDavid van Moolenbroek 	*ambiguous = 0;
564eda6f593SDavid van Moolenbroek 
565*0a6a1f1dSLionel Sambuc 	/* Look for $id first. */
566*0a6a1f1dSLionel Sambuc 	if ((s = cmd_lookup_session_id(name)) != NULL)
567*0a6a1f1dSLionel Sambuc 		return (s);
568*0a6a1f1dSLionel Sambuc 
569eda6f593SDavid van Moolenbroek 	/*
570eda6f593SDavid van Moolenbroek 	 * Look for matches. First look for exact matches - session names must
571eda6f593SDavid van Moolenbroek 	 * be unique so an exact match can't be ambigious and can just be
572eda6f593SDavid van Moolenbroek 	 * returned.
573eda6f593SDavid van Moolenbroek 	 */
574eda6f593SDavid van Moolenbroek 	if ((s = session_find(name)) != NULL)
575eda6f593SDavid van Moolenbroek 		return (s);
576eda6f593SDavid van Moolenbroek 
577eda6f593SDavid van Moolenbroek 	/*
578eda6f593SDavid van Moolenbroek 	 * Otherwise look for partial matches, returning early if it is found to
579eda6f593SDavid van Moolenbroek 	 * be ambiguous.
580eda6f593SDavid van Moolenbroek 	 */
581eda6f593SDavid van Moolenbroek 	sfound = NULL;
582eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
583eda6f593SDavid van Moolenbroek 		if (strncmp(name, s->name, strlen(name)) == 0 ||
584eda6f593SDavid van Moolenbroek 		    fnmatch(name, s->name, 0) == 0) {
585eda6f593SDavid van Moolenbroek 			if (sfound != NULL) {
586eda6f593SDavid van Moolenbroek 				*ambiguous = 1;
587eda6f593SDavid van Moolenbroek 				return (NULL);
588eda6f593SDavid van Moolenbroek 			}
589eda6f593SDavid van Moolenbroek 			sfound = s;
590eda6f593SDavid van Moolenbroek 		}
591eda6f593SDavid van Moolenbroek 	}
592eda6f593SDavid van Moolenbroek 	return (sfound);
593eda6f593SDavid van Moolenbroek }
594eda6f593SDavid van Moolenbroek 
595eda6f593SDavid van Moolenbroek /*
596eda6f593SDavid van Moolenbroek  * Lookup a window or return -1 if not found or ambigious. First try as an
597eda6f593SDavid van Moolenbroek  * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
598eda6f593SDavid van Moolenbroek  * idx if the window index is a valid number but there is no window with that
599eda6f593SDavid van Moolenbroek  * index.
600eda6f593SDavid van Moolenbroek  */
601eda6f593SDavid van Moolenbroek struct winlink *
cmd_lookup_window(struct session * s,const char * name,int * ambiguous)602eda6f593SDavid van Moolenbroek cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
603eda6f593SDavid van Moolenbroek {
604eda6f593SDavid van Moolenbroek 	struct winlink	*wl, *wlfound;
605eda6f593SDavid van Moolenbroek 	const char	*errstr;
606eda6f593SDavid van Moolenbroek 	u_int		 idx;
607eda6f593SDavid van Moolenbroek 
608eda6f593SDavid van Moolenbroek 	*ambiguous = 0;
609eda6f593SDavid van Moolenbroek 
610*0a6a1f1dSLionel Sambuc 	/* Try as a window id. */
611*0a6a1f1dSLionel Sambuc 	if ((wl = cmd_lookup_winlink_windowid(s, name)) != NULL)
612*0a6a1f1dSLionel Sambuc 	    return (wl);
613*0a6a1f1dSLionel Sambuc 
614eda6f593SDavid van Moolenbroek 	/* First see if this is a valid window index in this session. */
615eda6f593SDavid van Moolenbroek 	idx = strtonum(name, 0, INT_MAX, &errstr);
616eda6f593SDavid van Moolenbroek 	if (errstr == NULL) {
617eda6f593SDavid van Moolenbroek 		if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
618eda6f593SDavid van Moolenbroek 			return (wl);
619eda6f593SDavid van Moolenbroek 	}
620eda6f593SDavid van Moolenbroek 
621eda6f593SDavid van Moolenbroek 	/* Look for exact matches, error if more than one. */
622eda6f593SDavid van Moolenbroek 	wlfound = NULL;
623eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
624eda6f593SDavid van Moolenbroek 		if (strcmp(name, wl->window->name) == 0) {
625eda6f593SDavid van Moolenbroek 			if (wlfound != NULL) {
626eda6f593SDavid van Moolenbroek 				*ambiguous = 1;
627eda6f593SDavid van Moolenbroek 				return (NULL);
628eda6f593SDavid van Moolenbroek 			}
629eda6f593SDavid van Moolenbroek 			wlfound = wl;
630eda6f593SDavid van Moolenbroek 		}
631eda6f593SDavid van Moolenbroek 	}
632eda6f593SDavid van Moolenbroek 	if (wlfound != NULL)
633eda6f593SDavid van Moolenbroek 		return (wlfound);
634eda6f593SDavid van Moolenbroek 
635eda6f593SDavid van Moolenbroek 	/* Now look for pattern matches, again error if multiple. */
636eda6f593SDavid van Moolenbroek 	wlfound = NULL;
637eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
638eda6f593SDavid van Moolenbroek 		if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
639eda6f593SDavid van Moolenbroek 		    fnmatch(name, wl->window->name, 0) == 0) {
640eda6f593SDavid van Moolenbroek 			if (wlfound != NULL) {
641eda6f593SDavid van Moolenbroek 				*ambiguous = 1;
642eda6f593SDavid van Moolenbroek 				return (NULL);
643eda6f593SDavid van Moolenbroek 			}
644eda6f593SDavid van Moolenbroek 			wlfound = wl;
645eda6f593SDavid van Moolenbroek 		}
646eda6f593SDavid van Moolenbroek 	}
647eda6f593SDavid van Moolenbroek 	if (wlfound != NULL)
648eda6f593SDavid van Moolenbroek 		return (wlfound);
649eda6f593SDavid van Moolenbroek 
650eda6f593SDavid van Moolenbroek 	return (NULL);
651eda6f593SDavid van Moolenbroek }
652eda6f593SDavid van Moolenbroek 
653eda6f593SDavid van Moolenbroek /*
654eda6f593SDavid van Moolenbroek  * Find a window index - if the window doesn't exist, check if it is a
655eda6f593SDavid van Moolenbroek  * potential index and return it anyway.
656eda6f593SDavid van Moolenbroek  */
657eda6f593SDavid van Moolenbroek int
cmd_lookup_index(struct session * s,const char * name,int * ambiguous)658eda6f593SDavid van Moolenbroek cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
659eda6f593SDavid van Moolenbroek {
660eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
661eda6f593SDavid van Moolenbroek 	const char	*errstr;
662eda6f593SDavid van Moolenbroek 	u_int		 idx;
663eda6f593SDavid van Moolenbroek 
664eda6f593SDavid van Moolenbroek 	if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
665eda6f593SDavid van Moolenbroek 		return (wl->idx);
666eda6f593SDavid van Moolenbroek 	if (*ambiguous)
667eda6f593SDavid van Moolenbroek 		return (-1);
668eda6f593SDavid van Moolenbroek 
669eda6f593SDavid van Moolenbroek 	idx = strtonum(name, 0, INT_MAX, &errstr);
670eda6f593SDavid van Moolenbroek 	if (errstr == NULL)
671eda6f593SDavid van Moolenbroek 		return (idx);
672eda6f593SDavid van Moolenbroek 
673eda6f593SDavid van Moolenbroek 	return (-1);
674eda6f593SDavid van Moolenbroek }
675eda6f593SDavid van Moolenbroek 
676*0a6a1f1dSLionel Sambuc /* Lookup pane id. An initial % means a pane id. */
677eda6f593SDavid van Moolenbroek struct window_pane *
cmd_lookup_paneid(const char * arg)678eda6f593SDavid van Moolenbroek cmd_lookup_paneid(const char *arg)
679eda6f593SDavid van Moolenbroek {
680eda6f593SDavid van Moolenbroek 	const char	*errstr;
681eda6f593SDavid van Moolenbroek 	u_int		 paneid;
682eda6f593SDavid van Moolenbroek 
683eda6f593SDavid van Moolenbroek 	if (*arg != '%')
684eda6f593SDavid van Moolenbroek 		return (NULL);
685eda6f593SDavid van Moolenbroek 
686eda6f593SDavid van Moolenbroek 	paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
687eda6f593SDavid van Moolenbroek 	if (errstr != NULL)
688eda6f593SDavid van Moolenbroek 		return (NULL);
689eda6f593SDavid van Moolenbroek 	return (window_pane_find_by_id(paneid));
690eda6f593SDavid van Moolenbroek }
691eda6f593SDavid van Moolenbroek 
692*0a6a1f1dSLionel Sambuc /* Lookup window id in a session. An initial @ means a window id. */
693*0a6a1f1dSLionel Sambuc struct winlink *
cmd_lookup_winlink_windowid(struct session * s,const char * arg)694*0a6a1f1dSLionel Sambuc cmd_lookup_winlink_windowid(struct session *s, const char *arg)
695*0a6a1f1dSLionel Sambuc {
696*0a6a1f1dSLionel Sambuc 	const char	*errstr;
697*0a6a1f1dSLionel Sambuc 	u_int		 windowid;
698*0a6a1f1dSLionel Sambuc 
699*0a6a1f1dSLionel Sambuc 	if (*arg != '@')
700*0a6a1f1dSLionel Sambuc 		return (NULL);
701*0a6a1f1dSLionel Sambuc 
702*0a6a1f1dSLionel Sambuc 	windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
703*0a6a1f1dSLionel Sambuc 	if (errstr != NULL)
704*0a6a1f1dSLionel Sambuc 		return (NULL);
705*0a6a1f1dSLionel Sambuc 	return (winlink_find_by_window_id(&s->windows, windowid));
706*0a6a1f1dSLionel Sambuc }
707*0a6a1f1dSLionel Sambuc 
708*0a6a1f1dSLionel Sambuc /* Lookup window id. An initial @ means a window id. */
709*0a6a1f1dSLionel Sambuc struct window *
cmd_lookup_windowid(const char * arg)710*0a6a1f1dSLionel Sambuc cmd_lookup_windowid(const char *arg)
711*0a6a1f1dSLionel Sambuc {
712*0a6a1f1dSLionel Sambuc 	const char	*errstr;
713*0a6a1f1dSLionel Sambuc 	u_int		 windowid;
714*0a6a1f1dSLionel Sambuc 
715*0a6a1f1dSLionel Sambuc 	if (*arg != '@')
716*0a6a1f1dSLionel Sambuc 		return (NULL);
717*0a6a1f1dSLionel Sambuc 
718*0a6a1f1dSLionel Sambuc 	windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
719*0a6a1f1dSLionel Sambuc 	if (errstr != NULL)
720*0a6a1f1dSLionel Sambuc 		return (NULL);
721*0a6a1f1dSLionel Sambuc 	return (window_find_by_id(windowid));
722*0a6a1f1dSLionel Sambuc }
723*0a6a1f1dSLionel Sambuc 
724*0a6a1f1dSLionel Sambuc /* Find session and winlink for window. */
725eda6f593SDavid van Moolenbroek struct session *
cmd_window_session(struct cmd_q * cmdq,struct window * w,struct winlink ** wlp)726*0a6a1f1dSLionel Sambuc cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp)
727eda6f593SDavid van Moolenbroek {
728eda6f593SDavid van Moolenbroek 	struct session		*s;
729eda6f593SDavid van Moolenbroek 	struct sessionslist	 ss;
730eda6f593SDavid van Moolenbroek 	struct winlink		*wl;
731eda6f593SDavid van Moolenbroek 
732*0a6a1f1dSLionel Sambuc 	/* If this window is in the current session, return that winlink. */
733*0a6a1f1dSLionel Sambuc 	s = cmd_current_session(cmdq, 0);
734eda6f593SDavid van Moolenbroek 	if (s != NULL) {
735*0a6a1f1dSLionel Sambuc 		wl = winlink_find_by_window(&s->windows, w);
736eda6f593SDavid van Moolenbroek 		if (wl != NULL) {
737eda6f593SDavid van Moolenbroek 			if (wlp != NULL)
738eda6f593SDavid van Moolenbroek 				*wlp = wl;
739eda6f593SDavid van Moolenbroek 			return (s);
740eda6f593SDavid van Moolenbroek 		}
741eda6f593SDavid van Moolenbroek 	}
742eda6f593SDavid van Moolenbroek 
743*0a6a1f1dSLionel Sambuc 	/* Otherwise choose from all sessions with this window. */
744eda6f593SDavid van Moolenbroek 	ARRAY_INIT(&ss);
745eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
746*0a6a1f1dSLionel Sambuc 		if (winlink_find_by_window(&s->windows, w) != NULL)
747eda6f593SDavid van Moolenbroek 			ARRAY_ADD(&ss, s);
748eda6f593SDavid van Moolenbroek 	}
749eda6f593SDavid van Moolenbroek 	s = cmd_choose_session_list(&ss);
750eda6f593SDavid van Moolenbroek 	ARRAY_FREE(&ss);
751eda6f593SDavid van Moolenbroek 	if (wlp != NULL)
752*0a6a1f1dSLionel Sambuc 		*wlp = winlink_find_by_window(&s->windows, w);
753eda6f593SDavid van Moolenbroek 	return (s);
754eda6f593SDavid van Moolenbroek }
755eda6f593SDavid van Moolenbroek 
756eda6f593SDavid van Moolenbroek /* Find the target session or report an error and return NULL. */
757eda6f593SDavid van Moolenbroek struct session *
cmd_find_session(struct cmd_q * cmdq,const char * arg,int prefer_unattached)758*0a6a1f1dSLionel Sambuc cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached)
759eda6f593SDavid van Moolenbroek {
760eda6f593SDavid van Moolenbroek 	struct session		*s;
761eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
762*0a6a1f1dSLionel Sambuc 	struct window		*w;
763eda6f593SDavid van Moolenbroek 	struct client		*c;
764eda6f593SDavid van Moolenbroek 	char			*tmparg;
765eda6f593SDavid van Moolenbroek 	size_t			 arglen;
766eda6f593SDavid van Moolenbroek 	int			 ambiguous;
767eda6f593SDavid van Moolenbroek 
768eda6f593SDavid van Moolenbroek 	/* A NULL argument means the current session. */
769eda6f593SDavid van Moolenbroek 	if (arg == NULL)
770*0a6a1f1dSLionel Sambuc 		return (cmd_current_session(cmdq, prefer_unattached));
771eda6f593SDavid van Moolenbroek 
772*0a6a1f1dSLionel Sambuc 	/* Lookup as pane id or window id. */
773eda6f593SDavid van Moolenbroek 	if ((wp = cmd_lookup_paneid(arg)) != NULL)
774*0a6a1f1dSLionel Sambuc 		return (cmd_window_session(cmdq, wp->window, NULL));
775*0a6a1f1dSLionel Sambuc 	if ((w = cmd_lookup_windowid(arg)) != NULL)
776*0a6a1f1dSLionel Sambuc 		return (cmd_window_session(cmdq, w, NULL));
777eda6f593SDavid van Moolenbroek 
778eda6f593SDavid van Moolenbroek 	/* Trim a single trailing colon if any. */
779eda6f593SDavid van Moolenbroek 	tmparg = xstrdup(arg);
780eda6f593SDavid van Moolenbroek 	arglen = strlen(tmparg);
781eda6f593SDavid van Moolenbroek 	if (arglen != 0 && tmparg[arglen - 1] == ':')
782eda6f593SDavid van Moolenbroek 		tmparg[arglen - 1] = '\0';
783eda6f593SDavid van Moolenbroek 
784eda6f593SDavid van Moolenbroek 	/* An empty session name is the current session. */
785eda6f593SDavid van Moolenbroek 	if (*tmparg == '\0') {
786*0a6a1f1dSLionel Sambuc 		free(tmparg);
787*0a6a1f1dSLionel Sambuc 		return (cmd_current_session(cmdq, prefer_unattached));
788eda6f593SDavid van Moolenbroek 	}
789eda6f593SDavid van Moolenbroek 
790eda6f593SDavid van Moolenbroek 	/* Find the session, if any. */
791eda6f593SDavid van Moolenbroek 	s = cmd_lookup_session(tmparg, &ambiguous);
792eda6f593SDavid van Moolenbroek 
793eda6f593SDavid van Moolenbroek 	/* If it doesn't, try to match it as a client. */
794eda6f593SDavid van Moolenbroek 	if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
795eda6f593SDavid van Moolenbroek 		s = c->session;
796eda6f593SDavid van Moolenbroek 
797eda6f593SDavid van Moolenbroek 	/* If no session found, report an error. */
798eda6f593SDavid van Moolenbroek 	if (s == NULL) {
799eda6f593SDavid van Moolenbroek 		if (ambiguous)
800*0a6a1f1dSLionel Sambuc 			cmdq_error(cmdq, "more than one session: %s", tmparg);
801eda6f593SDavid van Moolenbroek 		else
802*0a6a1f1dSLionel Sambuc 			cmdq_error(cmdq, "session not found: %s", tmparg);
803eda6f593SDavid van Moolenbroek 	}
804eda6f593SDavid van Moolenbroek 
805*0a6a1f1dSLionel Sambuc 	free(tmparg);
806eda6f593SDavid van Moolenbroek 	return (s);
807eda6f593SDavid van Moolenbroek }
808eda6f593SDavid van Moolenbroek 
809eda6f593SDavid van Moolenbroek /* Find the target session and window or report an error and return NULL. */
810eda6f593SDavid van Moolenbroek struct winlink *
cmd_find_window(struct cmd_q * cmdq,const char * arg,struct session ** sp)811*0a6a1f1dSLionel Sambuc cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp)
812eda6f593SDavid van Moolenbroek {
813eda6f593SDavid van Moolenbroek 	struct session		*s;
814eda6f593SDavid van Moolenbroek 	struct winlink		*wl;
815eda6f593SDavid van Moolenbroek 	struct window_pane	*wp;
816eda6f593SDavid van Moolenbroek 	const char		*winptr;
817eda6f593SDavid van Moolenbroek 	char			*sessptr = NULL;
818eda6f593SDavid van Moolenbroek 	int			 ambiguous = 0;
819eda6f593SDavid van Moolenbroek 
820eda6f593SDavid van Moolenbroek 	/*
821eda6f593SDavid van Moolenbroek 	 * Find the current session. There must always be a current session, if
822eda6f593SDavid van Moolenbroek 	 * it can't be found, report an error.
823eda6f593SDavid van Moolenbroek 	 */
824*0a6a1f1dSLionel Sambuc 	if ((s = cmd_current_session(cmdq, 0)) == NULL) {
825*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "can't establish current session");
826eda6f593SDavid van Moolenbroek 		return (NULL);
827eda6f593SDavid van Moolenbroek 	}
828eda6f593SDavid van Moolenbroek 
829eda6f593SDavid van Moolenbroek 	/* A NULL argument means the current session and window. */
830eda6f593SDavid van Moolenbroek 	if (arg == NULL) {
831eda6f593SDavid van Moolenbroek 		if (sp != NULL)
832eda6f593SDavid van Moolenbroek 			*sp = s;
833eda6f593SDavid van Moolenbroek 		return (s->curw);
834eda6f593SDavid van Moolenbroek 	}
835eda6f593SDavid van Moolenbroek 
836eda6f593SDavid van Moolenbroek 	/* Lookup as pane id. */
837eda6f593SDavid van Moolenbroek 	if ((wp = cmd_lookup_paneid(arg)) != NULL) {
838*0a6a1f1dSLionel Sambuc 		s = cmd_window_session(cmdq, wp->window, &wl);
839eda6f593SDavid van Moolenbroek 		if (sp != NULL)
840eda6f593SDavid van Moolenbroek 			*sp = s;
841eda6f593SDavid van Moolenbroek 		return (wl);
842eda6f593SDavid van Moolenbroek 	}
843eda6f593SDavid van Moolenbroek 
844eda6f593SDavid van Moolenbroek 	/* Time to look at the argument. If it is empty, that is an error. */
845eda6f593SDavid van Moolenbroek 	if (*arg == '\0')
846eda6f593SDavid van Moolenbroek 		goto not_found;
847eda6f593SDavid van Moolenbroek 
848eda6f593SDavid van Moolenbroek 	/* Find the separating colon and split into window and session. */
849eda6f593SDavid van Moolenbroek 	winptr = strchr(arg, ':');
850eda6f593SDavid van Moolenbroek 	if (winptr == NULL)
851eda6f593SDavid van Moolenbroek 		goto no_colon;
852eda6f593SDavid van Moolenbroek 	winptr++;	/* skip : */
853eda6f593SDavid van Moolenbroek 	sessptr = xstrdup(arg);
854eda6f593SDavid van Moolenbroek 	*strchr(sessptr, ':') = '\0';
855eda6f593SDavid van Moolenbroek 
856eda6f593SDavid van Moolenbroek 	/* Try to lookup the session if present. */
857eda6f593SDavid van Moolenbroek 	if (*sessptr != '\0') {
858eda6f593SDavid van Moolenbroek 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
859eda6f593SDavid van Moolenbroek 			goto no_session;
860eda6f593SDavid van Moolenbroek 	}
861eda6f593SDavid van Moolenbroek 	if (sp != NULL)
862eda6f593SDavid van Moolenbroek 		*sp = s;
863eda6f593SDavid van Moolenbroek 
864eda6f593SDavid van Moolenbroek 	/*
865eda6f593SDavid van Moolenbroek 	 * Then work out the window. An empty string is the current window,
866eda6f593SDavid van Moolenbroek 	 * otherwise try special cases then to look it up in the session.
867eda6f593SDavid van Moolenbroek 	 */
868eda6f593SDavid van Moolenbroek 	if (*winptr == '\0')
869eda6f593SDavid van Moolenbroek 		wl = s->curw;
870eda6f593SDavid van Moolenbroek 	else if (winptr[0] == '!' && winptr[1] == '\0')
871eda6f593SDavid van Moolenbroek 		wl = TAILQ_FIRST(&s->lastw);
872*0a6a1f1dSLionel Sambuc 	else if (winptr[0] == '^' && winptr[1] == '\0')
873*0a6a1f1dSLionel Sambuc 		wl = RB_MIN(winlinks, &s->windows);
874*0a6a1f1dSLionel Sambuc 	else if (winptr[0] == '$' && winptr[1] == '\0')
875*0a6a1f1dSLionel Sambuc 		wl = RB_MAX(winlinks, &s->windows);
876eda6f593SDavid van Moolenbroek 	else if (winptr[0] == '+' || winptr[0] == '-')
877eda6f593SDavid van Moolenbroek 		wl = cmd_find_window_offset(winptr, s, &ambiguous);
878eda6f593SDavid van Moolenbroek 	else
879eda6f593SDavid van Moolenbroek 		wl = cmd_lookup_window(s, winptr, &ambiguous);
880eda6f593SDavid van Moolenbroek 	if (wl == NULL)
881eda6f593SDavid van Moolenbroek 		goto not_found;
882eda6f593SDavid van Moolenbroek 
883eda6f593SDavid van Moolenbroek 	if (sessptr != NULL)
884*0a6a1f1dSLionel Sambuc 		free(sessptr);
885eda6f593SDavid van Moolenbroek 	return (wl);
886eda6f593SDavid van Moolenbroek 
887eda6f593SDavid van Moolenbroek no_colon:
888eda6f593SDavid van Moolenbroek 	/*
889eda6f593SDavid van Moolenbroek 	 * No colon in the string, first try special cases, then as a window
890eda6f593SDavid van Moolenbroek 	 * and lastly as a session.
891eda6f593SDavid van Moolenbroek 	 */
892eda6f593SDavid van Moolenbroek 	if (arg[0] == '!' && arg[1] == '\0') {
893eda6f593SDavid van Moolenbroek 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
894eda6f593SDavid van Moolenbroek 			goto not_found;
895eda6f593SDavid van Moolenbroek 	} else if (arg[0] == '+' || arg[0] == '-') {
896eda6f593SDavid van Moolenbroek 		if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
897eda6f593SDavid van Moolenbroek 			goto lookup_session;
898eda6f593SDavid van Moolenbroek 	} else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
899eda6f593SDavid van Moolenbroek 		goto lookup_session;
900eda6f593SDavid van Moolenbroek 
901eda6f593SDavid van Moolenbroek 	if (sp != NULL)
902eda6f593SDavid van Moolenbroek 		*sp = s;
903eda6f593SDavid van Moolenbroek 
904eda6f593SDavid van Moolenbroek 	return (wl);
905eda6f593SDavid van Moolenbroek 
906eda6f593SDavid van Moolenbroek lookup_session:
907eda6f593SDavid van Moolenbroek 	if (ambiguous)
908eda6f593SDavid van Moolenbroek 		goto not_found;
909eda6f593SDavid van Moolenbroek 	if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
910eda6f593SDavid van Moolenbroek 		goto no_session;
911eda6f593SDavid van Moolenbroek 
912eda6f593SDavid van Moolenbroek 	if (sp != NULL)
913eda6f593SDavid van Moolenbroek 		*sp = s;
914eda6f593SDavid van Moolenbroek 
915eda6f593SDavid van Moolenbroek 	return (s->curw);
916eda6f593SDavid van Moolenbroek 
917eda6f593SDavid van Moolenbroek no_session:
918eda6f593SDavid van Moolenbroek 	if (ambiguous)
919*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "multiple sessions: %s", arg);
920eda6f593SDavid van Moolenbroek 	else
921*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "session not found: %s", arg);
922*0a6a1f1dSLionel Sambuc 	free(sessptr);
923eda6f593SDavid van Moolenbroek 	return (NULL);
924eda6f593SDavid van Moolenbroek 
925eda6f593SDavid van Moolenbroek not_found:
926eda6f593SDavid van Moolenbroek 	if (ambiguous)
927*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "multiple windows: %s", arg);
928eda6f593SDavid van Moolenbroek 	else
929*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "window not found: %s", arg);
930*0a6a1f1dSLionel Sambuc 	free(sessptr);
931eda6f593SDavid van Moolenbroek 	return (NULL);
932eda6f593SDavid van Moolenbroek }
933eda6f593SDavid van Moolenbroek 
934eda6f593SDavid van Moolenbroek struct winlink *
cmd_find_window_offset(const char * winptr,struct session * s,int * ambiguous)935eda6f593SDavid van Moolenbroek cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
936eda6f593SDavid van Moolenbroek {
937eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
938eda6f593SDavid van Moolenbroek 	int		 offset = 1;
939eda6f593SDavid van Moolenbroek 
940eda6f593SDavid van Moolenbroek 	if (winptr[1] != '\0')
941eda6f593SDavid van Moolenbroek 		offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
942eda6f593SDavid van Moolenbroek 	if (offset == 0)
943eda6f593SDavid van Moolenbroek 		wl = cmd_lookup_window(s, winptr, ambiguous);
944eda6f593SDavid van Moolenbroek 	else {
945eda6f593SDavid van Moolenbroek 		if (winptr[0] == '+')
946eda6f593SDavid van Moolenbroek 			wl = winlink_next_by_number(s->curw, s, offset);
947eda6f593SDavid van Moolenbroek 		else
948eda6f593SDavid van Moolenbroek 			wl = winlink_previous_by_number(s->curw, s, offset);
949eda6f593SDavid van Moolenbroek 	}
950eda6f593SDavid van Moolenbroek 
951eda6f593SDavid van Moolenbroek 	return (wl);
952eda6f593SDavid van Moolenbroek }
953eda6f593SDavid van Moolenbroek 
954eda6f593SDavid van Moolenbroek /*
955eda6f593SDavid van Moolenbroek  * Find the target session and window index, whether or not it exists in the
956eda6f593SDavid van Moolenbroek  * session. Return -2 on error or -1 if no window index is specified. This is
957eda6f593SDavid van Moolenbroek  * used when parsing an argument for a window target that may not exist (for
958eda6f593SDavid van Moolenbroek  * example if it is going to be created).
959eda6f593SDavid van Moolenbroek  */
960eda6f593SDavid van Moolenbroek int
cmd_find_index(struct cmd_q * cmdq,const char * arg,struct session ** sp)961*0a6a1f1dSLionel Sambuc cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp)
962eda6f593SDavid van Moolenbroek {
963eda6f593SDavid van Moolenbroek 	struct session	*s;
964eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
965eda6f593SDavid van Moolenbroek 	const char	*winptr;
966eda6f593SDavid van Moolenbroek 	char		*sessptr = NULL;
967eda6f593SDavid van Moolenbroek 	int		 idx, ambiguous = 0;
968eda6f593SDavid van Moolenbroek 
969eda6f593SDavid van Moolenbroek 	/*
970eda6f593SDavid van Moolenbroek 	 * Find the current session. There must always be a current session, if
971eda6f593SDavid van Moolenbroek 	 * it can't be found, report an error.
972eda6f593SDavid van Moolenbroek 	 */
973*0a6a1f1dSLionel Sambuc 	if ((s = cmd_current_session(cmdq, 0)) == NULL) {
974*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "can't establish current session");
975eda6f593SDavid van Moolenbroek 		return (-2);
976eda6f593SDavid van Moolenbroek 	}
977eda6f593SDavid van Moolenbroek 
978eda6f593SDavid van Moolenbroek 	/* A NULL argument means the current session and "no window" (-1). */
979eda6f593SDavid van Moolenbroek 	if (arg == NULL) {
980eda6f593SDavid van Moolenbroek 		if (sp != NULL)
981eda6f593SDavid van Moolenbroek 			*sp = s;
982eda6f593SDavid van Moolenbroek 		return (-1);
983eda6f593SDavid van Moolenbroek 	}
984eda6f593SDavid van Moolenbroek 
985eda6f593SDavid van Moolenbroek 	/* Time to look at the argument. If it is empty, that is an error. */
986eda6f593SDavid van Moolenbroek 	if (*arg == '\0')
987eda6f593SDavid van Moolenbroek 		goto not_found;
988eda6f593SDavid van Moolenbroek 
989eda6f593SDavid van Moolenbroek 	/* Find the separating colon. If none, assume the current session. */
990eda6f593SDavid van Moolenbroek 	winptr = strchr(arg, ':');
991eda6f593SDavid van Moolenbroek 	if (winptr == NULL)
992eda6f593SDavid van Moolenbroek 		goto no_colon;
993eda6f593SDavid van Moolenbroek 	winptr++;	/* skip : */
994eda6f593SDavid van Moolenbroek 	sessptr = xstrdup(arg);
995eda6f593SDavid van Moolenbroek 	*strchr(sessptr, ':') = '\0';
996eda6f593SDavid van Moolenbroek 
997eda6f593SDavid van Moolenbroek 	/* Try to lookup the session if present. */
998eda6f593SDavid van Moolenbroek 	if (sessptr != NULL && *sessptr != '\0') {
999eda6f593SDavid van Moolenbroek 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
1000eda6f593SDavid van Moolenbroek 			goto no_session;
1001eda6f593SDavid van Moolenbroek 	}
1002eda6f593SDavid van Moolenbroek 	if (sp != NULL)
1003eda6f593SDavid van Moolenbroek 		*sp = s;
1004eda6f593SDavid van Moolenbroek 
1005eda6f593SDavid van Moolenbroek 	/*
1006eda6f593SDavid van Moolenbroek 	 * Then work out the window. An empty string is a new window otherwise
1007eda6f593SDavid van Moolenbroek 	 * try to look it up in the session.
1008eda6f593SDavid van Moolenbroek 	 */
1009eda6f593SDavid van Moolenbroek 	if (*winptr == '\0')
1010eda6f593SDavid van Moolenbroek 		idx = -1;
1011eda6f593SDavid van Moolenbroek 	else if (winptr[0] == '!' && winptr[1] == '\0') {
1012eda6f593SDavid van Moolenbroek 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
1013eda6f593SDavid van Moolenbroek 			goto not_found;
1014eda6f593SDavid van Moolenbroek 		idx = wl->idx;
1015eda6f593SDavid van Moolenbroek 	} else if (winptr[0] == '+' || winptr[0] == '-') {
1016eda6f593SDavid van Moolenbroek 		if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
1017eda6f593SDavid van Moolenbroek 			goto invalid_index;
1018eda6f593SDavid van Moolenbroek 	} else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
1019eda6f593SDavid van Moolenbroek 		goto invalid_index;
1020eda6f593SDavid van Moolenbroek 
1021*0a6a1f1dSLionel Sambuc 	free(sessptr);
1022eda6f593SDavid van Moolenbroek 	return (idx);
1023eda6f593SDavid van Moolenbroek 
1024eda6f593SDavid van Moolenbroek no_colon:
1025eda6f593SDavid van Moolenbroek 	/*
1026eda6f593SDavid van Moolenbroek 	 * No colon in the string, first try special cases, then as a window
1027eda6f593SDavid van Moolenbroek 	 * and lastly as a session.
1028eda6f593SDavid van Moolenbroek 	 */
1029eda6f593SDavid van Moolenbroek 	if (arg[0] == '!' && arg[1] == '\0') {
1030eda6f593SDavid van Moolenbroek 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
1031eda6f593SDavid van Moolenbroek 			goto not_found;
1032eda6f593SDavid van Moolenbroek 		idx = wl->idx;
1033eda6f593SDavid van Moolenbroek 	} else if (arg[0] == '+' || arg[0] == '-') {
1034eda6f593SDavid van Moolenbroek 		if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
1035eda6f593SDavid van Moolenbroek 			goto lookup_session;
1036eda6f593SDavid van Moolenbroek 	} else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
1037eda6f593SDavid van Moolenbroek 		goto lookup_session;
1038eda6f593SDavid van Moolenbroek 
1039eda6f593SDavid van Moolenbroek 	if (sp != NULL)
1040eda6f593SDavid van Moolenbroek 		*sp = s;
1041eda6f593SDavid van Moolenbroek 
1042eda6f593SDavid van Moolenbroek 	return (idx);
1043eda6f593SDavid van Moolenbroek 
1044eda6f593SDavid van Moolenbroek lookup_session:
1045eda6f593SDavid van Moolenbroek 	if (ambiguous)
1046eda6f593SDavid van Moolenbroek 		goto not_found;
1047eda6f593SDavid van Moolenbroek 	if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
1048eda6f593SDavid van Moolenbroek 		goto no_session;
1049eda6f593SDavid van Moolenbroek 
1050eda6f593SDavid van Moolenbroek 	if (sp != NULL)
1051eda6f593SDavid van Moolenbroek 		*sp = s;
1052eda6f593SDavid van Moolenbroek 
1053eda6f593SDavid van Moolenbroek 	return (-1);
1054eda6f593SDavid van Moolenbroek 
1055eda6f593SDavid van Moolenbroek no_session:
1056eda6f593SDavid van Moolenbroek 	if (ambiguous)
1057*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "multiple sessions: %s", arg);
1058eda6f593SDavid van Moolenbroek 	else
1059*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "session not found: %s", arg);
1060*0a6a1f1dSLionel Sambuc 	free(sessptr);
1061eda6f593SDavid van Moolenbroek 	return (-2);
1062eda6f593SDavid van Moolenbroek 
1063eda6f593SDavid van Moolenbroek invalid_index:
1064eda6f593SDavid van Moolenbroek 	if (ambiguous)
1065eda6f593SDavid van Moolenbroek 		goto not_found;
1066*0a6a1f1dSLionel Sambuc 	cmdq_error(cmdq, "invalid index: %s", arg);
1067eda6f593SDavid van Moolenbroek 
1068*0a6a1f1dSLionel Sambuc 	free(sessptr);
1069eda6f593SDavid van Moolenbroek 	return (-2);
1070eda6f593SDavid van Moolenbroek 
1071eda6f593SDavid van Moolenbroek not_found:
1072eda6f593SDavid van Moolenbroek 	if (ambiguous)
1073*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "multiple windows: %s", arg);
1074eda6f593SDavid van Moolenbroek 	else
1075*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "window not found: %s", arg);
1076*0a6a1f1dSLionel Sambuc 	free(sessptr);
1077eda6f593SDavid van Moolenbroek 	return (-2);
1078eda6f593SDavid van Moolenbroek }
1079eda6f593SDavid van Moolenbroek 
1080eda6f593SDavid van Moolenbroek int
cmd_find_index_offset(const char * winptr,struct session * s,int * ambiguous)1081eda6f593SDavid van Moolenbroek cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
1082eda6f593SDavid van Moolenbroek {
1083eda6f593SDavid van Moolenbroek 	int	idx, offset = 1;
1084eda6f593SDavid van Moolenbroek 
1085eda6f593SDavid van Moolenbroek 	if (winptr[1] != '\0')
1086eda6f593SDavid van Moolenbroek 		offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
1087eda6f593SDavid van Moolenbroek 	if (offset == 0)
1088eda6f593SDavid van Moolenbroek 		idx = cmd_lookup_index(s, winptr, ambiguous);
1089eda6f593SDavid van Moolenbroek 	else {
1090eda6f593SDavid van Moolenbroek 		if (winptr[0] == '+') {
1091eda6f593SDavid van Moolenbroek 			if (s->curw->idx == INT_MAX)
1092eda6f593SDavid van Moolenbroek 				idx = cmd_lookup_index(s, winptr, ambiguous);
1093eda6f593SDavid van Moolenbroek 			else
1094eda6f593SDavid van Moolenbroek 				idx = s->curw->idx + offset;
1095eda6f593SDavid van Moolenbroek 		} else {
1096eda6f593SDavid van Moolenbroek 			if (s->curw->idx == 0)
1097eda6f593SDavid van Moolenbroek 				idx = cmd_lookup_index(s, winptr, ambiguous);
1098eda6f593SDavid van Moolenbroek 			else
1099eda6f593SDavid van Moolenbroek 				idx = s->curw->idx - offset;
1100eda6f593SDavid van Moolenbroek 		}
1101eda6f593SDavid van Moolenbroek 	}
1102eda6f593SDavid van Moolenbroek 
1103eda6f593SDavid van Moolenbroek 	return (idx);
1104eda6f593SDavid van Moolenbroek }
1105eda6f593SDavid van Moolenbroek 
1106eda6f593SDavid van Moolenbroek /*
1107eda6f593SDavid van Moolenbroek  * Find the target session, window and pane number or report an error and
1108eda6f593SDavid van Moolenbroek  * return NULL. The pane number is separated from the session:window by a .,
1109eda6f593SDavid van Moolenbroek  * such as mysession:mywindow.0.
1110eda6f593SDavid van Moolenbroek  */
1111eda6f593SDavid van Moolenbroek struct winlink *
cmd_find_pane(struct cmd_q * cmdq,const char * arg,struct session ** sp,struct window_pane ** wpp)1112*0a6a1f1dSLionel Sambuc cmd_find_pane(struct cmd_q *cmdq,
1113eda6f593SDavid van Moolenbroek     const char *arg, struct session **sp, struct window_pane **wpp)
1114eda6f593SDavid van Moolenbroek {
1115eda6f593SDavid van Moolenbroek 	struct session	*s;
1116eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
1117eda6f593SDavid van Moolenbroek 	const char	*period, *errstr;
1118eda6f593SDavid van Moolenbroek 	char		*winptr, *paneptr;
1119eda6f593SDavid van Moolenbroek 	u_int		 idx;
1120eda6f593SDavid van Moolenbroek 
1121eda6f593SDavid van Moolenbroek 	/* Get the current session. */
1122*0a6a1f1dSLionel Sambuc 	if ((s = cmd_current_session(cmdq, 0)) == NULL) {
1123*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "can't establish current session");
1124eda6f593SDavid van Moolenbroek 		return (NULL);
1125eda6f593SDavid van Moolenbroek 	}
1126eda6f593SDavid van Moolenbroek 	if (sp != NULL)
1127eda6f593SDavid van Moolenbroek 		*sp = s;
1128eda6f593SDavid van Moolenbroek 
1129eda6f593SDavid van Moolenbroek 	/* A NULL argument means the current session, window and pane. */
1130eda6f593SDavid van Moolenbroek 	if (arg == NULL) {
1131eda6f593SDavid van Moolenbroek 		*wpp = s->curw->window->active;
1132eda6f593SDavid van Moolenbroek 		return (s->curw);
1133eda6f593SDavid van Moolenbroek 	}
1134eda6f593SDavid van Moolenbroek 
1135eda6f593SDavid van Moolenbroek 	/* Lookup as pane id. */
1136eda6f593SDavid van Moolenbroek 	if ((*wpp = cmd_lookup_paneid(arg)) != NULL) {
1137*0a6a1f1dSLionel Sambuc 		s = cmd_window_session(cmdq, (*wpp)->window, &wl);
1138eda6f593SDavid van Moolenbroek 		if (sp != NULL)
1139eda6f593SDavid van Moolenbroek 			*sp = s;
1140eda6f593SDavid van Moolenbroek 		return (wl);
1141eda6f593SDavid van Moolenbroek 	}
1142eda6f593SDavid van Moolenbroek 
1143eda6f593SDavid van Moolenbroek 	/* Look for a separating period. */
1144eda6f593SDavid van Moolenbroek 	if ((period = strrchr(arg, '.')) == NULL)
1145eda6f593SDavid van Moolenbroek 		goto no_period;
1146eda6f593SDavid van Moolenbroek 
1147eda6f593SDavid van Moolenbroek 	/* Pull out the window part and parse it. */
1148eda6f593SDavid van Moolenbroek 	winptr = xstrdup(arg);
1149eda6f593SDavid van Moolenbroek 	winptr[period - arg] = '\0';
1150eda6f593SDavid van Moolenbroek 	if (*winptr == '\0')
1151eda6f593SDavid van Moolenbroek 		wl = s->curw;
1152*0a6a1f1dSLionel Sambuc 	else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL)
1153eda6f593SDavid van Moolenbroek 		goto error;
1154eda6f593SDavid van Moolenbroek 
1155eda6f593SDavid van Moolenbroek 	/* Find the pane section and look it up. */
1156eda6f593SDavid van Moolenbroek 	paneptr = winptr + (period - arg) + 1;
1157eda6f593SDavid van Moolenbroek 	if (*paneptr == '\0')
1158eda6f593SDavid van Moolenbroek 		*wpp = wl->window->active;
1159eda6f593SDavid van Moolenbroek 	else if (paneptr[0] == '+' || paneptr[0] == '-')
1160eda6f593SDavid van Moolenbroek 		*wpp = cmd_find_pane_offset(paneptr, wl);
1161eda6f593SDavid van Moolenbroek 	else {
1162eda6f593SDavid van Moolenbroek 		idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1163eda6f593SDavid van Moolenbroek 		if (errstr != NULL)
1164eda6f593SDavid van Moolenbroek 			goto lookup_string;
1165eda6f593SDavid van Moolenbroek 		*wpp = window_pane_at_index(wl->window, idx);
1166eda6f593SDavid van Moolenbroek 		if (*wpp == NULL)
1167eda6f593SDavid van Moolenbroek 			goto lookup_string;
1168eda6f593SDavid van Moolenbroek 	}
1169eda6f593SDavid van Moolenbroek 
1170*0a6a1f1dSLionel Sambuc 	free(winptr);
1171eda6f593SDavid van Moolenbroek 	return (wl);
1172eda6f593SDavid van Moolenbroek 
1173eda6f593SDavid van Moolenbroek lookup_string:
1174eda6f593SDavid van Moolenbroek 	/* Try pane string description. */
1175eda6f593SDavid van Moolenbroek 	if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) {
1176*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "can't find pane: %s", paneptr);
1177eda6f593SDavid van Moolenbroek 		goto error;
1178eda6f593SDavid van Moolenbroek 	}
1179eda6f593SDavid van Moolenbroek 
1180*0a6a1f1dSLionel Sambuc 	free(winptr);
1181eda6f593SDavid van Moolenbroek 	return (wl);
1182eda6f593SDavid van Moolenbroek 
1183eda6f593SDavid van Moolenbroek no_period:
1184eda6f593SDavid van Moolenbroek 	/* Try as a pane number alone. */
1185eda6f593SDavid van Moolenbroek 	idx = strtonum(arg, 0, INT_MAX, &errstr);
1186eda6f593SDavid van Moolenbroek 	if (errstr != NULL)
1187eda6f593SDavid van Moolenbroek 		goto lookup_window;
1188eda6f593SDavid van Moolenbroek 
1189eda6f593SDavid van Moolenbroek 	/* Try index in the current session and window. */
1190eda6f593SDavid van Moolenbroek 	if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1191eda6f593SDavid van Moolenbroek 		goto lookup_window;
1192eda6f593SDavid van Moolenbroek 
1193eda6f593SDavid van Moolenbroek 	return (s->curw);
1194eda6f593SDavid van Moolenbroek 
1195eda6f593SDavid van Moolenbroek lookup_window:
1196eda6f593SDavid van Moolenbroek 	/* Try pane string description. */
1197eda6f593SDavid van Moolenbroek 	if ((*wpp = window_find_string(s->curw->window, arg)) != NULL)
1198eda6f593SDavid van Moolenbroek 		return (s->curw);
1199eda6f593SDavid van Moolenbroek 
1200eda6f593SDavid van Moolenbroek 	/* Try as a window and use the active pane. */
1201*0a6a1f1dSLionel Sambuc 	if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL)
1202eda6f593SDavid van Moolenbroek 		*wpp = wl->window->active;
1203eda6f593SDavid van Moolenbroek 	return (wl);
1204eda6f593SDavid van Moolenbroek 
1205eda6f593SDavid van Moolenbroek error:
1206*0a6a1f1dSLionel Sambuc 	free(winptr);
1207eda6f593SDavid van Moolenbroek 	return (NULL);
1208eda6f593SDavid van Moolenbroek }
1209eda6f593SDavid van Moolenbroek 
1210eda6f593SDavid van Moolenbroek struct window_pane *
cmd_find_pane_offset(const char * paneptr,struct winlink * wl)1211eda6f593SDavid van Moolenbroek cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1212eda6f593SDavid van Moolenbroek {
1213eda6f593SDavid van Moolenbroek 	struct window		*w = wl->window;
1214eda6f593SDavid van Moolenbroek 	struct window_pane	*wp = w->active;
1215eda6f593SDavid van Moolenbroek 	u_int			 offset = 1;
1216eda6f593SDavid van Moolenbroek 
1217eda6f593SDavid van Moolenbroek 	if (paneptr[1] != '\0')
1218eda6f593SDavid van Moolenbroek 		offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1219eda6f593SDavid van Moolenbroek 	if (offset > 0) {
1220eda6f593SDavid van Moolenbroek 		if (paneptr[0] == '+')
1221eda6f593SDavid van Moolenbroek 			wp = window_pane_next_by_number(w, wp, offset);
1222eda6f593SDavid van Moolenbroek 		else
1223eda6f593SDavid van Moolenbroek 			wp = window_pane_previous_by_number(w, wp, offset);
1224eda6f593SDavid van Moolenbroek 	}
1225eda6f593SDavid van Moolenbroek 
1226eda6f593SDavid van Moolenbroek 	return (wp);
1227eda6f593SDavid van Moolenbroek }
1228eda6f593SDavid van Moolenbroek 
1229eda6f593SDavid van Moolenbroek /* Replace the first %% or %idx in template by s. */
1230eda6f593SDavid van Moolenbroek char *
cmd_template_replace(const char * template,const char * s,int idx)1231*0a6a1f1dSLionel Sambuc cmd_template_replace(const char *template, const char *s, int idx)
1232eda6f593SDavid van Moolenbroek {
1233*0a6a1f1dSLionel Sambuc 	char		 ch, *buf;
1234*0a6a1f1dSLionel Sambuc 	const char	*ptr;
1235eda6f593SDavid van Moolenbroek 	int		 replaced;
1236eda6f593SDavid van Moolenbroek 	size_t		 len;
1237eda6f593SDavid van Moolenbroek 
1238*0a6a1f1dSLionel Sambuc 	if (strchr(template, '%') == NULL)
1239eda6f593SDavid van Moolenbroek 		return (xstrdup(template));
1240eda6f593SDavid van Moolenbroek 
1241eda6f593SDavid van Moolenbroek 	buf = xmalloc(1);
1242eda6f593SDavid van Moolenbroek 	*buf = '\0';
1243eda6f593SDavid van Moolenbroek 	len = 0;
1244eda6f593SDavid van Moolenbroek 	replaced = 0;
1245eda6f593SDavid van Moolenbroek 
1246eda6f593SDavid van Moolenbroek 	ptr = template;
1247eda6f593SDavid van Moolenbroek 	while (*ptr != '\0') {
1248eda6f593SDavid van Moolenbroek 		switch (ch = *ptr++) {
1249eda6f593SDavid van Moolenbroek 		case '%':
1250eda6f593SDavid van Moolenbroek 			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1251eda6f593SDavid van Moolenbroek 				if (*ptr != '%' || replaced)
1252eda6f593SDavid van Moolenbroek 					break;
1253eda6f593SDavid van Moolenbroek 				replaced = 1;
1254eda6f593SDavid van Moolenbroek 			}
1255eda6f593SDavid van Moolenbroek 			ptr++;
1256eda6f593SDavid van Moolenbroek 
1257eda6f593SDavid van Moolenbroek 			len += strlen(s);
1258eda6f593SDavid van Moolenbroek 			buf = xrealloc(buf, 1, len + 1);
1259eda6f593SDavid van Moolenbroek 			strlcat(buf, s, len + 1);
1260eda6f593SDavid van Moolenbroek 			continue;
1261eda6f593SDavid van Moolenbroek 		}
1262eda6f593SDavid van Moolenbroek 		buf = xrealloc(buf, 1, len + 2);
1263eda6f593SDavid van Moolenbroek 		buf[len++] = ch;
1264eda6f593SDavid van Moolenbroek 		buf[len] = '\0';
1265eda6f593SDavid van Moolenbroek 	}
1266eda6f593SDavid van Moolenbroek 
1267eda6f593SDavid van Moolenbroek 	return (buf);
1268eda6f593SDavid van Moolenbroek }
1269