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