On second thoughts, do check DA2 for DECFRA and DECSLRM since that will
[tmux.git] / cmd-find.c
blobc651448d160811cd86e237463937b88d473f7583
1 /* $OpenBSD$ */
3 /*
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>
21 #include <fnmatch.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <paths.h>
26 #include <unistd.h>
28 #include "tmux.h"
30 static int cmd_find_session_better(struct session *, struct session *,
31 int);
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 *,
42 const char *);
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 *,
45 const char *);
46 static int cmd_find_get_pane_with_window(struct cmd_find_state *,
47 const char *);
49 static const char *cmd_find_session_table[][2] = {
50 { NULL, NULL }
52 static const char *cmd_find_window_table[][2] = {
53 { "{start}", "^" },
54 { "{last}", "!" },
55 { "{end}", "$" },
56 { "{next}", "+" },
57 { "{previous}", "-" },
58 { NULL, NULL }
60 static const char *cmd_find_pane_table[][2] = {
61 { "{last}", "!" },
62 { "{next}", "+" },
63 { "{previous}", "-" },
64 { "{top}", "top" },
65 { "{bottom}", "bottom" },
66 { "{left}", "left" },
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}" },
76 { NULL, NULL }
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;
86 if (c == NULL)
87 return (NULL);
89 RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
90 if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
91 break;
93 if (wp == NULL) {
94 envent = environ_find(c->environ, "TMUX_PANE");
95 if (envent != NULL)
96 wp = window_pane_find_by_id_str(envent->value);
98 if (wp != NULL)
99 log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
100 return (wp);
103 /* Is this client better? */
104 static int
105 cmd_find_client_better(struct client *c, struct client *than)
107 if (than == NULL)
108 return (1);
109 return (timercmp(&c->activity_time, &than->activity_time, >));
112 /* Find best client for session. */
113 struct client *
114 cmd_find_best_client(struct session *s)
116 struct client *c_loop, *c;
118 if (s->attached == 0)
119 s = NULL;
121 c = NULL;
122 TAILQ_FOREACH(c_loop, &clients, entry) {
123 if (c_loop->session == NULL)
124 continue;
125 if (s != NULL && c_loop->session != s)
126 continue;
127 if (cmd_find_client_better(c_loop, c))
128 c = c_loop;
130 return (c);
133 /* Is this session better? */
134 static int
135 cmd_find_session_better(struct session *s, struct session *than, int flags)
137 int attached;
139 if (than == NULL)
140 return (1);
141 if (flags & CMD_FIND_PREFER_UNATTACHED) {
142 attached = (than->attached != 0);
143 if (attached && s->attached == 0)
144 return (1);
145 else if (!attached && s->attached != 0)
146 return (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;
156 u_int i;
158 log_debug("%s: %u sessions to try", __func__, ssize);
160 s = NULL;
161 if (slist != NULL) {
162 for (i = 0; i < ssize; i++) {
163 if (cmd_find_session_better(slist[i], s, flags))
164 s = slist[i];
166 } else {
167 RB_FOREACH(s_loop, sessions, &sessions) {
168 if (cmd_find_session_better(s_loop, s, flags))
169 s = s_loop;
172 return (s);
175 /* Find best session and winlink for window. */
176 static int
177 cmd_find_best_session_with_window(struct cmd_find_state *fs)
179 struct session **slist = NULL;
180 u_int ssize;
181 struct session *s;
183 log_debug("%s: window is @%u", __func__, fs->w->id);
185 ssize = 0;
186 RB_FOREACH(s, sessions, &sessions) {
187 if (!session_has(s, fs->w))
188 continue;
189 slist = xreallocarray(slist, ssize + 1, sizeof *slist);
190 slist[ssize++] = s;
192 if (ssize == 0)
193 goto fail;
194 fs->s = cmd_find_best_session(slist, ssize, fs->flags);
195 if (fs->s == NULL)
196 goto fail;
197 free(slist);
198 return (cmd_find_best_winlink_with_window(fs));
200 fail:
201 free(slist);
202 return (-1);
206 * Find the best winlink for a window (the current if it contains the window,
207 * otherwise the first).
209 static int
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);
216 wl = NULL;
217 if (fs->s->curw != NULL && fs->s->curw->window == fs->w)
218 wl = fs->s->curw;
219 else {
220 RB_FOREACH(wl_loop, winlinks, &fs->s->windows) {
221 if (wl_loop->window == fs->w) {
222 wl = wl_loop;
223 break;
227 if (wl == NULL)
228 return (-1);
229 fs->wl = wl;
230 fs->idx = fs->wl->idx;
231 return (0);
234 /* Maps string in table. */
235 static const char *
236 cmd_find_map_table(const char *table[][2], const char *s)
238 u_int i;
240 for (i = 0; table[i][0] != NULL; i++) {
241 if (strcmp(s, table[i][0]) == 0)
242 return (table[i][1]);
244 return (s);
247 /* Find session from string. Fills in s. */
248 static int
249 cmd_find_get_session(struct cmd_find_state *fs, const char *session)
251 struct session *s, *s_loop;
252 struct client *c;
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);
259 if (fs->s == NULL)
260 return (-1);
261 return (0);
264 /* Look for exactly this session. */
265 fs->s = session_find(session);
266 if (fs->s != NULL)
267 return (0);
269 /* Look for as a client. */
270 c = cmd_find_client(NULL, session, 1);
271 if (c != NULL && c->session != NULL) {
272 fs->s = c->session;
273 return (0);
276 /* Stop now if exact only. */
277 if (fs->flags & CMD_FIND_EXACT_SESSION)
278 return (-1);
280 /* Otherwise look for prefix. */
281 s = NULL;
282 RB_FOREACH(s_loop, sessions, &sessions) {
283 if (strncmp(session, s_loop->name, strlen(session)) == 0) {
284 if (s != NULL)
285 return (-1);
286 s = s_loop;
289 if (s != NULL) {
290 fs->s = s;
291 return (0);
294 /* Then as a pattern. */
295 s = NULL;
296 RB_FOREACH(s_loop, sessions, &sessions) {
297 if (fnmatch(session, s_loop->name, 0) == 0) {
298 if (s != NULL)
299 return (-1);
300 s = s_loop;
303 if (s != NULL) {
304 fs->s = s;
305 return (0);
308 return (-1);
311 /* Find window from string. Fills in s, wl, w. */
312 static int
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);
320 if (fs->w == NULL)
321 return (-1);
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)
330 return (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;
338 return (0);
341 return (-1);
345 * Find window from string, assuming it is in given session. Needs s, fills in
346 * wl and w.
348 static int
349 cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
351 struct winlink *wl;
352 const char *errstr;
353 int idx, n, exact;
354 struct session *s;
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))
370 return (-1);
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);
378 else
379 n = 1;
380 s = fs->s;
381 if (fs->flags & CMD_FIND_WINDOW_INDEX) {
382 if (window[0] == '+') {
383 if (INT_MAX - s->curw->idx < n)
384 return (-1);
385 fs->idx = s->curw->idx + n;
386 } else {
387 if (n > s->curw->idx)
388 return (-1);
389 fs->idx = s->curw->idx - n;
391 return (0);
393 if (window[0] == '+')
394 fs->wl = winlink_next_by_number(s->curw, s, n);
395 else
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;
400 return (0);
404 /* Try special characters. */
405 if (!exact) {
406 if (strcmp(window, "!") == 0) {
407 fs->wl = TAILQ_FIRST(&fs->s->lastw);
408 if (fs->wl == NULL)
409 return (-1);
410 fs->idx = fs->wl->idx;
411 fs->w = fs->wl->window;
412 return (0);
413 } else if (strcmp(window, "^") == 0) {
414 fs->wl = RB_MIN(winlinks, &fs->s->windows);
415 if (fs->wl == NULL)
416 return (-1);
417 fs->idx = fs->wl->idx;
418 fs->w = fs->wl->window;
419 return (0);
420 } else if (strcmp(window, "$") == 0) {
421 fs->wl = RB_MAX(winlinks, &fs->s->windows);
422 if (fs->wl == NULL)
423 return (-1);
424 fs->idx = fs->wl->idx;
425 fs->w = fs->wl->window;
426 return (0);
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;
438 return (0);
440 if (fs->flags & CMD_FIND_WINDOW_INDEX) {
441 fs->idx = idx;
442 return (0);
447 /* Look for exact matches, error if more than one. */
448 fs->wl = NULL;
449 RB_FOREACH(wl, winlinks, &fs->s->windows) {
450 if (strcmp(window, wl->window->name) == 0) {
451 if (fs->wl != NULL)
452 return (-1);
453 fs->wl = wl;
456 if (fs->wl != NULL) {
457 fs->idx = fs->wl->idx;
458 fs->w = fs->wl->window;
459 return (0);
462 /* Stop now if exact only. */
463 if (exact)
464 return (-1);
466 /* Try as the start of a window name, error if multiple. */
467 fs->wl = NULL;
468 RB_FOREACH(wl, winlinks, &fs->s->windows) {
469 if (strncmp(window, wl->window->name, strlen(window)) == 0) {
470 if (fs->wl != NULL)
471 return (-1);
472 fs->wl = wl;
475 if (fs->wl != NULL) {
476 fs->idx = fs->wl->idx;
477 fs->w = fs->wl->window;
478 return (0);
481 /* Now look for pattern matches, again error if multiple. */
482 fs->wl = NULL;
483 RB_FOREACH(wl, winlinks, &fs->s->windows) {
484 if (fnmatch(window, wl->window->name, 0) == 0) {
485 if (fs->wl != NULL)
486 return (-1);
487 fs->wl = wl;
490 if (fs->wl != NULL) {
491 fs->idx = fs->wl->idx;
492 fs->w = fs->wl->window;
493 return (0);
496 return (-1);
499 /* Find pane from string. Fills in s, wl, w, wp. */
500 static int
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 %. */
506 if (*pane == '%') {
507 fs->wp = window_pane_find_by_id_str(pane);
508 if (fs->wp == NULL)
509 return (-1);
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)
522 return (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;
527 return (0);
530 return (-1);
534 * Find pane from string, assuming it is in given session. Needs s, fills in wl
535 * and w and wp.
537 static int
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 %. */
543 if (*pane == '%') {
544 fs->wp = window_pane_find_by_id_str(pane);
545 if (fs->wp == NULL)
546 return (-1);
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
562 * wp.
564 static int
565 cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
567 const char *errstr;
568 int idx;
569 struct window_pane *wp;
570 u_int n;
572 log_debug("%s: %s", __func__, pane);
574 /* Check for pane ids starting with %. */
575 if (*pane == '%') {
576 fs->wp = window_pane_find_by_id_str(pane);
577 if (fs->wp == NULL)
578 return (-1);
579 if (fs->wp->window != fs->w)
580 return (-1);
581 return (0);
584 /* Try special characters. */
585 if (strcmp(pane, "!") == 0) {
586 fs->wp = TAILQ_FIRST(&fs->w->last_panes);
587 if (fs->wp == NULL)
588 return (-1);
589 return (0);
590 } else if (strcmp(pane, "{up-of}") == 0) {
591 fs->wp = window_pane_find_up(fs->w->active);
592 if (fs->wp == NULL)
593 return (-1);
594 return (0);
595 } else if (strcmp(pane, "{down-of}") == 0) {
596 fs->wp = window_pane_find_down(fs->w->active);
597 if (fs->wp == NULL)
598 return (-1);
599 return (0);
600 } else if (strcmp(pane, "{left-of}") == 0) {
601 fs->wp = window_pane_find_left(fs->w->active);
602 if (fs->wp == NULL)
603 return (-1);
604 return (0);
605 } else if (strcmp(pane, "{right-of}") == 0) {
606 fs->wp = window_pane_find_right(fs->w->active);
607 if (fs->wp == NULL)
608 return (-1);
609 return (0);
612 /* Try as an offset. */
613 if (pane[0] == '+' || pane[0] == '-') {
614 if (pane[1] != '\0')
615 n = strtonum(pane + 1, 1, INT_MAX, NULL);
616 else
617 n = 1;
618 wp = fs->w->active;
619 if (pane[0] == '+')
620 fs->wp = window_pane_next_by_number(fs->w, wp, n);
621 else
622 fs->wp = window_pane_previous_by_number(fs->w, wp, n);
623 if (fs->wp != NULL)
624 return (0);
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);
631 if (fs->wp != NULL)
632 return (0);
635 /* Try as a description. */
636 fs->wp = window_find_string(fs->w, pane);
637 if (fs->wp != NULL)
638 return (0);
640 return (-1);
643 /* Clear state. */
644 void
645 cmd_find_clear_state(struct cmd_find_state *fs, int flags)
647 memset(fs, 0, sizeof *fs);
649 fs->flags = flags;
651 fs->idx = -1;
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)
659 return (1);
660 return (0);
663 /* Check if a state if valid. */
665 cmd_find_valid_state(struct cmd_find_state *fs)
667 struct winlink *wl;
669 if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL)
670 return (0);
672 if (!session_alive(fs->s))
673 return (0);
675 RB_FOREACH(wl, winlinks, &fs->s->windows) {
676 if (wl->window == fs->w && wl == fs->wl)
677 break;
679 if (wl == NULL)
680 return (0);
682 if (fs->w != fs->wl->window)
683 return (0);
685 return (window_has_pane(fs->w, fs->wp));
688 /* Copy a state. */
689 void
690 cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src)
692 dst->s = src->s;
693 dst->wl = src->wl;
694 dst->idx = src->idx;
695 dst->w = src->w;
696 dst->wp = src->wp;
699 /* Log the result. */
700 static void
701 cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
703 if (fs->s != NULL)
704 log_debug("%s: s=$%u %s", prefix, fs->s->id, fs->s->name);
705 else
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);
710 } else
711 log_debug("%s: wl=none", prefix);
712 if (fs->wp != NULL)
713 log_debug("%s: wp=%%%u", prefix, fs->wp->id);
714 else
715 log_debug("%s: wp=none", prefix);
716 if (fs->idx != -1)
717 log_debug("%s: idx=%d", prefix, fs->idx);
718 else
719 log_debug("%s: idx=none", prefix);
722 /* Find state from a session. */
723 void
724 cmd_find_from_session(struct cmd_find_state *fs, struct session *s, int flags)
726 cmd_find_clear_state(fs, flags);
728 fs->s = s;
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. */
737 void
738 cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl, int flags)
740 cmd_find_clear_state(fs, flags);
742 fs->s = wl->session;
743 fs->wl = wl;
744 fs->w = wl->window;
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);
757 fs->s = s;
758 fs->w = w;
759 if (cmd_find_best_winlink_with_window(fs) != 0) {
760 cmd_find_clear_state(fs, flags);
761 return (-1);
763 fs->wp = fs->w->active;
765 cmd_find_log_state(__func__, fs);
766 return (0);
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);
775 fs->w = w;
776 if (cmd_find_best_session_with_window(fs) != 0) {
777 cmd_find_clear_state(fs, flags);
778 return (-1);
780 if (cmd_find_best_winlink_with_window(fs) != 0) {
781 cmd_find_clear_state(fs, flags);
782 return (-1);
784 fs->wp = fs->w->active;
786 cmd_find_log_state(__func__, fs);
787 return (0);
790 /* Find state from a winlink and pane. */
791 void
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);
797 fs->s = wl->session;
798 fs->wl = wl;
799 fs->idx = fs->wl->idx;
800 fs->w = fs->wl->window;
801 fs->wp = wp;
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)
811 return (-1);
812 fs->wp = wp;
814 cmd_find_log_state(__func__, fs);
815 return (0);
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);
825 if (fs->s == NULL) {
826 cmd_find_clear_state(fs, flags);
827 return (-1);
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);
835 return (0);
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);
844 if (!m->valid)
845 return (-1);
847 fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
848 if (fs->wp == NULL) {
849 cmd_find_clear_state(fs, flags);
850 return (-1);
852 fs->w = fs->wl->window;
854 cmd_find_log_state(__func__, fs);
855 return (0);
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. */
865 if (c == NULL)
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);
875 return (0);
877 fs->s = c->session;
878 fs->wl = fs->s->curw;
879 fs->w = fs->wl->window;
881 cmd_find_log_state(__func__, fs);
882 return (0);
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);
891 if (wp == NULL)
892 goto unknown_pane;
895 * Don't have a session, or it doesn't have this pane. Try all
896 * sessions.
898 fs->w = wp->window;
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.
905 goto unknown_pane;
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);
912 return (0);
914 unknown_pane:
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)
939 s = "pane";
940 else if (type == CMD_FIND_WINDOW)
941 s = "window";
942 else if (type == CMD_FIND_SESSION)
943 s = "session";
944 else
945 s = "unknown";
946 *tmp = '\0';
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);
961 if (*tmp != '\0')
962 tmp[strlen(tmp) - 1] = '\0';
963 else
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(&current, cmdq_get_client(item),
979 flags) == 0) {
980 fs->current = &current;
981 log_debug("%s: current is from client", __func__);
982 } else {
983 if (~flags & CMD_FIND_QUIET)
984 cmdq_error(item, "no current target");
985 goto error;
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')
992 goto current;
994 /* Mouse target is a plain = or {mouse}. */
995 if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
996 m = &cmdq_get_event(item)->m;
997 switch (type) {
998 case CMD_FIND_PANE:
999 fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
1000 if (fs->wp != NULL) {
1001 fs->w = fs->wl->window;
1002 break;
1004 /* FALLTHROUGH */
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;
1014 break;
1016 if (fs->wp == NULL) {
1017 if (~flags & CMD_FIND_QUIET)
1018 cmdq_error(item, "no mouse target");
1019 goto error;
1021 goto found;
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");
1029 goto error;
1031 cmd_find_copy_state(fs, &marked_pane);
1032 goto found;
1035 /* Find separators if they exist. */
1036 copy = xstrdup(target);
1037 colon = strchr(copy, ':');
1038 if (colon != NULL)
1039 *colon++ = '\0';
1040 if (colon == NULL)
1041 period = strchr(copy, '.');
1042 else
1043 period = strchr(colon, '.');
1044 if (period != NULL)
1045 *period++ = '\0';
1047 /* Set session, window and pane parts. */
1048 session = window = pane = NULL;
1049 if (colon != NULL && period != NULL) {
1050 session = copy;
1051 window = colon;
1052 window_only = 1;
1053 pane = period;
1054 pane_only = 1;
1055 } else if (colon != NULL && period == NULL) {
1056 session = copy;
1057 window = colon;
1058 window_only = 1;
1059 } else if (colon == NULL && period != NULL) {
1060 window = copy;
1061 pane = period;
1062 pane_only = 1;
1063 } else {
1064 if (*copy == '$')
1065 session = copy;
1066 else if (*copy == '@')
1067 window = copy;
1068 else if (*copy == '%')
1069 pane = copy;
1070 else {
1071 switch (type) {
1072 case CMD_FIND_SESSION:
1073 session = copy;
1074 break;
1075 case CMD_FIND_WINDOW:
1076 window = copy;
1077 break;
1078 case CMD_FIND_PANE:
1079 pane = copy;
1080 break;
1085 /* Set exact match flags. */
1086 if (session != NULL && *session == '=') {
1087 session++;
1088 fs->flags |= CMD_FIND_EXACT_SESSION;
1090 if (window != NULL && *window == '=') {
1091 window++;
1092 fs->flags |= CMD_FIND_EXACT_WINDOW;
1095 /* Empty is the same as NULL. */
1096 if (session != NULL && *session == '\0')
1097 session = NULL;
1098 if (window != NULL && *window == '\0')
1099 window = NULL;
1100 if (pane != NULL && *pane == '\0')
1101 pane = NULL;
1103 /* Map though conversion table. */
1104 if (session != NULL)
1105 session = cmd_find_map_table(cmd_find_session_table, session);
1106 if (window != NULL)
1107 window = cmd_find_map_table(cmd_find_window_table, window);
1108 if (pane != NULL)
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",
1113 __func__, target,
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");
1126 goto error;
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)
1133 goto no_session;
1135 /* If window and pane are NULL, use that session's current. */
1136 if (window == NULL && pane == NULL) {
1137 fs->wl = fs->s->curw;
1138 fs->idx = -1;
1139 fs->w = fs->wl->window;
1140 fs->wp = fs->w->active;
1141 goto found;
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)
1148 goto no_window;
1149 if (fs->wl != NULL) /* can be NULL if index only */
1150 fs->wp = fs->wl->window->active;
1151 goto found;
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)
1158 goto no_pane;
1159 goto found;
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)
1167 goto no_window;
1168 /* This will fill in pane. */
1169 if (cmd_find_get_pane_with_window(fs, pane) != 0)
1170 goto no_pane;
1171 goto found;
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)
1178 goto no_window;
1179 /* This will fill in pane. */
1180 if (cmd_find_get_pane_with_window(fs, pane) != 0)
1181 goto no_pane;
1182 goto found;
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)
1189 goto no_window;
1190 if (fs->wl != NULL) /* can be NULL if index only */
1191 fs->wp = fs->wl->window->active;
1192 goto found;
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)
1199 goto no_pane;
1200 goto found;
1203 current:
1204 /* Use the current session. */
1205 cmd_find_copy_state(fs, fs->current);
1206 if (flags & CMD_FIND_WINDOW_INDEX)
1207 fs->idx = -1;
1208 goto found;
1210 error:
1211 fs->current = NULL;
1212 log_debug("%s: error", __func__);
1214 free(copy);
1215 if (flags & CMD_FIND_CANFAIL)
1216 return (0);
1217 return (-1);
1219 found:
1220 fs->current = NULL;
1221 cmd_find_log_state(__func__, fs);
1223 free(copy);
1224 return (0);
1226 no_session:
1227 if (~flags & CMD_FIND_QUIET)
1228 cmdq_error(item, "can't find session: %s", session);
1229 goto error;
1231 no_window:
1232 if (~flags & CMD_FIND_QUIET)
1233 cmdq_error(item, "can't find window: %s", window);
1234 goto error;
1236 no_pane:
1237 if (~flags & CMD_FIND_QUIET)
1238 cmdq_error(item, "can't find pane: %s", pane);
1239 goto error;
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;
1247 struct session *s;
1248 struct window_pane *wp;
1249 struct cmd_find_state fs;
1251 if (item != NULL)
1252 c = cmdq_get_client(item);
1253 if (c != NULL && c->session != NULL)
1254 return (c);
1256 found = NULL;
1257 if (c != NULL && (wp = cmd_find_inside_pane(c)) != NULL) {
1258 cmd_find_clear_state(&fs, CMD_FIND_QUIET);
1259 fs.w = wp->window;
1260 if (cmd_find_best_session_with_window(&fs) == 0)
1261 found = cmd_find_best_client(fs.s);
1262 } else {
1263 s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
1264 if (s != NULL)
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);
1270 return (found);
1273 /* Find the target client or report an error and return NULL. */
1274 struct client *
1275 cmd_find_client(struct cmdq_item *item, const char *target, int quiet)
1277 struct client *c;
1278 char *copy;
1279 size_t size;
1281 /* A NULL argument means the current client. */
1282 if (target == NULL)
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)
1294 continue;
1295 if (strcmp(copy, c->name) == 0)
1296 break;
1298 if (*c->ttyname == '\0')
1299 continue;
1300 if (strcmp(copy, c->ttyname) == 0)
1301 break;
1302 if (strncmp(c->ttyname, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
1303 continue;
1304 if (strcmp(copy, c->ttyname + (sizeof _PATH_DEV) - 1) == 0)
1305 break;
1308 /* If no client found, report an error. */
1309 if (c == NULL && !quiet)
1310 cmdq_error(item, "can't find client: %s", copy);
1312 free(copy);
1313 log_debug("%s: target %s, return %p", __func__, target, c);
1314 return (c);