Size on split-window is -l not -s. Doh.
[tmux-openbsd.git] / cmd.c
blob5333a023d8f54963bd55be61c73f9edf812f7bfd
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_buffer_entry,
36 &cmd_choose_client_entry,
37 &cmd_choose_session_entry,
38 &cmd_choose_window_entry,
39 &cmd_clear_history_entry,
40 &cmd_clock_mode_entry,
41 &cmd_command_prompt_entry,
42 &cmd_confirm_before_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
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 *);
126 cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
128 size_t arglen;
129 int i;
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;
140 return (0);
144 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
146 int i;
147 size_t arglen;
149 if (argc == 0)
150 return (0);
151 *argv = xcalloc(argc, sizeof **argv);
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);
160 arglen = strlen(buf) + 1;
161 (*argv)[i] = xstrdup(buf);
162 buf += arglen;
163 len -= arglen;
166 return (0);
169 char **
170 cmd_copy_argv(int argc, char *const *argv)
172 char **new_argv;
173 int i;
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]);
182 return (new_argv);
185 void
186 cmd_free_argv(int argc, char **argv)
188 int i;
190 if (argc == 0)
191 return;
192 for (i = 0; i < argc; i++) {
193 if (argv[i] != NULL)
194 xfree(argv[i]);
196 xfree(argv);
199 struct cmd *
200 cmd_parse(int argc, char **argv, char **cause)
202 const struct cmd_entry **entryp, *entry;
203 struct cmd *cmd;
204 struct args *args;
205 char s[BUFSIZ];
206 int ambiguous = 0;
208 *cause = NULL;
209 if (argc == 0) {
210 xasprintf(cause, "no command");
211 return (NULL);
214 entry = NULL;
215 for (entryp = cmd_table; *entryp != NULL; entryp++) {
216 if ((*entryp)->alias != NULL &&
217 strcmp((*entryp)->alias, argv[0]) == 0) {
218 ambiguous = 0;
219 entry = *entryp;
220 break;
223 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
224 continue;
225 if (entry != NULL)
226 ambiguous = 1;
227 entry = *entryp;
229 /* Bail now if an exact match. */
230 if (strcmp(entry->name, argv[0]) == 0)
231 break;
233 if (ambiguous)
234 goto ambiguous;
235 if (entry == NULL) {
236 xasprintf(cause, "unknown command: %s", argv[0]);
237 return (NULL);
240 args = args_parse(entry->args_template, argc, argv);
241 if (args == NULL)
242 goto usage;
243 if (entry->args_lower != -1 && args->argc < entry->args_lower)
244 goto usage;
245 if (entry->args_upper != -1 && args->argc > entry->args_upper)
246 goto usage;
247 if (entry->check != NULL && entry->check(args) != 0)
248 goto usage;
250 cmd = xmalloc(sizeof *cmd);
251 cmd->entry = entry;
252 cmd->args = args;
253 return (cmd);
255 ambiguous:
256 *s = '\0';
257 for (entryp = cmd_table; *entryp != NULL; entryp++) {
258 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
259 continue;
260 if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
261 break;
262 if (strlcat(s, ", ", sizeof s) >= sizeof s)
263 break;
265 s[strlen(s) - 2] = '\0';
266 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
267 return (NULL);
269 usage:
270 if (args != NULL)
271 args_free(args);
272 xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
273 return (NULL);
277 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
279 return (cmd->entry->exec(cmd, ctx));
282 void
283 cmd_free(struct cmd *cmd)
285 if (cmd->args != NULL)
286 args_free(cmd->args);
287 xfree(cmd);
290 size_t
291 cmd_print(struct cmd *cmd, char *buf, size_t len)
293 size_t off, used;
295 off = xsnprintf(buf, len, "%s ", cmd->entry->name);
296 if (off < len) {
297 used = args_print(cmd->args, buf + off, len - off);
298 if (used == 0)
299 buf[off - 1] = '\0';
300 else {
301 off += used;
302 buf[off] = '\0';
305 return (off);
309 * Figure out the current session. Use: 1) the current session, if the command
310 * context has one; 2) the most recently used session containing the pty of the
311 * calling client, if any; 3) the session specified in the TMUX variable from
312 * the environment (as passed from the client); 4) the most recently used
313 * session from all sessions.
315 struct session *
316 cmd_current_session(struct cmd_ctx *ctx)
318 struct msg_command_data *data = ctx->msgdata;
319 struct client *c = ctx->cmdclient;
320 struct session *s;
321 struct sessionslist ss;
322 struct winlink *wl;
323 struct window_pane *wp;
324 int found;
326 if (ctx->curclient != NULL && ctx->curclient->session != NULL)
327 return (ctx->curclient->session);
330 * If the name of the calling client's pty is know, build a list of the
331 * sessions that contain it and if any choose either the first or the
332 * newest.
334 if (c != NULL && c->tty.path != NULL) {
335 ARRAY_INIT(&ss);
336 RB_FOREACH(s, sessions, &sessions) {
337 found = 0;
338 RB_FOREACH(wl, winlinks, &s->windows) {
339 TAILQ_FOREACH(wp, &wl->window->panes, entry) {
340 if (strcmp(wp->tty, c->tty.path) == 0) {
341 found = 1;
342 break;
345 if (found)
346 break;
348 if (found)
349 ARRAY_ADD(&ss, s);
352 s = cmd_choose_session_list(&ss);
353 ARRAY_FREE(&ss);
354 if (s != NULL)
355 return (s);
358 /* Use the session from the TMUX environment variable. */
359 if (data != NULL && data->pid == getpid() && data->idx != -1) {
360 s = session_find_by_index(data->idx);
361 if (s != NULL)
362 return (s);
365 return (cmd_choose_session());
368 /* Find the most recently used session. */
369 struct session *
370 cmd_choose_session(void)
372 struct session *s, *sbest;
373 struct timeval *tv = NULL;
375 sbest = NULL;
376 RB_FOREACH(s, sessions, &sessions) {
377 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
378 sbest = s;
379 tv = &s->activity_time;
383 return (sbest);
386 /* Find the most recently used session from a list. */
387 struct session *
388 cmd_choose_session_list(struct sessionslist *ss)
390 struct session *s, *sbest;
391 struct timeval *tv = NULL;
392 u_int i;
394 sbest = NULL;
395 for (i = 0; i < ARRAY_LENGTH(ss); i++) {
396 if ((s = ARRAY_ITEM(ss, i)) == NULL)
397 continue;
399 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
400 sbest = s;
401 tv = &s->activity_time;
405 return (sbest);
409 * Find the current client. First try the current client if set, then pick the
410 * most recently used of the clients attached to the current session if any,
411 * then of all clients.
413 struct client *
414 cmd_current_client(struct cmd_ctx *ctx)
416 struct session *s;
417 struct client *c;
418 struct clients cc;
419 u_int i;
421 if (ctx->curclient != NULL)
422 return (ctx->curclient);
425 * No current client set. Find the current session and return the
426 * newest of its clients.
428 s = cmd_current_session(ctx);
429 if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
430 ARRAY_INIT(&cc);
431 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
432 if ((c = ARRAY_ITEM(&clients, i)) == NULL)
433 continue;
434 if (s == c->session)
435 ARRAY_ADD(&cc, c);
438 c = cmd_choose_client(&cc);
439 ARRAY_FREE(&cc);
440 if (c != NULL)
441 return (c);
444 return (cmd_choose_client(&clients));
447 /* Choose the most recently used client from a list. */
448 struct client *
449 cmd_choose_client(struct clients *cc)
451 struct client *c, *cbest;
452 struct timeval *tv = NULL;
453 u_int i;
455 cbest = NULL;
456 for (i = 0; i < ARRAY_LENGTH(cc); i++) {
457 if ((c = ARRAY_ITEM(cc, i)) == NULL)
458 continue;
459 if (c->session == NULL)
460 continue;
462 if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
463 cbest = c;
464 tv = &c->activity_time;
468 return (cbest);
471 /* Find the target client or report an error and return NULL. */
472 struct client *
473 cmd_find_client(struct cmd_ctx *ctx, const char *arg)
475 struct client *c;
476 char *tmparg;
477 size_t arglen;
479 /* A NULL argument means the current client. */
480 if (arg == NULL)
481 return (cmd_current_client(ctx));
482 tmparg = xstrdup(arg);
484 /* Trim a single trailing colon if any. */
485 arglen = strlen(tmparg);
486 if (arglen != 0 && tmparg[arglen - 1] == ':')
487 tmparg[arglen - 1] = '\0';
489 /* Find the client, if any. */
490 c = cmd_lookup_client(tmparg);
492 /* If no client found, report an error. */
493 if (c == NULL)
494 ctx->error(ctx, "client not found: %s", tmparg);
496 xfree(tmparg);
497 return (c);
501 * Lookup a client by device path. Either of a full match and a match without a
502 * leading _PATH_DEV ("/dev/") is accepted.
504 struct client *
505 cmd_lookup_client(const char *name)
507 struct client *c;
508 const char *path;
509 u_int i;
511 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
512 c = ARRAY_ITEM(&clients, i);
513 if (c == NULL || c->session == NULL)
514 continue;
515 path = c->tty.path;
517 /* Check for exact matches. */
518 if (strcmp(name, path) == 0)
519 return (c);
521 /* Check without leading /dev if present. */
522 if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
523 continue;
524 if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
525 return (c);
528 return (NULL);
531 /* Lookup a session by name. If no session is found, NULL is returned. */
532 struct session *
533 cmd_lookup_session(const char *name, int *ambiguous)
535 struct session *s, *sfound;
537 *ambiguous = 0;
540 * Look for matches. First look for exact matches - session names must
541 * be unique so an exact match can't be ambigious and can just be
542 * returned.
544 if ((s = session_find(name)) != NULL)
545 return (s);
548 * Otherwise look for partial matches, returning early if it is found to
549 * be ambiguous.
551 sfound = NULL;
552 RB_FOREACH(s, sessions, &sessions) {
553 if (strncmp(name, s->name, strlen(name)) == 0 ||
554 fnmatch(name, s->name, 0) == 0) {
555 if (sfound != NULL) {
556 *ambiguous = 1;
557 return (NULL);
559 sfound = s;
562 return (sfound);
566 * Lookup a window or return -1 if not found or ambigious. First try as an
567 * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
568 * idx if the window index is a valid number but there is no window with that
569 * index.
571 struct winlink *
572 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
574 struct winlink *wl, *wlfound;
575 const char *errstr;
576 u_int idx;
578 *ambiguous = 0;
580 /* First see if this is a valid window index in this session. */
581 idx = strtonum(name, 0, INT_MAX, &errstr);
582 if (errstr == NULL) {
583 if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
584 return (wl);
587 /* Look for exact matches, error if more than one. */
588 wlfound = NULL;
589 RB_FOREACH(wl, winlinks, &s->windows) {
590 if (strcmp(name, wl->window->name) == 0) {
591 if (wlfound != NULL) {
592 *ambiguous = 1;
593 return (NULL);
595 wlfound = wl;
598 if (wlfound != NULL)
599 return (wlfound);
601 /* Now look for pattern matches, again error if multiple. */
602 wlfound = NULL;
603 RB_FOREACH(wl, winlinks, &s->windows) {
604 if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
605 fnmatch(name, wl->window->name, 0) == 0) {
606 if (wlfound != NULL) {
607 *ambiguous = 1;
608 return (NULL);
610 wlfound = wl;
613 if (wlfound != NULL)
614 return (wlfound);
616 return (NULL);
620 * Find a window index - if the window doesn't exist, check if it is a
621 * potential index and return it anyway.
624 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
626 struct winlink *wl;
627 const char *errstr;
628 u_int idx;
630 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
631 return (wl->idx);
632 if (*ambiguous)
633 return (-1);
635 idx = strtonum(name, 0, INT_MAX, &errstr);
636 if (errstr == NULL)
637 return (idx);
639 return (-1);
642 /* Find the target session or report an error and return NULL. */
643 struct session *
644 cmd_find_session(struct cmd_ctx *ctx, const char *arg)
646 struct session *s;
647 struct client *c;
648 char *tmparg;
649 size_t arglen;
650 int ambiguous;
652 /* A NULL argument means the current session. */
653 if (arg == NULL)
654 return (cmd_current_session(ctx));
655 tmparg = xstrdup(arg);
657 /* Trim a single trailing colon if any. */
658 arglen = strlen(tmparg);
659 if (arglen != 0 && tmparg[arglen - 1] == ':')
660 tmparg[arglen - 1] = '\0';
662 /* Find the session, if any. */
663 s = cmd_lookup_session(tmparg, &ambiguous);
665 /* If it doesn't, try to match it as a client. */
666 if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
667 s = c->session;
669 /* If no session found, report an error. */
670 if (s == NULL) {
671 if (ambiguous)
672 ctx->error(ctx, "more than one session: %s", tmparg);
673 else
674 ctx->error(ctx, "session not found: %s", tmparg);
677 xfree(tmparg);
678 return (s);
681 /* Find the target session and window or report an error and return NULL. */
682 struct winlink *
683 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
685 struct session *s;
686 struct winlink *wl;
687 const char *winptr;
688 char *sessptr = NULL;
689 int ambiguous = 0;
692 * Find the current session. There must always be a current session, if
693 * it can't be found, report an error.
695 if ((s = cmd_current_session(ctx)) == NULL) {
696 ctx->error(ctx, "can't establish current session");
697 return (NULL);
700 /* A NULL argument means the current session and window. */
701 if (arg == NULL) {
702 if (sp != NULL)
703 *sp = s;
704 return (s->curw);
707 /* Time to look at the argument. If it is empty, that is an error. */
708 if (*arg == '\0')
709 goto not_found;
711 /* Find the separating colon and split into window and session. */
712 winptr = strchr(arg, ':');
713 if (winptr == NULL)
714 goto no_colon;
715 winptr++; /* skip : */
716 sessptr = xstrdup(arg);
717 *strchr(sessptr, ':') = '\0';
719 /* Try to lookup the session if present. */
720 if (*sessptr != '\0') {
721 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
722 goto no_session;
724 if (sp != NULL)
725 *sp = s;
728 * Then work out the window. An empty string is the current window,
729 * otherwise try special cases then to look it up in the session.
731 if (*winptr == '\0')
732 wl = s->curw;
733 else if (winptr[0] == '!' && winptr[1] == '\0')
734 wl = TAILQ_FIRST(&s->lastw);
735 else if (winptr[0] == '+' || winptr[0] == '-')
736 wl = cmd_find_window_offset(winptr, s, &ambiguous);
737 else
738 wl = cmd_lookup_window(s, winptr, &ambiguous);
739 if (wl == NULL)
740 goto not_found;
742 if (sessptr != NULL)
743 xfree(sessptr);
744 return (wl);
746 no_colon:
748 * No colon in the string, first try special cases, then as a window
749 * and lastly as a session.
751 if (arg[0] == '!' && arg[1] == '\0') {
752 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
753 goto not_found;
754 } else if (arg[0] == '+' || arg[0] == '-') {
755 if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
756 goto lookup_session;
757 } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
758 goto lookup_session;
760 if (sp != NULL)
761 *sp = s;
763 return (wl);
765 lookup_session:
766 if (ambiguous)
767 goto not_found;
768 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
769 goto no_session;
771 if (sp != NULL)
772 *sp = s;
774 return (s->curw);
776 no_session:
777 if (ambiguous)
778 ctx->error(ctx, "multiple sessions: %s", arg);
779 else
780 ctx->error(ctx, "session not found: %s", arg);
781 if (sessptr != NULL)
782 xfree(sessptr);
783 return (NULL);
785 not_found:
786 if (ambiguous)
787 ctx->error(ctx, "multiple windows: %s", arg);
788 else
789 ctx->error(ctx, "window not found: %s", arg);
790 if (sessptr != NULL)
791 xfree(sessptr);
792 return (NULL);
795 struct winlink *
796 cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
798 struct winlink *wl;
799 int offset = 1;
801 if (winptr[1] != '\0')
802 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
803 if (offset == 0)
804 wl = cmd_lookup_window(s, winptr, ambiguous);
805 else {
806 if (winptr[0] == '+')
807 wl = winlink_next_by_number(s->curw, s, offset);
808 else
809 wl = winlink_previous_by_number(s->curw, s, offset);
812 return (wl);
816 * Find the target session and window index, whether or not it exists in the
817 * session. Return -2 on error or -1 if no window index is specified. This is
818 * used when parsing an argument for a window target that may not exist (for
819 * example if it is going to be created).
822 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
824 struct session *s;
825 struct winlink *wl;
826 const char *winptr;
827 char *sessptr = NULL;
828 int idx, ambiguous = 0;
831 * Find the current session. There must always be a current session, if
832 * it can't be found, report an error.
834 if ((s = cmd_current_session(ctx)) == NULL) {
835 ctx->error(ctx, "can't establish current session");
836 return (-2);
839 /* A NULL argument means the current session and "no window" (-1). */
840 if (arg == NULL) {
841 if (sp != NULL)
842 *sp = s;
843 return (-1);
846 /* Time to look at the argument. If it is empty, that is an error. */
847 if (*arg == '\0')
848 goto not_found;
850 /* Find the separating colon. If none, assume the current session. */
851 winptr = strchr(arg, ':');
852 if (winptr == NULL)
853 goto no_colon;
854 winptr++; /* skip : */
855 sessptr = xstrdup(arg);
856 *strchr(sessptr, ':') = '\0';
858 /* Try to lookup the session if present. */
859 if (sessptr != NULL && *sessptr != '\0') {
860 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
861 goto no_session;
863 if (sp != NULL)
864 *sp = s;
867 * Then work out the window. An empty string is a new window otherwise
868 * try to look it up in the session.
870 if (*winptr == '\0')
871 idx = -1;
872 else if (winptr[0] == '!' && winptr[1] == '\0') {
873 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
874 goto not_found;
875 idx = wl->idx;
876 } else if (winptr[0] == '+' || winptr[0] == '-') {
877 if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
878 goto invalid_index;
879 } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
880 goto invalid_index;
882 if (sessptr != NULL)
883 xfree(sessptr);
884 return (idx);
886 no_colon:
888 * No colon in the string, first try special cases, then as a window
889 * and lastly as a session.
891 if (arg[0] == '!' && arg[1] == '\0') {
892 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
893 goto not_found;
894 idx = wl->idx;
895 } else if (arg[0] == '+' || arg[0] == '-') {
896 if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
897 goto lookup_session;
898 } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
899 goto lookup_session;
901 if (sp != NULL)
902 *sp = s;
904 return (idx);
906 lookup_session:
907 if (ambiguous)
908 goto not_found;
909 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
910 goto no_session;
912 if (sp != NULL)
913 *sp = s;
915 return (-1);
917 no_session:
918 if (ambiguous)
919 ctx->error(ctx, "multiple sessions: %s", arg);
920 else
921 ctx->error(ctx, "session not found: %s", arg);
922 if (sessptr != NULL)
923 xfree(sessptr);
924 return (-2);
926 invalid_index:
927 if (ambiguous)
928 goto not_found;
929 ctx->error(ctx, "invalid index: %s", arg);
931 if (sessptr != NULL)
932 xfree(sessptr);
933 return (-2);
935 not_found:
936 if (ambiguous)
937 ctx->error(ctx, "multiple windows: %s", arg);
938 else
939 ctx->error(ctx, "window not found: %s", arg);
940 if (sessptr != NULL)
941 xfree(sessptr);
942 return (-2);
946 cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
948 int idx, offset = 1;
950 if (winptr[1] != '\0')
951 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
952 if (offset == 0)
953 idx = cmd_lookup_index(s, winptr, ambiguous);
954 else {
955 if (winptr[0] == '+') {
956 if (s->curw->idx == INT_MAX)
957 idx = cmd_lookup_index(s, winptr, ambiguous);
958 else
959 idx = s->curw->idx + offset;
960 } else {
961 if (s->curw->idx == 0)
962 idx = cmd_lookup_index(s, winptr, ambiguous);
963 else
964 idx = s->curw->idx - offset;
968 return (idx);
972 * Find the target session, window and pane number or report an error and
973 * return NULL. The pane number is separated from the session:window by a .,
974 * such as mysession:mywindow.0.
976 struct winlink *
977 cmd_find_pane(struct cmd_ctx *ctx,
978 const char *arg, struct session **sp, struct window_pane **wpp)
980 struct session *s;
981 struct winlink *wl;
982 struct layout_cell *lc;
983 const char *period, *errstr;
984 char *winptr, *paneptr;
985 u_int idx;
987 /* Get the current session. */
988 if ((s = cmd_current_session(ctx)) == NULL) {
989 ctx->error(ctx, "can't establish current session");
990 return (NULL);
992 if (sp != NULL)
993 *sp = s;
995 /* A NULL argument means the current session, window and pane. */
996 if (arg == NULL) {
997 *wpp = s->curw->window->active;
998 return (s->curw);
1001 /* Look for a separating period. */
1002 if ((period = strrchr(arg, '.')) == NULL)
1003 goto no_period;
1005 /* Pull out the window part and parse it. */
1006 winptr = xstrdup(arg);
1007 winptr[period - arg] = '\0';
1008 if (*winptr == '\0')
1009 wl = s->curw;
1010 else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
1011 goto error;
1013 /* Find the pane section and look it up. */
1014 paneptr = winptr + (period - arg) + 1;
1015 if (*paneptr == '\0')
1016 *wpp = wl->window->active;
1017 else if (paneptr[0] == '+' || paneptr[0] == '-')
1018 *wpp = cmd_find_pane_offset(paneptr, wl);
1019 else {
1020 idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1021 if (errstr != NULL)
1022 goto lookup_string;
1023 *wpp = window_pane_at_index(wl->window, idx);
1024 if (*wpp == NULL)
1025 goto lookup_string;
1028 xfree(winptr);
1029 return (wl);
1031 lookup_string:
1032 /* Try pane string description. */
1033 if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
1034 ctx->error(ctx, "can't find pane: %s", paneptr);
1035 goto error;
1037 *wpp = lc->wp;
1039 xfree(winptr);
1040 return (wl);
1042 no_period:
1043 /* Try as a pane number alone. */
1044 idx = strtonum(arg, 0, INT_MAX, &errstr);
1045 if (errstr != NULL)
1046 goto lookup_window;
1048 /* Try index in the current session and window. */
1049 if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1050 goto lookup_window;
1052 return (s->curw);
1054 lookup_window:
1055 /* Try pane string description. */
1056 if ((lc = layout_find_string(s->curw->window, arg)) != NULL) {
1057 *wpp = lc->wp;
1058 return (s->curw);
1061 /* Try as a window and use the active pane. */
1062 if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1063 *wpp = wl->window->active;
1064 return (wl);
1066 error:
1067 xfree(winptr);
1068 return (NULL);
1071 struct window_pane *
1072 cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1074 struct window *w = wl->window;
1075 struct window_pane *wp = w->active;
1076 u_int offset = 1;
1078 if (paneptr[1] != '\0')
1079 offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1080 if (offset > 0) {
1081 if (paneptr[0] == '+')
1082 wp = window_pane_next_by_number(w, wp, offset);
1083 else
1084 wp = window_pane_previous_by_number(w, wp, offset);
1087 return (wp);
1090 /* Replace the first %% or %idx in template by s. */
1091 char *
1092 cmd_template_replace(char *template, const char *s, int idx)
1094 char ch;
1095 char *buf, *ptr;
1096 int replaced;
1097 size_t len;
1099 if (strstr(template, "%") == NULL)
1100 return (xstrdup(template));
1102 buf = xmalloc(1);
1103 *buf = '\0';
1104 len = 0;
1105 replaced = 0;
1107 ptr = template;
1108 while (*ptr != '\0') {
1109 switch (ch = *ptr++) {
1110 case '%':
1111 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1112 if (*ptr != '%' || replaced)
1113 break;
1114 replaced = 1;
1116 ptr++;
1118 len += strlen(s);
1119 buf = xrealloc(buf, 1, len + 1);
1120 strlcat(buf, s, len + 1);
1121 continue;
1123 buf = xrealloc(buf, 1, len + 2);
1124 buf[len++] = ch;
1125 buf[len] = '\0';
1128 return (buf);