1*a0aa010aSnicm /* $OpenBSD: cmd-find.c,v 1.83 2023/07/10 09:24:53 nicm Exp $ */
2f65f2164Snicm
3f65f2164Snicm /*
498ca8272Snicm * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
5f65f2164Snicm *
6f65f2164Snicm * Permission to use, copy, modify, and distribute this software for any
7f65f2164Snicm * purpose with or without fee is hereby granted, provided that the above
8f65f2164Snicm * copyright notice and this permission notice appear in all copies.
9f65f2164Snicm *
10f65f2164Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f65f2164Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f65f2164Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f65f2164Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f65f2164Snicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15f65f2164Snicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16f65f2164Snicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f65f2164Snicm */
18f65f2164Snicm
19f65f2164Snicm #include <sys/types.h>
20f65f2164Snicm
21f65f2164Snicm #include <fnmatch.h>
22f65f2164Snicm #include <limits.h>
23f65f2164Snicm #include <stdlib.h>
24f65f2164Snicm #include <string.h>
25f65f2164Snicm #include <paths.h>
269bdf6084Snicm #include <unistd.h>
27f65f2164Snicm
28f65f2164Snicm #include "tmux.h"
29f65f2164Snicm
30dc1f0f5fSnicm static int cmd_find_session_better(struct session *, struct session *,
31f65f2164Snicm int);
32dc1f0f5fSnicm static struct session *cmd_find_best_session(struct session **, u_int, int);
33dc1f0f5fSnicm static int cmd_find_best_session_with_window(struct cmd_find_state *);
34dc1f0f5fSnicm static int cmd_find_best_winlink_with_window(struct cmd_find_state *);
35f65f2164Snicm
36dc1f0f5fSnicm static const char *cmd_find_map_table(const char *[][2], const char *);
37f65f2164Snicm
384e325abeSnicm static void cmd_find_log_state(const char *, struct cmd_find_state *);
39dc1f0f5fSnicm static int cmd_find_get_session(struct cmd_find_state *, const char *);
4010f437f1Snicm static int cmd_find_get_window(struct cmd_find_state *, const char *, int);
41dc1f0f5fSnicm static int cmd_find_get_window_with_session(struct cmd_find_state *,
42dc1f0f5fSnicm const char *);
4310f437f1Snicm static int cmd_find_get_pane(struct cmd_find_state *, const char *, int);
44dc1f0f5fSnicm static int cmd_find_get_pane_with_session(struct cmd_find_state *,
45dc1f0f5fSnicm const char *);
46dc1f0f5fSnicm static int cmd_find_get_pane_with_window(struct cmd_find_state *,
47dc1f0f5fSnicm const char *);
48f65f2164Snicm
49dc1f0f5fSnicm static const char *cmd_find_session_table[][2] = {
50f65f2164Snicm { NULL, NULL }
51f65f2164Snicm };
52dc1f0f5fSnicm static const char *cmd_find_window_table[][2] = {
53f65f2164Snicm { "{start}", "^" },
54f65f2164Snicm { "{last}", "!" },
55f65f2164Snicm { "{end}", "$" },
56f65f2164Snicm { "{next}", "+" },
57f65f2164Snicm { "{previous}", "-" },
58f65f2164Snicm { NULL, NULL }
59f65f2164Snicm };
60dc1f0f5fSnicm static const char *cmd_find_pane_table[][2] = {
61f65f2164Snicm { "{last}", "!" },
62f65f2164Snicm { "{next}", "+" },
63f65f2164Snicm { "{previous}", "-" },
64f65f2164Snicm { "{top}", "top" },
65f65f2164Snicm { "{bottom}", "bottom" },
66f65f2164Snicm { "{left}", "left" },
67f65f2164Snicm { "{right}", "right" },
68f65f2164Snicm { "{top-left}", "top-left" },
69f65f2164Snicm { "{top-right}", "top-right" },
70f65f2164Snicm { "{bottom-left}", "bottom-left" },
71f65f2164Snicm { "{bottom-right}", "bottom-right" },
72baf68abeSnicm { "{up-of}", "{up-of}" },
73baf68abeSnicm { "{down-of}", "{down-of}" },
74baf68abeSnicm { "{left-of}", "{left-of}" },
75baf68abeSnicm { "{right-of}", "{right-of}" },
76f65f2164Snicm { NULL, NULL }
77f65f2164Snicm };
78f65f2164Snicm
796b339e7dSnicm /* Find pane containing client if any. */
806b339e7dSnicm static struct window_pane *
cmd_find_inside_pane(struct client * c)816b339e7dSnicm cmd_find_inside_pane(struct client *c)
826b339e7dSnicm {
836b339e7dSnicm struct window_pane *wp;
84403a76d5Snicm struct environ_entry *envent;
856b339e7dSnicm
866b339e7dSnicm if (c == NULL)
876b339e7dSnicm return (NULL);
886b339e7dSnicm
896b339e7dSnicm RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
907fe37febSnicm if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
916b339e7dSnicm break;
926b339e7dSnicm }
93403a76d5Snicm if (wp == NULL) {
94403a76d5Snicm envent = environ_find(c->environ, "TMUX_PANE");
95403a76d5Snicm if (envent != NULL)
96403a76d5Snicm wp = window_pane_find_by_id_str(envent->value);
97403a76d5Snicm }
982fe539a5Snicm if (wp != NULL)
992fe539a5Snicm log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
1006b339e7dSnicm return (wp);
1016b339e7dSnicm }
1026b339e7dSnicm
103f65f2164Snicm /* Is this client better? */
104dc1f0f5fSnicm static int
cmd_find_client_better(struct client * c,struct client * than)105f65f2164Snicm cmd_find_client_better(struct client *c, struct client *than)
106f65f2164Snicm {
107f65f2164Snicm if (than == NULL)
108f65f2164Snicm return (1);
109f65f2164Snicm return (timercmp(&c->activity_time, &than->activity_time, >));
110f65f2164Snicm }
111f65f2164Snicm
11293e732aaSnicm /* Find best client for session. */
113b6908644Snicm struct client *
cmd_find_best_client(struct session * s)11493e732aaSnicm cmd_find_best_client(struct session *s)
115f65f2164Snicm {
116f65f2164Snicm struct client *c_loop, *c;
11793e732aaSnicm
118647c5c18Snicm if (s->attached == 0)
11993e732aaSnicm s = NULL;
120f65f2164Snicm
121f65f2164Snicm c = NULL;
122f65f2164Snicm TAILQ_FOREACH(c_loop, &clients, entry) {
1233826ac9fSnicm if (c_loop->session == NULL)
1243826ac9fSnicm continue;
12593e732aaSnicm if (s != NULL && c_loop->session != s)
12693e732aaSnicm continue;
127f65f2164Snicm if (cmd_find_client_better(c_loop, c))
1283a7a224fSnicm c = c_loop;
129f65f2164Snicm }
130f65f2164Snicm return (c);
131f65f2164Snicm }
132f65f2164Snicm
133f65f2164Snicm /* Is this session better? */
134dc1f0f5fSnicm static int
cmd_find_session_better(struct session * s,struct session * than,int flags)135f65f2164Snicm cmd_find_session_better(struct session *s, struct session *than, int flags)
136f65f2164Snicm {
137f65f2164Snicm int attached;
138f65f2164Snicm
139f65f2164Snicm if (than == NULL)
140f65f2164Snicm return (1);
141f65f2164Snicm if (flags & CMD_FIND_PREFER_UNATTACHED) {
142647c5c18Snicm attached = (than->attached != 0);
143647c5c18Snicm if (attached && s->attached == 0)
144f65f2164Snicm return (1);
145647c5c18Snicm else if (!attached && s->attached != 0)
146f65f2164Snicm return (0);
147f65f2164Snicm }
148f65f2164Snicm return (timercmp(&s->activity_time, &than->activity_time, >));
149f65f2164Snicm }
150f65f2164Snicm
151f65f2164Snicm /* Find best session from a list, or all if list is NULL. */
152dc1f0f5fSnicm static struct session *
cmd_find_best_session(struct session ** slist,u_int ssize,int flags)153f65f2164Snicm cmd_find_best_session(struct session **slist, u_int ssize, int flags)
154f65f2164Snicm {
155f65f2164Snicm struct session *s_loop, *s;
156f65f2164Snicm u_int i;
157f65f2164Snicm
1582fe539a5Snicm log_debug("%s: %u sessions to try", __func__, ssize);
1592fe539a5Snicm
160f65f2164Snicm s = NULL;
161f65f2164Snicm if (slist != NULL) {
162f65f2164Snicm for (i = 0; i < ssize; i++) {
163f65f2164Snicm if (cmd_find_session_better(slist[i], s, flags))
164f65f2164Snicm s = slist[i];
165f65f2164Snicm }
166f65f2164Snicm } else {
167f65f2164Snicm RB_FOREACH(s_loop, sessions, &sessions) {
168f65f2164Snicm if (cmd_find_session_better(s_loop, s, flags))
169f65f2164Snicm s = s_loop;
170f65f2164Snicm }
171f65f2164Snicm }
172f65f2164Snicm return (s);
173f65f2164Snicm }
174f65f2164Snicm
175f65f2164Snicm /* Find best session and winlink for window. */
176dc1f0f5fSnicm static int
cmd_find_best_session_with_window(struct cmd_find_state * fs)177f65f2164Snicm cmd_find_best_session_with_window(struct cmd_find_state *fs)
178f65f2164Snicm {
179f65f2164Snicm struct session **slist = NULL;
180f65f2164Snicm u_int ssize;
181f65f2164Snicm struct session *s;
182f65f2164Snicm
1832fe539a5Snicm log_debug("%s: window is @%u", __func__, fs->w->id);
1842fe539a5Snicm
185f65f2164Snicm ssize = 0;
186f65f2164Snicm RB_FOREACH(s, sessions, &sessions) {
187f65f2164Snicm if (!session_has(s, fs->w))
188f65f2164Snicm continue;
189f65f2164Snicm slist = xreallocarray(slist, ssize + 1, sizeof *slist);
190f65f2164Snicm slist[ssize++] = s;
191f65f2164Snicm }
192f65f2164Snicm if (ssize == 0)
193f65f2164Snicm goto fail;
194f65f2164Snicm fs->s = cmd_find_best_session(slist, ssize, fs->flags);
195f65f2164Snicm if (fs->s == NULL)
196f65f2164Snicm goto fail;
197f65f2164Snicm free(slist);
198f65f2164Snicm return (cmd_find_best_winlink_with_window(fs));
199f65f2164Snicm
200f65f2164Snicm fail:
201f65f2164Snicm free(slist);
202f65f2164Snicm return (-1);
203f65f2164Snicm }
204f65f2164Snicm
205f65f2164Snicm /*
2067fe37febSnicm * Find the best winlink for a window (the current if it contains the window,
207f65f2164Snicm * otherwise the first).
208f65f2164Snicm */
209dc1f0f5fSnicm static int
cmd_find_best_winlink_with_window(struct cmd_find_state * fs)210f65f2164Snicm cmd_find_best_winlink_with_window(struct cmd_find_state *fs)
211f65f2164Snicm {
212f65f2164Snicm struct winlink *wl, *wl_loop;
213f65f2164Snicm
2142fe539a5Snicm log_debug("%s: window is @%u", __func__, fs->w->id);
2152fe539a5Snicm
216f65f2164Snicm wl = NULL;
2172ae124feSnicm if (fs->s->curw != NULL && fs->s->curw->window == fs->w)
218f65f2164Snicm wl = fs->s->curw;
219f65f2164Snicm else {
220f65f2164Snicm RB_FOREACH(wl_loop, winlinks, &fs->s->windows) {
221f65f2164Snicm if (wl_loop->window == fs->w) {
222f65f2164Snicm wl = wl_loop;
223f65f2164Snicm break;
224f65f2164Snicm }
225f65f2164Snicm }
226f65f2164Snicm }
227f65f2164Snicm if (wl == NULL)
228f65f2164Snicm return (-1);
229f65f2164Snicm fs->wl = wl;
230f65f2164Snicm fs->idx = fs->wl->idx;
231f65f2164Snicm return (0);
232f65f2164Snicm }
233f65f2164Snicm
234f65f2164Snicm /* Maps string in table. */
235dc1f0f5fSnicm static const char *
cmd_find_map_table(const char * table[][2],const char * s)236f65f2164Snicm cmd_find_map_table(const char *table[][2], const char *s)
237f65f2164Snicm {
238f65f2164Snicm u_int i;
239f65f2164Snicm
240f65f2164Snicm for (i = 0; table[i][0] != NULL; i++) {
241f65f2164Snicm if (strcmp(s, table[i][0]) == 0)
242f65f2164Snicm return (table[i][1]);
243f65f2164Snicm }
244f65f2164Snicm return (s);
245f65f2164Snicm }
246f65f2164Snicm
247f65f2164Snicm /* Find session from string. Fills in s. */
248dc1f0f5fSnicm static int
cmd_find_get_session(struct cmd_find_state * fs,const char * session)249f65f2164Snicm cmd_find_get_session(struct cmd_find_state *fs, const char *session)
250f65f2164Snicm {
251f65f2164Snicm struct session *s, *s_loop;
2527fb2b001Snicm struct client *c;
253f65f2164Snicm
254f65f2164Snicm log_debug("%s: %s", __func__, session);
255f65f2164Snicm
256f65f2164Snicm /* Check for session ids starting with $. */
257f65f2164Snicm if (*session == '$') {
258f65f2164Snicm fs->s = session_find_by_id_str(session);
259f65f2164Snicm if (fs->s == NULL)
260f65f2164Snicm return (-1);
261f65f2164Snicm return (0);
262f65f2164Snicm }
263f65f2164Snicm
264f65f2164Snicm /* Look for exactly this session. */
265f65f2164Snicm fs->s = session_find(session);
266f65f2164Snicm if (fs->s != NULL)
267f65f2164Snicm return (0);
268f65f2164Snicm
2697fb2b001Snicm /* Look for as a client. */
2707fb2b001Snicm c = cmd_find_client(NULL, session, 1);
2717fb2b001Snicm if (c != NULL && c->session != NULL) {
2727fb2b001Snicm fs->s = c->session;
2737fb2b001Snicm return (0);
2747fb2b001Snicm }
2757fb2b001Snicm
276a7f2e23cSnicm /* Stop now if exact only. */
277a7f2e23cSnicm if (fs->flags & CMD_FIND_EXACT_SESSION)
278a7f2e23cSnicm return (-1);
279a7f2e23cSnicm
280f65f2164Snicm /* Otherwise look for prefix. */
281f65f2164Snicm s = NULL;
282f65f2164Snicm RB_FOREACH(s_loop, sessions, &sessions) {
283f65f2164Snicm if (strncmp(session, s_loop->name, strlen(session)) == 0) {
284f65f2164Snicm if (s != NULL)
285f65f2164Snicm return (-1);
286f65f2164Snicm s = s_loop;
287f65f2164Snicm }
288f65f2164Snicm }
289f65f2164Snicm if (s != NULL) {
290f65f2164Snicm fs->s = s;
291f65f2164Snicm return (0);
292f65f2164Snicm }
293f65f2164Snicm
294f65f2164Snicm /* Then as a pattern. */
295f65f2164Snicm s = NULL;
296f65f2164Snicm RB_FOREACH(s_loop, sessions, &sessions) {
297f65f2164Snicm if (fnmatch(session, s_loop->name, 0) == 0) {
298f65f2164Snicm if (s != NULL)
299f65f2164Snicm return (-1);
300f65f2164Snicm s = s_loop;
301f65f2164Snicm }
302f65f2164Snicm }
303f65f2164Snicm if (s != NULL) {
304f65f2164Snicm fs->s = s;
305f65f2164Snicm return (0);
306f65f2164Snicm }
307f65f2164Snicm
308f65f2164Snicm return (-1);
309f65f2164Snicm }
310f65f2164Snicm
311f65f2164Snicm /* Find window from string. Fills in s, wl, w. */
312dc1f0f5fSnicm static int
cmd_find_get_window(struct cmd_find_state * fs,const char * window,int only)31310f437f1Snicm cmd_find_get_window(struct cmd_find_state *fs, const char *window, int only)
314f65f2164Snicm {
315f65f2164Snicm log_debug("%s: %s", __func__, window);
316f65f2164Snicm
317f65f2164Snicm /* Check for window ids starting with @. */
318f65f2164Snicm if (*window == '@') {
319f65f2164Snicm fs->w = window_find_by_id_str(window);
320f65f2164Snicm if (fs->w == NULL)
321f65f2164Snicm return (-1);
322f65f2164Snicm return (cmd_find_best_session_with_window(fs));
323f65f2164Snicm }
324f65f2164Snicm
325f65f2164Snicm /* Not a window id, so use the current session. */
326f65f2164Snicm fs->s = fs->current->s;
327f65f2164Snicm
328f65f2164Snicm /* We now only need to find the winlink in this session. */
329d296cf69Snicm if (cmd_find_get_window_with_session(fs, window) == 0)
330d296cf69Snicm return (0);
331d296cf69Snicm
33238f80cb9Snicm /* Otherwise try as a session itself. */
33310f437f1Snicm if (!only && cmd_find_get_session(fs, window) == 0) {
334d296cf69Snicm fs->wl = fs->s->curw;
335d296cf69Snicm fs->w = fs->wl->window;
33659d5062aSnicm if (~fs->flags & CMD_FIND_WINDOW_INDEX)
337a4443f9dSnicm fs->idx = fs->wl->idx;
338d296cf69Snicm return (0);
339d296cf69Snicm }
340d296cf69Snicm
341d296cf69Snicm return (-1);
342f65f2164Snicm }
343f65f2164Snicm
344f65f2164Snicm /*
345f65f2164Snicm * Find window from string, assuming it is in given session. Needs s, fills in
346f65f2164Snicm * wl and w.
347f65f2164Snicm */
348dc1f0f5fSnicm static int
cmd_find_get_window_with_session(struct cmd_find_state * fs,const char * window)349f65f2164Snicm cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
350f65f2164Snicm {
351f65f2164Snicm struct winlink *wl;
352f65f2164Snicm const char *errstr;
353a7f2e23cSnicm int idx, n, exact;
354f65f2164Snicm struct session *s;
355f65f2164Snicm
356f65f2164Snicm log_debug("%s: %s", __func__, window);
357a7f2e23cSnicm exact = (fs->flags & CMD_FIND_EXACT_WINDOW);
358f65f2164Snicm
35959d5062aSnicm /*
36059d5062aSnicm * Start with the current window as the default. So if only an index is
36159d5062aSnicm * found, the window will be the current.
36259d5062aSnicm */
36359d5062aSnicm fs->wl = fs->s->curw;
36459d5062aSnicm fs->w = fs->wl->window;
36559d5062aSnicm
366f65f2164Snicm /* Check for window ids starting with @. */
367f65f2164Snicm if (*window == '@') {
368f65f2164Snicm fs->w = window_find_by_id_str(window);
369f65f2164Snicm if (fs->w == NULL || !session_has(fs->s, fs->w))
370f65f2164Snicm return (-1);
371f65f2164Snicm return (cmd_find_best_winlink_with_window(fs));
372f65f2164Snicm }
373f65f2164Snicm
374f65f2164Snicm /* Try as an offset. */
375e0456e38Snicm if (!exact && (window[0] == '+' || window[0] == '-')) {
376f65f2164Snicm if (window[1] != '\0')
377f65f2164Snicm n = strtonum(window + 1, 1, INT_MAX, NULL);
378f65f2164Snicm else
379f65f2164Snicm n = 1;
380f65f2164Snicm s = fs->s;
381f65f2164Snicm if (fs->flags & CMD_FIND_WINDOW_INDEX) {
382f65f2164Snicm if (window[0] == '+') {
383f65f2164Snicm if (INT_MAX - s->curw->idx < n)
384f65f2164Snicm return (-1);
385f65f2164Snicm fs->idx = s->curw->idx + n;
386f65f2164Snicm } else {
387ccbc1512Snicm if (n > s->curw->idx)
388f65f2164Snicm return (-1);
389f65f2164Snicm fs->idx = s->curw->idx - n;
390f65f2164Snicm }
391f65f2164Snicm return (0);
392f65f2164Snicm }
393f65f2164Snicm if (window[0] == '+')
394f65f2164Snicm fs->wl = winlink_next_by_number(s->curw, s, n);
395f65f2164Snicm else
396f65f2164Snicm fs->wl = winlink_previous_by_number(s->curw, s, n);
397f65f2164Snicm if (fs->wl != NULL) {
398f65f2164Snicm fs->idx = fs->wl->idx;
399f65f2164Snicm fs->w = fs->wl->window;
400f65f2164Snicm return (0);
401f65f2164Snicm }
402f65f2164Snicm }
403f65f2164Snicm
404f65f2164Snicm /* Try special characters. */
405a7f2e23cSnicm if (!exact) {
406f65f2164Snicm if (strcmp(window, "!") == 0) {
407f65f2164Snicm fs->wl = TAILQ_FIRST(&fs->s->lastw);
408f65f2164Snicm if (fs->wl == NULL)
409f65f2164Snicm return (-1);
410f65f2164Snicm fs->idx = fs->wl->idx;
411f65f2164Snicm fs->w = fs->wl->window;
412f65f2164Snicm return (0);
413f65f2164Snicm } else if (strcmp(window, "^") == 0) {
414f65f2164Snicm fs->wl = RB_MIN(winlinks, &fs->s->windows);
415f65f2164Snicm if (fs->wl == NULL)
416f65f2164Snicm return (-1);
417f65f2164Snicm fs->idx = fs->wl->idx;
418f65f2164Snicm fs->w = fs->wl->window;
419f65f2164Snicm return (0);
420f65f2164Snicm } else if (strcmp(window, "$") == 0) {
421f65f2164Snicm fs->wl = RB_MAX(winlinks, &fs->s->windows);
422f65f2164Snicm if (fs->wl == NULL)
423f65f2164Snicm return (-1);
424f65f2164Snicm fs->idx = fs->wl->idx;
425f65f2164Snicm fs->w = fs->wl->window;
426f65f2164Snicm return (0);
427f65f2164Snicm }
428a7f2e23cSnicm }
429f65f2164Snicm
430f65f2164Snicm /* First see if this is a valid window index in this session. */
431a7f2e23cSnicm if (window[0] != '+' && window[0] != '-') {
432f65f2164Snicm idx = strtonum(window, 0, INT_MAX, &errstr);
433f65f2164Snicm if (errstr == NULL) {
434f65f2164Snicm fs->wl = winlink_find_by_index(&fs->s->windows, idx);
435f65f2164Snicm if (fs->wl != NULL) {
43638f68d44Snicm fs->idx = fs->wl->idx;
437f65f2164Snicm fs->w = fs->wl->window;
438f65f2164Snicm return (0);
439f65f2164Snicm }
4404bd93a15Snicm if (fs->flags & CMD_FIND_WINDOW_INDEX) {
4414bd93a15Snicm fs->idx = idx;
4424bd93a15Snicm return (0);
4434bd93a15Snicm }
444f65f2164Snicm }
445a7f2e23cSnicm }
446f65f2164Snicm
447f65f2164Snicm /* Look for exact matches, error if more than one. */
448f65f2164Snicm fs->wl = NULL;
449f65f2164Snicm RB_FOREACH(wl, winlinks, &fs->s->windows) {
450f65f2164Snicm if (strcmp(window, wl->window->name) == 0) {
451f65f2164Snicm if (fs->wl != NULL)
452f65f2164Snicm return (-1);
453f65f2164Snicm fs->wl = wl;
454f65f2164Snicm }
455f65f2164Snicm }
456f65f2164Snicm if (fs->wl != NULL) {
457f65f2164Snicm fs->idx = fs->wl->idx;
458f65f2164Snicm fs->w = fs->wl->window;
459f65f2164Snicm return (0);
460f65f2164Snicm }
461f65f2164Snicm
462a7f2e23cSnicm /* Stop now if exact only. */
463a7f2e23cSnicm if (exact)
464a7f2e23cSnicm return (-1);
465a7f2e23cSnicm
466f65f2164Snicm /* Try as the start of a window name, error if multiple. */
467f65f2164Snicm fs->wl = NULL;
468f65f2164Snicm RB_FOREACH(wl, winlinks, &fs->s->windows) {
469f65f2164Snicm if (strncmp(window, wl->window->name, strlen(window)) == 0) {
470f65f2164Snicm if (fs->wl != NULL)
471f65f2164Snicm return (-1);
472f65f2164Snicm fs->wl = wl;
473f65f2164Snicm }
474f65f2164Snicm }
475f65f2164Snicm if (fs->wl != NULL) {
476f65f2164Snicm fs->idx = fs->wl->idx;
477f65f2164Snicm fs->w = fs->wl->window;
478f65f2164Snicm return (0);
479f65f2164Snicm }
480f65f2164Snicm
481f65f2164Snicm /* Now look for pattern matches, again error if multiple. */
482f65f2164Snicm fs->wl = NULL;
483f65f2164Snicm RB_FOREACH(wl, winlinks, &fs->s->windows) {
484f65f2164Snicm if (fnmatch(window, wl->window->name, 0) == 0) {
485f65f2164Snicm if (fs->wl != NULL)
486f65f2164Snicm return (-1);
487f65f2164Snicm fs->wl = wl;
488f65f2164Snicm }
489f65f2164Snicm }
490f65f2164Snicm if (fs->wl != NULL) {
491f65f2164Snicm fs->idx = fs->wl->idx;
492f65f2164Snicm fs->w = fs->wl->window;
493f65f2164Snicm return (0);
494f65f2164Snicm }
495f65f2164Snicm
496f65f2164Snicm return (-1);
497f65f2164Snicm }
498f65f2164Snicm
499f65f2164Snicm /* Find pane from string. Fills in s, wl, w, wp. */
500dc1f0f5fSnicm static int
cmd_find_get_pane(struct cmd_find_state * fs,const char * pane,int only)50110f437f1Snicm cmd_find_get_pane(struct cmd_find_state *fs, const char *pane, int only)
502f65f2164Snicm {
503f65f2164Snicm log_debug("%s: %s", __func__, pane);
504f65f2164Snicm
505f65f2164Snicm /* Check for pane ids starting with %. */
506f65f2164Snicm if (*pane == '%') {
507f65f2164Snicm fs->wp = window_pane_find_by_id_str(pane);
508b81c3e5bSnicm if (fs->wp == NULL)
509f65f2164Snicm return (-1);
510f65f2164Snicm fs->w = fs->wp->window;
511f65f2164Snicm return (cmd_find_best_session_with_window(fs));
512f65f2164Snicm }
513f65f2164Snicm
514d296cf69Snicm /* Not a pane id, so try the current session and window. */
515f65f2164Snicm fs->s = fs->current->s;
516f65f2164Snicm fs->wl = fs->current->wl;
517f65f2164Snicm fs->idx = fs->current->idx;
518f65f2164Snicm fs->w = fs->current->w;
519f65f2164Snicm
520f65f2164Snicm /* We now only need to find the pane in this window. */
521d296cf69Snicm if (cmd_find_get_pane_with_window(fs, pane) == 0)
522d296cf69Snicm return (0);
523d296cf69Snicm
52438f80cb9Snicm /* Otherwise try as a window itself (this will also try as session). */
52510f437f1Snicm if (!only && cmd_find_get_window(fs, pane, 0) == 0) {
526d296cf69Snicm fs->wp = fs->w->active;
527d296cf69Snicm return (0);
528d296cf69Snicm }
529d296cf69Snicm
530d296cf69Snicm return (-1);
531f65f2164Snicm }
532f65f2164Snicm
533f65f2164Snicm /*
534f65f2164Snicm * Find pane from string, assuming it is in given session. Needs s, fills in wl
535f65f2164Snicm * and w and wp.
536f65f2164Snicm */
537dc1f0f5fSnicm static int
cmd_find_get_pane_with_session(struct cmd_find_state * fs,const char * pane)538f65f2164Snicm cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane)
539f65f2164Snicm {
540f65f2164Snicm log_debug("%s: %s", __func__, pane);
541f65f2164Snicm
542f65f2164Snicm /* Check for pane ids starting with %. */
543f65f2164Snicm if (*pane == '%') {
544f65f2164Snicm fs->wp = window_pane_find_by_id_str(pane);
545b81c3e5bSnicm if (fs->wp == NULL)
546f65f2164Snicm return (-1);
547f65f2164Snicm fs->w = fs->wp->window;
548f65f2164Snicm return (cmd_find_best_winlink_with_window(fs));
549f65f2164Snicm }
550f65f2164Snicm
551f65f2164Snicm /* Otherwise use the current window. */
552f65f2164Snicm fs->wl = fs->s->curw;
553f65f2164Snicm fs->idx = fs->wl->idx;
554f65f2164Snicm fs->w = fs->wl->window;
555f65f2164Snicm
556f65f2164Snicm /* Now we just need to look up the pane. */
557f65f2164Snicm return (cmd_find_get_pane_with_window(fs, pane));
558f65f2164Snicm }
559f65f2164Snicm
560f65f2164Snicm /*
561f65f2164Snicm * Find pane from string, assuming it is in the given window. Needs w, fills in
562f65f2164Snicm * wp.
563f65f2164Snicm */
564dc1f0f5fSnicm static int
cmd_find_get_pane_with_window(struct cmd_find_state * fs,const char * pane)565f65f2164Snicm cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
566f65f2164Snicm {
567f65f2164Snicm const char *errstr;
568f65f2164Snicm int idx;
569f65f2164Snicm struct window_pane *wp;
570f65f2164Snicm u_int n;
571f65f2164Snicm
572f65f2164Snicm log_debug("%s: %s", __func__, pane);
573f65f2164Snicm
574f65f2164Snicm /* Check for pane ids starting with %. */
575f65f2164Snicm if (*pane == '%') {
576f65f2164Snicm fs->wp = window_pane_find_by_id_str(pane);
577b81c3e5bSnicm if (fs->wp == NULL)
578315f8009Snicm return (-1);
579315f8009Snicm if (fs->wp->window != fs->w)
580f65f2164Snicm return (-1);
581f65f2164Snicm return (0);
582f65f2164Snicm }
583f65f2164Snicm
584f65f2164Snicm /* Try special characters. */
585f65f2164Snicm if (strcmp(pane, "!") == 0) {
586*a0aa010aSnicm fs->wp = TAILQ_FIRST(&fs->w->last_panes);
587b81c3e5bSnicm if (fs->wp == NULL)
588315f8009Snicm return (-1);
589f65f2164Snicm return (0);
590baf68abeSnicm } else if (strcmp(pane, "{up-of}") == 0) {
591c2b9e1d8Snicm fs->wp = window_pane_find_up(fs->w->active);
592b81c3e5bSnicm if (fs->wp == NULL)
593f65f2164Snicm return (-1);
594f65f2164Snicm return (0);
595baf68abeSnicm } else if (strcmp(pane, "{down-of}") == 0) {
596c2b9e1d8Snicm fs->wp = window_pane_find_down(fs->w->active);
597b81c3e5bSnicm if (fs->wp == NULL)
598f65f2164Snicm return (-1);
599f65f2164Snicm return (0);
600baf68abeSnicm } else if (strcmp(pane, "{left-of}") == 0) {
601c2b9e1d8Snicm fs->wp = window_pane_find_left(fs->w->active);
602b81c3e5bSnicm if (fs->wp == NULL)
603f65f2164Snicm return (-1);
604f65f2164Snicm return (0);
605baf68abeSnicm } else if (strcmp(pane, "{right-of}") == 0) {
606c2b9e1d8Snicm fs->wp = window_pane_find_right(fs->w->active);
607b81c3e5bSnicm if (fs->wp == NULL)
608f65f2164Snicm return (-1);
609f65f2164Snicm return (0);
610f65f2164Snicm }
611f65f2164Snicm
612f65f2164Snicm /* Try as an offset. */
613f65f2164Snicm if (pane[0] == '+' || pane[0] == '-') {
614f65f2164Snicm if (pane[1] != '\0')
615f65f2164Snicm n = strtonum(pane + 1, 1, INT_MAX, NULL);
616f65f2164Snicm else
617f65f2164Snicm n = 1;
618c2b9e1d8Snicm wp = fs->w->active;
619f65f2164Snicm if (pane[0] == '+')
620f65f2164Snicm fs->wp = window_pane_next_by_number(fs->w, wp, n);
621f65f2164Snicm else
622f65f2164Snicm fs->wp = window_pane_previous_by_number(fs->w, wp, n);
623b81c3e5bSnicm if (fs->wp != NULL)
624f65f2164Snicm return (0);
625f65f2164Snicm }
626f65f2164Snicm
627f65f2164Snicm /* Get pane by index. */
628f65f2164Snicm idx = strtonum(pane, 0, INT_MAX, &errstr);
629f65f2164Snicm if (errstr == NULL) {
630f65f2164Snicm fs->wp = window_pane_at_index(fs->w, idx);
631b81c3e5bSnicm if (fs->wp != NULL)
632f65f2164Snicm return (0);
633f65f2164Snicm }
634f65f2164Snicm
635f65f2164Snicm /* Try as a description. */
636f65f2164Snicm fs->wp = window_find_string(fs->w, pane);
637b81c3e5bSnicm if (fs->wp != NULL)
638f65f2164Snicm return (0);
639f65f2164Snicm
640f65f2164Snicm return (-1);
641f65f2164Snicm }
642f65f2164Snicm
643f65f2164Snicm /* Clear state. */
644f65f2164Snicm void
cmd_find_clear_state(struct cmd_find_state * fs,int flags)64593e732aaSnicm cmd_find_clear_state(struct cmd_find_state *fs, int flags)
646f65f2164Snicm {
647f65f2164Snicm memset(fs, 0, sizeof *fs);
648f65f2164Snicm
649f65f2164Snicm fs->flags = flags;
650f65f2164Snicm
651f65f2164Snicm fs->idx = -1;
652f65f2164Snicm }
653f65f2164Snicm
65493e732aaSnicm /* Check if state is empty. */
655788e192fSnicm int
cmd_find_empty_state(struct cmd_find_state * fs)656788e192fSnicm cmd_find_empty_state(struct cmd_find_state *fs)
657788e192fSnicm {
658788e192fSnicm if (fs->s == NULL && fs->wl == NULL && fs->w == NULL && fs->wp == NULL)
659788e192fSnicm return (1);
660788e192fSnicm return (0);
661788e192fSnicm }
662788e192fSnicm
6637519eda3Snicm /* Check if a state if valid. */
6647519eda3Snicm int
cmd_find_valid_state(struct cmd_find_state * fs)6657519eda3Snicm cmd_find_valid_state(struct cmd_find_state *fs)
6667519eda3Snicm {
6677519eda3Snicm struct winlink *wl;
6687519eda3Snicm
6697519eda3Snicm if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL)
6707519eda3Snicm return (0);
6717519eda3Snicm
6727519eda3Snicm if (!session_alive(fs->s))
6737519eda3Snicm return (0);
6747519eda3Snicm
6757519eda3Snicm RB_FOREACH(wl, winlinks, &fs->s->windows) {
6767519eda3Snicm if (wl->window == fs->w && wl == fs->wl)
6777519eda3Snicm break;
6787519eda3Snicm }
6797519eda3Snicm if (wl == NULL)
6807519eda3Snicm return (0);
6817519eda3Snicm
6827519eda3Snicm if (fs->w != fs->wl->window)
6837519eda3Snicm return (0);
6847519eda3Snicm
685b81c3e5bSnicm return (window_has_pane(fs->w, fs->wp));
6867519eda3Snicm }
6877519eda3Snicm
6887519eda3Snicm /* Copy a state. */
6897519eda3Snicm void
cmd_find_copy_state(struct cmd_find_state * dst,struct cmd_find_state * src)6907519eda3Snicm cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src)
6917519eda3Snicm {
6927519eda3Snicm dst->s = src->s;
6937519eda3Snicm dst->wl = src->wl;
6941d19b2ecSnicm dst->idx = src->idx;
6951d19b2ecSnicm dst->w = src->w;
6967519eda3Snicm dst->wp = src->wp;
6977519eda3Snicm }
6987519eda3Snicm
6997519eda3Snicm /* Log the result. */
7004e325abeSnicm static void
cmd_find_log_state(const char * prefix,struct cmd_find_state * fs)7017519eda3Snicm cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
7027519eda3Snicm {
7037519eda3Snicm if (fs->s != NULL)
7042fe539a5Snicm log_debug("%s: s=$%u %s", prefix, fs->s->id, fs->s->name);
7057519eda3Snicm else
7067519eda3Snicm log_debug("%s: s=none", prefix);
7077519eda3Snicm if (fs->wl != NULL) {
7087519eda3Snicm log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx,
7097519eda3Snicm fs->wl->window == fs->w, fs->w->id, fs->w->name);
7107519eda3Snicm } else
7117519eda3Snicm log_debug("%s: wl=none", prefix);
7127519eda3Snicm if (fs->wp != NULL)
7137519eda3Snicm log_debug("%s: wp=%%%u", prefix, fs->wp->id);
7147519eda3Snicm else
7157519eda3Snicm log_debug("%s: wp=none", prefix);
7167519eda3Snicm if (fs->idx != -1)
7177519eda3Snicm log_debug("%s: idx=%d", prefix, fs->idx);
7187519eda3Snicm else
7197519eda3Snicm log_debug("%s: idx=none", prefix);
7207519eda3Snicm }
7217519eda3Snicm
7224fc586aaSnicm /* Find state from a session. */
7231adc95ebSnicm void
cmd_find_from_session(struct cmd_find_state * fs,struct session * s,int flags)7240772530eSnicm cmd_find_from_session(struct cmd_find_state *fs, struct session *s, int flags)
7254fc586aaSnicm {
7260772530eSnicm cmd_find_clear_state(fs, flags);
7274fc586aaSnicm
7284fc586aaSnicm fs->s = s;
7294fc586aaSnicm fs->wl = fs->s->curw;
7304fc586aaSnicm fs->w = fs->wl->window;
7314fc586aaSnicm fs->wp = fs->w->active;
7324fc586aaSnicm
7334fc586aaSnicm cmd_find_log_state(__func__, fs);
7344fc586aaSnicm }
7354fc586aaSnicm
7366cd60b5aSnicm /* Find state from a winlink. */
7371adc95ebSnicm void
cmd_find_from_winlink(struct cmd_find_state * fs,struct winlink * wl,int flags)7380772530eSnicm cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl, int flags)
7396cd60b5aSnicm {
7400772530eSnicm cmd_find_clear_state(fs, flags);
7416cd60b5aSnicm
7426aa2e59aSnicm fs->s = wl->session;
7436cd60b5aSnicm fs->wl = wl;
7446cd60b5aSnicm fs->w = wl->window;
7456cd60b5aSnicm fs->wp = wl->window->active;
7466cd60b5aSnicm
7476cd60b5aSnicm cmd_find_log_state(__func__, fs);
7486cd60b5aSnicm }
7496cd60b5aSnicm
7503938e8abSnicm /* Find state from a session and window. */
7513938e8abSnicm int
cmd_find_from_session_window(struct cmd_find_state * fs,struct session * s,struct window * w,int flags)7523938e8abSnicm cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s,
7530772530eSnicm struct window *w, int flags)
7543938e8abSnicm {
7550772530eSnicm cmd_find_clear_state(fs, flags);
7563938e8abSnicm
7573938e8abSnicm fs->s = s;
7583938e8abSnicm fs->w = w;
7599a5690feSnicm if (cmd_find_best_winlink_with_window(fs) != 0) {
7600772530eSnicm cmd_find_clear_state(fs, flags);
7613938e8abSnicm return (-1);
7629a5690feSnicm }
7633938e8abSnicm fs->wp = fs->w->active;
7643938e8abSnicm
7653938e8abSnicm cmd_find_log_state(__func__, fs);
7663938e8abSnicm return (0);
7673938e8abSnicm }
7683938e8abSnicm
7694fc586aaSnicm /* Find state from a window. */
7704fc586aaSnicm int
cmd_find_from_window(struct cmd_find_state * fs,struct window * w,int flags)7710772530eSnicm cmd_find_from_window(struct cmd_find_state *fs, struct window *w, int flags)
7724fc586aaSnicm {
7730772530eSnicm cmd_find_clear_state(fs, flags);
7744fc586aaSnicm
7754fc586aaSnicm fs->w = w;
7769a5690feSnicm if (cmd_find_best_session_with_window(fs) != 0) {
7770772530eSnicm cmd_find_clear_state(fs, flags);
7784fc586aaSnicm return (-1);
7799a5690feSnicm }
7809a5690feSnicm if (cmd_find_best_winlink_with_window(fs) != 0) {
7810772530eSnicm cmd_find_clear_state(fs, flags);
7824fc586aaSnicm return (-1);
7839a5690feSnicm }
7843938e8abSnicm fs->wp = fs->w->active;
7854fc586aaSnicm
7864fc586aaSnicm cmd_find_log_state(__func__, fs);
7874fc586aaSnicm return (0);
7884fc586aaSnicm }
7894fc586aaSnicm
7901adc95ebSnicm /* Find state from a winlink and pane. */
7911adc95ebSnicm void
cmd_find_from_winlink_pane(struct cmd_find_state * fs,struct winlink * wl,struct window_pane * wp,int flags)7921adc95ebSnicm cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl,
7930772530eSnicm struct window_pane *wp, int flags)
7941adc95ebSnicm {
7950772530eSnicm cmd_find_clear_state(fs, flags);
7961adc95ebSnicm
7971adc95ebSnicm fs->s = wl->session;
7981adc95ebSnicm fs->wl = wl;
7991adc95ebSnicm fs->idx = fs->wl->idx;
8001adc95ebSnicm fs->w = fs->wl->window;
8011adc95ebSnicm fs->wp = wp;
8021adc95ebSnicm
8031adc95ebSnicm cmd_find_log_state(__func__, fs);
8041adc95ebSnicm }
8051adc95ebSnicm
8064fc586aaSnicm /* Find state from a pane. */
8074fc586aaSnicm int
cmd_find_from_pane(struct cmd_find_state * fs,struct window_pane * wp,int flags)8080772530eSnicm cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp, int flags)
8094fc586aaSnicm {
8100772530eSnicm if (cmd_find_from_window(fs, wp->window, flags) != 0)
8114fc586aaSnicm return (-1);
8124fc586aaSnicm fs->wp = wp;
8134fc586aaSnicm
8144fc586aaSnicm cmd_find_log_state(__func__, fs);
8154fc586aaSnicm return (0);
8164fc586aaSnicm }
8174fc586aaSnicm
81893e732aaSnicm /* Find state from nothing. */
819dead13bbSnicm int
cmd_find_from_nothing(struct cmd_find_state * fs,int flags)8200772530eSnicm cmd_find_from_nothing(struct cmd_find_state *fs, int flags)
821dead13bbSnicm {
8220772530eSnicm cmd_find_clear_state(fs, flags);
82393e732aaSnicm
8240772530eSnicm fs->s = cmd_find_best_session(NULL, 0, flags);
8259a5690feSnicm if (fs->s == NULL) {
8260772530eSnicm cmd_find_clear_state(fs, flags);
82793e732aaSnicm return (-1);
8289a5690feSnicm }
82993e732aaSnicm fs->wl = fs->s->curw;
83093e732aaSnicm fs->idx = fs->wl->idx;
83193e732aaSnicm fs->w = fs->wl->window;
83293e732aaSnicm fs->wp = fs->w->active;
83393e732aaSnicm
83493e732aaSnicm cmd_find_log_state(__func__, fs);
83593e732aaSnicm return (0);
83693e732aaSnicm }
83793e732aaSnicm
83893e732aaSnicm /* Find state from mouse. */
83993e732aaSnicm int
cmd_find_from_mouse(struct cmd_find_state * fs,struct mouse_event * m,int flags)8400772530eSnicm cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
84193e732aaSnicm {
8420772530eSnicm cmd_find_clear_state(fs, flags);
84393e732aaSnicm
84493e732aaSnicm if (!m->valid)
84593e732aaSnicm return (-1);
84693e732aaSnicm
84793e732aaSnicm fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
8489a5690feSnicm if (fs->wp == NULL) {
8490772530eSnicm cmd_find_clear_state(fs, flags);
85093e732aaSnicm return (-1);
8519a5690feSnicm }
85293e732aaSnicm fs->w = fs->wl->window;
85393e732aaSnicm
85493e732aaSnicm cmd_find_log_state(__func__, fs);
85593e732aaSnicm return (0);
85693e732aaSnicm }
85793e732aaSnicm
85893e732aaSnicm /* Find state from client. */
85993e732aaSnicm int
cmd_find_from_client(struct cmd_find_state * fs,struct client * c,int flags)8600772530eSnicm cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
86193e732aaSnicm {
86293e732aaSnicm struct window_pane *wp;
86393e732aaSnicm
86493e732aaSnicm /* If no client, treat as from nothing. */
86593e732aaSnicm if (c == NULL)
8660772530eSnicm return (cmd_find_from_nothing(fs, flags));
86793e732aaSnicm
86893e732aaSnicm /* If this is an attached client, all done. */
86993e732aaSnicm if (c->session != NULL) {
870249e1654Snicm cmd_find_clear_state(fs, flags);
871249e1654Snicm
872249e1654Snicm fs->wp = server_client_get_pane(c);
873249e1654Snicm if (fs->wp == NULL) {
8740772530eSnicm cmd_find_from_session(fs, c->session, flags);
87593e732aaSnicm return (0);
87693e732aaSnicm }
877249e1654Snicm fs->s = c->session;
878249e1654Snicm fs->wl = fs->s->curw;
879249e1654Snicm fs->w = fs->wl->window;
880249e1654Snicm
881249e1654Snicm cmd_find_log_state(__func__, fs);
882249e1654Snicm return (0);
883249e1654Snicm }
8840772530eSnicm cmd_find_clear_state(fs, flags);
88593e732aaSnicm
88693e732aaSnicm /*
88793e732aaSnicm * If this is an unattached client running in a pane, we can use that
88893e732aaSnicm * to limit the list of sessions to those containing that pane.
88993e732aaSnicm */
8906b339e7dSnicm wp = cmd_find_inside_pane(c);
89193e732aaSnicm if (wp == NULL)
89293e732aaSnicm goto unknown_pane;
89393e732aaSnicm
89493e732aaSnicm /*
89593e732aaSnicm * Don't have a session, or it doesn't have this pane. Try all
89693e732aaSnicm * sessions.
89793e732aaSnicm */
89893e732aaSnicm fs->w = wp->window;
89993e732aaSnicm if (cmd_find_best_session_with_window(fs) != 0) {
90093e732aaSnicm /*
90193e732aaSnicm * The window may have been destroyed but the pane
90293e732aaSnicm * still on all_window_panes due to something else
90393e732aaSnicm * holding a reference.
90493e732aaSnicm */
90593e732aaSnicm goto unknown_pane;
90693e732aaSnicm }
90793e732aaSnicm fs->wl = fs->s->curw;
90893e732aaSnicm fs->w = fs->wl->window;
90993e732aaSnicm fs->wp = fs->w->active; /* use active pane */
91093e732aaSnicm
91193e732aaSnicm cmd_find_log_state(__func__, fs);
912dead13bbSnicm return (0);
91393e732aaSnicm
91493e732aaSnicm unknown_pane:
915403a76d5Snicm /* We can't find the pane so need to guess. */
9160772530eSnicm return (cmd_find_from_nothing(fs, flags));
917dead13bbSnicm }
918dead13bbSnicm
919ed390037Snicm /*
920ed390037Snicm * Split target into pieces and resolve for the given type. Fills in the given
921ed390037Snicm * state. Returns 0 on success or -1 on error.
922ed390037Snicm */
923ed390037Snicm int
cmd_find_target(struct cmd_find_state * fs,struct cmdq_item * item,const char * target,enum cmd_find_type type,int flags)92493e732aaSnicm cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
92593e732aaSnicm const char *target, enum cmd_find_type type, int flags)
926f65f2164Snicm {
927f65f2164Snicm struct mouse_event *m;
92893e732aaSnicm struct cmd_find_state current;
929a7e27df1Snicm char *colon, *period, *copy = NULL, tmp[256];
9306e8d04c2Snicm const char *session, *window, *pane, *s;
93110f437f1Snicm int window_only = 0, pane_only = 0;
932f65f2164Snicm
933bf0d297eSnicm /* Can fail flag implies quiet. */
934bf0d297eSnicm if (flags & CMD_FIND_CANFAIL)
935bf0d297eSnicm flags |= CMD_FIND_QUIET;
936bf0d297eSnicm
937765d031fSnicm /* Log the arguments. */
9386e8d04c2Snicm if (type == CMD_FIND_PANE)
9396e8d04c2Snicm s = "pane";
9406e8d04c2Snicm else if (type == CMD_FIND_WINDOW)
9416e8d04c2Snicm s = "window";
9426e8d04c2Snicm else if (type == CMD_FIND_SESSION)
9436e8d04c2Snicm s = "session";
944765d031fSnicm else
9456e8d04c2Snicm s = "unknown";
946a7e27df1Snicm *tmp = '\0';
947a7e27df1Snicm if (flags & CMD_FIND_PREFER_UNATTACHED)
948a7e27df1Snicm strlcat(tmp, "PREFER_UNATTACHED,", sizeof tmp);
949a7e27df1Snicm if (flags & CMD_FIND_QUIET)
950a7e27df1Snicm strlcat(tmp, "QUIET,", sizeof tmp);
951a7e27df1Snicm if (flags & CMD_FIND_WINDOW_INDEX)
952a7e27df1Snicm strlcat(tmp, "WINDOW_INDEX,", sizeof tmp);
953a7e27df1Snicm if (flags & CMD_FIND_DEFAULT_MARKED)
954a7e27df1Snicm strlcat(tmp, "DEFAULT_MARKED,", sizeof tmp);
955a7e27df1Snicm if (flags & CMD_FIND_EXACT_SESSION)
956a7e27df1Snicm strlcat(tmp, "EXACT_SESSION,", sizeof tmp);
957a7e27df1Snicm if (flags & CMD_FIND_EXACT_WINDOW)
958a7e27df1Snicm strlcat(tmp, "EXACT_WINDOW,", sizeof tmp);
959a7e27df1Snicm if (flags & CMD_FIND_CANFAIL)
960a7e27df1Snicm strlcat(tmp, "CANFAIL,", sizeof tmp);
961a7e27df1Snicm if (*tmp != '\0')
962a7e27df1Snicm tmp[strlen(tmp) - 1] = '\0';
963403a76d5Snicm else
964403a76d5Snicm strlcat(tmp, "NONE", sizeof tmp);
965a7e27df1Snicm log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
966a7e27df1Snicm target == NULL ? "none" : target, s, item, tmp);
967765d031fSnicm
96856d83ebbSnicm /* Clear new state. */
96993e732aaSnicm cmd_find_clear_state(fs, flags);
97056d83ebbSnicm
971f65f2164Snicm /* Find current state. */
972788e192fSnicm if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) {
97356d83ebbSnicm fs->current = &marked_pane;
974691b2064Snicm log_debug("%s: current is marked pane", __func__);
975823b6d6dSnicm } else if (cmd_find_valid_state(cmdq_get_current(item))) {
976823b6d6dSnicm fs->current = cmdq_get_current(item);
977691b2064Snicm log_debug("%s: current is from queue", __func__);
978040343aeSnicm } else if (cmd_find_from_client(¤t, cmdq_get_client(item),
979040343aeSnicm flags) == 0) {
98093e732aaSnicm fs->current = ¤t;
98193e732aaSnicm log_debug("%s: current is from client", __func__);
9824d203398Snicm } else {
9834d203398Snicm if (~flags & CMD_FIND_QUIET)
9844d203398Snicm cmdq_error(item, "no current target");
985d655f083Snicm goto error;
9864d203398Snicm }
987d655f083Snicm if (!cmd_find_valid_state(fs->current))
988788e192fSnicm fatalx("invalid current find state");
989f65f2164Snicm
990f65f2164Snicm /* An empty or NULL target is the current. */
991f65f2164Snicm if (target == NULL || *target == '\0')
992f65f2164Snicm goto current;
993f65f2164Snicm
994f65f2164Snicm /* Mouse target is a plain = or {mouse}. */
995f65f2164Snicm if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
996823b6d6dSnicm m = &cmdq_get_event(item)->m;
997f65f2164Snicm switch (type) {
998f65f2164Snicm case CMD_FIND_PANE:
999ed390037Snicm fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
10009b9cde72Snicm if (fs->wp != NULL) {
1001ed390037Snicm fs->w = fs->wl->window;
1002f65f2164Snicm break;
10039b9cde72Snicm }
10049b9cde72Snicm /* FALLTHROUGH */
1005f65f2164Snicm case CMD_FIND_WINDOW:
1006f65f2164Snicm case CMD_FIND_SESSION:
1007ed390037Snicm fs->wl = cmd_mouse_window(m, &fs->s);
10089b9cde72Snicm if (fs->wl == NULL && fs->s != NULL)
10099b9cde72Snicm fs->wl = fs->s->curw;
1010ed390037Snicm if (fs->wl != NULL) {
1011ed390037Snicm fs->w = fs->wl->window;
1012ed390037Snicm fs->wp = fs->w->active;
1013f65f2164Snicm }
1014f65f2164Snicm break;
1015f65f2164Snicm }
1016ed390037Snicm if (fs->wp == NULL) {
1017f65f2164Snicm if (~flags & CMD_FIND_QUIET)
101868e0a7f2Snicm cmdq_error(item, "no mouse target");
1019f65f2164Snicm goto error;
1020f65f2164Snicm }
1021765d031fSnicm goto found;
1022f65f2164Snicm }
10232986c025Snicm
10242986c025Snicm /* Marked target is a plain ~ or {marked}. */
10252986c025Snicm if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) {
10262986c025Snicm if (!server_check_marked()) {
10272986c025Snicm if (~flags & CMD_FIND_QUIET)
102868e0a7f2Snicm cmdq_error(item, "no marked target");
10292986c025Snicm goto error;
10302986c025Snicm }
10317519eda3Snicm cmd_find_copy_state(fs, &marked_pane);
1032765d031fSnicm goto found;
10332986c025Snicm }
1034f65f2164Snicm
1035f65f2164Snicm /* Find separators if they exist. */
10362986c025Snicm copy = xstrdup(target);
1037f65f2164Snicm colon = strchr(copy, ':');
1038f65f2164Snicm if (colon != NULL)
1039f65f2164Snicm *colon++ = '\0';
1040f65f2164Snicm if (colon == NULL)
1041f65f2164Snicm period = strchr(copy, '.');
1042f65f2164Snicm else
1043f65f2164Snicm period = strchr(colon, '.');
1044f65f2164Snicm if (period != NULL)
1045f65f2164Snicm *period++ = '\0';
1046f65f2164Snicm
1047f65f2164Snicm /* Set session, window and pane parts. */
1048f65f2164Snicm session = window = pane = NULL;
1049f65f2164Snicm if (colon != NULL && period != NULL) {
1050f65f2164Snicm session = copy;
1051f65f2164Snicm window = colon;
105210f437f1Snicm window_only = 1;
1053f65f2164Snicm pane = period;
105410f437f1Snicm pane_only = 1;
1055f65f2164Snicm } else if (colon != NULL && period == NULL) {
1056f65f2164Snicm session = copy;
1057f65f2164Snicm window = colon;
105810f437f1Snicm window_only = 1;
1059f65f2164Snicm } else if (colon == NULL && period != NULL) {
1060f65f2164Snicm window = copy;
1061f65f2164Snicm pane = period;
106210f437f1Snicm pane_only = 1;
1063f65f2164Snicm } else {
1064f65f2164Snicm if (*copy == '$')
1065f65f2164Snicm session = copy;
1066f65f2164Snicm else if (*copy == '@')
1067f65f2164Snicm window = copy;
1068f65f2164Snicm else if (*copy == '%')
1069f65f2164Snicm pane = copy;
1070f65f2164Snicm else {
1071f65f2164Snicm switch (type) {
1072f65f2164Snicm case CMD_FIND_SESSION:
1073f65f2164Snicm session = copy;
1074f65f2164Snicm break;
1075f65f2164Snicm case CMD_FIND_WINDOW:
1076f65f2164Snicm window = copy;
1077f65f2164Snicm break;
1078f65f2164Snicm case CMD_FIND_PANE:
1079f65f2164Snicm pane = copy;
1080f65f2164Snicm break;
1081f65f2164Snicm }
1082f65f2164Snicm }
1083f65f2164Snicm }
1084f65f2164Snicm
1085a7f2e23cSnicm /* Set exact match flags. */
1086a7f2e23cSnicm if (session != NULL && *session == '=') {
1087a7f2e23cSnicm session++;
1088ed390037Snicm fs->flags |= CMD_FIND_EXACT_SESSION;
1089a7f2e23cSnicm }
1090a7f2e23cSnicm if (window != NULL && *window == '=') {
1091a7f2e23cSnicm window++;
1092ed390037Snicm fs->flags |= CMD_FIND_EXACT_WINDOW;
1093a7f2e23cSnicm }
1094a7f2e23cSnicm
1095f65f2164Snicm /* Empty is the same as NULL. */
1096f65f2164Snicm if (session != NULL && *session == '\0')
1097f65f2164Snicm session = NULL;
1098f65f2164Snicm if (window != NULL && *window == '\0')
1099f65f2164Snicm window = NULL;
1100f65f2164Snicm if (pane != NULL && *pane == '\0')
1101f65f2164Snicm pane = NULL;
1102f65f2164Snicm
1103f65f2164Snicm /* Map though conversion table. */
1104f65f2164Snicm if (session != NULL)
1105f65f2164Snicm session = cmd_find_map_table(cmd_find_session_table, session);
1106f65f2164Snicm if (window != NULL)
1107f65f2164Snicm window = cmd_find_map_table(cmd_find_window_table, window);
1108f65f2164Snicm if (pane != NULL)
1109f65f2164Snicm pane = cmd_find_map_table(cmd_find_pane_table, pane);
1110f65f2164Snicm
1111a7e27df1Snicm if (session != NULL || window != NULL || pane != NULL) {
1112a7e27df1Snicm log_debug("%s: target %s is %s%s%s%s%s%s",
1113a7e27df1Snicm __func__, target,
1114a7e27df1Snicm session == NULL ? "" : "session ",
1115a7e27df1Snicm session == NULL ? "" : session,
1116a7e27df1Snicm window == NULL ? "" : "window ",
1117a7e27df1Snicm window == NULL ? "" : window,
1118a7e27df1Snicm pane == NULL ? "" : "pane ",
1119a7e27df1Snicm pane == NULL ? "" : pane);
1120a7e27df1Snicm }
1121f65f2164Snicm
1122f65f2164Snicm /* No pane is allowed if want an index. */
1123f65f2164Snicm if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) {
1124f65f2164Snicm if (~flags & CMD_FIND_QUIET)
112568e0a7f2Snicm cmdq_error(item, "can't specify pane here");
1126f65f2164Snicm goto error;
1127f65f2164Snicm }
1128f65f2164Snicm
1129f65f2164Snicm /* If the session isn't NULL, look it up. */
1130f65f2164Snicm if (session != NULL) {
1131f65f2164Snicm /* This will fill in session. */
1132ed390037Snicm if (cmd_find_get_session(fs, session) != 0)
1133f65f2164Snicm goto no_session;
1134f65f2164Snicm
1135f65f2164Snicm /* If window and pane are NULL, use that session's current. */
1136f65f2164Snicm if (window == NULL && pane == NULL) {
1137ed390037Snicm fs->wl = fs->s->curw;
1138ed390037Snicm fs->idx = -1;
1139ed390037Snicm fs->w = fs->wl->window;
1140ed390037Snicm fs->wp = fs->w->active;
1141f65f2164Snicm goto found;
1142f65f2164Snicm }
1143f65f2164Snicm
1144f65f2164Snicm /* If window is present but pane not, find window in session. */
1145f65f2164Snicm if (window != NULL && pane == NULL) {
1146f65f2164Snicm /* This will fill in winlink and window. */
1147ed390037Snicm if (cmd_find_get_window_with_session(fs, window) != 0)
1148f65f2164Snicm goto no_window;
11494d00024eSnicm if (fs->wl != NULL) /* can be NULL if index only */
1150ed390037Snicm fs->wp = fs->wl->window->active;
1151f65f2164Snicm goto found;
1152f65f2164Snicm }
1153f65f2164Snicm
1154f65f2164Snicm /* If pane is present but window not, find pane. */
1155f65f2164Snicm if (window == NULL && pane != NULL) {
1156f65f2164Snicm /* This will fill in winlink and window and pane. */
1157ed390037Snicm if (cmd_find_get_pane_with_session(fs, pane) != 0)
1158f65f2164Snicm goto no_pane;
1159f65f2164Snicm goto found;
1160f65f2164Snicm }
1161f65f2164Snicm
1162f65f2164Snicm /*
1163f65f2164Snicm * If window and pane are present, find both in session. This
1164f65f2164Snicm * will fill in winlink and window.
1165f65f2164Snicm */
1166ed390037Snicm if (cmd_find_get_window_with_session(fs, window) != 0)
1167f65f2164Snicm goto no_window;
1168f65f2164Snicm /* This will fill in pane. */
1169ed390037Snicm if (cmd_find_get_pane_with_window(fs, pane) != 0)
1170f65f2164Snicm goto no_pane;
1171f65f2164Snicm goto found;
1172f65f2164Snicm }
1173f65f2164Snicm
1174f65f2164Snicm /* No session. If window and pane, try them. */
1175f65f2164Snicm if (window != NULL && pane != NULL) {
1176f65f2164Snicm /* This will fill in session, winlink and window. */
117710f437f1Snicm if (cmd_find_get_window(fs, window, window_only) != 0)
1178f65f2164Snicm goto no_window;
1179f65f2164Snicm /* This will fill in pane. */
1180ed390037Snicm if (cmd_find_get_pane_with_window(fs, pane) != 0)
1181f65f2164Snicm goto no_pane;
1182f65f2164Snicm goto found;
1183f65f2164Snicm }
1184f65f2164Snicm
1185f65f2164Snicm /* If just window is present, try it. */
1186f65f2164Snicm if (window != NULL && pane == NULL) {
1187f65f2164Snicm /* This will fill in session, winlink and window. */
118810f437f1Snicm if (cmd_find_get_window(fs, window, window_only) != 0)
1189f65f2164Snicm goto no_window;
11904bd93a15Snicm if (fs->wl != NULL) /* can be NULL if index only */
1191ed390037Snicm fs->wp = fs->wl->window->active;
1192f65f2164Snicm goto found;
1193f65f2164Snicm }
1194f65f2164Snicm
1195f65f2164Snicm /* If just pane is present, try it. */
1196f65f2164Snicm if (window == NULL && pane != NULL) {
1197f65f2164Snicm /* This will fill in session, winlink, window and pane. */
119810f437f1Snicm if (cmd_find_get_pane(fs, pane, pane_only) != 0)
1199f65f2164Snicm goto no_pane;
1200f65f2164Snicm goto found;
1201f65f2164Snicm }
1202f65f2164Snicm
1203f65f2164Snicm current:
1204ed390037Snicm /* Use the current session. */
120556d83ebbSnicm cmd_find_copy_state(fs, fs->current);
1206f65f2164Snicm if (flags & CMD_FIND_WINDOW_INDEX)
120756d83ebbSnicm fs->idx = -1;
1208ed390037Snicm goto found;
1209f65f2164Snicm
1210f65f2164Snicm error:
1211ed390037Snicm fs->current = NULL;
1212691b2064Snicm log_debug("%s: error", __func__);
1213ed390037Snicm
1214ed390037Snicm free(copy);
1215bf0d297eSnicm if (flags & CMD_FIND_CANFAIL)
1216bf0d297eSnicm return (0);
1217ed390037Snicm return (-1);
1218f65f2164Snicm
1219f65f2164Snicm found:
1220ed390037Snicm fs->current = NULL;
1221ed390037Snicm cmd_find_log_state(__func__, fs);
1222ed390037Snicm
1223f65f2164Snicm free(copy);
1224ed390037Snicm return (0);
1225f65f2164Snicm
1226f65f2164Snicm no_session:
1227f65f2164Snicm if (~flags & CMD_FIND_QUIET)
1228fe57de0cSnicm cmdq_error(item, "can't find session: %s", session);
1229f65f2164Snicm goto error;
1230f65f2164Snicm
1231f65f2164Snicm no_window:
1232f65f2164Snicm if (~flags & CMD_FIND_QUIET)
1233fe57de0cSnicm cmdq_error(item, "can't find window: %s", window);
1234f65f2164Snicm goto error;
1235f65f2164Snicm
1236f65f2164Snicm no_pane:
1237f65f2164Snicm if (~flags & CMD_FIND_QUIET)
1238fe57de0cSnicm cmdq_error(item, "can't find pane: %s", pane);
1239f65f2164Snicm goto error;
1240f65f2164Snicm }
1241f65f2164Snicm
12426b339e7dSnicm /* Find the current client. */
12436b339e7dSnicm static struct client *
cmd_find_current_client(struct cmdq_item * item,int quiet)12446b339e7dSnicm cmd_find_current_client(struct cmdq_item *item, int quiet)
1245f65f2164Snicm {
12462db665b3Snicm struct client *c = NULL, *found;
124793e732aaSnicm struct session *s;
12486b339e7dSnicm struct window_pane *wp;
12496b339e7dSnicm struct cmd_find_state fs;
1250f65f2164Snicm
12512db665b3Snicm if (item != NULL)
1252040343aeSnicm c = cmdq_get_client(item);
12532db665b3Snicm if (c != NULL && c->session != NULL)
12542db665b3Snicm return (c);
12556b339e7dSnicm
12562db665b3Snicm found = NULL;
12572db665b3Snicm if (c != NULL && (wp = cmd_find_inside_pane(c)) != NULL) {
12586b339e7dSnicm cmd_find_clear_state(&fs, CMD_FIND_QUIET);
12596b339e7dSnicm fs.w = wp->window;
12606b339e7dSnicm if (cmd_find_best_session_with_window(&fs) == 0)
12612db665b3Snicm found = cmd_find_best_client(fs.s);
12626b339e7dSnicm } else {
126393e732aaSnicm s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
126493e732aaSnicm if (s != NULL)
12652db665b3Snicm found = cmd_find_best_client(s);
126693e732aaSnicm }
12672db665b3Snicm if (found == NULL && item != NULL && !quiet)
1268ea0386afSnicm cmdq_error(item, "no current client");
12692db665b3Snicm log_debug("%s: no target, return %p", __func__, found);
12702db665b3Snicm return (found);
1271f65f2164Snicm }
12726b339e7dSnicm
12736b339e7dSnicm /* Find the target client or report an error and return NULL. */
12746b339e7dSnicm struct client *
cmd_find_client(struct cmdq_item * item,const char * target,int quiet)12756b339e7dSnicm cmd_find_client(struct cmdq_item *item, const char *target, int quiet)
12766b339e7dSnicm {
12776b339e7dSnicm struct client *c;
12786b339e7dSnicm char *copy;
12796b339e7dSnicm size_t size;
12806b339e7dSnicm
12816b339e7dSnicm /* A NULL argument means the current client. */
12826b339e7dSnicm if (target == NULL)
12836b339e7dSnicm return (cmd_find_current_client(item, quiet));
1284f65f2164Snicm copy = xstrdup(target);
1285f65f2164Snicm
1286f65f2164Snicm /* Trim a single trailing colon if any. */
1287f65f2164Snicm size = strlen(copy);
1288f65f2164Snicm if (size != 0 && copy[size - 1] == ':')
1289f65f2164Snicm copy[size - 1] = '\0';
1290f65f2164Snicm
1291bcdd9d70Snicm /* Check name and path of each client. */
1292f65f2164Snicm TAILQ_FOREACH(c, &clients, entry) {
1293bcdd9d70Snicm if (c->session == NULL)
1294f65f2164Snicm continue;
1295bcdd9d70Snicm if (strcmp(copy, c->name) == 0)
1296f65f2164Snicm break;
1297f65f2164Snicm
1298bcdd9d70Snicm if (*c->ttyname == '\0')
1299f65f2164Snicm continue;
1300bcdd9d70Snicm if (strcmp(copy, c->ttyname) == 0)
1301bcdd9d70Snicm break;
1302bcdd9d70Snicm if (strncmp(c->ttyname, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
1303bcdd9d70Snicm continue;
1304bcdd9d70Snicm if (strcmp(copy, c->ttyname + (sizeof _PATH_DEV) - 1) == 0)
1305f65f2164Snicm break;
1306f65f2164Snicm }
1307f65f2164Snicm
1308f65f2164Snicm /* If no client found, report an error. */
1309f65f2164Snicm if (c == NULL && !quiet)
1310fe57de0cSnicm cmdq_error(item, "can't find client: %s", copy);
1311f65f2164Snicm
1312f65f2164Snicm free(copy);
13135b8ac713Snicm log_debug("%s: target %s, return %p", __func__, target, c);
1314f65f2164Snicm return (c);
1315f65f2164Snicm }
1316