When the mode-mouse option is on, support dragging to make a selection
[tmux-openbsd.git] / cmd.c
blobd82a765e6824bc7a05ec24e2b726f2ade321f773
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/time.h>
22 #include <fnmatch.h>
23 #include <paths.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "tmux.h"
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
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 *);
121 cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
123 size_t arglen;
124 int i;
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;
135 return (0);
139 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
141 int i;
142 size_t arglen;
144 if (argc == 0)
145 return (0);
146 *argv = xcalloc(argc, sizeof **argv);
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);
155 arglen = strlen(buf) + 1;
156 (*argv)[i] = xstrdup(buf);
157 buf += arglen;
158 len -= arglen;
161 return (0);
164 void
165 cmd_free_argv(int argc, char **argv)
167 int i;
169 if (argc == 0)
170 return;
171 for (i = 0; i < argc; i++) {
172 if (argv[i] != NULL)
173 xfree(argv[i]);
175 xfree(argv);
178 struct cmd *
179 cmd_parse(int argc, char **argv, char **cause)
181 const struct cmd_entry **entryp, *entry;
182 struct cmd *cmd;
183 char s[BUFSIZ];
184 int opt, ambiguous = 0;
186 *cause = NULL;
187 if (argc == 0) {
188 xasprintf(cause, "no command");
189 return (NULL);
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;
201 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
202 continue;
203 if (entry != NULL)
204 ambiguous = 1;
205 entry = *entryp;
207 /* Bail now if an exact match. */
208 if (strcmp(entry->name, argv[0]) == 0)
209 break;
211 if (ambiguous)
212 goto ambiguous;
213 if (entry == NULL) {
214 xasprintf(cause, "unknown command: %s", argv[0]);
215 return (NULL);
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;
227 argc -= optind;
228 argv += optind;
229 if (argc != 0)
230 goto usage;
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);
242 return (cmd);
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;
254 s[strlen(s) - 2] = '\0';
255 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
256 return (NULL);
258 usage:
259 xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
260 return (NULL);
264 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
266 return (cmd->entry->exec(cmd, ctx));
269 void
270 cmd_free(struct cmd *cmd)
272 if (cmd->data != NULL && cmd->entry->free != NULL)
273 cmd->entry->free(cmd);
274 xfree(cmd);
277 size_t
278 cmd_print(struct cmd *cmd, char *buf, size_t len)
280 if (cmd->entry->print == NULL)
281 return (xsnprintf(buf, len, "%s", cmd->entry->name));
282 return (cmd->entry->print(cmd, buf, len));
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.
292 struct session *
293 cmd_current_session(struct cmd_ctx *ctx)
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;
304 if (ctx->curclient != NULL && ctx->curclient->session != NULL)
305 return (ctx->curclient->session);
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.
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;
325 if (found)
326 break;
328 if (found)
329 ARRAY_ADD(&ss, s);
332 s = cmd_choose_session(&ss);
333 ARRAY_FREE(&ss);
334 if (s != NULL)
335 return (s);
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);
349 return (cmd_choose_session(&sessions));
352 /* Find the most recently used session from a list. */
353 struct session *
354 cmd_choose_session(struct sessions *ss)
356 struct session *s, *sbest;
357 struct timeval *tv = NULL;
358 u_int i;
360 sbest = NULL;
361 for (i = 0; i < ARRAY_LENGTH(ss); i++) {
362 if ((s = ARRAY_ITEM(ss, i)) == NULL)
363 continue;
365 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
366 sbest = s;
367 tv = &s->activity_time;
371 return (sbest);
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.
379 struct client *
380 cmd_current_client(struct cmd_ctx *ctx)
382 struct session *s;
383 struct client *c;
384 struct clients cc;
385 u_int i;
387 if (ctx->curclient != NULL)
388 return (ctx->curclient);
391 * No current client set. Find the current session and return the
392 * newest of its clients.
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);
404 c = cmd_choose_client(&cc);
405 ARRAY_FREE(&cc);
406 if (c != NULL)
407 return (c);
410 return (cmd_choose_client(&clients));
413 /* Choose the most recently used client from a list. */
414 struct client *
415 cmd_choose_client(struct clients *cc)
417 struct client *c, *cbest;
418 struct timeval *tv = NULL;
419 u_int i;
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;
428 if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
429 cbest = c;
430 tv = &c->activity_time;
434 return (cbest);
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)
441 struct client *c;
442 char *tmparg;
443 size_t arglen;
445 /* A NULL argument means the current client. */
446 if (arg == NULL)
447 return (cmd_current_client(ctx));
448 tmparg = xstrdup(arg);
450 /* Trim a single trailing colon if any. */
451 arglen = strlen(tmparg);
452 if (arglen != 0 && tmparg[arglen - 1] == ':')
453 tmparg[arglen - 1] = '\0';
455 /* Find the client, if any. */
456 c = cmd_lookup_client(tmparg);
458 /* If no client found, report an error. */
459 if (c == NULL)
460 ctx->error(ctx, "client not found: %s", tmparg);
462 xfree(tmparg);
463 return (c);
467 * Lookup a client by device path. Either of a full match and a match without a
468 * leading _PATH_DEV ("/dev/") is accepted.
470 struct client *
471 cmd_lookup_client(const char *name)
473 struct client *c;
474 const char *path;
475 u_int i;
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;
483 /* Check for exact matches. */
484 if (strcmp(name, path) == 0)
485 return (c);
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);
494 return (NULL);
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)
501 struct session *s, *sfound;
502 u_int i;
504 *ambiguous = 0;
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.
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);
519 * Otherwise look for partial matches, returning early if it is found to
520 * be ambiguous.
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);
532 sfound = s;
535 return (sfound);
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.
544 struct winlink *
545 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
547 struct winlink *wl, *wlfound;
548 const char *errstr;
549 u_int idx;
551 *ambiguous = 0;
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);
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);
568 wlfound = wl;
571 if (wlfound != NULL)
572 return (wlfound);
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);
583 wlfound = wl;
586 if (wlfound != NULL)
587 return (wlfound);
589 return (NULL);
593 * Find a window index - if the window doesn't exist, check if it is a
594 * potential index and return it anyway.
597 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
599 struct winlink *wl;
600 const char *errstr;
601 u_int idx;
603 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
604 return (wl->idx);
605 if (*ambiguous)
606 return (-1);
608 idx = strtonum(name, 0, INT_MAX, &errstr);
609 if (errstr == NULL)
610 return (idx);
612 return (-1);
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)
619 struct session *s;
620 struct client *c;
621 char *tmparg;
622 size_t arglen;
623 int ambiguous;
625 /* A NULL argument means the current session. */
626 if (arg == NULL)
627 return (cmd_current_session(ctx));
628 tmparg = xstrdup(arg);
630 /* Trim a single trailing colon if any. */
631 arglen = strlen(tmparg);
632 if (arglen != 0 && tmparg[arglen - 1] == ':')
633 tmparg[arglen - 1] = '\0';
635 /* Find the session, if any. */
636 s = cmd_lookup_session(tmparg, &ambiguous);
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;
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);
650 xfree(tmparg);
651 return (s);
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)
658 struct session *s;
659 struct winlink *wl;
660 const char *winptr;
661 char *sessptr = NULL;
662 int ambiguous = 0;
665 * Find the current session. There must always be a current session, if
666 * it can't be found, report an error.
668 if ((s = cmd_current_session(ctx)) == NULL) {
669 ctx->error(ctx, "can't establish current session");
670 return (NULL);
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);
680 /* Time to look at the argument. If it is empty, that is an error. */
681 if (*arg == '\0')
682 goto not_found;
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';
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;
697 if (sp != NULL)
698 *sp = s;
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.
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;
717 if (sessptr != NULL)
718 xfree(sessptr);
719 return (wl);
721 no_colon:
723 * No colon in the string, first try special cases, then as a window
724 * and lastly as a session.
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;
743 if (sp != NULL)
744 *sp = s;
746 return (wl);
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);
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);
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).
774 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
776 struct session *s;
777 struct winlink *wl;
778 const char *winptr;
779 char *sessptr = NULL;
780 int idx, ambiguous = 0;
783 * Find the current session. There must always be a current session, if
784 * it can't be found, report an error.
786 if ((s = cmd_current_session(ctx)) == NULL) {
787 ctx->error(ctx, "can't establish current session");
788 return (-2);
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);
798 /* Time to look at the argument. If it is empty, that is an error. */
799 if (*arg == '\0')
800 goto not_found;
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';
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;
815 if (sp != NULL)
816 *sp = s;
819 * Then work out the window. An empty string is a new window otherwise
820 * try to look it up in the session.
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;
843 if (sessptr != NULL)
844 xfree(sessptr);
845 return (idx);
847 no_colon:
849 * No colon in the string, first try special cases, then as a window
850 * and lastly as a session.
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;
872 if (sp != NULL)
873 *sp = s;
875 return (idx);
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);
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);
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.
901 struct winlink *
902 cmd_find_pane(struct cmd_ctx *ctx,
903 const char *arg, struct session **sp, struct window_pane **wpp)
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;
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);
917 if (sp != NULL)
918 *sp = s;
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);
926 /* Look for a separating period. */
927 if ((period = strrchr(arg, '.')) == NULL)
928 goto no_period;
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;
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;
951 xfree(winptr);
952 return (wl);
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);
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);
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;
974 *wpp = lc->wp;
976 xfree(winptr);
977 return (wl);
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;
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;
989 return (s->curw);
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);
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);
1003 error:
1004 xfree(winptr);
1005 return (NULL);
1008 /* Replace the first %% or %idx in template by s. */
1009 char *
1010 cmd_template_replace(char *template, const char *s, int idx)
1012 char ch;
1013 char *buf, *ptr;
1014 int replaced;
1015 size_t len;
1017 if (strstr(template, "%") == NULL)
1018 return (xstrdup(template));
1020 buf = xmalloc(1);
1021 *buf = '\0';
1022 len = 0;
1023 replaced = 0;
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;
1034 ptr++;
1036 len += strlen(s);
1037 buf = xrealloc(buf, 1, len + 1);
1038 strlcat(buf, s, len + 1);
1039 continue;
1041 buf = xrealloc(buf, 1, len + 2);
1042 buf[len++] = ch;
1043 buf[len] = '\0';
1046 return (buf);