4 * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
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>
30 static int cmd_find_session_better(struct session
*, struct session
*,
32 static struct session
*cmd_find_best_session(struct session
**, u_int
, int);
33 static int cmd_find_best_session_with_window(struct cmd_find_state
*);
34 static int cmd_find_best_winlink_with_window(struct cmd_find_state
*);
36 static const char *cmd_find_map_table(const char *[][2], const char *);
38 static void cmd_find_log_state(const char *, struct cmd_find_state
*);
39 static int cmd_find_get_session(struct cmd_find_state
*, const char *);
40 static int cmd_find_get_window(struct cmd_find_state
*, const char *, int);
41 static int cmd_find_get_window_with_session(struct cmd_find_state
*,
43 static int cmd_find_get_pane(struct cmd_find_state
*, const char *, int);
44 static int cmd_find_get_pane_with_session(struct cmd_find_state
*,
46 static int cmd_find_get_pane_with_window(struct cmd_find_state
*,
49 static const char *cmd_find_session_table
[][2] = {
52 static const char *cmd_find_window_table
[][2] = {
57 { "{previous}", "-" },
60 static const char *cmd_find_pane_table
[][2] = {
63 { "{previous}", "-" },
65 { "{bottom}", "bottom" },
67 { "{right}", "right" },
68 { "{top-left}", "top-left" },
69 { "{top-right}", "top-right" },
70 { "{bottom-left}", "bottom-left" },
71 { "{bottom-right}", "bottom-right" },
72 { "{up-of}", "{up-of}" },
73 { "{down-of}", "{down-of}" },
74 { "{left-of}", "{left-of}" },
75 { "{right-of}", "{right-of}" },
79 /* Find pane containing client if any. */
80 static struct window_pane
*
81 cmd_find_inside_pane(struct client
*c
)
83 struct window_pane
*wp
;
84 struct environ_entry
*envent
;
89 RB_FOREACH(wp
, window_pane_tree
, &all_window_panes
) {
90 if (wp
->fd
!= -1 && strcmp(wp
->tty
, c
->ttyname
) == 0)
94 envent
= environ_find(c
->environ
, "TMUX_PANE");
96 wp
= window_pane_find_by_id_str(envent
->value
);
99 log_debug("%s: got pane %%%u (%s)", __func__
, wp
->id
, wp
->tty
);
103 /* Is this client better? */
105 cmd_find_client_better(struct client
*c
, struct client
*than
)
109 return (timercmp(&c
->activity_time
, &than
->activity_time
, >));
112 /* Find best client for session. */
114 cmd_find_best_client(struct session
*s
)
116 struct client
*c_loop
, *c
;
118 if (s
->attached
== 0)
122 TAILQ_FOREACH(c_loop
, &clients
, entry
) {
123 if (c_loop
->session
== NULL
)
125 if (s
!= NULL
&& c_loop
->session
!= s
)
127 if (cmd_find_client_better(c_loop
, c
))
133 /* Is this session better? */
135 cmd_find_session_better(struct session
*s
, struct session
*than
, int flags
)
141 if (flags
& CMD_FIND_PREFER_UNATTACHED
) {
142 attached
= (than
->attached
!= 0);
143 if (attached
&& s
->attached
== 0)
145 else if (!attached
&& s
->attached
!= 0)
148 return (timercmp(&s
->activity_time
, &than
->activity_time
, >));
151 /* Find best session from a list, or all if list is NULL. */
152 static struct session
*
153 cmd_find_best_session(struct session
**slist
, u_int ssize
, int flags
)
155 struct session
*s_loop
, *s
;
158 log_debug("%s: %u sessions to try", __func__
, ssize
);
162 for (i
= 0; i
< ssize
; i
++) {
163 if (cmd_find_session_better(slist
[i
], s
, flags
))
167 RB_FOREACH(s_loop
, sessions
, &sessions
) {
168 if (cmd_find_session_better(s_loop
, s
, flags
))
175 /* Find best session and winlink for window. */
177 cmd_find_best_session_with_window(struct cmd_find_state
*fs
)
179 struct session
**slist
= NULL
;
183 log_debug("%s: window is @%u", __func__
, fs
->w
->id
);
186 RB_FOREACH(s
, sessions
, &sessions
) {
187 if (!session_has(s
, fs
->w
))
189 slist
= xreallocarray(slist
, ssize
+ 1, sizeof *slist
);
194 fs
->s
= cmd_find_best_session(slist
, ssize
, fs
->flags
);
198 return (cmd_find_best_winlink_with_window(fs
));
206 * Find the best winlink for a window (the current if it contains the window,
207 * otherwise the first).
210 cmd_find_best_winlink_with_window(struct cmd_find_state
*fs
)
212 struct winlink
*wl
, *wl_loop
;
214 log_debug("%s: window is @%u", __func__
, fs
->w
->id
);
217 if (fs
->s
->curw
!= NULL
&& fs
->s
->curw
->window
== fs
->w
)
220 RB_FOREACH(wl_loop
, winlinks
, &fs
->s
->windows
) {
221 if (wl_loop
->window
== fs
->w
) {
230 fs
->idx
= fs
->wl
->idx
;
234 /* Maps string in table. */
236 cmd_find_map_table(const char *table
[][2], const char *s
)
240 for (i
= 0; table
[i
][0] != NULL
; i
++) {
241 if (strcmp(s
, table
[i
][0]) == 0)
242 return (table
[i
][1]);
247 /* Find session from string. Fills in s. */
249 cmd_find_get_session(struct cmd_find_state
*fs
, const char *session
)
251 struct session
*s
, *s_loop
;
254 log_debug("%s: %s", __func__
, session
);
256 /* Check for session ids starting with $. */
257 if (*session
== '$') {
258 fs
->s
= session_find_by_id_str(session
);
264 /* Look for exactly this session. */
265 fs
->s
= session_find(session
);
269 /* Look for as a client. */
270 c
= cmd_find_client(NULL
, session
, 1);
271 if (c
!= NULL
&& c
->session
!= NULL
) {
276 /* Stop now if exact only. */
277 if (fs
->flags
& CMD_FIND_EXACT_SESSION
)
280 /* Otherwise look for prefix. */
282 RB_FOREACH(s_loop
, sessions
, &sessions
) {
283 if (strncmp(session
, s_loop
->name
, strlen(session
)) == 0) {
294 /* Then as a pattern. */
296 RB_FOREACH(s_loop
, sessions
, &sessions
) {
297 if (fnmatch(session
, s_loop
->name
, 0) == 0) {
311 /* Find window from string. Fills in s, wl, w. */
313 cmd_find_get_window(struct cmd_find_state
*fs
, const char *window
, int only
)
315 log_debug("%s: %s", __func__
, window
);
317 /* Check for window ids starting with @. */
318 if (*window
== '@') {
319 fs
->w
= window_find_by_id_str(window
);
322 return (cmd_find_best_session_with_window(fs
));
325 /* Not a window id, so use the current session. */
326 fs
->s
= fs
->current
->s
;
328 /* We now only need to find the winlink in this session. */
329 if (cmd_find_get_window_with_session(fs
, window
) == 0)
332 /* Otherwise try as a session itself. */
333 if (!only
&& cmd_find_get_session(fs
, window
) == 0) {
334 fs
->wl
= fs
->s
->curw
;
335 fs
->w
= fs
->wl
->window
;
336 if (~fs
->flags
& CMD_FIND_WINDOW_INDEX
)
337 fs
->idx
= fs
->wl
->idx
;
345 * Find window from string, assuming it is in given session. Needs s, fills in
349 cmd_find_get_window_with_session(struct cmd_find_state
*fs
, const char *window
)
356 log_debug("%s: %s", __func__
, window
);
357 exact
= (fs
->flags
& CMD_FIND_EXACT_WINDOW
);
360 * Start with the current window as the default. So if only an index is
361 * found, the window will be the current.
363 fs
->wl
= fs
->s
->curw
;
364 fs
->w
= fs
->wl
->window
;
366 /* Check for window ids starting with @. */
367 if (*window
== '@') {
368 fs
->w
= window_find_by_id_str(window
);
369 if (fs
->w
== NULL
|| !session_has(fs
->s
, fs
->w
))
371 return (cmd_find_best_winlink_with_window(fs
));
374 /* Try as an offset. */
375 if (!exact
&& (window
[0] == '+' || window
[0] == '-')) {
376 if (window
[1] != '\0')
377 n
= strtonum(window
+ 1, 1, INT_MAX
, NULL
);
381 if (fs
->flags
& CMD_FIND_WINDOW_INDEX
) {
382 if (window
[0] == '+') {
383 if (INT_MAX
- s
->curw
->idx
< n
)
385 fs
->idx
= s
->curw
->idx
+ n
;
387 if (n
> s
->curw
->idx
)
389 fs
->idx
= s
->curw
->idx
- n
;
393 if (window
[0] == '+')
394 fs
->wl
= winlink_next_by_number(s
->curw
, s
, n
);
396 fs
->wl
= winlink_previous_by_number(s
->curw
, s
, n
);
397 if (fs
->wl
!= NULL
) {
398 fs
->idx
= fs
->wl
->idx
;
399 fs
->w
= fs
->wl
->window
;
404 /* Try special characters. */
406 if (strcmp(window
, "!") == 0) {
407 fs
->wl
= TAILQ_FIRST(&fs
->s
->lastw
);
410 fs
->idx
= fs
->wl
->idx
;
411 fs
->w
= fs
->wl
->window
;
413 } else if (strcmp(window
, "^") == 0) {
414 fs
->wl
= RB_MIN(winlinks
, &fs
->s
->windows
);
417 fs
->idx
= fs
->wl
->idx
;
418 fs
->w
= fs
->wl
->window
;
420 } else if (strcmp(window
, "$") == 0) {
421 fs
->wl
= RB_MAX(winlinks
, &fs
->s
->windows
);
424 fs
->idx
= fs
->wl
->idx
;
425 fs
->w
= fs
->wl
->window
;
430 /* First see if this is a valid window index in this session. */
431 if (window
[0] != '+' && window
[0] != '-') {
432 idx
= strtonum(window
, 0, INT_MAX
, &errstr
);
433 if (errstr
== NULL
) {
434 fs
->wl
= winlink_find_by_index(&fs
->s
->windows
, idx
);
435 if (fs
->wl
!= NULL
) {
436 fs
->idx
= fs
->wl
->idx
;
437 fs
->w
= fs
->wl
->window
;
440 if (fs
->flags
& CMD_FIND_WINDOW_INDEX
) {
447 /* Look for exact matches, error if more than one. */
449 RB_FOREACH(wl
, winlinks
, &fs
->s
->windows
) {
450 if (strcmp(window
, wl
->window
->name
) == 0) {
456 if (fs
->wl
!= NULL
) {
457 fs
->idx
= fs
->wl
->idx
;
458 fs
->w
= fs
->wl
->window
;
462 /* Stop now if exact only. */
466 /* Try as the start of a window name, error if multiple. */
468 RB_FOREACH(wl
, winlinks
, &fs
->s
->windows
) {
469 if (strncmp(window
, wl
->window
->name
, strlen(window
)) == 0) {
475 if (fs
->wl
!= NULL
) {
476 fs
->idx
= fs
->wl
->idx
;
477 fs
->w
= fs
->wl
->window
;
481 /* Now look for pattern matches, again error if multiple. */
483 RB_FOREACH(wl
, winlinks
, &fs
->s
->windows
) {
484 if (fnmatch(window
, wl
->window
->name
, 0) == 0) {
490 if (fs
->wl
!= NULL
) {
491 fs
->idx
= fs
->wl
->idx
;
492 fs
->w
= fs
->wl
->window
;
499 /* Find pane from string. Fills in s, wl, w, wp. */
501 cmd_find_get_pane(struct cmd_find_state
*fs
, const char *pane
, int only
)
503 log_debug("%s: %s", __func__
, pane
);
505 /* Check for pane ids starting with %. */
507 fs
->wp
= window_pane_find_by_id_str(pane
);
510 fs
->w
= fs
->wp
->window
;
511 return (cmd_find_best_session_with_window(fs
));
514 /* Not a pane id, so try the current session and window. */
515 fs
->s
= fs
->current
->s
;
516 fs
->wl
= fs
->current
->wl
;
517 fs
->idx
= fs
->current
->idx
;
518 fs
->w
= fs
->current
->w
;
520 /* We now only need to find the pane in this window. */
521 if (cmd_find_get_pane_with_window(fs
, pane
) == 0)
524 /* Otherwise try as a window itself (this will also try as session). */
525 if (!only
&& cmd_find_get_window(fs
, pane
, 0) == 0) {
526 fs
->wp
= fs
->w
->active
;
534 * Find pane from string, assuming it is in given session. Needs s, fills in wl
538 cmd_find_get_pane_with_session(struct cmd_find_state
*fs
, const char *pane
)
540 log_debug("%s: %s", __func__
, pane
);
542 /* Check for pane ids starting with %. */
544 fs
->wp
= window_pane_find_by_id_str(pane
);
547 fs
->w
= fs
->wp
->window
;
548 return (cmd_find_best_winlink_with_window(fs
));
551 /* Otherwise use the current window. */
552 fs
->wl
= fs
->s
->curw
;
553 fs
->idx
= fs
->wl
->idx
;
554 fs
->w
= fs
->wl
->window
;
556 /* Now we just need to look up the pane. */
557 return (cmd_find_get_pane_with_window(fs
, pane
));
561 * Find pane from string, assuming it is in the given window. Needs w, fills in
565 cmd_find_get_pane_with_window(struct cmd_find_state
*fs
, const char *pane
)
569 struct window_pane
*wp
;
572 log_debug("%s: %s", __func__
, pane
);
574 /* Check for pane ids starting with %. */
576 fs
->wp
= window_pane_find_by_id_str(pane
);
579 if (fs
->wp
->window
!= fs
->w
)
584 /* Try special characters. */
585 if (strcmp(pane
, "!") == 0) {
586 fs
->wp
= fs
->w
->last
;
590 } else if (strcmp(pane
, "{up-of}") == 0) {
591 fs
->wp
= window_pane_find_up(fs
->current
->wp
);
595 } else if (strcmp(pane
, "{down-of}") == 0) {
596 fs
->wp
= window_pane_find_down(fs
->current
->wp
);
600 } else if (strcmp(pane
, "{left-of}") == 0) {
601 fs
->wp
= window_pane_find_left(fs
->current
->wp
);
605 } else if (strcmp(pane
, "{right-of}") == 0) {
606 fs
->wp
= window_pane_find_right(fs
->current
->wp
);
612 /* Try as an offset. */
613 if (pane
[0] == '+' || pane
[0] == '-') {
615 n
= strtonum(pane
+ 1, 1, INT_MAX
, NULL
);
618 wp
= fs
->current
->wp
;
620 fs
->wp
= window_pane_next_by_number(fs
->w
, wp
, n
);
622 fs
->wp
= window_pane_previous_by_number(fs
->w
, wp
, n
);
627 /* Get pane by index. */
628 idx
= strtonum(pane
, 0, INT_MAX
, &errstr
);
629 if (errstr
== NULL
) {
630 fs
->wp
= window_pane_at_index(fs
->w
, idx
);
635 /* Try as a description. */
636 fs
->wp
= window_find_string(fs
->w
, pane
);
645 cmd_find_clear_state(struct cmd_find_state
*fs
, int flags
)
647 memset(fs
, 0, sizeof *fs
);
654 /* Check if state is empty. */
656 cmd_find_empty_state(struct cmd_find_state
*fs
)
658 if (fs
->s
== NULL
&& fs
->wl
== NULL
&& fs
->w
== NULL
&& fs
->wp
== NULL
)
663 /* Check if a state if valid. */
665 cmd_find_valid_state(struct cmd_find_state
*fs
)
669 if (fs
->s
== NULL
|| fs
->wl
== NULL
|| fs
->w
== NULL
|| fs
->wp
== NULL
)
672 if (!session_alive(fs
->s
))
675 RB_FOREACH(wl
, winlinks
, &fs
->s
->windows
) {
676 if (wl
->window
== fs
->w
&& wl
== fs
->wl
)
682 if (fs
->w
!= fs
->wl
->window
)
685 return (window_has_pane(fs
->w
, fs
->wp
));
690 cmd_find_copy_state(struct cmd_find_state
*dst
, struct cmd_find_state
*src
)
699 /* Log the result. */
701 cmd_find_log_state(const char *prefix
, struct cmd_find_state
*fs
)
704 log_debug("%s: s=$%u %s", prefix
, fs
->s
->id
, fs
->s
->name
);
706 log_debug("%s: s=none", prefix
);
707 if (fs
->wl
!= NULL
) {
708 log_debug("%s: wl=%u %d w=@%u %s", prefix
, fs
->wl
->idx
,
709 fs
->wl
->window
== fs
->w
, fs
->w
->id
, fs
->w
->name
);
711 log_debug("%s: wl=none", prefix
);
713 log_debug("%s: wp=%%%u", prefix
, fs
->wp
->id
);
715 log_debug("%s: wp=none", prefix
);
717 log_debug("%s: idx=%d", prefix
, fs
->idx
);
719 log_debug("%s: idx=none", prefix
);
722 /* Find state from a session. */
724 cmd_find_from_session(struct cmd_find_state
*fs
, struct session
*s
, int flags
)
726 cmd_find_clear_state(fs
, flags
);
729 fs
->wl
= fs
->s
->curw
;
730 fs
->w
= fs
->wl
->window
;
731 fs
->wp
= fs
->w
->active
;
733 cmd_find_log_state(__func__
, fs
);
736 /* Find state from a winlink. */
738 cmd_find_from_winlink(struct cmd_find_state
*fs
, struct winlink
*wl
, int flags
)
740 cmd_find_clear_state(fs
, flags
);
745 fs
->wp
= wl
->window
->active
;
747 cmd_find_log_state(__func__
, fs
);
750 /* Find state from a session and window. */
752 cmd_find_from_session_window(struct cmd_find_state
*fs
, struct session
*s
,
753 struct window
*w
, int flags
)
755 cmd_find_clear_state(fs
, flags
);
759 if (cmd_find_best_winlink_with_window(fs
) != 0) {
760 cmd_find_clear_state(fs
, flags
);
763 fs
->wp
= fs
->w
->active
;
765 cmd_find_log_state(__func__
, fs
);
769 /* Find state from a window. */
771 cmd_find_from_window(struct cmd_find_state
*fs
, struct window
*w
, int flags
)
773 cmd_find_clear_state(fs
, flags
);
776 if (cmd_find_best_session_with_window(fs
) != 0) {
777 cmd_find_clear_state(fs
, flags
);
780 if (cmd_find_best_winlink_with_window(fs
) != 0) {
781 cmd_find_clear_state(fs
, flags
);
784 fs
->wp
= fs
->w
->active
;
786 cmd_find_log_state(__func__
, fs
);
790 /* Find state from a winlink and pane. */
792 cmd_find_from_winlink_pane(struct cmd_find_state
*fs
, struct winlink
*wl
,
793 struct window_pane
*wp
, int flags
)
795 cmd_find_clear_state(fs
, flags
);
799 fs
->idx
= fs
->wl
->idx
;
800 fs
->w
= fs
->wl
->window
;
803 cmd_find_log_state(__func__
, fs
);
806 /* Find state from a pane. */
808 cmd_find_from_pane(struct cmd_find_state
*fs
, struct window_pane
*wp
, int flags
)
810 if (cmd_find_from_window(fs
, wp
->window
, flags
) != 0)
814 cmd_find_log_state(__func__
, fs
);
818 /* Find state from nothing. */
820 cmd_find_from_nothing(struct cmd_find_state
*fs
, int flags
)
822 cmd_find_clear_state(fs
, flags
);
824 fs
->s
= cmd_find_best_session(NULL
, 0, flags
);
826 cmd_find_clear_state(fs
, flags
);
829 fs
->wl
= fs
->s
->curw
;
830 fs
->idx
= fs
->wl
->idx
;
831 fs
->w
= fs
->wl
->window
;
832 fs
->wp
= fs
->w
->active
;
834 cmd_find_log_state(__func__
, fs
);
838 /* Find state from mouse. */
840 cmd_find_from_mouse(struct cmd_find_state
*fs
, struct mouse_event
*m
, int flags
)
842 cmd_find_clear_state(fs
, flags
);
847 fs
->wp
= cmd_mouse_pane(m
, &fs
->s
, &fs
->wl
);
848 if (fs
->wp
== NULL
) {
849 cmd_find_clear_state(fs
, flags
);
852 fs
->w
= fs
->wl
->window
;
854 cmd_find_log_state(__func__
, fs
);
858 /* Find state from client. */
860 cmd_find_from_client(struct cmd_find_state
*fs
, struct client
*c
, int flags
)
862 struct window_pane
*wp
;
864 /* If no client, treat as from nothing. */
866 return (cmd_find_from_nothing(fs
, flags
));
868 /* If this is an attached client, all done. */
869 if (c
->session
!= NULL
) {
870 cmd_find_clear_state(fs
, flags
);
872 fs
->wp
= server_client_get_pane(c
);
873 if (fs
->wp
== NULL
) {
874 cmd_find_from_session(fs
, c
->session
, flags
);
878 fs
->wl
= fs
->s
->curw
;
879 fs
->w
= fs
->wl
->window
;
881 cmd_find_log_state(__func__
, fs
);
884 cmd_find_clear_state(fs
, flags
);
887 * If this is an unattached client running in a pane, we can use that
888 * to limit the list of sessions to those containing that pane.
890 wp
= cmd_find_inside_pane(c
);
895 * Don't have a session, or it doesn't have this pane. Try all
899 if (cmd_find_best_session_with_window(fs
) != 0) {
901 * The window may have been destroyed but the pane
902 * still on all_window_panes due to something else
903 * holding a reference.
907 fs
->wl
= fs
->s
->curw
;
908 fs
->w
= fs
->wl
->window
;
909 fs
->wp
= fs
->w
->active
; /* use active pane */
911 cmd_find_log_state(__func__
, fs
);
915 /* We can't find the pane so need to guess. */
916 return (cmd_find_from_nothing(fs
, flags
));
920 * Split target into pieces and resolve for the given type. Fills in the given
921 * state. Returns 0 on success or -1 on error.
924 cmd_find_target(struct cmd_find_state
*fs
, struct cmdq_item
*item
,
925 const char *target
, enum cmd_find_type type
, int flags
)
927 struct mouse_event
*m
;
928 struct cmd_find_state current
;
929 char *colon
, *period
, *copy
= NULL
, tmp
[256];
930 const char *session
, *window
, *pane
, *s
;
931 int window_only
= 0, pane_only
= 0;
933 /* Can fail flag implies quiet. */
934 if (flags
& CMD_FIND_CANFAIL
)
935 flags
|= CMD_FIND_QUIET
;
937 /* Log the arguments. */
938 if (type
== CMD_FIND_PANE
)
940 else if (type
== CMD_FIND_WINDOW
)
942 else if (type
== CMD_FIND_SESSION
)
947 if (flags
& CMD_FIND_PREFER_UNATTACHED
)
948 strlcat(tmp
, "PREFER_UNATTACHED,", sizeof tmp
);
949 if (flags
& CMD_FIND_QUIET
)
950 strlcat(tmp
, "QUIET,", sizeof tmp
);
951 if (flags
& CMD_FIND_WINDOW_INDEX
)
952 strlcat(tmp
, "WINDOW_INDEX,", sizeof tmp
);
953 if (flags
& CMD_FIND_DEFAULT_MARKED
)
954 strlcat(tmp
, "DEFAULT_MARKED,", sizeof tmp
);
955 if (flags
& CMD_FIND_EXACT_SESSION
)
956 strlcat(tmp
, "EXACT_SESSION,", sizeof tmp
);
957 if (flags
& CMD_FIND_EXACT_WINDOW
)
958 strlcat(tmp
, "EXACT_WINDOW,", sizeof tmp
);
959 if (flags
& CMD_FIND_CANFAIL
)
960 strlcat(tmp
, "CANFAIL,", sizeof tmp
);
962 tmp
[strlen(tmp
) - 1] = '\0';
964 strlcat(tmp
, "NONE", sizeof tmp
);
965 log_debug("%s: target %s, type %s, item %p, flags %s", __func__
,
966 target
== NULL
? "none" : target
, s
, item
, tmp
);
968 /* Clear new state. */
969 cmd_find_clear_state(fs
, flags
);
971 /* Find current state. */
972 if (server_check_marked() && (flags
& CMD_FIND_DEFAULT_MARKED
)) {
973 fs
->current
= &marked_pane
;
974 log_debug("%s: current is marked pane", __func__
);
975 } else if (cmd_find_valid_state(cmdq_get_current(item
))) {
976 fs
->current
= cmdq_get_current(item
);
977 log_debug("%s: current is from queue", __func__
);
978 } else if (cmd_find_from_client(¤t
, cmdq_get_client(item
),
980 fs
->current
= ¤t
;
981 log_debug("%s: current is from client", __func__
);
983 if (~flags
& CMD_FIND_QUIET
)
984 cmdq_error(item
, "no current target");
987 if (!cmd_find_valid_state(fs
->current
))
988 fatalx("invalid current find state");
990 /* An empty or NULL target is the current. */
991 if (target
== NULL
|| *target
== '\0')
994 /* Mouse target is a plain = or {mouse}. */
995 if (strcmp(target
, "=") == 0 || strcmp(target
, "{mouse}") == 0) {
996 m
= &cmdq_get_event(item
)->m
;
999 fs
->wp
= cmd_mouse_pane(m
, &fs
->s
, &fs
->wl
);
1000 if (fs
->wp
!= NULL
) {
1001 fs
->w
= fs
->wl
->window
;
1005 case CMD_FIND_WINDOW
:
1006 case CMD_FIND_SESSION
:
1007 fs
->wl
= cmd_mouse_window(m
, &fs
->s
);
1008 if (fs
->wl
== NULL
&& fs
->s
!= NULL
)
1009 fs
->wl
= fs
->s
->curw
;
1010 if (fs
->wl
!= NULL
) {
1011 fs
->w
= fs
->wl
->window
;
1012 fs
->wp
= fs
->w
->active
;
1016 if (fs
->wp
== NULL
) {
1017 if (~flags
& CMD_FIND_QUIET
)
1018 cmdq_error(item
, "no mouse target");
1024 /* Marked target is a plain ~ or {marked}. */
1025 if (strcmp(target
, "~") == 0 || strcmp(target
, "{marked}") == 0) {
1026 if (!server_check_marked()) {
1027 if (~flags
& CMD_FIND_QUIET
)
1028 cmdq_error(item
, "no marked target");
1031 cmd_find_copy_state(fs
, &marked_pane
);
1035 /* Find separators if they exist. */
1036 copy
= xstrdup(target
);
1037 colon
= strchr(copy
, ':');
1041 period
= strchr(copy
, '.');
1043 period
= strchr(colon
, '.');
1047 /* Set session, window and pane parts. */
1048 session
= window
= pane
= NULL
;
1049 if (colon
!= NULL
&& period
!= NULL
) {
1055 } else if (colon
!= NULL
&& period
== NULL
) {
1059 } else if (colon
== NULL
&& period
!= NULL
) {
1066 else if (*copy
== '@')
1068 else if (*copy
== '%')
1072 case CMD_FIND_SESSION
:
1075 case CMD_FIND_WINDOW
:
1085 /* Set exact match flags. */
1086 if (session
!= NULL
&& *session
== '=') {
1088 fs
->flags
|= CMD_FIND_EXACT_SESSION
;
1090 if (window
!= NULL
&& *window
== '=') {
1092 fs
->flags
|= CMD_FIND_EXACT_WINDOW
;
1095 /* Empty is the same as NULL. */
1096 if (session
!= NULL
&& *session
== '\0')
1098 if (window
!= NULL
&& *window
== '\0')
1100 if (pane
!= NULL
&& *pane
== '\0')
1103 /* Map though conversion table. */
1104 if (session
!= NULL
)
1105 session
= cmd_find_map_table(cmd_find_session_table
, session
);
1107 window
= cmd_find_map_table(cmd_find_window_table
, window
);
1109 pane
= cmd_find_map_table(cmd_find_pane_table
, pane
);
1111 if (session
!= NULL
|| window
!= NULL
|| pane
!= NULL
) {
1112 log_debug("%s: target %s is %s%s%s%s%s%s",
1114 session
== NULL
? "" : "session ",
1115 session
== NULL
? "" : session
,
1116 window
== NULL
? "" : "window ",
1117 window
== NULL
? "" : window
,
1118 pane
== NULL
? "" : "pane ",
1119 pane
== NULL
? "" : pane
);
1122 /* No pane is allowed if want an index. */
1123 if (pane
!= NULL
&& (flags
& CMD_FIND_WINDOW_INDEX
)) {
1124 if (~flags
& CMD_FIND_QUIET
)
1125 cmdq_error(item
, "can't specify pane here");
1129 /* If the session isn't NULL, look it up. */
1130 if (session
!= NULL
) {
1131 /* This will fill in session. */
1132 if (cmd_find_get_session(fs
, session
) != 0)
1135 /* If window and pane are NULL, use that session's current. */
1136 if (window
== NULL
&& pane
== NULL
) {
1137 fs
->wl
= fs
->s
->curw
;
1139 fs
->w
= fs
->wl
->window
;
1140 fs
->wp
= fs
->w
->active
;
1144 /* If window is present but pane not, find window in session. */
1145 if (window
!= NULL
&& pane
== NULL
) {
1146 /* This will fill in winlink and window. */
1147 if (cmd_find_get_window_with_session(fs
, window
) != 0)
1149 if (fs
->wl
!= NULL
) /* can be NULL if index only */
1150 fs
->wp
= fs
->wl
->window
->active
;
1154 /* If pane is present but window not, find pane. */
1155 if (window
== NULL
&& pane
!= NULL
) {
1156 /* This will fill in winlink and window and pane. */
1157 if (cmd_find_get_pane_with_session(fs
, pane
) != 0)
1163 * If window and pane are present, find both in session. This
1164 * will fill in winlink and window.
1166 if (cmd_find_get_window_with_session(fs
, window
) != 0)
1168 /* This will fill in pane. */
1169 if (cmd_find_get_pane_with_window(fs
, pane
) != 0)
1174 /* No session. If window and pane, try them. */
1175 if (window
!= NULL
&& pane
!= NULL
) {
1176 /* This will fill in session, winlink and window. */
1177 if (cmd_find_get_window(fs
, window
, window_only
) != 0)
1179 /* This will fill in pane. */
1180 if (cmd_find_get_pane_with_window(fs
, pane
) != 0)
1185 /* If just window is present, try it. */
1186 if (window
!= NULL
&& pane
== NULL
) {
1187 /* This will fill in session, winlink and window. */
1188 if (cmd_find_get_window(fs
, window
, window_only
) != 0)
1190 if (fs
->wl
!= NULL
) /* can be NULL if index only */
1191 fs
->wp
= fs
->wl
->window
->active
;
1195 /* If just pane is present, try it. */
1196 if (window
== NULL
&& pane
!= NULL
) {
1197 /* This will fill in session, winlink, window and pane. */
1198 if (cmd_find_get_pane(fs
, pane
, pane_only
) != 0)
1204 /* Use the current session. */
1205 cmd_find_copy_state(fs
, fs
->current
);
1206 if (flags
& CMD_FIND_WINDOW_INDEX
)
1212 log_debug("%s: error", __func__
);
1215 if (flags
& CMD_FIND_CANFAIL
)
1221 cmd_find_log_state(__func__
, fs
);
1227 if (~flags
& CMD_FIND_QUIET
)
1228 cmdq_error(item
, "can't find session: %s", session
);
1232 if (~flags
& CMD_FIND_QUIET
)
1233 cmdq_error(item
, "can't find window: %s", window
);
1237 if (~flags
& CMD_FIND_QUIET
)
1238 cmdq_error(item
, "can't find pane: %s", pane
);
1242 /* Find the current client. */
1243 static struct client
*
1244 cmd_find_current_client(struct cmdq_item
*item
, int quiet
)
1246 struct client
*c
= NULL
, *found
;
1248 struct window_pane
*wp
;
1249 struct cmd_find_state fs
;
1252 c
= cmdq_get_client(item
);
1253 if (c
!= NULL
&& c
->session
!= NULL
)
1257 if (c
!= NULL
&& (wp
= cmd_find_inside_pane(c
)) != NULL
) {
1258 cmd_find_clear_state(&fs
, CMD_FIND_QUIET
);
1260 if (cmd_find_best_session_with_window(&fs
) == 0)
1261 found
= cmd_find_best_client(fs
.s
);
1263 s
= cmd_find_best_session(NULL
, 0, CMD_FIND_QUIET
);
1265 found
= cmd_find_best_client(s
);
1267 if (found
== NULL
&& item
!= NULL
&& !quiet
)
1268 cmdq_error(item
, "no current client");
1269 log_debug("%s: no target, return %p", __func__
, found
);
1273 /* Find the target client or report an error and return NULL. */
1275 cmd_find_client(struct cmdq_item
*item
, const char *target
, int quiet
)
1281 /* A NULL argument means the current client. */
1283 return (cmd_find_current_client(item
, quiet
));
1284 copy
= xstrdup(target
);
1286 /* Trim a single trailing colon if any. */
1287 size
= strlen(copy
);
1288 if (size
!= 0 && copy
[size
- 1] == ':')
1289 copy
[size
- 1] = '\0';
1291 /* Check name and path of each client. */
1292 TAILQ_FOREACH(c
, &clients
, entry
) {
1293 if (c
->session
== NULL
)
1295 if (strcmp(copy
, c
->name
) == 0)
1298 if (*c
->ttyname
== '\0')
1300 if (strcmp(copy
, c
->ttyname
) == 0)
1302 if (strncmp(c
->ttyname
, _PATH_DEV
, (sizeof _PATH_DEV
) - 1) != 0)
1304 if (strcmp(copy
, c
->ttyname
+ (sizeof _PATH_DEV
) - 1) == 0)
1308 /* If no client found, report an error. */
1309 if (c
== NULL
&& !quiet
)
1310 cmdq_error(item
, "can't find client: %s", copy
);
1313 log_debug("%s: target %s, return %p", __func__
, target
, c
);