Change so that an empty session name always means the current sessions
[tmux-openbsd.git] / cmd.c
blobee893a25c570dd47431fe84d00d252c4b8600231
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(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 *);
129 cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
131 size_t arglen;
132 int i;
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;
143 return (0);
147 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
149 int i;
150 size_t arglen;
152 if (argc == 0)
153 return (0);
154 *argv = xcalloc(argc, sizeof **argv);
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);
163 arglen = strlen(buf) + 1;
164 (*argv)[i] = xstrdup(buf);
165 buf += arglen;
166 len -= arglen;
169 return (0);
172 char **
173 cmd_copy_argv(int argc, char *const *argv)
175 char **new_argv;
176 int i;
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]);
185 return (new_argv);
188 void
189 cmd_free_argv(int argc, char **argv)
191 int i;
193 if (argc == 0)
194 return;
195 for (i = 0; i < argc; i++) {
196 if (argv[i] != NULL)
197 xfree(argv[i]);
199 xfree(argv);
202 struct cmd *
203 cmd_parse(int argc, char **argv, char **cause)
205 const struct cmd_entry **entryp, *entry;
206 struct cmd *cmd;
207 struct args *args;
208 char s[BUFSIZ];
209 int ambiguous = 0;
211 *cause = NULL;
212 if (argc == 0) {
213 xasprintf(cause, "no command");
214 return (NULL);
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;
226 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
227 continue;
228 if (entry != NULL)
229 ambiguous = 1;
230 entry = *entryp;
232 /* Bail now if an exact match. */
233 if (strcmp(entry->name, argv[0]) == 0)
234 break;
236 if (ambiguous)
237 goto ambiguous;
238 if (entry == NULL) {
239 xasprintf(cause, "unknown command: %s", argv[0]);
240 return (NULL);
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;
253 cmd = xmalloc(sizeof *cmd);
254 cmd->entry = entry;
255 cmd->args = args;
256 return (cmd);
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;
268 s[strlen(s) - 2] = '\0';
269 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
270 return (NULL);
272 usage:
273 if (args != NULL)
274 args_free(args);
275 xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
276 return (NULL);
280 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
282 return (cmd->entry->exec(cmd, ctx));
285 void
286 cmd_free(struct cmd *cmd)
288 if (cmd->args != NULL)
289 args_free(cmd->args);
290 xfree(cmd);
293 size_t
294 cmd_print(struct cmd *cmd, char *buf, size_t len)
296 size_t off, used;
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';
308 return (off);
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.
318 struct session *
319 cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached)
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;
329 if (ctx->curclient != NULL && ctx->curclient->session != NULL)
330 return (ctx->curclient->session);
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.
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;
348 if (found)
349 break;
351 if (found)
352 ARRAY_ADD(&ss, s);
355 s = cmd_choose_session_list(&ss);
356 ARRAY_FREE(&ss);
357 if (s != NULL)
358 return (s);
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);
368 return (cmd_choose_session(prefer_unattached));
372 * Find the most recently used session, preferring unattached if the flag is
373 * set.
375 struct session *
376 cmd_choose_session(int prefer_unattached)
378 struct session *s, *sbest;
379 struct timeval *tv = NULL;
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;
392 return (sbest);
395 /* Find the most recently used session from a list. */
396 struct session *
397 cmd_choose_session_list(struct sessionslist *ss)
399 struct session *s, *sbest;
400 struct timeval *tv = NULL;
401 u_int i;
403 sbest = NULL;
404 for (i = 0; i < ARRAY_LENGTH(ss); i++) {
405 if ((s = ARRAY_ITEM(ss, i)) == NULL)
406 continue;
408 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
409 sbest = s;
410 tv = &s->activity_time;
414 return (sbest);
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.
422 struct client *
423 cmd_current_client(struct cmd_ctx *ctx)
425 struct session *s;
426 struct client *c;
427 struct clients cc;
428 u_int i;
430 if (ctx->curclient != NULL)
431 return (ctx->curclient);
434 * No current client set. Find the current session and return the
435 * newest of its clients.
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);
447 c = cmd_choose_client(&cc);
448 ARRAY_FREE(&cc);
449 if (c != NULL)
450 return (c);
453 return (cmd_choose_client(&clients));
456 /* Choose the most recently used client from a list. */
457 struct client *
458 cmd_choose_client(struct clients *cc)
460 struct client *c, *cbest;
461 struct timeval *tv = NULL;
462 u_int i;
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;
471 if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
472 cbest = c;
473 tv = &c->activity_time;
477 return (cbest);
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)
484 struct client *c;
485 char *tmparg;
486 size_t arglen;
488 /* A NULL argument means the current client. */
489 if (arg == NULL)
490 return (cmd_current_client(ctx));
491 tmparg = xstrdup(arg);
493 /* Trim a single trailing colon if any. */
494 arglen = strlen(tmparg);
495 if (arglen != 0 && tmparg[arglen - 1] == ':')
496 tmparg[arglen - 1] = '\0';
498 /* Find the client, if any. */
499 c = cmd_lookup_client(tmparg);
501 /* If no client found, report an error. */
502 if (c == NULL)
503 ctx->error(ctx, "client not found: %s", tmparg);
505 xfree(tmparg);
506 return (c);
510 * Lookup a client by device path. Either of a full match and a match without a
511 * leading _PATH_DEV ("/dev/") is accepted.
513 struct client *
514 cmd_lookup_client(const char *name)
516 struct client *c;
517 const char *path;
518 u_int i;
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;
526 /* Check for exact matches. */
527 if (strcmp(name, path) == 0)
528 return (c);
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);
537 return (NULL);
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)
544 struct session *s, *sfound;
546 *ambiguous = 0;
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.
553 if ((s = session_find(name)) != NULL)
554 return (s);
557 * Otherwise look for partial matches, returning early if it is found to
558 * be ambiguous.
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);
568 sfound = s;
571 return (sfound);
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.
580 struct winlink *
581 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
583 struct winlink *wl, *wlfound;
584 const char *errstr;
585 u_int idx;
587 *ambiguous = 0;
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);
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);
604 wlfound = wl;
607 if (wlfound != NULL)
608 return (wlfound);
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);
619 wlfound = wl;
622 if (wlfound != NULL)
623 return (wlfound);
625 return (NULL);
629 * Find a window index - if the window doesn't exist, check if it is a
630 * potential index and return it anyway.
633 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
635 struct winlink *wl;
636 const char *errstr;
637 u_int idx;
639 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
640 return (wl->idx);
641 if (*ambiguous)
642 return (-1);
644 idx = strtonum(name, 0, INT_MAX, &errstr);
645 if (errstr == NULL)
646 return (idx);
648 return (-1);
652 * Lookup pane id. An initial % means a pane id. sp must already point to the
653 * current session.
655 struct window_pane *
656 cmd_lookup_paneid(const char *arg)
658 const char *errstr;
659 u_int paneid;
661 if (*arg != '%')
662 return (NULL);
664 paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
665 if (errstr != NULL)
666 return (NULL);
667 return (window_pane_find_by_id(paneid));
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)
675 struct session *s;
676 struct sessionslist ss;
677 struct winlink *wl;
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);
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);
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);
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)
707 struct session *s;
708 struct window_pane *wp;
709 struct client *c;
710 char *tmparg;
711 size_t arglen;
712 int ambiguous;
714 /* A NULL argument means the current session. */
715 if (arg == NULL)
716 return (cmd_current_session(ctx, prefer_unattached));
717 tmparg = xstrdup(arg);
719 /* Lookup as pane id. */
720 if ((wp = cmd_lookup_paneid(arg)) != NULL)
721 return (cmd_pane_session(ctx, wp, NULL));
723 /* Trim a single trailing colon if any. */
724 arglen = strlen(tmparg);
725 if (arglen != 0 && tmparg[arglen - 1] == ':')
726 tmparg[arglen - 1] = '\0';
728 /* An empty session name is the current session. */
729 if (*tmparg == '\0') {
730 xfree (tmparg);
731 return (cmd_current_session(ctx, prefer_unattached));
734 /* Find the session, if any. */
735 s = cmd_lookup_session(tmparg, &ambiguous);
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;
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);
749 xfree(tmparg);
750 return (s);
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)
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;
765 * Find the current session. There must always be a current session, if
766 * it can't be found, report an error.
768 if ((s = cmd_current_session(ctx, 0)) == NULL) {
769 ctx->error(ctx, "can't establish current session");
770 return (NULL);
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);
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);
788 /* Time to look at the argument. If it is empty, that is an error. */
789 if (*arg == '\0')
790 goto not_found;
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';
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;
805 if (sp != NULL)
806 *sp = s;
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.
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;
823 if (sessptr != NULL)
824 xfree(sessptr);
825 return (wl);
827 no_colon:
829 * No colon in the string, first try special cases, then as a window
830 * and lastly as a session.
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;
841 if (sp != NULL)
842 *sp = s;
844 return (wl);
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;
852 if (sp != NULL)
853 *sp = s;
855 return (s->curw);
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);
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);
876 struct winlink *
877 cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
879 struct winlink *wl;
880 int offset = 1;
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);
893 return (wl);
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).
903 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
905 struct session *s;
906 struct winlink *wl;
907 const char *winptr;
908 char *sessptr = NULL;
909 int idx, ambiguous = 0;
912 * Find the current session. There must always be a current session, if
913 * it can't be found, report an error.
915 if ((s = cmd_current_session(ctx, 0)) == NULL) {
916 ctx->error(ctx, "can't establish current session");
917 return (-2);
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);
927 /* Time to look at the argument. If it is empty, that is an error. */
928 if (*arg == '\0')
929 goto not_found;
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';
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;
944 if (sp != NULL)
945 *sp = s;
948 * Then work out the window. An empty string is a new window otherwise
949 * try to look it up in the session.
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;
963 if (sessptr != NULL)
964 xfree(sessptr);
965 return (idx);
967 no_colon:
969 * No colon in the string, first try special cases, then as a window
970 * and lastly as a session.
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;
982 if (sp != NULL)
983 *sp = s;
985 return (idx);
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;
993 if (sp != NULL)
994 *sp = s;
996 return (-1);
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);
1007 invalid_index:
1008 if (ambiguous)
1009 goto not_found;
1010 ctx->error(ctx, "invalid index: %s", arg);
1012 if (sessptr != NULL)
1013 xfree(sessptr);
1014 return (-2);
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);
1027 cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
1029 int idx, offset = 1;
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;
1049 return (idx);
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.
1057 struct winlink *
1058 cmd_find_pane(struct cmd_ctx *ctx,
1059 const char *arg, struct session **sp, struct window_pane **wpp)
1061 struct session *s;
1062 struct winlink *wl;
1063 struct layout_cell *lc;
1064 const char *period, *errstr;
1065 char *winptr, *paneptr;
1066 u_int idx;
1068 /* Get the current session. */
1069 if ((s = cmd_current_session(ctx, 0)) == NULL) {
1070 ctx->error(ctx, "can't establish current session");
1071 return (NULL);
1073 if (sp != NULL)
1074 *sp = s;
1076 /* A NULL argument means the current session, window and pane. */
1077 if (arg == NULL) {
1078 *wpp = s->curw->window->active;
1079 return (s->curw);
1082 /* Lookup as pane id. */
1083 if ((*wpp = cmd_lookup_paneid(arg)) != NULL) {
1084 s = cmd_pane_session(ctx, *wpp, &wl);
1085 if (sp != NULL)
1086 *sp = s;
1087 return (wl);
1090 /* Look for a separating period. */
1091 if ((period = strrchr(arg, '.')) == NULL)
1092 goto no_period;
1094 /* Pull out the window part and parse it. */
1095 winptr = xstrdup(arg);
1096 winptr[period - arg] = '\0';
1097 if (*winptr == '\0')
1098 wl = s->curw;
1099 else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
1100 goto error;
1102 /* Find the pane section and look it up. */
1103 paneptr = winptr + (period - arg) + 1;
1104 if (*paneptr == '\0')
1105 *wpp = wl->window->active;
1106 else if (paneptr[0] == '+' || paneptr[0] == '-')
1107 *wpp = cmd_find_pane_offset(paneptr, wl);
1108 else {
1109 idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1110 if (errstr != NULL)
1111 goto lookup_string;
1112 *wpp = window_pane_at_index(wl->window, idx);
1113 if (*wpp == NULL)
1114 goto lookup_string;
1117 xfree(winptr);
1118 return (wl);
1120 lookup_string:
1121 /* Try pane string description. */
1122 if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
1123 ctx->error(ctx, "can't find pane: %s", paneptr);
1124 goto error;
1126 *wpp = lc->wp;
1128 xfree(winptr);
1129 return (wl);
1131 no_period:
1132 /* Try as a pane number alone. */
1133 idx = strtonum(arg, 0, INT_MAX, &errstr);
1134 if (errstr != NULL)
1135 goto lookup_window;
1137 /* Try index in the current session and window. */
1138 if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1139 goto lookup_window;
1141 return (s->curw);
1143 lookup_window:
1144 /* Try pane string description. */
1145 if ((lc = layout_find_string(s->curw->window, arg)) != NULL) {
1146 *wpp = lc->wp;
1147 return (s->curw);
1150 /* Try as a window and use the active pane. */
1151 if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1152 *wpp = wl->window->active;
1153 return (wl);
1155 error:
1156 xfree(winptr);
1157 return (NULL);
1160 struct window_pane *
1161 cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1163 struct window *w = wl->window;
1164 struct window_pane *wp = w->active;
1165 u_int offset = 1;
1167 if (paneptr[1] != '\0')
1168 offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1169 if (offset > 0) {
1170 if (paneptr[0] == '+')
1171 wp = window_pane_next_by_number(w, wp, offset);
1172 else
1173 wp = window_pane_previous_by_number(w, wp, offset);
1176 return (wp);
1179 /* Replace the first %% or %idx in template by s. */
1180 char *
1181 cmd_template_replace(char *template, const char *s, int idx)
1183 char ch;
1184 char *buf, *ptr;
1185 int replaced;
1186 size_t len;
1188 if (strstr(template, "%") == NULL)
1189 return (xstrdup(template));
1191 buf = xmalloc(1);
1192 *buf = '\0';
1193 len = 0;
1194 replaced = 0;
1196 ptr = template;
1197 while (*ptr != '\0') {
1198 switch (ch = *ptr++) {
1199 case '%':
1200 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1201 if (*ptr != '%' || replaced)
1202 break;
1203 replaced = 1;
1205 ptr++;
1207 len += strlen(s);
1208 buf = xrealloc(buf, 1, len + 1);
1209 strlcat(buf, s, len + 1);
1210 continue;
1212 buf = xrealloc(buf, 1, len + 2);
1213 buf[len++] = ch;
1214 buf[len] = '\0';
1217 return (buf);