Update man page for update-environment.
[tmux-openbsd.git] / cmd.c
blobaf038b7b542c66d9fef830714c618a041c3ced11
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_buffer_entry,
44 &cmd_copy_mode_entry,
45 &cmd_delete_buffer_entry,
46 &cmd_detach_client_entry,
47 &cmd_display_message_entry,
48 &cmd_display_panes_entry,
49 &cmd_find_window_entry,
50 &cmd_has_session_entry,
51 &cmd_if_shell_entry,
52 &cmd_join_pane_entry,
53 &cmd_kill_pane_entry,
54 &cmd_kill_server_entry,
55 &cmd_kill_session_entry,
56 &cmd_kill_window_entry,
57 &cmd_last_pane_entry,
58 &cmd_last_window_entry,
59 &cmd_link_window_entry,
60 &cmd_list_buffers_entry,
61 &cmd_list_clients_entry,
62 &cmd_list_commands_entry,
63 &cmd_list_keys_entry,
64 &cmd_list_panes_entry,
65 &cmd_list_sessions_entry,
66 &cmd_list_windows_entry,
67 &cmd_load_buffer_entry,
68 &cmd_lock_client_entry,
69 &cmd_lock_server_entry,
70 &cmd_lock_session_entry,
71 &cmd_move_window_entry,
72 &cmd_new_session_entry,
73 &cmd_new_window_entry,
74 &cmd_next_layout_entry,
75 &cmd_next_window_entry,
76 &cmd_paste_buffer_entry,
77 &cmd_pipe_pane_entry,
78 &cmd_previous_layout_entry,
79 &cmd_previous_window_entry,
80 &cmd_refresh_client_entry,
81 &cmd_rename_session_entry,
82 &cmd_rename_window_entry,
83 &cmd_resize_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(struct sessions *);
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 **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 char s[BUFSIZ];
205 int opt, ambiguous = 0;
207 *cause = NULL;
208 if (argc == 0) {
209 xasprintf(cause, "no command");
210 return (NULL);
213 entry = NULL;
214 for (entryp = cmd_table; *entryp != NULL; entryp++) {
215 if ((*entryp)->alias != NULL &&
216 strcmp((*entryp)->alias, argv[0]) == 0) {
217 ambiguous = 0;
218 entry = *entryp;
219 break;
222 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
223 continue;
224 if (entry != NULL)
225 ambiguous = 1;
226 entry = *entryp;
228 /* Bail now if an exact match. */
229 if (strcmp(entry->name, argv[0]) == 0)
230 break;
232 if (ambiguous)
233 goto ambiguous;
234 if (entry == NULL) {
235 xasprintf(cause, "unknown command: %s", argv[0]);
236 return (NULL);
239 optreset = 1;
240 optind = 1;
241 if (entry->parse == NULL) {
242 while ((opt = getopt(argc, argv, "")) != -1) {
243 switch (opt) {
244 default:
245 goto usage;
248 argc -= optind;
249 argv += optind;
250 if (argc != 0)
251 goto usage;
254 cmd = xmalloc(sizeof *cmd);
255 cmd->entry = entry;
256 cmd->data = NULL;
257 if (entry->parse != NULL) {
258 if (entry->parse(cmd, argc, argv, cause) != 0) {
259 xfree(cmd);
260 return (NULL);
263 return (cmd);
265 ambiguous:
266 *s = '\0';
267 for (entryp = cmd_table; *entryp != NULL; entryp++) {
268 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
269 continue;
270 if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
271 break;
272 if (strlcat(s, ", ", sizeof s) >= sizeof s)
273 break;
275 s[strlen(s) - 2] = '\0';
276 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
277 return (NULL);
279 usage:
280 xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
281 return (NULL);
285 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
287 return (cmd->entry->exec(cmd, ctx));
290 void
291 cmd_free(struct cmd *cmd)
293 if (cmd->data != NULL && cmd->entry->free != NULL)
294 cmd->entry->free(cmd);
295 xfree(cmd);
298 size_t
299 cmd_print(struct cmd *cmd, char *buf, size_t len)
301 if (cmd->entry->print == NULL)
302 return (xsnprintf(buf, len, "%s", cmd->entry->name));
303 return (cmd->entry->print(cmd, buf, len));
307 * Figure out the current session. Use: 1) the current session, if the command
308 * context has one; 2) the most recently used session containing the pty of the
309 * calling client, if any; 3) the session specified in the TMUX variable from
310 * the environment (as passed from the client); 4) the most recently used
311 * session from all sessions.
313 struct session *
314 cmd_current_session(struct cmd_ctx *ctx)
316 struct msg_command_data *data = ctx->msgdata;
317 struct client *c = ctx->cmdclient;
318 struct session *s;
319 struct sessions ss;
320 struct winlink *wl;
321 struct window_pane *wp;
322 u_int i;
323 int found;
325 if (ctx->curclient != NULL && ctx->curclient->session != NULL)
326 return (ctx->curclient->session);
329 * If the name of the calling client's pty is know, build a list of the
330 * sessions that contain it and if any choose either the first or the
331 * newest.
333 if (c != NULL && c->tty.path != NULL) {
334 ARRAY_INIT(&ss);
335 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
336 if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
337 continue;
338 found = 0;
339 RB_FOREACH(wl, winlinks, &s->windows) {
340 TAILQ_FOREACH(wp, &wl->window->panes, entry) {
341 if (strcmp(wp->tty, c->tty.path) == 0) {
342 found = 1;
343 break;
346 if (found)
347 break;
349 if (found)
350 ARRAY_ADD(&ss, s);
353 s = cmd_choose_session(&ss);
354 ARRAY_FREE(&ss);
355 if (s != NULL)
356 return (s);
359 /* Use the session from the TMUX environment variable. */
360 if (data != NULL &&
361 data->pid == getpid() &&
362 data->idx <= ARRAY_LENGTH(&sessions) &&
363 (s = ARRAY_ITEM(&sessions, data->idx)) != NULL)
364 return (s);
366 return (cmd_choose_session(&sessions));
369 /* Find the most recently used session from a list. */
370 struct session *
371 cmd_choose_session(struct sessions *ss)
373 struct session *s, *sbest;
374 struct timeval *tv = NULL;
375 u_int i;
377 sbest = NULL;
378 for (i = 0; i < ARRAY_LENGTH(ss); i++) {
379 if ((s = ARRAY_ITEM(ss, i)) == NULL)
380 continue;
382 if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
383 sbest = s;
384 tv = &s->activity_time;
388 return (sbest);
392 * Find the current client. First try the current client if set, then pick the
393 * most recently used of the clients attached to the current session if any,
394 * then of all clients.
396 struct client *
397 cmd_current_client(struct cmd_ctx *ctx)
399 struct session *s;
400 struct client *c;
401 struct clients cc;
402 u_int i;
404 if (ctx->curclient != NULL)
405 return (ctx->curclient);
408 * No current client set. Find the current session and return the
409 * newest of its clients.
411 s = cmd_current_session(ctx);
412 if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
413 ARRAY_INIT(&cc);
414 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
415 if ((c = ARRAY_ITEM(&clients, i)) == NULL)
416 continue;
417 if (s == c->session)
418 ARRAY_ADD(&cc, c);
421 c = cmd_choose_client(&cc);
422 ARRAY_FREE(&cc);
423 if (c != NULL)
424 return (c);
427 return (cmd_choose_client(&clients));
430 /* Choose the most recently used client from a list. */
431 struct client *
432 cmd_choose_client(struct clients *cc)
434 struct client *c, *cbest;
435 struct timeval *tv = NULL;
436 u_int i;
438 cbest = NULL;
439 for (i = 0; i < ARRAY_LENGTH(cc); i++) {
440 if ((c = ARRAY_ITEM(cc, i)) == NULL)
441 continue;
442 if (c->session == NULL)
443 continue;
445 if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
446 cbest = c;
447 tv = &c->activity_time;
451 return (cbest);
454 /* Find the target client or report an error and return NULL. */
455 struct client *
456 cmd_find_client(struct cmd_ctx *ctx, const char *arg)
458 struct client *c;
459 char *tmparg;
460 size_t arglen;
462 /* A NULL argument means the current client. */
463 if (arg == NULL)
464 return (cmd_current_client(ctx));
465 tmparg = xstrdup(arg);
467 /* Trim a single trailing colon if any. */
468 arglen = strlen(tmparg);
469 if (arglen != 0 && tmparg[arglen - 1] == ':')
470 tmparg[arglen - 1] = '\0';
472 /* Find the client, if any. */
473 c = cmd_lookup_client(tmparg);
475 /* If no client found, report an error. */
476 if (c == NULL)
477 ctx->error(ctx, "client not found: %s", tmparg);
479 xfree(tmparg);
480 return (c);
484 * Lookup a client by device path. Either of a full match and a match without a
485 * leading _PATH_DEV ("/dev/") is accepted.
487 struct client *
488 cmd_lookup_client(const char *name)
490 struct client *c;
491 const char *path;
492 u_int i;
494 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
495 c = ARRAY_ITEM(&clients, i);
496 if (c == NULL || c->session == NULL)
497 continue;
498 path = c->tty.path;
500 /* Check for exact matches. */
501 if (strcmp(name, path) == 0)
502 return (c);
504 /* Check without leading /dev if present. */
505 if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
506 continue;
507 if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
508 return (c);
511 return (NULL);
514 /* Lookup a session by name. If no session is found, NULL is returned. */
515 struct session *
516 cmd_lookup_session(const char *name, int *ambiguous)
518 struct session *s, *sfound;
519 u_int i;
521 *ambiguous = 0;
524 * Look for matches. First look for exact matches - session names must
525 * be unique so an exact match can't be ambigious and can just be
526 * returned.
528 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
529 if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
530 continue;
531 if (strcmp(name, s->name) == 0)
532 return (s);
536 * Otherwise look for partial matches, returning early if it is found to
537 * be ambiguous.
539 sfound = NULL;
540 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
541 if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
542 continue;
543 if (strncmp(name, s->name, strlen(name)) == 0 ||
544 fnmatch(name, s->name, 0) == 0) {
545 if (sfound != NULL) {
546 *ambiguous = 1;
547 return (NULL);
549 sfound = s;
552 return (sfound);
556 * Lookup a window or return -1 if not found or ambigious. First try as an
557 * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
558 * idx if the window index is a valid number but there is no window with that
559 * index.
561 struct winlink *
562 cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
564 struct winlink *wl, *wlfound;
565 const char *errstr;
566 u_int idx;
568 *ambiguous = 0;
570 /* First see if this is a valid window index in this session. */
571 idx = strtonum(name, 0, INT_MAX, &errstr);
572 if (errstr == NULL) {
573 if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
574 return (wl);
577 /* Look for exact matches, error if more than one. */
578 wlfound = NULL;
579 RB_FOREACH(wl, winlinks, &s->windows) {
580 if (strcmp(name, wl->window->name) == 0) {
581 if (wlfound != NULL) {
582 *ambiguous = 1;
583 return (NULL);
585 wlfound = wl;
588 if (wlfound != NULL)
589 return (wlfound);
591 /* Now look for pattern matches, again error if multiple. */
592 wlfound = NULL;
593 RB_FOREACH(wl, winlinks, &s->windows) {
594 if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
595 fnmatch(name, wl->window->name, 0) == 0) {
596 if (wlfound != NULL) {
597 *ambiguous = 1;
598 return (NULL);
600 wlfound = wl;
603 if (wlfound != NULL)
604 return (wlfound);
606 return (NULL);
610 * Find a window index - if the window doesn't exist, check if it is a
611 * potential index and return it anyway.
614 cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
616 struct winlink *wl;
617 const char *errstr;
618 u_int idx;
620 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
621 return (wl->idx);
622 if (*ambiguous)
623 return (-1);
625 idx = strtonum(name, 0, INT_MAX, &errstr);
626 if (errstr == NULL)
627 return (idx);
629 return (-1);
632 /* Find the target session or report an error and return NULL. */
633 struct session *
634 cmd_find_session(struct cmd_ctx *ctx, const char *arg)
636 struct session *s;
637 struct client *c;
638 char *tmparg;
639 size_t arglen;
640 int ambiguous;
642 /* A NULL argument means the current session. */
643 if (arg == NULL)
644 return (cmd_current_session(ctx));
645 tmparg = xstrdup(arg);
647 /* Trim a single trailing colon if any. */
648 arglen = strlen(tmparg);
649 if (arglen != 0 && tmparg[arglen - 1] == ':')
650 tmparg[arglen - 1] = '\0';
652 /* Find the session, if any. */
653 s = cmd_lookup_session(tmparg, &ambiguous);
655 /* If it doesn't, try to match it as a client. */
656 if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
657 s = c->session;
659 /* If no session found, report an error. */
660 if (s == NULL) {
661 if (ambiguous)
662 ctx->error(ctx, "more than one session: %s", tmparg);
663 else
664 ctx->error(ctx, "session not found: %s", tmparg);
667 xfree(tmparg);
668 return (s);
671 /* Find the target session and window or report an error and return NULL. */
672 struct winlink *
673 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
675 struct session *s;
676 struct winlink *wl;
677 const char *winptr;
678 char *sessptr = NULL;
679 int ambiguous = 0;
682 * Find the current session. There must always be a current session, if
683 * it can't be found, report an error.
685 if ((s = cmd_current_session(ctx)) == NULL) {
686 ctx->error(ctx, "can't establish current session");
687 return (NULL);
690 /* A NULL argument means the current session and window. */
691 if (arg == NULL) {
692 if (sp != NULL)
693 *sp = s;
694 return (s->curw);
697 /* Time to look at the argument. If it is empty, that is an error. */
698 if (*arg == '\0')
699 goto not_found;
701 /* Find the separating colon and split into window and session. */
702 winptr = strchr(arg, ':');
703 if (winptr == NULL)
704 goto no_colon;
705 winptr++; /* skip : */
706 sessptr = xstrdup(arg);
707 *strchr(sessptr, ':') = '\0';
709 /* Try to lookup the session if present. */
710 if (*sessptr != '\0') {
711 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
712 goto no_session;
714 if (sp != NULL)
715 *sp = s;
718 * Then work out the window. An empty string is the current window,
719 * otherwise try special cases then to look it up in the session.
721 if (*winptr == '\0')
722 wl = s->curw;
723 else if (winptr[0] == '!' && winptr[1] == '\0')
724 wl = TAILQ_FIRST(&s->lastw);
725 else if (winptr[0] == '+' || winptr[0] == '-')
726 wl = cmd_find_window_offset(winptr, s, &ambiguous);
727 else
728 wl = cmd_lookup_window(s, winptr, &ambiguous);
729 if (wl == NULL)
730 goto not_found;
732 if (sessptr != NULL)
733 xfree(sessptr);
734 return (wl);
736 no_colon:
738 * No colon in the string, first try special cases, then as a window
739 * and lastly as a session.
741 if (arg[0] == '!' && arg[1] == '\0') {
742 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
743 goto not_found;
744 } else if (arg[0] == '+' || arg[0] == '-') {
745 if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
746 goto lookup_session;
747 } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
748 goto lookup_session;
750 if (sp != NULL)
751 *sp = s;
753 return (wl);
755 lookup_session:
756 if (ambiguous)
757 goto not_found;
758 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
759 goto no_session;
761 if (sp != NULL)
762 *sp = s;
764 return (s->curw);
766 no_session:
767 if (ambiguous)
768 ctx->error(ctx, "multiple sessions: %s", arg);
769 else
770 ctx->error(ctx, "session not found: %s", arg);
771 if (sessptr != NULL)
772 xfree(sessptr);
773 return (NULL);
775 not_found:
776 if (ambiguous)
777 ctx->error(ctx, "multiple windows: %s", arg);
778 else
779 ctx->error(ctx, "window not found: %s", arg);
780 if (sessptr != NULL)
781 xfree(sessptr);
782 return (NULL);
785 struct winlink *
786 cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
788 struct winlink *wl;
789 int offset = 1;
791 if (winptr[1] != '\0')
792 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
793 if (offset == 0)
794 wl = cmd_lookup_window(s, winptr, ambiguous);
795 else {
796 if (winptr[0] == '+')
797 wl = winlink_next_by_number(s->curw, s, offset);
798 else
799 wl = winlink_previous_by_number(s->curw, s, offset);
802 return (wl);
806 * Find the target session and window index, whether or not it exists in the
807 * session. Return -2 on error or -1 if no window index is specified. This is
808 * used when parsing an argument for a window target that may not exist (for
809 * example if it is going to be created).
812 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
814 struct session *s;
815 struct winlink *wl;
816 const char *winptr;
817 char *sessptr = NULL;
818 int idx, ambiguous = 0;
821 * Find the current session. There must always be a current session, if
822 * it can't be found, report an error.
824 if ((s = cmd_current_session(ctx)) == NULL) {
825 ctx->error(ctx, "can't establish current session");
826 return (-2);
829 /* A NULL argument means the current session and "no window" (-1). */
830 if (arg == NULL) {
831 if (sp != NULL)
832 *sp = s;
833 return (-1);
836 /* Time to look at the argument. If it is empty, that is an error. */
837 if (*arg == '\0')
838 goto not_found;
840 /* Find the separating colon. If none, assume the current session. */
841 winptr = strchr(arg, ':');
842 if (winptr == NULL)
843 goto no_colon;
844 winptr++; /* skip : */
845 sessptr = xstrdup(arg);
846 *strchr(sessptr, ':') = '\0';
848 /* Try to lookup the session if present. */
849 if (sessptr != NULL && *sessptr != '\0') {
850 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
851 goto no_session;
853 if (sp != NULL)
854 *sp = s;
857 * Then work out the window. An empty string is a new window otherwise
858 * try to look it up in the session.
860 if (*winptr == '\0')
861 idx = -1;
862 else if (winptr[0] == '!' && winptr[1] == '\0') {
863 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
864 goto not_found;
865 idx = wl->idx;
866 } else if (winptr[0] == '+' || winptr[0] == '-') {
867 if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
868 goto invalid_index;
869 } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
870 goto invalid_index;
872 if (sessptr != NULL)
873 xfree(sessptr);
874 return (idx);
876 no_colon:
878 * No colon in the string, first try special cases, then as a window
879 * and lastly as a session.
881 if (arg[0] == '!' && arg[1] == '\0') {
882 if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
883 goto not_found;
884 idx = wl->idx;
885 } else if (arg[0] == '+' || arg[0] == '-') {
886 if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
887 goto lookup_session;
888 } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
889 goto lookup_session;
891 if (sp != NULL)
892 *sp = s;
894 return (idx);
896 lookup_session:
897 if (ambiguous)
898 goto not_found;
899 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
900 goto no_session;
902 if (sp != NULL)
903 *sp = s;
905 return (-1);
907 no_session:
908 if (ambiguous)
909 ctx->error(ctx, "multiple sessions: %s", arg);
910 else
911 ctx->error(ctx, "session not found: %s", arg);
912 if (sessptr != NULL)
913 xfree(sessptr);
914 return (-2);
916 invalid_index:
917 if (ambiguous)
918 goto not_found;
919 ctx->error(ctx, "invalid index: %s", arg);
921 if (sessptr != NULL)
922 xfree(sessptr);
923 return (-2);
925 not_found:
926 if (ambiguous)
927 ctx->error(ctx, "multiple windows: %s", arg);
928 else
929 ctx->error(ctx, "window not found: %s", arg);
930 if (sessptr != NULL)
931 xfree(sessptr);
932 return (-2);
936 cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
938 int idx, offset = 1;
940 if (winptr[1] != '\0')
941 offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
942 if (offset == 0)
943 idx = cmd_lookup_index(s, winptr, ambiguous);
944 else {
945 if (winptr[0] == '+') {
946 if (s->curw->idx == INT_MAX)
947 idx = cmd_lookup_index(s, winptr, ambiguous);
948 else
949 idx = s->curw->idx + offset;
950 } else {
951 if (s->curw->idx == 0)
952 idx = cmd_lookup_index(s, winptr, ambiguous);
953 else
954 idx = s->curw->idx - offset;
958 return (idx);
962 * Find the target session, window and pane number or report an error and
963 * return NULL. The pane number is separated from the session:window by a .,
964 * such as mysession:mywindow.0.
966 struct winlink *
967 cmd_find_pane(struct cmd_ctx *ctx,
968 const char *arg, struct session **sp, struct window_pane **wpp)
970 struct session *s;
971 struct winlink *wl;
972 struct layout_cell *lc;
973 const char *period, *errstr;
974 char *winptr, *paneptr;
975 u_int idx;
977 /* Get the current session. */
978 if ((s = cmd_current_session(ctx)) == NULL) {
979 ctx->error(ctx, "can't establish current session");
980 return (NULL);
982 if (sp != NULL)
983 *sp = s;
985 /* A NULL argument means the current session, window and pane. */
986 if (arg == NULL) {
987 *wpp = s->curw->window->active;
988 return (s->curw);
991 /* Look for a separating period. */
992 if ((period = strrchr(arg, '.')) == NULL)
993 goto no_period;
995 /* Pull out the window part and parse it. */
996 winptr = xstrdup(arg);
997 winptr[period - arg] = '\0';
998 if (*winptr == '\0')
999 wl = s->curw;
1000 else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
1001 goto error;
1003 /* Find the pane section and look it up. */
1004 paneptr = winptr + (period - arg) + 1;
1005 if (*paneptr == '\0')
1006 *wpp = wl->window->active;
1007 else if (paneptr[0] == '+' || paneptr[0] == '-')
1008 *wpp = cmd_find_pane_offset(paneptr, wl);
1009 else {
1010 idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1011 if (errstr != NULL)
1012 goto lookup_string;
1013 *wpp = window_pane_at_index(wl->window, idx);
1014 if (*wpp == NULL)
1015 goto lookup_string;
1018 xfree(winptr);
1019 return (wl);
1021 lookup_string:
1022 /* Try pane string description. */
1023 if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
1024 ctx->error(ctx, "can't find pane: %s", paneptr);
1025 goto error;
1027 *wpp = lc->wp;
1029 xfree(winptr);
1030 return (wl);
1032 no_period:
1033 /* Try as a pane number alone. */
1034 idx = strtonum(arg, 0, INT_MAX, &errstr);
1035 if (errstr != NULL)
1036 goto lookup_window;
1038 /* Try index in the current session and window. */
1039 if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1040 goto lookup_window;
1042 return (s->curw);
1044 lookup_window:
1045 /* Try pane string description. */
1046 if ((lc = layout_find_string(s->curw->window, arg)) != NULL) {
1047 *wpp = lc->wp;
1048 return (s->curw);
1051 /* Try as a window and use the active pane. */
1052 if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1053 *wpp = wl->window->active;
1054 return (wl);
1056 error:
1057 xfree(winptr);
1058 return (NULL);
1061 struct window_pane *
1062 cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1064 struct window *w = wl->window;
1065 struct window_pane *wp = w->active;
1066 u_int offset = 1;
1068 if (paneptr[1] != '\0')
1069 offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1070 if (offset > 0) {
1071 if (paneptr[0] == '+')
1072 wp = window_pane_next_by_number(w, wp, offset);
1073 else
1074 wp = window_pane_previous_by_number(w, wp, offset);
1077 return (wp);
1080 /* Replace the first %% or %idx in template by s. */
1081 char *
1082 cmd_template_replace(char *template, const char *s, int idx)
1084 char ch;
1085 char *buf, *ptr;
1086 int replaced;
1087 size_t len;
1089 if (strstr(template, "%") == NULL)
1090 return (xstrdup(template));
1092 buf = xmalloc(1);
1093 *buf = '\0';
1094 len = 0;
1095 replaced = 0;
1097 ptr = template;
1098 while (*ptr != '\0') {
1099 switch (ch = *ptr++) {
1100 case '%':
1101 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1102 if (*ptr != '%' || replaced)
1103 break;
1104 replaced = 1;
1106 ptr++;
1108 len += strlen(s);
1109 buf = xrealloc(buf, 1, len + 1);
1110 strlcat(buf, s, len + 1);
1111 continue;
1113 buf = xrealloc(buf, 1, len + 2);
1114 buf[len++] = ch;
1115 buf[len] = '\0';
1118 return (buf);