Didn't really think the else behaviour through - requiring argv to
[tmux-openbsd.git] / cmd.c
blobdb72337d4cac2cf7bf86a8dcbea19d0cdd050369
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_pane_entry,
84 &cmd_respawn_window_entry,
85 &cmd_rotate_window_entry,
86 &cmd_run_shell_entry,
87 &cmd_save_buffer_entry,
88 &cmd_select_layout_entry,
89 &cmd_select_pane_entry,
90 &cmd_select_window_entry,
91 &cmd_send_keys_entry,
92 &cmd_send_prefix_entry,
93 &cmd_server_info_entry,
94 &cmd_set_buffer_entry,
95 &cmd_set_environment_entry,
96 &cmd_set_option_entry,
97 &cmd_set_window_option_entry,
98 &cmd_show_buffer_entry,
99 &cmd_show_environment_entry,
100 &cmd_show_messages_entry,
101 &cmd_show_options_entry,
102 &cmd_show_window_options_entry,
103 &cmd_source_file_entry,
104 &cmd_split_window_entry,
105 &cmd_start_server_entry,
106 &cmd_suspend_client_entry,
107 &cmd_swap_pane_entry,
108 &cmd_swap_window_entry,
109 &cmd_switch_client_entry,
110 &cmd_unbind_key_entry,
111 &cmd_unlink_window_entry,
112 NULL
115 struct session *cmd_choose_session_list(struct sessionslist *);
116 struct session *cmd_choose_session(int);
117 struct client *cmd_choose_client(struct clients *);
118 struct client *cmd_lookup_client(const char *);
119 struct session *cmd_lookup_session(const char *, int *);
120 struct winlink *cmd_lookup_window(struct session *, const char *, int *);
121 int cmd_lookup_index(struct session *, const char *, int *);
122 struct window_pane *cmd_lookup_paneid(const char *);
123 struct session *cmd_pane_session(struct cmd_ctx *,
124 struct window_pane *, struct winlink **);
125 struct winlink *cmd_find_window_offset(const char *, struct session *, int *);
126 int cmd_find_index_offset(const char *, struct session *, int *);
127 struct window_pane *cmd_find_pane_offset(const char *, struct winlink *);
130 cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
132 size_t arglen;
133 int i;
135 *buf = '\0';
136 for (i = 0; i < argc; i++) {
137 if (strlcpy(buf, argv[i], len) >= len)
138 return (-1);
139 arglen = strlen(argv[i]) + 1;
140 buf += arglen;
141 len -= arglen;
144 return (0);
148 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
150 int i;
151 size_t arglen;
153 if (argc == 0)
154 return (0);
155 *argv = xcalloc(argc, sizeof **argv);
157 buf[len - 1] = '\0';
158 for (i = 0; i < argc; i++) {
159 if (len == 0) {
160 cmd_free_argv(argc, *argv);
161 return (-1);
164 arglen = strlen(buf) + 1;
165 (*argv)[i] = xstrdup(buf);
166 buf += arglen;
167 len -= arglen;
170 return (0);
173 char **
174 cmd_copy_argv(int argc, char *const *argv)
176 char **new_argv;
177 int i;
179 if (argc == 0)
180 return (NULL);
181 new_argv = xcalloc(argc, sizeof *new_argv);
182 for (i = 0; i < argc; i++) {
183 if (argv[i] != NULL)
184 new_argv[i] = xstrdup(argv[i]);
186 return (new_argv);
189 void
190 cmd_free_argv(int argc, char **argv)
192 int i;
194 if (argc == 0)
195 return;
196 for (i = 0; i < argc; i++) {
197 if (argv[i] != NULL)
198 xfree(argv[i]);
200 xfree(argv);
203 struct cmd *
204 cmd_parse(int argc, char **argv, char **cause)
206 const struct cmd_entry **entryp, *entry;
207 struct cmd *cmd;
208 struct args *args;
209 char s[BUFSIZ];
210 int ambiguous = 0;
212 *cause = NULL;
213 if (argc == 0) {
214 xasprintf(cause, "no command");
215 return (NULL);
218 entry = NULL;
219 for (entryp = cmd_table; *entryp != NULL; entryp++) {
220 if ((*entryp)->alias != NULL &&
221 strcmp((*entryp)->alias, argv[0]) == 0) {
222 ambiguous = 0;
223 entry = *entryp;
224 break;
227 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
228 continue;
229 if (entry != NULL)
230 ambiguous = 1;
231 entry = *entryp;
233 /* Bail now if an exact match. */
234 if (strcmp(entry->name, argv[0]) == 0)
235 break;
237 if (ambiguous)
238 goto ambiguous;
239 if (entry == NULL) {
240 xasprintf(cause, "unknown command: %s", argv[0]);
241 return (NULL);
244 args = args_parse(entry->args_template, argc, argv);
245 if (args == NULL)
246 goto usage;
247 if (entry->args_lower != -1 && args->argc < entry->args_lower)
248 goto usage;
249 if (entry->args_upper != -1 && args->argc > entry->args_upper)
250 goto usage;
251 if (entry->check != NULL && entry->check(args) != 0)
252 goto usage;
254 cmd = xmalloc(sizeof *cmd);
255 cmd->entry = entry;
256 cmd->args = args;
257 return (cmd);
259 ambiguous:
260 *s = '\0';
261 for (entryp = cmd_table; *entryp != NULL; entryp++) {
262 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
263 continue;
264 if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
265 break;
266 if (strlcat(s, ", ", sizeof s) >= sizeof s)
267 break;
269 s[strlen(s) - 2] = '\0';
270 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
271 return (NULL);
273 usage:
274 if (args != NULL)
275 args_free(args);
276 xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
277 return (NULL);
281 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
283 return (cmd->entry->exec(cmd, ctx));
286 void
287 cmd_free(struct cmd *cmd)
289 if (cmd->args != NULL)
290 args_free(cmd->args);
291 xfree(cmd);
294 size_t
295 cmd_print(struct cmd *cmd, char *buf, size_t len)
297 size_t off, used;
299 off = xsnprintf(buf, len, "%s ", cmd->entry->name);
300 if (off < len) {
301 used = args_print(cmd->args, buf + off, len - off);
302 if (used == 0)
303 buf[off - 1] = '\0';
304 else {
305 off += used;
306 buf[off] = '\0';
309 return (off);
313 * Figure out the current session. Use: 1) the current session, if the command
314 * context has one; 2) the most recently used session containing the pty of the
315 * calling client, if any; 3) the session specified in the TMUX variable from
316 * the environment (as passed from the client); 4) the most recently used
317 * session from all sessions.
319 struct session *
320 cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached)
322 struct msg_command_data *data = ctx->msgdata;
323 struct client *c = ctx->cmdclient;
324 struct session *s;
325 struct sessionslist ss;
326 struct winlink *wl;
327 struct window_pane *wp;
328 int found;
330 if (ctx->curclient != NULL && ctx->curclient->session != NULL)
331 return (ctx->curclient->session);
334 * If the name of the calling client's pty is know, build a list of the
335 * sessions that contain it and if any choose either the first or the
336 * newest.
338 if (c != NULL && c->tty.path != NULL) {
339 ARRAY_INIT(&ss);
340 RB_FOREACH(s, sessions, &sessions) {
341 found = 0;
342 RB_FOREACH(wl, winlinks, &s->windows) {
343 TAILQ_FOREACH(wp, &wl->window->panes, entry) {
344 if (strcmp(wp->tty, c->tty.path) == 0) {
345 found = 1;
346 break;
349 if (found)
350 break;
352 if (found)
353 ARRAY_ADD(&ss, s);
356 s = cmd_choose_session_list(&ss);
357 ARRAY_FREE(&ss);
358 if (s != NULL)
359 return (s);
362 /* Use the session from the TMUX environment variable. */
363 if (data != NULL && data->pid == getpid() && data->idx != -1) {
364 s = session_find_by_index(data->idx);
365 if (s != NULL)
366 return (s);
369 return (cmd_choose_session(prefer_unattached));
373 * Find the most recently used session, preferring unattached if the flag is
374 * set.
376 struct session *
377 cmd_choose_session(int prefer_unattached)
379 struct session *s, *sbest;
380 struct timeval *tv = NULL;
382 sbest = NULL;
383 RB_FOREACH(s, sessions, &sessions) {
384 if (tv == NULL || timercmp(&s->activity_time, tv, >) ||
385 (prefer_unattached &&
386 !(sbest->flags & SESSION_UNATTACHED) &&
387 (s->flags & SESSION_UNATTACHED))) {
388 sbest = s;
389 tv = &s->activity_time;
393 return (sbest);
396 /* Find the most recently used session from a list. */
397 struct session *
398 cmd_choose_session_list(struct sessionslist *ss)
400 struct session *s, *sbest;
401 struct timeval *tv = NULL;
402 u_int i;
404 sbest = NULL;
405 for (i = 0; i < ARRAY_LENGTH(ss); i++) {
406 if ((s = ARRAY_ITEM(ss, i)) == NULL)
407 continue;
409 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
410 sbest = s;
411 tv = &s->activity_time;
415 return (sbest);
419 * Find the current client. First try the current client if set, then pick the
420 * most recently used of the clients attached to the current session if any,
421 * then of all clients.
423 struct client *
424 cmd_current_client(struct cmd_ctx *ctx)
426 struct session *s;
427 struct client *c;
428 struct clients cc;
429 u_int i;
431 if (ctx->curclient != NULL)
432 return (ctx->curclient);
435 * No current client set. Find the current session and return the
436 * newest of its clients.
438 s = cmd_current_session(ctx, 0);
439 if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
440 ARRAY_INIT(&cc);
441 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
442 if ((c = ARRAY_ITEM(&clients, i)) == NULL)
443 continue;
444 if (s == c->session)
445 ARRAY_ADD(&cc, c);
448 c = cmd_choose_client(&cc);
449 ARRAY_FREE(&cc);
450 if (c != NULL)
451 return (c);
454 return (cmd_choose_client(&clients));
457 /* Choose the most recently used client from a list. */
458 struct client *
459 cmd_choose_client(struct clients *cc)
461 struct client *c, *cbest;
462 struct timeval *tv = NULL;
463 u_int i;
465 cbest = NULL;
466 for (i = 0; i < ARRAY_LENGTH(cc); i++) {
467 if ((c = ARRAY_ITEM(cc, i)) == NULL)
468 continue;
469 if (c->session == NULL)
470 continue;
472 if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
473 cbest = c;
474 tv = &c->activity_time;
478 return (cbest);
481 /* Find the target client or report an error and return NULL. */
482 struct client *
483 cmd_find_client(struct cmd_ctx *ctx, const char *arg)
485 struct client *c;
486 char *tmparg;
487 size_t arglen;
489 /* A NULL argument means the current client. */
490 if (arg == NULL)
491 return (cmd_current_client(ctx));
492 tmparg = xstrdup(arg);
494 /* Trim a single trailing colon if any. */
495 arglen = strlen(tmparg);
496 if (arglen != 0 && tmparg[arglen - 1] == ':')
497 tmparg[arglen - 1] = '\0';
499 /* Find the client, if any. */
500 c = cmd_lookup_client(tmparg);
502 /* If no client found, report an error. */
503 if (c == NULL)
504 ctx->error(ctx, "client not found: %s", tmparg);
506 xfree(tmparg);
507 return (c);
511 * Lookup a client by device path. Either of a full match and a match without a
512 * leading _PATH_DEV ("/dev/") is accepted.
514 struct client *
515 cmd_lookup_client(const char *name)
517 struct client *c;
518 const char *path;
519 u_int i;
521 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
522 c = ARRAY_ITEM(&clients, i);
523 if (c == NULL || c->session == NULL)
524 continue;
525 path = c->tty.path;
527 /* Check for exact matches. */
528 if (strcmp(name, path) == 0)
529 return (c);
531 /* Check without leading /dev if present. */
532 if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
533 continue;
534 if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
535 return (c);
538 return (NULL);
541 /* Lookup a session by name. If no session is found, NULL is returned. */
542 struct session *
543 cmd_lookup_session(const char *name, int *ambiguous)
545 struct session *s, *sfound;
547 *ambiguous = 0;
550 * Look for matches. First look for exact matches - session names must
551 * be unique so an exact match can't be ambigious and can just be
552 * returned.
554 if ((s = session_find(name)) != NULL)
555 return (s);
558 * Otherwise look for partial matches, returning early if it is found to
559 * be ambiguous.
561 sfound = NULL;
562 RB_FOREACH(s, sessions, &sessions) {
563 if (strncmp(name, s->name, strlen(name)) == 0 ||
564 fnmatch(name, s->name, 0) == 0) {
565 if (sfound != NULL) {
566 *ambiguous = 1;
567 return (NULL);
569 sfound = s;
572 return (sfound);
576 * Lookup a window or return -1 if not found or ambigious. First try as an
577 * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
578 * idx if the window index is a valid number but there is no window with that
579 * index.
581 struct winlink *
582 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
584 struct winlink *wl, *wlfound;
585 const char *errstr;
586 u_int idx;
588 *ambiguous = 0;
590 /* First see if this is a valid window index in this session. */
591 idx = strtonum(name, 0, INT_MAX, &errstr);
592 if (errstr == NULL) {
593 if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
594 return (wl);
597 /* Look for exact matches, error if more than one. */
598 wlfound = NULL;
599 RB_FOREACH(wl, winlinks, &s->windows) {
600 if (strcmp(name, wl->window->name) == 0) {
601 if (wlfound != NULL) {
602 *ambiguous = 1;
603 return (NULL);
605 wlfound = wl;
608 if (wlfound != NULL)
609 return (wlfound);
611 /* Now look for pattern matches, again error if multiple. */
612 wlfound = NULL;
613 RB_FOREACH(wl, winlinks, &s->windows) {
614 if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
615 fnmatch(name, wl->window->name, 0) == 0) {
616 if (wlfound != NULL) {
617 *ambiguous = 1;
618 return (NULL);
620 wlfound = wl;
623 if (wlfound != NULL)
624 return (wlfound);
626 return (NULL);
630 * Find a window index - if the window doesn't exist, check if it is a
631 * potential index and return it anyway.
634 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
636 struct winlink *wl;
637 const char *errstr;
638 u_int idx;
640 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
641 return (wl->idx);
642 if (*ambiguous)
643 return (-1);
645 idx = strtonum(name, 0, INT_MAX, &errstr);
646 if (errstr == NULL)
647 return (idx);
649 return (-1);
653 * Lookup pane id. An initial % means a pane id. sp must already point to the
654 * current session.
656 struct window_pane *
657 cmd_lookup_paneid(const char *arg)
659 const char *errstr;
660 u_int paneid;
662 if (*arg != '%')
663 return (NULL);
665 paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
666 if (errstr != NULL)
667 return (NULL);
668 return (window_pane_find_by_id(paneid));
671 /* Find session and winlink for pane. */
672 struct session *
673 cmd_pane_session(struct cmd_ctx *ctx, struct window_pane *wp,
674 struct winlink **wlp)
676 struct session *s;
677 struct sessionslist ss;
678 struct winlink *wl;
680 /* If this pane is in the current session, return that winlink. */
681 s = cmd_current_session(ctx, 0);
682 if (s != NULL) {
683 wl = winlink_find_by_window(&s->windows, wp->window);
684 if (wl != NULL) {
685 if (wlp != NULL)
686 *wlp = wl;
687 return (s);
691 /* Otherwise choose from all sessions with this pane. */
692 ARRAY_INIT(&ss);
693 RB_FOREACH(s, sessions, &sessions) {
694 if (winlink_find_by_window(&s->windows, wp->window) != NULL)
695 ARRAY_ADD(&ss, s);
697 s = cmd_choose_session_list(&ss);
698 ARRAY_FREE(&ss);
699 if (wlp != NULL)
700 *wlp = winlink_find_by_window(&s->windows, wp->window);
701 return (s);
704 /* Find the target session or report an error and return NULL. */
705 struct session *
706 cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached)
708 struct session *s;
709 struct window_pane *wp;
710 struct client *c;
711 char *tmparg;
712 size_t arglen;
713 int ambiguous;
715 /* A NULL argument means the current session. */
716 if (arg == NULL)
717 return (cmd_current_session(ctx, prefer_unattached));
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 tmparg = xstrdup(arg);
725 arglen = strlen(tmparg);
726 if (arglen != 0 && tmparg[arglen - 1] == ':')
727 tmparg[arglen - 1] = '\0';
729 /* An empty session name is the current session. */
730 if (*tmparg == '\0') {
731 xfree(tmparg);
732 return (cmd_current_session(ctx, prefer_unattached));
735 /* Find the session, if any. */
736 s = cmd_lookup_session(tmparg, &ambiguous);
738 /* If it doesn't, try to match it as a client. */
739 if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
740 s = c->session;
742 /* If no session found, report an error. */
743 if (s == NULL) {
744 if (ambiguous)
745 ctx->error(ctx, "more than one session: %s", tmparg);
746 else
747 ctx->error(ctx, "session not found: %s", tmparg);
750 xfree(tmparg);
751 return (s);
754 /* Find the target session and window or report an error and return NULL. */
755 struct winlink *
756 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
758 struct session *s;
759 struct winlink *wl;
760 struct window_pane *wp;
761 const char *winptr;
762 char *sessptr = NULL;
763 int ambiguous = 0;
766 * Find the current session. There must always be a current session, if
767 * it can't be found, report an error.
769 if ((s = cmd_current_session(ctx, 0)) == NULL) {
770 ctx->error(ctx, "can't establish current session");
771 return (NULL);
774 /* A NULL argument means the current session and window. */
775 if (arg == NULL) {
776 if (sp != NULL)
777 *sp = s;
778 return (s->curw);
781 /* Lookup as pane id. */
782 if ((wp = cmd_lookup_paneid(arg)) != NULL) {
783 s = cmd_pane_session(ctx, wp, &wl);
784 if (sp != NULL)
785 *sp = s;
786 return (wl);
789 /* Time to look at the argument. If it is empty, that is an error. */
790 if (*arg == '\0')
791 goto not_found;
793 /* Find the separating colon and split into window and session. */
794 winptr = strchr(arg, ':');
795 if (winptr == NULL)
796 goto no_colon;
797 winptr++; /* skip : */
798 sessptr = xstrdup(arg);
799 *strchr(sessptr, ':') = '\0';
801 /* Try to lookup the session if present. */
802 if (*sessptr != '\0') {
803 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
804 goto no_session;
806 if (sp != NULL)
807 *sp = s;
810 * Then work out the window. An empty string is the current window,
811 * otherwise try special cases then to look it up in the session.
813 if (*winptr == '\0')
814 wl = s->curw;
815 else if (winptr[0] == '!' && winptr[1] == '\0')
816 wl = TAILQ_FIRST(&s->lastw);
817 else if (winptr[0] == '+' || winptr[0] == '-')
818 wl = cmd_find_window_offset(winptr, s, &ambiguous);
819 else
820 wl = cmd_lookup_window(s, winptr, &ambiguous);
821 if (wl == NULL)
822 goto not_found;
824 if (sessptr != NULL)
825 xfree(sessptr);
826 return (wl);
828 no_colon:
830 * No colon in the string, first try special cases, then as a window
831 * and lastly as a session.
833 if (arg[0] == '!' && arg[1] == '\0') {
834 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
835 goto not_found;
836 } else if (arg[0] == '+' || arg[0] == '-') {
837 if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
838 goto lookup_session;
839 } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
840 goto lookup_session;
842 if (sp != NULL)
843 *sp = s;
845 return (wl);
847 lookup_session:
848 if (ambiguous)
849 goto not_found;
850 if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
851 goto no_session;
853 if (sp != NULL)
854 *sp = s;
856 return (s->curw);
858 no_session:
859 if (ambiguous)
860 ctx->error(ctx, "multiple sessions: %s", arg);
861 else
862 ctx->error(ctx, "session not found: %s", arg);
863 if (sessptr != NULL)
864 xfree(sessptr);
865 return (NULL);
867 not_found:
868 if (ambiguous)
869 ctx->error(ctx, "multiple windows: %s", arg);
870 else
871 ctx->error(ctx, "window not found: %s", arg);
872 if (sessptr != NULL)
873 xfree(sessptr);
874 return (NULL);
877 struct winlink *
878 cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
880 struct winlink *wl;
881 int offset = 1;
883 if (winptr[1] != '\0')
884 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
885 if (offset == 0)
886 wl = cmd_lookup_window(s, winptr, ambiguous);
887 else {
888 if (winptr[0] == '+')
889 wl = winlink_next_by_number(s->curw, s, offset);
890 else
891 wl = winlink_previous_by_number(s->curw, s, offset);
894 return (wl);
898 * Find the target session and window index, whether or not it exists in the
899 * session. Return -2 on error or -1 if no window index is specified. This is
900 * used when parsing an argument for a window target that may not exist (for
901 * example if it is going to be created).
904 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
906 struct session *s;
907 struct winlink *wl;
908 const char *winptr;
909 char *sessptr = NULL;
910 int idx, ambiguous = 0;
913 * Find the current session. There must always be a current session, if
914 * it can't be found, report an error.
916 if ((s = cmd_current_session(ctx, 0)) == NULL) {
917 ctx->error(ctx, "can't establish current session");
918 return (-2);
921 /* A NULL argument means the current session and "no window" (-1). */
922 if (arg == NULL) {
923 if (sp != NULL)
924 *sp = s;
925 return (-1);
928 /* Time to look at the argument. If it is empty, that is an error. */
929 if (*arg == '\0')
930 goto not_found;
932 /* Find the separating colon. If none, assume the current session. */
933 winptr = strchr(arg, ':');
934 if (winptr == NULL)
935 goto no_colon;
936 winptr++; /* skip : */
937 sessptr = xstrdup(arg);
938 *strchr(sessptr, ':') = '\0';
940 /* Try to lookup the session if present. */
941 if (sessptr != NULL && *sessptr != '\0') {
942 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
943 goto no_session;
945 if (sp != NULL)
946 *sp = s;
949 * Then work out the window. An empty string is a new window otherwise
950 * try to look it up in the session.
952 if (*winptr == '\0')
953 idx = -1;
954 else if (winptr[0] == '!' && winptr[1] == '\0') {
955 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
956 goto not_found;
957 idx = wl->idx;
958 } else if (winptr[0] == '+' || winptr[0] == '-') {
959 if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
960 goto invalid_index;
961 } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
962 goto invalid_index;
964 if (sessptr != NULL)
965 xfree(sessptr);
966 return (idx);
968 no_colon:
970 * No colon in the string, first try special cases, then as a window
971 * and lastly as a session.
973 if (arg[0] == '!' && arg[1] == '\0') {
974 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
975 goto not_found;
976 idx = wl->idx;
977 } else if (arg[0] == '+' || arg[0] == '-') {
978 if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
979 goto lookup_session;
980 } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
981 goto lookup_session;
983 if (sp != NULL)
984 *sp = s;
986 return (idx);
988 lookup_session:
989 if (ambiguous)
990 goto not_found;
991 if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
992 goto no_session;
994 if (sp != NULL)
995 *sp = s;
997 return (-1);
999 no_session:
1000 if (ambiguous)
1001 ctx->error(ctx, "multiple sessions: %s", arg);
1002 else
1003 ctx->error(ctx, "session not found: %s", arg);
1004 if (sessptr != NULL)
1005 xfree(sessptr);
1006 return (-2);
1008 invalid_index:
1009 if (ambiguous)
1010 goto not_found;
1011 ctx->error(ctx, "invalid index: %s", arg);
1013 if (sessptr != NULL)
1014 xfree(sessptr);
1015 return (-2);
1017 not_found:
1018 if (ambiguous)
1019 ctx->error(ctx, "multiple windows: %s", arg);
1020 else
1021 ctx->error(ctx, "window not found: %s", arg);
1022 if (sessptr != NULL)
1023 xfree(sessptr);
1024 return (-2);
1028 cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
1030 int idx, offset = 1;
1032 if (winptr[1] != '\0')
1033 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
1034 if (offset == 0)
1035 idx = cmd_lookup_index(s, winptr, ambiguous);
1036 else {
1037 if (winptr[0] == '+') {
1038 if (s->curw->idx == INT_MAX)
1039 idx = cmd_lookup_index(s, winptr, ambiguous);
1040 else
1041 idx = s->curw->idx + offset;
1042 } else {
1043 if (s->curw->idx == 0)
1044 idx = cmd_lookup_index(s, winptr, ambiguous);
1045 else
1046 idx = s->curw->idx - offset;
1050 return (idx);
1054 * Find the target session, window and pane number or report an error and
1055 * return NULL. The pane number is separated from the session:window by a .,
1056 * such as mysession:mywindow.0.
1058 struct winlink *
1059 cmd_find_pane(struct cmd_ctx *ctx,
1060 const char *arg, struct session **sp, struct window_pane **wpp)
1062 struct session *s;
1063 struct winlink *wl;
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 ((*wpp = window_find_string(wl->window, paneptr)) == NULL) {
1123 ctx->error(ctx, "can't find pane: %s", paneptr);
1124 goto error;
1127 xfree(winptr);
1128 return (wl);
1130 no_period:
1131 /* Try as a pane number alone. */
1132 idx = strtonum(arg, 0, INT_MAX, &errstr);
1133 if (errstr != NULL)
1134 goto lookup_window;
1136 /* Try index in the current session and window. */
1137 if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1138 goto lookup_window;
1140 return (s->curw);
1142 lookup_window:
1143 /* Try pane string description. */
1144 if ((*wpp = window_find_string(s->curw->window, arg)) != NULL)
1145 return (s->curw);
1147 /* Try as a window and use the active pane. */
1148 if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1149 *wpp = wl->window->active;
1150 return (wl);
1152 error:
1153 xfree(winptr);
1154 return (NULL);
1157 struct window_pane *
1158 cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1160 struct window *w = wl->window;
1161 struct window_pane *wp = w->active;
1162 u_int offset = 1;
1164 if (paneptr[1] != '\0')
1165 offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1166 if (offset > 0) {
1167 if (paneptr[0] == '+')
1168 wp = window_pane_next_by_number(w, wp, offset);
1169 else
1170 wp = window_pane_previous_by_number(w, wp, offset);
1173 return (wp);
1176 /* Replace the first %% or %idx in template by s. */
1177 char *
1178 cmd_template_replace(char *template, const char *s, int idx)
1180 char ch;
1181 char *buf, *ptr;
1182 int replaced;
1183 size_t len;
1185 if (strstr(template, "%") == NULL)
1186 return (xstrdup(template));
1188 buf = xmalloc(1);
1189 *buf = '\0';
1190 len = 0;
1191 replaced = 0;
1193 ptr = template;
1194 while (*ptr != '\0') {
1195 switch (ch = *ptr++) {
1196 case '%':
1197 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1198 if (*ptr != '%' || replaced)
1199 break;
1200 replaced = 1;
1202 ptr++;
1204 len += strlen(s);
1205 buf = xrealloc(buf, 1, len + 1);
1206 strlcat(buf, s, len + 1);
1207 continue;
1209 buf = xrealloc(buf, 1, len + 2);
1210 buf[len++] = ch;
1211 buf[len] = '\0';
1214 return (buf);