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