xref: /openbsd-src/usr.bin/tmux/cmd.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /* $OpenBSD: cmd.c,v 1.27 2009/10/26 21:42:04 deraadt 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 <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "tmux.h"
29 
30 const struct cmd_entry *cmd_table[] = {
31 	&cmd_attach_session_entry,
32 	&cmd_bind_key_entry,
33 	&cmd_break_pane_entry,
34 	&cmd_choose_client_entry,
35 	&cmd_choose_session_entry,
36 	&cmd_choose_window_entry,
37 	&cmd_clear_history_entry,
38 	&cmd_clock_mode_entry,
39 	&cmd_command_prompt_entry,
40 	&cmd_confirm_before_entry,
41 	&cmd_copy_buffer_entry,
42 	&cmd_copy_mode_entry,
43 	&cmd_delete_buffer_entry,
44 	&cmd_detach_client_entry,
45 	&cmd_display_message_entry,
46 	&cmd_display_panes_entry,
47 	&cmd_down_pane_entry,
48 	&cmd_find_window_entry,
49 	&cmd_has_session_entry,
50 	&cmd_if_shell_entry,
51 	&cmd_kill_pane_entry,
52 	&cmd_kill_server_entry,
53 	&cmd_kill_session_entry,
54 	&cmd_kill_window_entry,
55 	&cmd_last_window_entry,
56 	&cmd_link_window_entry,
57 	&cmd_list_buffers_entry,
58 	&cmd_list_clients_entry,
59 	&cmd_list_commands_entry,
60 	&cmd_list_keys_entry,
61 	&cmd_list_panes_entry,
62 	&cmd_list_sessions_entry,
63 	&cmd_list_windows_entry,
64 	&cmd_load_buffer_entry,
65 	&cmd_lock_client_entry,
66 	&cmd_lock_server_entry,
67 	&cmd_lock_session_entry,
68 	&cmd_move_window_entry,
69 	&cmd_new_session_entry,
70 	&cmd_new_window_entry,
71 	&cmd_next_layout_entry,
72 	&cmd_next_window_entry,
73 	&cmd_paste_buffer_entry,
74 	&cmd_pipe_pane_entry,
75 	&cmd_previous_layout_entry,
76 	&cmd_previous_window_entry,
77 	&cmd_refresh_client_entry,
78 	&cmd_rename_session_entry,
79 	&cmd_rename_window_entry,
80 	&cmd_resize_pane_entry,
81 	&cmd_respawn_window_entry,
82 	&cmd_rotate_window_entry,
83 	&cmd_run_shell_entry,
84 	&cmd_save_buffer_entry,
85 	&cmd_select_layout_entry,
86 	&cmd_select_pane_entry,
87 	&cmd_select_prompt_entry,
88 	&cmd_select_window_entry,
89 	&cmd_send_keys_entry,
90 	&cmd_send_prefix_entry,
91 	&cmd_server_info_entry,
92 	&cmd_set_buffer_entry,
93 	&cmd_set_environment_entry,
94 	&cmd_set_option_entry,
95 	&cmd_set_window_option_entry,
96 	&cmd_show_buffer_entry,
97 	&cmd_show_environment_entry,
98 	&cmd_show_options_entry,
99 	&cmd_show_window_options_entry,
100 	&cmd_source_file_entry,
101 	&cmd_split_window_entry,
102 	&cmd_start_server_entry,
103 	&cmd_suspend_client_entry,
104 	&cmd_swap_pane_entry,
105 	&cmd_swap_window_entry,
106 	&cmd_switch_client_entry,
107 	&cmd_unbind_key_entry,
108 	&cmd_unlink_window_entry,
109 	&cmd_up_pane_entry,
110 	NULL
111 };
112 
113 struct session	*cmd_newest_session(struct sessions *);
114 struct client	*cmd_newest_client(void);
115 struct client	*cmd_lookup_client(const char *);
116 struct session	*cmd_lookup_session(const char *, int *);
117 struct winlink	*cmd_lookup_window(struct session *, const char *, int *);
118 int		 cmd_lookup_index(struct session *, const char *, int *);
119 
120 int
121 cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
122 {
123 	size_t	arglen;
124 	int	i;
125 
126 	*buf = '\0';
127 	for (i = 0; i < argc; i++) {
128 		if (strlcpy(buf, argv[i], len) >= len)
129 			return (-1);
130 		arglen = strlen(argv[i]) + 1;
131 		buf += arglen;
132 		len -= arglen;
133 	}
134 
135 	return (0);
136 }
137 
138 int
139 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
140 {
141 	int	i;
142 	size_t	arglen;
143 
144 	if (argc == 0)
145 		return (0);
146 	*argv = xcalloc(argc, sizeof **argv);
147 
148 	buf[len - 1] = '\0';
149 	for (i = 0; i < argc; i++) {
150 		if (len == 0) {
151 			cmd_free_argv(argc, *argv);
152 			return (-1);
153 		}
154 
155 		arglen = strlen(buf) + 1;
156 		(*argv)[i] = xstrdup(buf);
157 		buf += arglen;
158 		len -= arglen;
159 	}
160 
161 	return (0);
162 }
163 
164 void
165 cmd_free_argv(int argc, char **argv)
166 {
167 	int	i;
168 
169 	if (argc == 0)
170 		return;
171 	for (i = 0; i < argc; i++) {
172 		if (argv[i] != NULL)
173 			xfree(argv[i]);
174 	}
175 	xfree(argv);
176 }
177 
178 struct cmd *
179 cmd_parse(int argc, char **argv, char **cause)
180 {
181 	const struct cmd_entry **entryp, *entry;
182 	struct cmd		*cmd;
183 	char			 s[BUFSIZ];
184 	int			 opt, ambiguous = 0;
185 
186 	*cause = NULL;
187 	if (argc == 0) {
188 		xasprintf(cause, "no command");
189 		return (NULL);
190 	}
191 
192 	entry = NULL;
193 	for (entryp = cmd_table; *entryp != NULL; entryp++) {
194 		if ((*entryp)->alias != NULL &&
195 		    strcmp((*entryp)->alias, argv[0]) == 0) {
196 			ambiguous = 0;
197 			entry = *entryp;
198 			break;
199 		}
200 
201 		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
202 			continue;
203 		if (entry != NULL)
204 			ambiguous = 1;
205 		entry = *entryp;
206 
207 		/* Bail now if an exact match. */
208 		if (strcmp(entry->name, argv[0]) == 0)
209 			break;
210 	}
211 	if (ambiguous)
212 		goto ambiguous;
213 	if (entry == NULL) {
214 		xasprintf(cause, "unknown command: %s", argv[0]);
215 		return (NULL);
216 	}
217 
218 	optreset = 1;
219 	optind = 1;
220 	if (entry->parse == NULL) {
221 		while ((opt = getopt(argc, argv, "")) != -1) {
222 			switch (opt) {
223 			default:
224 				goto usage;
225 			}
226 		}
227 		argc -= optind;
228 		argv += optind;
229 		if (argc != 0)
230 			goto usage;
231 	}
232 
233 	cmd = xmalloc(sizeof *cmd);
234 	cmd->entry = entry;
235 	cmd->data = NULL;
236 	if (entry->parse != NULL) {
237 		if (entry->parse(cmd, argc, argv, cause) != 0) {
238 			xfree(cmd);
239 			return (NULL);
240 		}
241 	}
242 	return (cmd);
243 
244 ambiguous:
245 	*s = '\0';
246 	for (entryp = cmd_table; *entryp != NULL; entryp++) {
247 		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
248 			continue;
249 		if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
250 			break;
251 		if (strlcat(s, ", ", sizeof s) >= sizeof s)
252 			break;
253 	}
254 	s[strlen(s) - 2] = '\0';
255 	xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
256 	return (NULL);
257 
258 usage:
259 	xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
260 	return (NULL);
261 }
262 
263 int
264 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
265 {
266 	return (cmd->entry->exec(cmd, ctx));
267 }
268 
269 void
270 cmd_free(struct cmd *cmd)
271 {
272 	if (cmd->data != NULL && cmd->entry->free != NULL)
273 		cmd->entry->free(cmd);
274 	xfree(cmd);
275 }
276 
277 size_t
278 cmd_print(struct cmd *cmd, char *buf, size_t len)
279 {
280 	if (cmd->entry->print == NULL) {
281 		return (xsnprintf(buf, len, "%s", cmd->entry->name));
282 	}
283 	return (cmd->entry->print(cmd, buf, len));
284 }
285 
286 /*
287  * Figure out the current session. Use: 1) the current session, if the command
288  * context has one; 2) the session containing the pty of the calling client, if
289  * any 3) the session specified in the TMUX variable from the environment (as
290  * passed from the client); 3) the newest session.
291  */
292 struct session *
293 cmd_current_session(struct cmd_ctx *ctx)
294 {
295 	struct msg_command_data	*data = ctx->msgdata;
296 	struct client		*c = ctx->cmdclient;
297 	struct session		*s;
298 	struct sessions		 ss;
299 	struct winlink		*wl;
300 	struct window_pane	*wp;
301 	u_int			 i;
302 	int			 found;
303 
304 	if (ctx->curclient != NULL && ctx->curclient->session != NULL)
305 		return (ctx->curclient->session);
306 
307 	/*
308 	 * If the name of the calling client's pty is know, build a list of the
309 	 * sessions that contain it and if any choose either the first or the
310 	 * newest.
311 	 */
312 	if (c != NULL && c->tty.path != NULL) {
313 		ARRAY_INIT(&ss);
314 		for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
315 			if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
316 				continue;
317 			found = 0;
318 			RB_FOREACH(wl, winlinks, &s->windows) {
319 				TAILQ_FOREACH(wp, &wl->window->panes, entry) {
320 					if (strcmp(wp->tty, c->tty.path) == 0) {
321 						found = 1;
322 						break;
323 					}
324 				}
325 				if (found)
326 					break;
327 			}
328 			if (found)
329 				ARRAY_ADD(&ss, s);
330 		}
331 
332 		s = cmd_newest_session(&ss);
333 		ARRAY_FREE(&ss);
334 		if (s != NULL)
335 			return (s);
336 	}
337 
338 	/* Use the session from the TMUX environment variable. */
339 	if (data != NULL && data->pid != -1) {
340 		if (data->pid != getpid())
341 			return (NULL);
342 		if (data->idx > ARRAY_LENGTH(&sessions))
343 			return (NULL);
344 		if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL)
345 			return (NULL);
346 		return (s);
347 	}
348 
349 	return (cmd_newest_session(&sessions));
350 }
351 
352 /* Find the newest session. */
353 struct session *
354 cmd_newest_session(struct sessions *ss)
355 {
356 	struct session	*s, *snewest;
357 	struct timeval	*tv = NULL;
358 	u_int		 i;
359 
360 	snewest = NULL;
361 	for (i = 0; i < ARRAY_LENGTH(ss); i++) {
362 		if ((s = ARRAY_ITEM(ss, i)) == NULL)
363 			continue;
364 
365 		if (tv == NULL || timercmp(&s->tv, tv, >)) {
366 			snewest = s;
367 			tv = &s->tv;
368 		}
369 	}
370 
371 	return (snewest);
372 }
373 
374 /* Find the newest client. */
375 struct client *
376 cmd_newest_client(void)
377 {
378 	struct client	*c, *cnewest;
379 	struct timeval	*tv = NULL;
380 	u_int		 i;
381 
382 	cnewest = NULL;
383 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
384 		if ((c = ARRAY_ITEM(&clients, i)) == NULL)
385 			continue;
386 		if (c->session == NULL)
387 			continue;
388 
389 		if (tv == NULL || timercmp(&c->tv, tv, >)) {
390 			cnewest = c;
391 			tv = &c->tv;
392 		}
393 	}
394 
395 	return (cnewest);
396 }
397 
398 /* Find the target client or report an error and return NULL. */
399 struct client *
400 cmd_find_client(struct cmd_ctx *ctx, const char *arg)
401 {
402 	struct client	*c, *lastc;
403 	struct session	*s;
404 	char		*tmparg;
405 	size_t		 arglen;
406 	u_int		 i;
407 
408 	/* A NULL argument means the current client. */
409 	if (arg == NULL) {
410 		if (ctx->curclient != NULL)
411 			return (ctx->curclient);
412 		/*
413 		 * No current client set. Find the current session and see if
414 		 * it has only one client.
415 		 */
416 		s = cmd_current_session(ctx);
417 		if (s != NULL) {
418 			lastc = NULL;
419 			for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
420 				c = ARRAY_ITEM(&clients, i);
421 				if (c != NULL && c->session == s) {
422 					if (lastc != NULL)
423 						break;
424 					lastc = c;
425 				}
426 			}
427 			if (i == ARRAY_LENGTH(&clients) && lastc != NULL)
428 				return (lastc);
429 		}
430 		return (cmd_newest_client());
431 	}
432 	tmparg = xstrdup(arg);
433 
434 	/* Trim a single trailing colon if any. */
435 	arglen = strlen(tmparg);
436 	if (arglen != 0 && tmparg[arglen - 1] == ':')
437 		tmparg[arglen - 1] = '\0';
438 
439 	/* Find the client, if any. */
440 	c = cmd_lookup_client(tmparg);
441 
442 	/* If no client found, report an error. */
443 	if (c == NULL)
444 		ctx->error(ctx, "client not found: %s", tmparg);
445 
446 	xfree(tmparg);
447 	return (c);
448 }
449 
450 /*
451  * Lookup a client by device path. Either of a full match and a match without a
452  * leading _PATH_DEV ("/dev/") is accepted.
453  */
454 struct client *
455 cmd_lookup_client(const char *name)
456 {
457 	struct client	*c;
458 	const char	*path;
459 	u_int		 i;
460 
461 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
462 		c = ARRAY_ITEM(&clients, i);
463 		if (c == NULL || c->session == NULL)
464 			continue;
465 		path = c->tty.path;
466 
467 		/* Check for exact matches. */
468 		if (strcmp(name, path) == 0)
469 			return (c);
470 
471 		/* Check without leading /dev if present. */
472 		if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
473 			continue;
474 		if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
475 			return (c);
476 	}
477 
478 	return (NULL);
479 }
480 
481 /* Lookup a session by name. If no session is found, NULL is returned. */
482 struct session *
483 cmd_lookup_session(const char *name, int *ambiguous)
484 {
485 	struct session	*s, *sfound;
486 	u_int		 i;
487 
488 	*ambiguous = 0;
489 
490 	/*
491 	 * Look for matches. Session names must be unique so an exact match
492 	 * can't be ambigious and can just be returned.
493 	 */
494 	sfound = NULL;
495 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
496 		if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
497 			continue;
498 
499 		/* Check for an exact match and return it if found. */
500 		if (strcmp(name, s->name) == 0)
501 			return (s);
502 
503 		/* Then check for pattern matches. */
504 		if (strncmp(name, s->name, strlen(name)) == 0 ||
505 		    fnmatch(name, s->name, 0) == 0) {
506 			if (sfound != NULL) {
507 				*ambiguous = 1;
508 				return (NULL);
509 			}
510 			sfound = s;
511 		}
512 	}
513 
514  	return (sfound);
515 }
516 
517 /*
518  * Lookup a window or return -1 if not found or ambigious. First try as an
519  * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
520  * idx if the window index is a valid number but there is now window with that
521  * index.
522  */
523 struct winlink *
524 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
525 {
526 	struct winlink	*wl, *wlfound;
527 	const char	*errstr;
528 	u_int		 idx;
529 
530 	*ambiguous = 0;
531 
532 	/* First see if this is a valid window index in this session. */
533 	idx = strtonum(name, 0, INT_MAX, &errstr);
534 	if (errstr == NULL) {
535 		if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
536 			return (wl);
537 	}
538 
539 	/* Look for exact matches, error if more than one. */
540 	wlfound = NULL;
541 	RB_FOREACH(wl, winlinks, &s->windows) {
542 		if (strcmp(name, wl->window->name) == 0) {
543 			if (wlfound != NULL) {
544 				*ambiguous = 1;
545 				return (NULL);
546 			}
547 			wlfound = wl;
548 		}
549 	}
550 	if (wlfound != NULL)
551 		return (wlfound);
552 
553 	/* Now look for pattern matches, again error if multiple. */
554 	wlfound = NULL;
555 	RB_FOREACH(wl, winlinks, &s->windows) {
556 		if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
557 		    fnmatch(name, wl->window->name, 0) == 0) {
558 			if (wlfound != NULL) {
559 				*ambiguous = 1;
560 				return (NULL);
561 			}
562 			wlfound = wl;
563 		}
564 	}
565 	if (wlfound != NULL)
566 		return (wlfound);
567 
568 	return (NULL);
569 }
570 
571 /*
572  * Find a window index - if the window doesn't exist, check if it is a
573  * potential index and return it anyway.
574  */
575 int
576 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
577 {
578 	struct winlink	*wl;
579 	const char	*errstr;
580 	u_int		 idx;
581 
582 	if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
583 		return (wl->idx);
584 	if (*ambiguous)
585 		return (-1);
586 
587 	idx = strtonum(name, 0, INT_MAX, &errstr);
588 	if (errstr == NULL)
589 		return (idx);
590 
591 	return (-1);
592 }
593 
594 /* Find the target session or report an error and return NULL. */
595 struct session *
596 cmd_find_session(struct cmd_ctx *ctx, const char *arg)
597 {
598 	struct session	*s;
599 	struct client	*c;
600 	char		*tmparg;
601 	size_t		 arglen;
602 	int		 ambiguous;
603 
604 	/* A NULL argument means the current session. */
605 	if (arg == NULL)
606 		return (cmd_current_session(ctx));
607 	tmparg = xstrdup(arg);
608 
609 	/* Trim a single trailing colon if any. */
610 	arglen = strlen(tmparg);
611 	if (arglen != 0 && tmparg[arglen - 1] == ':')
612 		tmparg[arglen - 1] = '\0';
613 
614 	/* Find the session, if any. */
615 	s = cmd_lookup_session(tmparg, &ambiguous);
616 
617 	/* If it doesn't, try to match it as a client. */
618 	if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
619 		s = c->session;
620 
621 	/* If no session found, report an error. */
622 	if (s == NULL) {
623 		if (ambiguous)
624 			ctx->error(ctx, "more than one session: %s", tmparg);
625 		else
626 			ctx->error(ctx, "session not found: %s", tmparg);
627 	}
628 
629 	xfree(tmparg);
630 	return (s);
631 }
632 
633 /* Find the target session and window or report an error and return NULL. */
634 struct winlink *
635 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
636 {
637 	struct session	*s;
638 	struct winlink	*wl;
639 	const char	*winptr;
640 	char		*sessptr = NULL;
641 	int		 ambiguous = 0;
642 
643 	/*
644 	 * Find the current session. There must always be a current session, if
645 	 * it can't be found, report an error.
646 	 */
647 	if ((s = cmd_current_session(ctx)) == NULL) {
648 		ctx->error(ctx, "can't establish current session");
649 		return (NULL);
650 	}
651 
652 	/* A NULL argument means the current session and window. */
653 	if (arg == NULL) {
654 		if (sp != NULL)
655 			*sp = s;
656 		return (s->curw);
657 	}
658 
659 	/* Time to look at the argument. If it is empty, that is an error. */
660 	if (*arg == '\0')
661 		goto not_found;
662 
663 	/* Find the separating colon and split into window and session. */
664 	winptr = strchr(arg, ':');
665 	if (winptr == NULL)
666 		goto no_colon;
667 	winptr++;	/* skip : */
668 	sessptr = xstrdup(arg);
669 	*strchr(sessptr, ':') = '\0';
670 
671 	/* Try to lookup the session if present. */
672 	if (*sessptr != '\0') {
673 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
674 			goto no_session;
675 	}
676 	if (sp != NULL)
677 		*sp = s;
678 
679 	/*
680 	 * Then work out the window. An empty string is the current window,
681 	 * otherwise try to look it up in the session.
682 	 */
683 	if (*winptr == '\0')
684 		wl = s->curw;
685 	else if ((wl = cmd_lookup_window(s, winptr, &ambiguous)) == NULL)
686 		goto not_found;
687 
688 	if (sessptr != NULL)
689 		xfree(sessptr);
690 	return (wl);
691 
692 no_colon:
693 	/* No colon in the string, first try as a window then as a session. */
694 	if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) {
695 		if (ambiguous)
696 			goto not_found;
697 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
698 			goto no_session;
699 		wl = s->curw;
700 	}
701 
702 	if (sp != NULL)
703 		*sp = s;
704 
705 	return (wl);
706 
707 no_session:
708 	if (ambiguous)
709 		ctx->error(ctx, "multiple sessions: %s", arg);
710 	else
711 		ctx->error(ctx, "session not found: %s", arg);
712 	if (sessptr != NULL)
713 		xfree(sessptr);
714 	return (NULL);
715 
716 not_found:
717 	if (ambiguous)
718 		ctx->error(ctx, "multiple windows: %s", arg);
719 	else
720 		ctx->error(ctx, "window not found: %s", arg);
721 	if (sessptr != NULL)
722 		xfree(sessptr);
723 	return (NULL);
724 }
725 
726 /*
727  * Find the target session and window index, whether or not it exists in the
728  * session. Return -2 on error or -1 if no window index is specified. This is
729  * used when parsing an argument for a window target that may not exist (for
730  * example if it is going to be created).
731  */
732 int
733 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
734 {
735 	struct session	*s;
736 	const char	*winptr;
737 	char		*sessptr = NULL;
738 	int		 idx, ambiguous = 0;
739 
740 	/*
741 	 * Find the current session. There must always be a current session, if
742 	 * it can't be found, report an error.
743 	 */
744 	if ((s = cmd_current_session(ctx)) == NULL) {
745 		ctx->error(ctx, "can't establish current session");
746 		return (-2);
747 	}
748 
749 	/* A NULL argument means the current session and "no window" (-1). */
750 	if (arg == NULL) {
751 		if (sp != NULL)
752 			*sp = s;
753 		return (-1);
754 	}
755 
756 	/* Time to look at the argument. If it is empty, that is an error. */
757 	if (*arg == '\0')
758 		goto not_found;
759 
760 	/* Find the separating colon. If none, assume the current session. */
761 	winptr = strchr(arg, ':');
762 	if (winptr == NULL)
763 		goto no_colon;
764 	winptr++;	/* skip : */
765 	sessptr = xstrdup(arg);
766 	*strchr(sessptr, ':') = '\0';
767 
768 	/* Try to lookup the session if present. */
769 	if (sessptr != NULL && *sessptr != '\0') {
770 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
771 			goto no_session;
772 	}
773 	if (sp != NULL)
774 		*sp = s;
775 
776 	/*
777 	 * Then work out the window. An empty string is a new window otherwise
778 	 * try to look it up in the session.
779 	 */
780 	if (*winptr == '\0')
781 		 idx = -1;
782 	else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) {
783 		if (ambiguous)
784 			goto not_found;
785 		ctx->error(ctx, "invalid index: %s", arg);
786 		idx = -2;
787 	}
788 
789 	if (sessptr != NULL)
790 		xfree(sessptr);
791 	return (idx);
792 
793 no_colon:
794 	/* No colon in the string, first try as a window then as a session. */
795 	if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) {
796 		if (ambiguous)
797 			goto not_found;
798 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
799 			goto no_session;
800 		idx = -1;
801 	}
802 
803 	if (sp != NULL)
804 		*sp = s;
805 
806 	return (idx);
807 
808 no_session:
809 	if (ambiguous)
810  		ctx->error(ctx, "multiple sessions: %s", arg);
811 	else
812 		ctx->error(ctx, "session not found: %s", arg);
813 	if (sessptr != NULL)
814 		xfree(sessptr);
815 	return (-2);
816 
817 not_found:
818 	if (ambiguous)
819 		ctx->error(ctx, "multiple windows: %s", arg);
820 	else
821 		ctx->error(ctx, "window not found: %s", arg);
822 	if (sessptr != NULL)
823 		xfree(sessptr);
824 	return (-2);
825 }
826 
827 /*
828  * Find the target session, window and pane number or report an error and
829  * return NULL. The pane number is separated from the session:window by a .,
830  * such as mysession:mywindow.0.
831  */
832 struct winlink *
833 cmd_find_pane(struct cmd_ctx *ctx,
834     const char *arg, struct session **sp, struct window_pane **wpp)
835 {
836 	struct session	*s;
837 	struct winlink	*wl;
838 	const char	*period;
839 	char		*winptr, *paneptr;
840 	const char	*errstr;
841 	u_int		 idx;
842 
843 	/* Get the current session. */
844 	if ((s = cmd_current_session(ctx)) == NULL) {
845 		ctx->error(ctx, "can't establish current session");
846 		return (NULL);
847 	}
848 	if (sp != NULL)
849 		*sp = s;
850 
851 	/* A NULL argument means the current session, window and pane. */
852 	if (arg == NULL) {
853 		*wpp = s->curw->window->active;
854 		return (s->curw);
855 	}
856 
857 	/* Look for a separating period. */
858 	if ((period = strrchr(arg, '.')) == NULL)
859 		goto no_period;
860 
861 	/* Pull out the window part and parse it. */
862 	winptr = xstrdup(arg);
863 	winptr[period - arg] = '\0';
864 	if (*winptr == '\0')
865 		wl = s->curw;
866 	else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
867 		goto error;
868 
869 	/* Find the pane section and look it up. */
870 	paneptr = winptr + (period - arg) + 1;
871 	if (*paneptr == '\0')
872 		*wpp = wl->window->active;
873 	else {
874 		idx = strtonum(paneptr, 0, INT_MAX, &errstr);
875 		if (errstr != NULL) {
876 			ctx->error(ctx, "pane %s: %s", errstr, paneptr);
877 			goto error;
878 		}
879 		*wpp = window_pane_at_index(wl->window, idx);
880 		if (*wpp == NULL) {
881 			ctx->error(ctx, "no such pane: %u", idx);
882 			goto error;
883 		}
884 	}
885 
886 	xfree(winptr);
887 	return (wl);
888 
889 no_period:
890 	/* Try as a pane number alone. */
891 	idx = strtonum(arg, 0, INT_MAX, &errstr);
892 	if (errstr != NULL)
893 		goto lookup_window;
894 
895 	/* Try index in the current session and window. */
896 	if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
897 		goto lookup_window;
898 
899 	return (s->curw);
900 
901 lookup_window:
902 	/* Try as a window and use the active pane. */
903 	if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
904 		*wpp = wl->window->active;
905 	return (wl);
906 
907 error:
908 	xfree(winptr);
909 	return (NULL);
910 }
911 
912 /* Replace the first %% or %idx in template by s. */
913 char *
914 cmd_template_replace(char *template, const char *s, int idx)
915 {
916 	char	 ch;
917 	char	*buf, *ptr;
918 	int	 replaced;
919 	size_t	 len;
920 
921 	if (strstr(template, "%") == NULL)
922 		return (xstrdup(template));
923 
924 	buf = xmalloc(1);
925 	*buf = '\0';
926 	len = 0;
927 	replaced = 0;
928 
929 	ptr = template;
930 	while (*ptr != '\0') {
931 		switch (ch = *ptr++) {
932 		case '%':
933 			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
934 				if (*ptr != '%' || replaced)
935 					break;
936 				replaced = 1;
937 			}
938 			ptr++;
939 
940 			len += strlen(s);
941 			buf = xrealloc(buf, 1, len + 1);
942 			strlcat(buf, s, len + 1);
943 			continue;
944 		}
945 		buf = xrealloc(buf, 1, len + 2);
946 		buf[len++] = ch;
947 		buf[len] = '\0';
948 	}
949 
950 	return (buf);
951 }
952