xref: /openbsd-src/usr.bin/tmux/cmd.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: cmd.c,v 1.24 2009/10/11 10:04:27 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 <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;
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 			c = NULL;
419 			for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
420 				if (ARRAY_ITEM(&clients, i)->session == s) {
421 					if (c != NULL)
422 						break;
423 					c = ARRAY_ITEM(&clients, i);
424 				}
425 			}
426 			if (i == ARRAY_LENGTH(&clients) && c != NULL)
427 				return (c);
428 		}
429 		return (cmd_newest_client());
430 	}
431 	tmparg = xstrdup(arg);
432 
433 	/* Trim a single trailing colon if any. */
434 	arglen = strlen(tmparg);
435 	if (arglen != 0 && tmparg[arglen - 1] == ':')
436 		tmparg[arglen - 1] = '\0';
437 
438 	/* Find the client, if any. */
439 	c = cmd_lookup_client(tmparg);
440 
441 	/* If no client found, report an error. */
442 	if (c == NULL)
443 		ctx->error(ctx, "client not found: %s", tmparg);
444 
445 	xfree(tmparg);
446 	return (c);
447 }
448 
449 /*
450  * Lookup a client by device path. Either of a full match and a match without a
451  * leading _PATH_DEV ("/dev/") is accepted.
452  */
453 struct client *
454 cmd_lookup_client(const char *name)
455 {
456 	struct client	*c;
457 	const char	*path;
458 	u_int		 i;
459 
460 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
461 		if ((c = ARRAY_ITEM(&clients, i)) == NULL)
462 			continue;
463 		path = c->tty.path;
464 
465 		/* Check for exact matches. */
466 		if (strcmp(name, path) == 0)
467 			return (c);
468 
469 		/* Check without leading /dev if present. */
470 		if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
471 			continue;
472 		if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
473 			return (c);
474 	}
475 
476 	return (NULL);
477 }
478 
479 /* Lookup a session by name. If no session is found, NULL is returned. */
480 struct session *
481 cmd_lookup_session(const char *name, int *ambiguous)
482 {
483 	struct session	*s, *sfound;
484 	u_int		 i;
485 
486 	*ambiguous = 0;
487 
488 	/*
489 	 * Look for matches. Session names must be unique so an exact match
490 	 * can't be ambigious and can just be returned.
491 	 */
492 	sfound = NULL;
493 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
494 		if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
495 			continue;
496 
497 		/* Check for an exact match and return it if found. */
498 		if (strcmp(name, s->name) == 0)
499 			return (s);
500 
501 		/* Then check for pattern matches. */
502 		if (strncmp(name, s->name, strlen(name)) == 0 ||
503 		    fnmatch(name, s->name, 0) == 0) {
504 			if (sfound != NULL) {
505 				*ambiguous = 1;
506 				return (NULL);
507 			}
508 			sfound = s;
509 		}
510 	}
511 
512  	return (sfound);
513 }
514 
515 /*
516  * Lookup a window or return -1 if not found or ambigious. First try as an
517  * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
518  * idx if the window index is a valid number but there is now window with that
519  * index.
520  */
521 struct winlink *
522 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
523 {
524 	struct winlink	*wl, *wlfound;
525 	const char	*errstr;
526 	u_int		 idx;
527 
528 	*ambiguous = 0;
529 
530 	/* First see if this is a valid window index in this session. */
531 	idx = strtonum(name, 0, INT_MAX, &errstr);
532 	if (errstr == NULL) {
533 		if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
534 			return (wl);
535 	}
536 
537 	/* Look for exact matches, error if more than one. */
538 	wlfound = NULL;
539 	RB_FOREACH(wl, winlinks, &s->windows) {
540 		if (strcmp(name, wl->window->name) == 0) {
541 			if (wlfound != NULL) {
542 				*ambiguous = 1;
543 				return (NULL);
544 			}
545 			wlfound = wl;
546 		}
547 	}
548 	if (wlfound != NULL)
549 		return (wlfound);
550 
551 	/* Now look for pattern matches, again error if multiple. */
552 	wlfound = NULL;
553 	RB_FOREACH(wl, winlinks, &s->windows) {
554 		if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
555 		    fnmatch(name, wl->window->name, 0) == 0) {
556 			if (wlfound != NULL) {
557 				*ambiguous = 1;
558 				return (NULL);
559 			}
560 			wlfound = wl;
561 		}
562 	}
563 	if (wlfound != NULL)
564 		return (wlfound);
565 
566 	return (NULL);
567 }
568 
569 /*
570  * Find a window index - if the window doesn't exist, check if it is a
571  * potential index and return it anyway.
572  */
573 int
574 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
575 {
576 	struct winlink	*wl;
577 	const char	*errstr;
578 	u_int		 idx;
579 
580 	if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
581 		return (wl->idx);
582 	if (*ambiguous)
583 		return (-1);
584 
585 	idx = strtonum(name, 0, INT_MAX, &errstr);
586 	if (errstr == NULL)
587 		return (idx);
588 
589 	return (-1);
590 }
591 
592 /* Find the target session or report an error and return NULL. */
593 struct session *
594 cmd_find_session(struct cmd_ctx *ctx, const char *arg)
595 {
596 	struct session	*s;
597 	struct client	*c;
598 	char		*tmparg;
599 	size_t		 arglen;
600 	int		 ambiguous;
601 
602 	/* A NULL argument means the current session. */
603 	if (arg == NULL)
604 		return (cmd_current_session(ctx));
605 	tmparg = xstrdup(arg);
606 
607 	/* Trim a single trailing colon if any. */
608 	arglen = strlen(tmparg);
609 	if (arglen != 0 && tmparg[arglen - 1] == ':')
610 		tmparg[arglen - 1] = '\0';
611 
612 	/* Find the session, if any. */
613 	s = cmd_lookup_session(tmparg, &ambiguous);
614 
615 	/* If it doesn't, try to match it as a client. */
616 	if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
617 		s = c->session;
618 
619 	/* If no session found, report an error. */
620 	if (s == NULL) {
621 		if (ambiguous)
622 			ctx->error(ctx, "more than one session: %s", tmparg);
623 		else
624 			ctx->error(ctx, "session not found: %s", tmparg);
625 	}
626 
627 	xfree(tmparg);
628 	return (s);
629 }
630 
631 /* Find the target session and window or report an error and return NULL. */
632 struct winlink *
633 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
634 {
635 	struct session	*s;
636 	struct winlink	*wl;
637 	const char	*winptr;
638 	char		*sessptr = NULL;
639 	int		 ambiguous = 0;
640 
641 	/*
642 	 * Find the current session. There must always be a current session, if
643 	 * it can't be found, report an error.
644 	 */
645 	if ((s = cmd_current_session(ctx)) == NULL) {
646 		ctx->error(ctx, "can't establish current session");
647 		return (NULL);
648 	}
649 
650 	/* A NULL argument means the current session and window. */
651 	if (arg == NULL) {
652 		if (sp != NULL)
653 			*sp = s;
654 		return (s->curw);
655 	}
656 
657 	/* Time to look at the argument. If it is empty, that is an error. */
658 	if (*arg == '\0')
659 		goto not_found;
660 
661 	/* Find the separating colon and split into window and session. */
662 	winptr = strchr(arg, ':');
663 	if (winptr == NULL)
664 		goto no_colon;
665 	winptr++;	/* skip : */
666 	sessptr = xstrdup(arg);
667 	*strchr(sessptr, ':') = '\0';
668 
669 	/* Try to lookup the session if present. */
670 	if (*sessptr != '\0') {
671 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
672 			goto no_session;
673 	}
674 	if (sp != NULL)
675 		*sp = s;
676 
677 	/*
678 	 * Then work out the window. An empty string is the current window,
679 	 * otherwise try to look it up in the session.
680 	 */
681 	if (*winptr == '\0')
682 		wl = s->curw;
683 	else if ((wl = cmd_lookup_window(s, winptr, &ambiguous)) == NULL)
684 		goto not_found;
685 
686 	if (sessptr != NULL)
687 		xfree(sessptr);
688 	return (wl);
689 
690 no_colon:
691 	/* No colon in the string, first try as a window then as a session. */
692 	if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) {
693 		if (ambiguous)
694 			goto not_found;
695 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
696 			goto no_session;
697 		wl = s->curw;
698 	}
699 
700 	if (sp != NULL)
701 		*sp = s;
702 
703 	return (wl);
704 
705 no_session:
706 	if (ambiguous)
707 		ctx->error(ctx, "multiple sessions: %s", arg);
708 	else
709 		ctx->error(ctx, "session not found: %s", arg);
710 	if (sessptr != NULL)
711 		xfree(sessptr);
712 	return (NULL);
713 
714 not_found:
715 	if (ambiguous)
716 		ctx->error(ctx, "multiple windows: %s", arg);
717 	else
718 		ctx->error(ctx, "window not found: %s", arg);
719 	if (sessptr != NULL)
720 		xfree(sessptr);
721 	return (NULL);
722 }
723 
724 /*
725  * Find the target session and window index, whether or not it exists in the
726  * session. Return -2 on error or -1 if no window index is specified. This is
727  * used when parsing an argument for a window target that may not exist (for
728  * example if it is going to be created).
729  */
730 int
731 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
732 {
733 	struct session	*s;
734 	const char	*winptr;
735 	char		*sessptr = NULL;
736 	int		 idx, ambiguous = 0;
737 
738 	/*
739 	 * Find the current session. There must always be a current session, if
740 	 * it can't be found, report an error.
741 	 */
742 	if ((s = cmd_current_session(ctx)) == NULL) {
743 		ctx->error(ctx, "can't establish current session");
744 		return (-2);
745 	}
746 
747 	/* A NULL argument means the current session and "no window" (-1). */
748 	if (arg == NULL) {
749 		if (sp != NULL)
750 			*sp = s;
751 		return (-1);
752 	}
753 
754 	/* Time to look at the argument. If it is empty, that is an error. */
755 	if (*arg == '\0')
756 		goto not_found;
757 
758 	/* Find the separating colon. If none, assume the current session. */
759 	winptr = strchr(arg, ':');
760 	if (winptr == NULL)
761 		goto no_colon;
762 	winptr++;	/* skip : */
763 	sessptr = xstrdup(arg);
764 	*strchr(sessptr, ':') = '\0';
765 
766 	/* Try to lookup the session if present. */
767 	if (sessptr != NULL && *sessptr != '\0') {
768 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
769 			goto no_session;
770 	}
771 	if (sp != NULL)
772 		*sp = s;
773 
774 	/*
775 	 * Then work out the window. An empty string is a new window otherwise
776 	 * try to look it up in the session.
777 	 */
778 	if (*winptr == '\0')
779 		 idx = -1;
780 	else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) {
781 		if (ambiguous)
782 			goto not_found;
783 		ctx->error(ctx, "invalid index: %s", arg);
784 		idx = -2;
785 	}
786 
787 	if (sessptr != NULL)
788 		xfree(sessptr);
789 	return (idx);
790 
791 no_colon:
792 	/* No colon in the string, first try as a window then as a session. */
793 	if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) {
794 		if (ambiguous)
795 			goto not_found;
796 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
797 			goto no_session;
798 		idx = -1;
799 	}
800 
801 	if (sp != NULL)
802 		*sp = s;
803 
804 	return (idx);
805 
806 no_session:
807 	if (ambiguous)
808  		ctx->error(ctx, "multiple sessions: %s", arg);
809 	else
810 		ctx->error(ctx, "session not found: %s", arg);
811 	if (sessptr != NULL)
812 		xfree(sessptr);
813 	return (-2);
814 
815 not_found:
816 	if (ambiguous)
817 		ctx->error(ctx, "multiple windows: %s", arg);
818 	else
819 		ctx->error(ctx, "window not found: %s", arg);
820 	if (sessptr != NULL)
821 		xfree(sessptr);
822 	return (-2);
823 }
824 
825 /*
826  * Find the target session, window and pane number or report an error and
827  * return NULL. The pane number is separated from the session:window by a .,
828  * such as mysession:mywindow.0.
829  */
830 struct winlink *
831 cmd_find_pane(struct cmd_ctx *ctx,
832     const char *arg, struct session **sp, struct window_pane **wpp)
833 {
834 	struct session	*s;
835 	struct winlink	*wl;
836 	const char	*period;
837 	char		*winptr, *paneptr;
838 	const char	*errstr;
839 	u_int		 idx;
840 
841 	/* Get the current session. */
842 	if ((s = cmd_current_session(ctx)) == NULL) {
843        		ctx->error(ctx, "can't establish current session");
844 		return (NULL);
845 	}
846 	if (sp != NULL)
847 		*sp = s;
848 
849 	/* A NULL argument means the current session, window and pane. */
850 	if (arg == NULL) {
851 		*wpp = s->curw->window->active;
852 		return (s->curw);
853 	}
854 
855 	/* Look for a separating period. */
856 	if ((period = strrchr(arg, '.')) == NULL)
857 		goto no_period;
858 
859 	/* Pull out the window part and parse it. */
860 	winptr = xstrdup(arg);
861 	winptr[period - arg] = '\0';
862 	if (*winptr == '\0')
863 		wl = s->curw;
864 	else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
865 		goto error;
866 
867 	/* Find the pane section and look it up. */
868 	paneptr = winptr + (period - arg) + 1;
869 	if (*paneptr == '\0')
870 		*wpp = wl->window->active;
871 	else {
872 		idx = strtonum(paneptr, 0, INT_MAX, &errstr);
873 		if (errstr != NULL) {
874 			ctx->error(ctx, "pane %s: %s", errstr, paneptr);
875 			goto error;
876 		}
877 		*wpp = window_pane_at_index(wl->window, idx);
878 		if (*wpp == NULL) {
879 			ctx->error(ctx, "no such pane: %u", idx);
880 			goto error;
881 		}
882 	}
883 
884 	xfree(winptr);
885 	return (wl);
886 
887 no_period:
888 	/* Try as a pane number alone. */
889 	idx = strtonum(arg, 0, INT_MAX, &errstr);
890 	if (errstr != NULL)
891 		goto lookup_window;
892 
893 	/* Try index in the current session and window. */
894 	if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
895 		goto lookup_window;
896 
897 	return (s->curw);
898 
899 lookup_window:
900 	/* Try as a window and use the active pane. */
901 	if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
902 		*wpp = wl->window->active;
903 	return (wl);
904 
905 error:
906 	xfree(winptr);
907 	return (NULL);
908 }
909 
910 /* Replace the first %% or %idx in template by s. */
911 char *
912 cmd_template_replace(char *template, const char *s, int idx)
913 {
914 	char	 ch;
915 	char	*buf, *ptr;
916 	int	 replaced;
917 	size_t	 len;
918 
919 	if (strstr(template, "%") == NULL)
920 		return (xstrdup(template));
921 
922 	buf = xmalloc(1);
923 	*buf = '\0';
924 	len = 0;
925 	replaced = 0;
926 
927 	ptr = template;
928 	while (*ptr != '\0') {
929 		switch (ch = *ptr++) {
930 		case '%':
931 			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
932 				if (*ptr != '%' || replaced)
933 					break;
934 				replaced = 1;
935 			}
936 			ptr++;
937 
938 			len += strlen(s);
939 			buf = xrealloc(buf, 1, len + 1);
940 			strlcat(buf, s, len + 1);
941 			continue;
942 		}
943 		buf = xrealloc(buf, 1, len + 2);
944 		buf[len++] = ch;
945 		buf[len] = '\0';
946 	}
947 
948 	return (buf);
949 }
950