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