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