xref: /openbsd-src/usr.bin/tmux/cmd.c (revision cd1eb269cafb12c415be1749cd4a4b5422710415)
1 /* $OpenBSD: cmd.c,v 1.40 2010/05/05 23:24:23 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_find_window_entry,
49 	&cmd_has_session_entry,
50 	&cmd_if_shell_entry,
51 	&cmd_join_pane_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_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_messages_entry,
99 	&cmd_show_options_entry,
100 	&cmd_show_window_options_entry,
101 	&cmd_source_file_entry,
102 	&cmd_split_window_entry,
103 	&cmd_start_server_entry,
104 	&cmd_suspend_client_entry,
105 	&cmd_swap_pane_entry,
106 	&cmd_swap_window_entry,
107 	&cmd_switch_client_entry,
108 	&cmd_unbind_key_entry,
109 	&cmd_unlink_window_entry,
110 	NULL
111 };
112 
113 struct session	*cmd_choose_session(struct sessions *);
114 struct client	*cmd_choose_client(struct clients *);
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 	return (cmd->entry->print(cmd, buf, len));
283 }
284 
285 /*
286  * Figure out the current session. Use: 1) the current session, if the command
287  * context has one; 2) the most recently used session containing the pty of the
288  * calling client, if any; 3) the session specified in the TMUX variable from
289  * the environment (as passed from the client); 4) the most recently used
290  * session from all sessions.
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_choose_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_choose_session(&sessions));
350 }
351 
352 /* Find the most recently used session from a list. */
353 struct session *
354 cmd_choose_session(struct sessions *ss)
355 {
356 	struct session	*s, *sbest;
357 	struct timeval	*tv = NULL;
358 	u_int		 i;
359 
360 	sbest = 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->activity_time, tv, >)) {
366 			sbest = s;
367 			tv = &s->activity_time;
368 		}
369 	}
370 
371 	return (sbest);
372 }
373 
374 /*
375  * Find the current client. First try the current client if set, then pick the
376  * most recently used of the clients attached to the current session if any,
377  * then of all clients.
378  */
379 struct client *
380 cmd_current_client(struct cmd_ctx *ctx)
381 {
382 	struct session		*s;
383 	struct client		*c;
384 	struct clients		 cc;
385 	u_int			 i;
386 
387 	if (ctx->curclient != NULL)
388 		return (ctx->curclient);
389 
390 	/*
391 	 * No current client set. Find the current session and return the
392 	 * newest of its clients.
393 	 */
394 	s = cmd_current_session(ctx);
395 	if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
396 		ARRAY_INIT(&cc);
397 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
398 			if ((c = ARRAY_ITEM(&clients, i)) == NULL)
399 				continue;
400 			if (s == c->session)
401 				ARRAY_ADD(&cc, c);
402 		}
403 
404 		c = cmd_choose_client(&cc);
405 		ARRAY_FREE(&cc);
406 		if (c != NULL)
407 			return (c);
408 	}
409 
410 	return (cmd_choose_client(&clients));
411 }
412 
413 /* Choose the most recently used client from a list. */
414 struct client *
415 cmd_choose_client(struct clients *cc)
416 {
417 	struct client	*c, *cbest;
418 	struct timeval	*tv = NULL;
419 	u_int		 i;
420 
421 	cbest = NULL;
422 	for (i = 0; i < ARRAY_LENGTH(cc); i++) {
423 		if ((c = ARRAY_ITEM(cc, i)) == NULL)
424 			continue;
425 		if (c->session == NULL)
426 			continue;
427 
428 		if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
429 			cbest = c;
430 			tv = &c->activity_time;
431 		}
432 	}
433 
434 	return (cbest);
435 }
436 
437 /* Find the target client or report an error and return NULL. */
438 struct client *
439 cmd_find_client(struct cmd_ctx *ctx, const char *arg)
440 {
441 	struct client	*c;
442 	char		*tmparg;
443 	size_t		 arglen;
444 
445 	/* A NULL argument means the current client. */
446 	if (arg == NULL)
447 		return (cmd_current_client(ctx));
448 	tmparg = xstrdup(arg);
449 
450 	/* Trim a single trailing colon if any. */
451 	arglen = strlen(tmparg);
452 	if (arglen != 0 && tmparg[arglen - 1] == ':')
453 		tmparg[arglen - 1] = '\0';
454 
455 	/* Find the client, if any. */
456 	c = cmd_lookup_client(tmparg);
457 
458 	/* If no client found, report an error. */
459 	if (c == NULL)
460 		ctx->error(ctx, "client not found: %s", tmparg);
461 
462 	xfree(tmparg);
463 	return (c);
464 }
465 
466 /*
467  * Lookup a client by device path. Either of a full match and a match without a
468  * leading _PATH_DEV ("/dev/") is accepted.
469  */
470 struct client *
471 cmd_lookup_client(const char *name)
472 {
473 	struct client	*c;
474 	const char	*path;
475 	u_int		 i;
476 
477 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
478 		c = ARRAY_ITEM(&clients, i);
479 		if (c == NULL || c->session == NULL)
480 			continue;
481 		path = c->tty.path;
482 
483 		/* Check for exact matches. */
484 		if (strcmp(name, path) == 0)
485 			return (c);
486 
487 		/* Check without leading /dev if present. */
488 		if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
489 			continue;
490 		if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
491 			return (c);
492 	}
493 
494 	return (NULL);
495 }
496 
497 /* Lookup a session by name. If no session is found, NULL is returned. */
498 struct session *
499 cmd_lookup_session(const char *name, int *ambiguous)
500 {
501 	struct session	*s, *sfound;
502 	u_int		 i;
503 
504 	*ambiguous = 0;
505 
506 	/*
507 	 * Look for matches. First look for exact matches - session names must
508 	 * be unique so an exact match can't be ambigious and can just be
509 	 * returned.
510 	 */
511 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
512 		if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
513 			continue;
514 		if (strcmp(name, s->name) == 0)
515 			return (s);
516 	}
517 
518 	/*
519 	 * Otherwise look for partial matches, returning early if it is found to
520 	 * be ambiguous.
521 	 */
522 	sfound = NULL;
523 	for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
524 		if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
525 			continue;
526 		if (strncmp(name, s->name, strlen(name)) == 0 ||
527 		    fnmatch(name, s->name, 0) == 0) {
528 			if (sfound != NULL) {
529 				*ambiguous = 1;
530 				return (NULL);
531 			}
532 			sfound = s;
533 		}
534 	}
535 	return (sfound);
536 }
537 
538 /*
539  * Lookup a window or return -1 if not found or ambigious. First try as an
540  * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
541  * idx if the window index is a valid number but there is now window with that
542  * index.
543  */
544 struct winlink *
545 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
546 {
547 	struct winlink	*wl, *wlfound;
548 	const char	*errstr;
549 	u_int		 idx;
550 
551 	*ambiguous = 0;
552 
553 	/* First see if this is a valid window index in this session. */
554 	idx = strtonum(name, 0, INT_MAX, &errstr);
555 	if (errstr == NULL) {
556 		if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
557 			return (wl);
558 	}
559 
560 	/* Look for exact matches, error if more than one. */
561 	wlfound = NULL;
562 	RB_FOREACH(wl, winlinks, &s->windows) {
563 		if (strcmp(name, wl->window->name) == 0) {
564 			if (wlfound != NULL) {
565 				*ambiguous = 1;
566 				return (NULL);
567 			}
568 			wlfound = wl;
569 		}
570 	}
571 	if (wlfound != NULL)
572 		return (wlfound);
573 
574 	/* Now look for pattern matches, again error if multiple. */
575 	wlfound = NULL;
576 	RB_FOREACH(wl, winlinks, &s->windows) {
577 		if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
578 		    fnmatch(name, wl->window->name, 0) == 0) {
579 			if (wlfound != NULL) {
580 				*ambiguous = 1;
581 				return (NULL);
582 			}
583 			wlfound = wl;
584 		}
585 	}
586 	if (wlfound != NULL)
587 		return (wlfound);
588 
589 	return (NULL);
590 }
591 
592 /*
593  * Find a window index - if the window doesn't exist, check if it is a
594  * potential index and return it anyway.
595  */
596 int
597 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
598 {
599 	struct winlink	*wl;
600 	const char	*errstr;
601 	u_int		 idx;
602 
603 	if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
604 		return (wl->idx);
605 	if (*ambiguous)
606 		return (-1);
607 
608 	idx = strtonum(name, 0, INT_MAX, &errstr);
609 	if (errstr == NULL)
610 		return (idx);
611 
612 	return (-1);
613 }
614 
615 /* Find the target session or report an error and return NULL. */
616 struct session *
617 cmd_find_session(struct cmd_ctx *ctx, const char *arg)
618 {
619 	struct session	*s;
620 	struct client	*c;
621 	char		*tmparg;
622 	size_t		 arglen;
623 	int		 ambiguous;
624 
625 	/* A NULL argument means the current session. */
626 	if (arg == NULL)
627 		return (cmd_current_session(ctx));
628 	tmparg = xstrdup(arg);
629 
630 	/* Trim a single trailing colon if any. */
631 	arglen = strlen(tmparg);
632 	if (arglen != 0 && tmparg[arglen - 1] == ':')
633 		tmparg[arglen - 1] = '\0';
634 
635 	/* Find the session, if any. */
636 	s = cmd_lookup_session(tmparg, &ambiguous);
637 
638 	/* If it doesn't, try to match it as a client. */
639 	if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
640 		s = c->session;
641 
642 	/* If no session found, report an error. */
643 	if (s == NULL) {
644 		if (ambiguous)
645 			ctx->error(ctx, "more than one session: %s", tmparg);
646 		else
647 			ctx->error(ctx, "session not found: %s", tmparg);
648 	}
649 
650 	xfree(tmparg);
651 	return (s);
652 }
653 
654 /* Find the target session and window or report an error and return NULL. */
655 struct winlink *
656 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
657 {
658 	struct session	*s;
659 	struct winlink	*wl;
660 	const char	*winptr;
661 	char		*sessptr = NULL;
662 	int		 ambiguous = 0;
663 
664 	/*
665 	 * Find the current session. There must always be a current session, if
666 	 * it can't be found, report an error.
667 	 */
668 	if ((s = cmd_current_session(ctx)) == NULL) {
669 		ctx->error(ctx, "can't establish current session");
670 		return (NULL);
671 	}
672 
673 	/* A NULL argument means the current session and window. */
674 	if (arg == NULL) {
675 		if (sp != NULL)
676 			*sp = s;
677 		return (s->curw);
678 	}
679 
680 	/* Time to look at the argument. If it is empty, that is an error. */
681 	if (*arg == '\0')
682 		goto not_found;
683 
684 	/* Find the separating colon and split into window and session. */
685 	winptr = strchr(arg, ':');
686 	if (winptr == NULL)
687 		goto no_colon;
688 	winptr++;	/* skip : */
689 	sessptr = xstrdup(arg);
690 	*strchr(sessptr, ':') = '\0';
691 
692 	/* Try to lookup the session if present. */
693 	if (*sessptr != '\0') {
694 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
695 			goto no_session;
696 	}
697 	if (sp != NULL)
698 		*sp = s;
699 
700 	/*
701 	 * Then work out the window. An empty string is the current window,
702 	 * otherwise try special cases then to look it up in the session.
703 	 */
704 	if (*winptr == '\0')
705 		wl = s->curw;
706 	else if (winptr[0] == '!' && winptr[1] == '\0')
707 		wl = TAILQ_FIRST(&s->lastw);
708 	else if (winptr[0] == '+' && winptr[1] == '\0')
709 		wl = winlink_next(s->curw);
710 	else if (winptr[0] == '-' && winptr[1] == '\0')
711 		wl = winlink_previous(s->curw);
712 	else
713 		wl = cmd_lookup_window(s, winptr, &ambiguous);
714 	if (wl == NULL)
715 		goto not_found;
716 
717 	if (sessptr != NULL)
718 		xfree(sessptr);
719 	return (wl);
720 
721 no_colon:
722 	/*
723 	 * No colon in the string, first try special cases, then as a window
724 	 * and lastly as a session.
725 	 */
726 	if (arg[0] == '!' && arg[1] == '\0') {
727 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
728 			goto not_found;
729 	} else if (arg[0] == '+' && arg[1] == '\0') {
730 		if ((wl = winlink_next(s->curw)) == NULL)
731 			goto not_found;
732 	} else if (arg[0] == '-' && arg[1] == '\0') {
733 		if ((wl = winlink_previous(s->curw)) == NULL)
734 			goto not_found;
735 	} else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) {
736 		if (ambiguous)
737 			goto not_found;
738 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
739 			goto no_session;
740 		wl = s->curw;
741 	}
742 
743 	if (sp != NULL)
744 		*sp = s;
745 
746 	return (wl);
747 
748 no_session:
749 	if (ambiguous)
750 		ctx->error(ctx, "multiple sessions: %s", arg);
751 	else
752 		ctx->error(ctx, "session not found: %s", arg);
753 	if (sessptr != NULL)
754 		xfree(sessptr);
755 	return (NULL);
756 
757 not_found:
758 	if (ambiguous)
759 		ctx->error(ctx, "multiple windows: %s", arg);
760 	else
761 		ctx->error(ctx, "window not found: %s", arg);
762 	if (sessptr != NULL)
763 		xfree(sessptr);
764 	return (NULL);
765 }
766 
767 /*
768  * Find the target session and window index, whether or not it exists in the
769  * session. Return -2 on error or -1 if no window index is specified. This is
770  * used when parsing an argument for a window target that may not exist (for
771  * example if it is going to be created).
772  */
773 int
774 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
775 {
776 	struct session	*s;
777 	struct winlink	*wl;
778 	const char	*winptr;
779 	char		*sessptr = NULL;
780 	int		 idx, ambiguous = 0;
781 
782 	/*
783 	 * Find the current session. There must always be a current session, if
784 	 * it can't be found, report an error.
785 	 */
786 	if ((s = cmd_current_session(ctx)) == NULL) {
787 		ctx->error(ctx, "can't establish current session");
788 		return (-2);
789 	}
790 
791 	/* A NULL argument means the current session and "no window" (-1). */
792 	if (arg == NULL) {
793 		if (sp != NULL)
794 			*sp = s;
795 		return (-1);
796 	}
797 
798 	/* Time to look at the argument. If it is empty, that is an error. */
799 	if (*arg == '\0')
800 		goto not_found;
801 
802 	/* Find the separating colon. If none, assume the current session. */
803 	winptr = strchr(arg, ':');
804 	if (winptr == NULL)
805 		goto no_colon;
806 	winptr++;	/* skip : */
807 	sessptr = xstrdup(arg);
808 	*strchr(sessptr, ':') = '\0';
809 
810 	/* Try to lookup the session if present. */
811 	if (sessptr != NULL && *sessptr != '\0') {
812 		if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
813 			goto no_session;
814 	}
815 	if (sp != NULL)
816 		*sp = s;
817 
818 	/*
819 	 * Then work out the window. An empty string is a new window otherwise
820 	 * try to look it up in the session.
821 	 */
822 	if (*winptr == '\0')
823 		idx = -1;
824 	else if (winptr[0] == '!' && winptr[1] == '\0') {
825 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
826 			goto not_found;
827 		idx = wl->idx;
828 	} else if (winptr[0] == '+' && winptr[1] == '\0') {
829 		if (s->curw->idx == INT_MAX)
830 			goto not_found;
831 		idx = s->curw->idx + 1;
832 	} else if (winptr[0] == '-' && winptr[1] == '\0') {
833 		if (s->curw->idx == 0)
834 			goto not_found;
835 		idx = s->curw->idx - 1;
836 	} else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) {
837 		if (ambiguous)
838 			goto not_found;
839 		ctx->error(ctx, "invalid index: %s", arg);
840 		idx = -2;
841 	}
842 
843 	if (sessptr != NULL)
844 		xfree(sessptr);
845 	return (idx);
846 
847 no_colon:
848 	/*
849 	 * No colon in the string, first try special cases, then as a window
850 	 * and lastly as a session.
851 	 */
852 	if (arg[0] == '!' && arg[1] == '\0') {
853 		if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
854 			goto not_found;
855 		idx = wl->idx;
856 	} else if (arg[0] == '+' && arg[1] == '\0') {
857 		if (s->curw->idx == INT_MAX)
858 			goto not_found;
859 		idx = s->curw->idx + 1;
860 	} else if (arg[0] == '-' && arg[1] == '\0') {
861 		if (s->curw->idx == 0)
862 			goto not_found;
863 		idx = s->curw->idx - 1;
864 	} else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) {
865 		if (ambiguous)
866 			goto not_found;
867 		if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
868 			goto no_session;
869 		idx = -1;
870 	}
871 
872 	if (sp != NULL)
873 		*sp = s;
874 
875 	return (idx);
876 
877 no_session:
878 	if (ambiguous)
879 		ctx->error(ctx, "multiple sessions: %s", arg);
880 	else
881 		ctx->error(ctx, "session not found: %s", arg);
882 	if (sessptr != NULL)
883 		xfree(sessptr);
884 	return (-2);
885 
886 not_found:
887 	if (ambiguous)
888 		ctx->error(ctx, "multiple windows: %s", arg);
889 	else
890 		ctx->error(ctx, "window not found: %s", arg);
891 	if (sessptr != NULL)
892 		xfree(sessptr);
893 	return (-2);
894 }
895 
896 /*
897  * Find the target session, window and pane number or report an error and
898  * return NULL. The pane number is separated from the session:window by a .,
899  * such as mysession:mywindow.0.
900  */
901 struct winlink *
902 cmd_find_pane(struct cmd_ctx *ctx,
903     const char *arg, struct session **sp, struct window_pane **wpp)
904 {
905 	struct session		*s;
906 	struct winlink		*wl;
907 	struct layout_cell	*lc;
908 	const char		*period, *errstr;
909 	char			*winptr, *paneptr;
910 	u_int			 idx;
911 
912 	/* Get the current session. */
913 	if ((s = cmd_current_session(ctx)) == NULL) {
914 		ctx->error(ctx, "can't establish current session");
915 		return (NULL);
916 	}
917 	if (sp != NULL)
918 		*sp = s;
919 
920 	/* A NULL argument means the current session, window and pane. */
921 	if (arg == NULL) {
922 		*wpp = s->curw->window->active;
923 		return (s->curw);
924 	}
925 
926 	/* Look for a separating period. */
927 	if ((period = strrchr(arg, '.')) == NULL)
928 		goto no_period;
929 
930 	/* Pull out the window part and parse it. */
931 	winptr = xstrdup(arg);
932 	winptr[period - arg] = '\0';
933 	if (*winptr == '\0')
934 		wl = s->curw;
935 	else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
936 		goto error;
937 
938 	/* Find the pane section and look it up. */
939 	paneptr = winptr + (period - arg) + 1;
940 	if (*paneptr == '\0')
941 		*wpp = wl->window->active;
942 	else {
943 		idx = strtonum(paneptr, 0, INT_MAX, &errstr);
944 		if (errstr != NULL)
945 			goto lookup_string;
946 		*wpp = window_pane_at_index(wl->window, idx);
947 		if (*wpp == NULL)
948 			goto lookup_string;
949 	}
950 
951 	xfree(winptr);
952 	return (wl);
953 
954 lookup_string:
955 	/* Try as next or previous pane. */
956 	if (paneptr[0] == '+' && paneptr[1] == '\0') {
957 		*wpp = TAILQ_NEXT(wl->window->active, entry);
958 		if (*wpp == NULL)
959 			*wpp = TAILQ_FIRST(&wl->window->panes);
960 		return (wl);
961 	}
962 	if (paneptr[0] == '-' && paneptr[1] == '\0') {
963 		*wpp = TAILQ_PREV(wl->window->active, window_panes, entry);
964 		if (*wpp == NULL)
965 			*wpp = TAILQ_LAST(&wl->window->panes, window_panes);
966 		return (wl);
967 	}
968 
969 	/* Try pane string description. */
970 	if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
971 		ctx->error(ctx, "can't find pane: %s", paneptr);
972 		goto error;
973 	}
974 	*wpp = lc->wp;
975 
976 	xfree(winptr);
977 	return (wl);
978 
979 no_period:
980 	/* Try as a pane number alone. */
981 	idx = strtonum(arg, 0, INT_MAX, &errstr);
982 	if (errstr != NULL)
983 		goto lookup_window;
984 
985 	/* Try index in the current session and window. */
986 	if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
987 		goto lookup_window;
988 
989 	return (s->curw);
990 
991 lookup_window:
992 	/* Try pane string description. */
993 	if ((lc = layout_find_string(s->curw->window, arg)) != NULL) {
994 		*wpp = lc->wp;
995 		return (s->curw);
996 	}
997 
998 	/* Try as a window and use the active pane. */
999 	if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1000 		*wpp = wl->window->active;
1001 	return (wl);
1002 
1003 error:
1004 	xfree(winptr);
1005 	return (NULL);
1006 }
1007 
1008 /* Replace the first %% or %idx in template by s. */
1009 char *
1010 cmd_template_replace(char *template, const char *s, int idx)
1011 {
1012 	char	 ch;
1013 	char	*buf, *ptr;
1014 	int	 replaced;
1015 	size_t	 len;
1016 
1017 	if (strstr(template, "%") == NULL)
1018 		return (xstrdup(template));
1019 
1020 	buf = xmalloc(1);
1021 	*buf = '\0';
1022 	len = 0;
1023 	replaced = 0;
1024 
1025 	ptr = template;
1026 	while (*ptr != '\0') {
1027 		switch (ch = *ptr++) {
1028 		case '%':
1029 			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1030 				if (*ptr != '%' || replaced)
1031 					break;
1032 				replaced = 1;
1033 			}
1034 			ptr++;
1035 
1036 			len += strlen(s);
1037 			buf = xrealloc(buf, 1, len + 1);
1038 			strlcat(buf, s, len + 1);
1039 			continue;
1040 		}
1041 		buf = xrealloc(buf, 1, len + 2);
1042 		buf[len++] = ch;
1043 		buf[len] = '\0';
1044 	}
1045 
1046 	return (buf);
1047 }
1048