Annotation of src/usr.bin/tmux/cmd-find.c, Revision 1.14
1.14 ! nicm 1: /* $OpenBSD: cmd-find.c,v 1.13 2015/09/14 13:22:02 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2015 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
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.
9: *
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.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <fnmatch.h>
22: #include <limits.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <paths.h>
26:
27: #include "tmux.h"
28:
29: #define CMD_FIND_PREFER_UNATTACHED 0x1
30: #define CMD_FIND_QUIET 0x2
31: #define CMD_FIND_WINDOW_INDEX 0x4
1.8 nicm 32: #define CMD_FIND_DEFAULT_MARKED 0x8
1.9 nicm 33: #define CMD_FIND_EXACT_SESSION 0x10
34: #define CMD_FIND_EXACT_WINDOW 0x20
1.1 nicm 35:
36: enum cmd_find_type {
37: CMD_FIND_PANE,
38: CMD_FIND_WINDOW,
39: CMD_FIND_SESSION,
40: };
41:
42: struct cmd_find_state {
43: struct cmd_q *cmdq;
44: int flags;
45: struct cmd_find_state *current;
46:
47: struct session *s;
48: struct winlink *wl;
49: struct window *w;
50: struct window_pane *wp;
51: int idx;
52: };
53:
54: int cmd_find_client_better(struct client *, struct client *);
55: struct client *cmd_find_best_client(struct client **, u_int);
56: int cmd_find_session_better(struct session *, struct session *,
57: int);
58: struct session *cmd_find_best_session(struct session **, u_int, int);
59: int cmd_find_best_session_with_window(struct cmd_find_state *);
60: int cmd_find_best_winlink_with_window(struct cmd_find_state *);
61:
62: int cmd_find_current_session_with_client(struct cmd_find_state *);
63: int cmd_find_current_session(struct cmd_find_state *);
64: struct client *cmd_find_current_client(struct cmd_q *);
65:
66: const char *cmd_find_map_table(const char *[][2], const char *);
67:
68: int cmd_find_get_session(struct cmd_find_state *, const char *);
69: int cmd_find_get_window(struct cmd_find_state *, const char *);
70: int cmd_find_get_window_with_session(struct cmd_find_state *, const char *);
71: int cmd_find_get_window_with_pane(struct cmd_find_state *);
72: int cmd_find_get_pane(struct cmd_find_state *, const char *);
73: int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *);
74: int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *);
75:
76: void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int);
77: void cmd_find_log_state(const char *, const char *, struct cmd_find_state *);
78:
79: struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *,
80: enum cmd_find_type, int);
81:
82: const char *cmd_find_session_table[][2] = {
83: { NULL, NULL }
84: };
85: const char *cmd_find_window_table[][2] = {
86: { "{start}", "^" },
87: { "{last}", "!" },
88: { "{end}", "$" },
89: { "{next}", "+" },
90: { "{previous}", "-" },
91: { NULL, NULL }
92: };
93: const char *cmd_find_pane_table[][2] = {
94: { "{last}", "!" },
95: { "{next}", "+" },
96: { "{previous}", "-" },
97: { "{top}", "top" },
98: { "{bottom}", "bottom" },
99: { "{left}", "left" },
100: { "{right}", "right" },
101: { "{top-left}", "top-left" },
102: { "{top-right}", "top-right" },
103: { "{bottom-left}", "bottom-left" },
104: { "{bottom-right}", "bottom-right" },
1.12 nicm 105: { "{up-of}", "{up-of}" },
106: { "{down-of}", "{down-of}" },
107: { "{left-of}", "{left-of}" },
108: { "{right-of}", "{right-of}" },
1.1 nicm 109: { NULL, NULL }
110: };
111:
112: /* Is this client better? */
113: int
114: cmd_find_client_better(struct client *c, struct client *than)
115: {
116: if (than == NULL)
117: return (1);
118: return (timercmp(&c->activity_time, &than->activity_time, >));
119: }
120:
121: /* Find best client from a list, or all if list is NULL. */
122: struct client *
123: cmd_find_best_client(struct client **clist, u_int csize)
124: {
125: struct client *c_loop, *c;
126: u_int i;
127:
128: c = NULL;
129: if (clist != NULL) {
130: for (i = 0; i < csize; i++) {
1.3 nicm 131: if (clist[i]->session == NULL)
132: continue;
1.1 nicm 133: if (cmd_find_client_better(clist[i], c))
134: c = clist[i];
135: }
136: } else {
137: TAILQ_FOREACH(c_loop, &clients, entry) {
1.3 nicm 138: if (c_loop->session == NULL)
139: continue;
1.1 nicm 140: if (cmd_find_client_better(c_loop, c))
1.2 nicm 141: c = c_loop;
1.1 nicm 142: }
143: }
144: return (c);
145: }
146:
147: /* Is this session better? */
148: int
149: cmd_find_session_better(struct session *s, struct session *than, int flags)
150: {
151: int attached;
152:
153: if (than == NULL)
154: return (1);
155: if (flags & CMD_FIND_PREFER_UNATTACHED) {
156: attached = (~than->flags & SESSION_UNATTACHED);
157: if (attached && (s->flags & SESSION_UNATTACHED))
158: return (1);
159: else if (!attached && (~s->flags & SESSION_UNATTACHED))
160: return (0);
161: }
162: return (timercmp(&s->activity_time, &than->activity_time, >));
163: }
164:
165: /* Find best session from a list, or all if list is NULL. */
166: struct session *
167: cmd_find_best_session(struct session **slist, u_int ssize, int flags)
168: {
169: struct session *s_loop, *s;
170: u_int i;
171:
172: s = NULL;
173: if (slist != NULL) {
174: for (i = 0; i < ssize; i++) {
175: if (cmd_find_session_better(slist[i], s, flags))
176: s = slist[i];
177: }
178: } else {
179: RB_FOREACH(s_loop, sessions, &sessions) {
180: if (cmd_find_session_better(s_loop, s, flags))
181: s = s_loop;
182: }
183: }
184: return (s);
185: }
186:
187: /* Find best session and winlink for window. */
188: int
189: cmd_find_best_session_with_window(struct cmd_find_state *fs)
190: {
191: struct session **slist = NULL;
192: u_int ssize;
193: struct session *s;
194:
195: ssize = 0;
196: RB_FOREACH(s, sessions, &sessions) {
197: if (!session_has(s, fs->w))
198: continue;
1.7 nicm 199: slist = xreallocarray(slist, ssize + 1, sizeof *slist);
1.1 nicm 200: slist[ssize++] = s;
201: }
202: if (ssize == 0)
203: goto fail;
204: fs->s = cmd_find_best_session(slist, ssize, fs->flags);
205: if (fs->s == NULL)
206: goto fail;
1.7 nicm 207: free(slist);
1.1 nicm 208: return (cmd_find_best_winlink_with_window(fs));
209:
210: fail:
211: free(slist);
212: return (-1);
213: }
214:
215: /*
216: * Find the best winlink for a window (the current if it contains the pane,
217: * otherwise the first).
218: */
219: int
220: cmd_find_best_winlink_with_window(struct cmd_find_state *fs)
221: {
222: struct winlink *wl, *wl_loop;
223:
224: wl = NULL;
225: if (fs->s->curw->window == fs->w)
226: wl = fs->s->curw;
227: else {
228: RB_FOREACH(wl_loop, winlinks, &fs->s->windows) {
229: if (wl_loop->window == fs->w) {
230: wl = wl_loop;
231: break;
232: }
233: }
234: }
235: if (wl == NULL)
236: return (-1);
237: fs->wl = wl;
238: fs->idx = fs->wl->idx;
239: return (0);
240: }
241:
242: /* Find current session when we have an unattached client. */
243: int
244: cmd_find_current_session_with_client(struct cmd_find_state *fs)
245: {
246: struct window_pane *wp;
247:
248: /* If this is running in a pane, that's great. */
1.5 nicm 249: if (fs->cmdq->client->tty.path != NULL) {
250: RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
251: if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0)
252: break;
253: }
254: } else
255: wp = NULL;
1.1 nicm 256:
257: /* Not running in a pane. We know nothing. Find the best session. */
1.14 ! nicm 258: if (wp == NULL)
! 259: goto unknown_pane;
1.1 nicm 260:
261: /* We now know the window and pane. */
262: fs->w = wp->window;
263: fs->wp = wp;
264:
265: /* Find the best session and winlink. */
1.14 ! nicm 266: if (cmd_find_best_session_with_window(fs) != 0) {
! 267: if (wp != NULL) {
! 268: /*
! 269: * The window may have been destroyed but the pane
! 270: * still on all_window_panes due to something else
! 271: * holding a reference.
! 272: */
! 273: goto unknown_pane;
! 274: }
! 275: return (-1);
! 276: }
! 277: return (0);
! 278:
! 279: unknown_pane:
! 280: fs->s = cmd_find_best_session(NULL, 0, fs->flags);
! 281: if (fs->s == NULL)
1.1 nicm 282: return (-1);
1.14 ! nicm 283: fs->wl = fs->s->curw;
! 284: fs->idx = fs->wl->idx;
! 285: fs->w = fs->wl->window;
! 286: fs->wp = fs->w->active;
1.1 nicm 287: return (0);
288: }
289:
290: /*
291: * Work out the best current state. If this function succeeds, the state is
292: * guaranteed to be completely filled in.
293: */
294: int
295: cmd_find_current_session(struct cmd_find_state *fs)
296: {
297: /* If we know the current client, use it. */
298: if (fs->cmdq->client != NULL) {
299: if (fs->cmdq->client->session == NULL)
300: return (cmd_find_current_session_with_client(fs));
301: fs->s = fs->cmdq->client->session;
302: fs->wl = fs->s->curw;
303: fs->idx = fs->wl->idx;
304: fs->w = fs->wl->window;
305: fs->wp = fs->w->active;
306: return (0);
307: }
308:
309: /* We know nothing, find the best session and client. */
310: fs->s = cmd_find_best_session(NULL, 0, fs->flags);
311: if (fs->s == NULL)
312: return (-1);
313: fs->wl = fs->s->curw;
314: fs->idx = fs->wl->idx;
315: fs->w = fs->wl->window;
316: fs->wp = fs->w->active;
317:
318: return (0);
319: }
320:
321: /* Work out the best current client. */
322: struct client *
323: cmd_find_current_client(struct cmd_q *cmdq)
324: {
325: struct cmd_find_state current;
326: struct session *s;
327: struct client *c, **clist = NULL;
328: u_int csize;
329:
330: /* If the queue client has a session, use it. */
331: if (cmdq->client != NULL && cmdq->client->session != NULL)
332: return (cmdq->client);
333:
334: /* Otherwise find the current session. */
335: cmd_find_clear_state(¤t, cmdq, 0);
336: if (cmd_find_current_session(¤t) != 0)
337: return (NULL);
338:
339: /* If it is attached, find the best of it's clients. */
340: s = current.s;
341: if (~s->flags & SESSION_UNATTACHED) {
342: csize = 0;
343: TAILQ_FOREACH(c, &clients, entry) {
344: if (c->session != s)
345: continue;
1.7 nicm 346: clist = xreallocarray(clist, csize + 1, sizeof *clist);
1.1 nicm 347: clist[csize++] = c;
348: }
349: if (csize != 0) {
350: c = cmd_find_best_client(clist, csize);
351: if (c != NULL) {
352: free(clist);
353: return (c);
354: }
355: }
356: free(clist);
357: }
358:
359: /* Otherwise pick best of all clients. */
360: return (cmd_find_best_client(NULL, 0));
361: }
362:
363: /* Maps string in table. */
364: const char *
365: cmd_find_map_table(const char *table[][2], const char *s)
366: {
367: u_int i;
368:
369: for (i = 0; table[i][0] != NULL; i++) {
370: if (strcmp(s, table[i][0]) == 0)
371: return (table[i][1]);
372: }
373: return (s);
374: }
375:
376: /* Find session from string. Fills in s. */
377: int
378: cmd_find_get_session(struct cmd_find_state *fs, const char *session)
379: {
380: struct session *s, *s_loop;
381:
382: log_debug("%s: %s", __func__, session);
383:
384: /* Check for session ids starting with $. */
385: if (*session == '$') {
386: fs->s = session_find_by_id_str(session);
387: if (fs->s == NULL)
388: return (-1);
389: return (0);
390: }
391:
392: /* Look for exactly this session. */
393: fs->s = session_find(session);
394: if (fs->s != NULL)
395: return (0);
396:
1.9 nicm 397: /* Stop now if exact only. */
398: if (fs->flags & CMD_FIND_EXACT_SESSION)
399: return (-1);
400:
1.1 nicm 401: /* Otherwise look for prefix. */
402: s = NULL;
403: RB_FOREACH(s_loop, sessions, &sessions) {
404: if (strncmp(session, s_loop->name, strlen(session)) == 0) {
405: if (s != NULL)
406: return (-1);
407: s = s_loop;
408: }
409: }
410: if (s != NULL) {
411: fs->s = s;
412: return (0);
413: }
414:
415: /* Then as a pattern. */
416: s = NULL;
417: RB_FOREACH(s_loop, sessions, &sessions) {
418: if (fnmatch(session, s_loop->name, 0) == 0) {
419: if (s != NULL)
420: return (-1);
421: s = s_loop;
422: }
423: }
424: if (s != NULL) {
425: fs->s = s;
426: return (0);
427: }
428:
429: return (-1);
430: }
431:
432: /* Find window from string. Fills in s, wl, w. */
433: int
434: cmd_find_get_window(struct cmd_find_state *fs, const char *window)
435: {
436: log_debug("%s: %s", __func__, window);
437:
438: /* Check for window ids starting with @. */
439: if (*window == '@') {
440: fs->w = window_find_by_id_str(window);
441: if (fs->w == NULL)
442: return (-1);
443: return (cmd_find_best_session_with_window(fs));
444: }
445:
446: /* Not a window id, so use the current session. */
447: fs->s = fs->current->s;
448:
449: /* We now only need to find the winlink in this session. */
1.4 nicm 450: if (cmd_find_get_window_with_session(fs, window) == 0)
451: return (0);
452:
453: /* Otherwise try as a session itself. */
454: if (cmd_find_get_session(fs, window) == 0) {
1.6 nicm 455: if (~fs->flags & CMD_FIND_WINDOW_INDEX) {
456: fs->wl = fs->s->curw;
457: fs->w = fs->wl->window;
458: fs->idx = fs->wl->idx;
459: }
1.4 nicm 460: return (0);
461: }
462:
463: return (-1);
1.1 nicm 464: }
465:
466: /*
467: * Find window from string, assuming it is in given session. Needs s, fills in
468: * wl and w.
469: */
470: int
471: cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
472: {
473: struct winlink *wl;
474: const char *errstr;
1.9 nicm 475: int idx, n, exact;
1.1 nicm 476: struct session *s;
477:
478: log_debug("%s: %s", __func__, window);
1.9 nicm 479: exact = (fs->flags & CMD_FIND_EXACT_WINDOW);
1.1 nicm 480:
481: /* Check for window ids starting with @. */
482: if (*window == '@') {
483: fs->w = window_find_by_id_str(window);
484: if (fs->w == NULL || !session_has(fs->s, fs->w))
485: return (-1);
486: return (cmd_find_best_winlink_with_window(fs));
487: }
488:
489: /* Try as an offset. */
1.10 nicm 490: if (!exact && (window[0] == '+' || window[0] == '-')) {
1.1 nicm 491: if (window[1] != '\0')
492: n = strtonum(window + 1, 1, INT_MAX, NULL);
493: else
494: n = 1;
495: s = fs->s;
496: if (fs->flags & CMD_FIND_WINDOW_INDEX) {
497: if (window[0] == '+') {
498: if (INT_MAX - s->curw->idx < n)
499: return (-1);
500: fs->idx = s->curw->idx + n;
501: } else {
502: if (n < s->curw->idx)
503: return (-1);
504: fs->idx = s->curw->idx - n;
505: }
506: return (0);
507: }
508: if (window[0] == '+')
509: fs->wl = winlink_next_by_number(s->curw, s, n);
510: else
511: fs->wl = winlink_previous_by_number(s->curw, s, n);
512: if (fs->wl != NULL) {
513: fs->idx = fs->wl->idx;
514: fs->w = fs->wl->window;
515: return (0);
516: }
517: }
518:
519: /* Try special characters. */
1.9 nicm 520: if (!exact) {
521: if (strcmp(window, "!") == 0) {
522: fs->wl = TAILQ_FIRST(&fs->s->lastw);
523: if (fs->wl == NULL)
524: return (-1);
525: fs->idx = fs->wl->idx;
526: fs->w = fs->wl->window;
527: return (0);
528: } else if (strcmp(window, "^") == 0) {
529: fs->wl = RB_MIN(winlinks, &fs->s->windows);
530: if (fs->wl == NULL)
531: return (-1);
532: fs->idx = fs->wl->idx;
533: fs->w = fs->wl->window;
534: return (0);
535: } else if (strcmp(window, "$") == 0) {
536: fs->wl = RB_MAX(winlinks, &fs->s->windows);
537: if (fs->wl == NULL)
538: return (-1);
539: fs->idx = fs->wl->idx;
540: fs->w = fs->wl->window;
541: return (0);
542: }
1.1 nicm 543: }
544:
545: /* First see if this is a valid window index in this session. */
1.9 nicm 546: if (window[0] != '+' && window[0] != '-') {
547: idx = strtonum(window, 0, INT_MAX, &errstr);
548: if (errstr == NULL) {
549: if (fs->flags & CMD_FIND_WINDOW_INDEX) {
550: fs->idx = idx;
551: return (0);
552: }
553: fs->wl = winlink_find_by_index(&fs->s->windows, idx);
554: if (fs->wl != NULL) {
555: fs->w = fs->wl->window;
556: return (0);
557: }
1.1 nicm 558: }
559: }
560:
561: /* Look for exact matches, error if more than one. */
562: fs->wl = NULL;
563: RB_FOREACH(wl, winlinks, &fs->s->windows) {
564: if (strcmp(window, wl->window->name) == 0) {
565: if (fs->wl != NULL)
566: return (-1);
567: fs->wl = wl;
568: }
569: }
570: if (fs->wl != NULL) {
571: fs->idx = fs->wl->idx;
572: fs->w = fs->wl->window;
573: return (0);
574: }
1.9 nicm 575:
576: /* Stop now if exact only. */
577: if (exact)
578: return (-1);
579:
1.1 nicm 580: /* Try as the start of a window name, error if multiple. */
581: fs->wl = NULL;
582: RB_FOREACH(wl, winlinks, &fs->s->windows) {
583: if (strncmp(window, wl->window->name, strlen(window)) == 0) {
584: if (fs->wl != NULL)
585: return (-1);
586: fs->wl = wl;
587: }
588: }
589: if (fs->wl != NULL) {
590: fs->idx = fs->wl->idx;
591: fs->w = fs->wl->window;
592: return (0);
593: }
594:
595: /* Now look for pattern matches, again error if multiple. */
596: fs->wl = NULL;
597: RB_FOREACH(wl, winlinks, &fs->s->windows) {
598: if (fnmatch(window, wl->window->name, 0) == 0) {
599: if (fs->wl != NULL)
600: return (-1);
601: fs->wl = wl;
602: }
603: }
604: if (fs->wl != NULL) {
605: fs->idx = fs->wl->idx;
606: fs->w = fs->wl->window;
607: return (0);
608: }
609:
610: return (-1);
611: }
612:
613: /* Find window from given pane. Needs wp, fills in s and wl and w. */
614: int
615: cmd_find_get_window_with_pane(struct cmd_find_state *fs)
616: {
617: log_debug("%s", __func__);
618:
619: fs->w = fs->wp->window;
620: return (cmd_find_best_session_with_window(fs));
621: }
622:
623: /* Find pane from string. Fills in s, wl, w, wp. */
624: int
625: cmd_find_get_pane(struct cmd_find_state *fs, const char *pane)
626: {
627: log_debug("%s: %s", __func__, pane);
628:
629: /* Check for pane ids starting with %. */
630: if (*pane == '%') {
631: fs->wp = window_pane_find_by_id_str(pane);
632: if (fs->wp == NULL)
633: return (-1);
634: fs->w = fs->wp->window;
635: return (cmd_find_best_session_with_window(fs));
636: }
637:
1.4 nicm 638: /* Not a pane id, so try the current session and window. */
1.1 nicm 639: fs->s = fs->current->s;
640: fs->wl = fs->current->wl;
641: fs->idx = fs->current->idx;
642: fs->w = fs->current->w;
643:
644: /* We now only need to find the pane in this window. */
1.4 nicm 645: if (cmd_find_get_pane_with_window(fs, pane) == 0)
646: return (0);
647:
648: /* Otherwise try as a window itself (this will also try as session). */
649: if (cmd_find_get_window(fs, pane) == 0) {
650: fs->wp = fs->w->active;
651: return (0);
652: }
653:
654: return (-1);
1.1 nicm 655: }
656:
657: /*
658: * Find pane from string, assuming it is in given session. Needs s, fills in wl
659: * and w and wp.
660: */
661: int
662: cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane)
663: {
664: log_debug("%s: %s", __func__, pane);
665:
666: /* Check for pane ids starting with %. */
667: if (*pane == '%') {
668: fs->wp = window_pane_find_by_id_str(pane);
669: if (fs->wp == NULL)
670: return (-1);
671: fs->w = fs->wp->window;
672: return (cmd_find_best_winlink_with_window(fs));
673: }
674:
675: /* Otherwise use the current window. */
676: fs->wl = fs->s->curw;
677: fs->idx = fs->wl->idx;
678: fs->w = fs->wl->window;
679:
680: /* Now we just need to look up the pane. */
681: return (cmd_find_get_pane_with_window(fs, pane));
682: }
683:
684: /*
685: * Find pane from string, assuming it is in the given window. Needs w, fills in
686: * wp.
687: */
688: int
689: cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
690: {
691: const char *errstr;
692: int idx;
693: struct window_pane *wp;
694: u_int n;
695:
696: log_debug("%s: %s", __func__, pane);
697:
698: /* Check for pane ids starting with %. */
699: if (*pane == '%') {
700: fs->wp = window_pane_find_by_id_str(pane);
701: if (fs->wp == NULL || fs->wp->window != fs->w)
702: return (-1);
703: return (0);
704: }
705:
706: /* Try special characters. */
707: if (strcmp(pane, "!") == 0) {
708: if (fs->w->last == NULL)
709: return (-1);
710: fs->wp = fs->w->last;
711: return (0);
1.12 nicm 712: } else if (strcmp(pane, "{up-of}") == 0) {
1.1 nicm 713: fs->wp = window_pane_find_up(fs->w->active);
714: if (fs->wp == NULL)
715: return (-1);
716: return (0);
1.12 nicm 717: } else if (strcmp(pane, "{down-of}") == 0) {
1.1 nicm 718: fs->wp = window_pane_find_down(fs->w->active);
719: if (fs->wp == NULL)
720: return (-1);
721: return (0);
1.12 nicm 722: } else if (strcmp(pane, "{left-of}") == 0) {
1.1 nicm 723: fs->wp = window_pane_find_left(fs->w->active);
724: if (fs->wp == NULL)
725: return (-1);
726: return (0);
1.12 nicm 727: } else if (strcmp(pane, "{right-of}") == 0) {
1.1 nicm 728: fs->wp = window_pane_find_right(fs->w->active);
729: if (fs->wp == NULL)
730: return (-1);
731: return (0);
732: }
733:
734: /* Try as an offset. */
735: if (pane[0] == '+' || pane[0] == '-') {
736: if (pane[1] != '\0')
737: n = strtonum(pane + 1, 1, INT_MAX, NULL);
738: else
739: n = 1;
740: wp = fs->w->active;
741: if (pane[0] == '+')
742: fs->wp = window_pane_next_by_number(fs->w, wp, n);
743: else
744: fs->wp = window_pane_previous_by_number(fs->w, wp, n);
745: if (fs->wp != NULL)
746: return (0);
747: }
748:
749: /* Get pane by index. */
750: idx = strtonum(pane, 0, INT_MAX, &errstr);
751: if (errstr == NULL) {
752: fs->wp = window_pane_at_index(fs->w, idx);
753: if (fs->wp != NULL)
754: return (0);
755: }
756:
757: /* Try as a description. */
758: fs->wp = window_find_string(fs->w, pane);
759: if (fs->wp != NULL)
760: return (0);
761:
762: return (-1);
763: }
764:
765: /* Clear state. */
766: void
767: cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags)
768: {
1.7 nicm 769: memset(fs, 0, sizeof *fs);
1.1 nicm 770:
771: fs->cmdq = cmdq;
772: fs->flags = flags;
773:
774: fs->idx = -1;
775: }
776:
777: /* Split target into pieces and resolve for the given type. */
778: struct cmd_find_state *
779: cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type,
780: int flags)
781: {
782: static struct cmd_find_state fs, current;
783: struct mouse_event *m;
784: char *colon, *period, *copy = NULL;
785: const char *session, *window, *pane;
786:
787: /* Find current state. */
788: cmd_find_clear_state(¤t, cmdq, flags);
1.8 nicm 789: if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) {
790: current.s = marked_session;
791: current.wl = marked_winlink;
792: current.idx = current.wl->idx;
793: current.w = current.wl->window;
794: current.wp = marked_window_pane;
795: }
796: if (current.s == NULL && cmd_find_current_session(¤t) != 0) {
1.1 nicm 797: if (~flags & CMD_FIND_QUIET)
798: cmdq_error(cmdq, "no current session");
799: goto error;
800: }
801:
802: /* Clear new state. */
803: cmd_find_clear_state(&fs, cmdq, flags);
804: fs.current = ¤t;
805:
806: /* An empty or NULL target is the current. */
807: if (target == NULL || *target == '\0')
808: goto current;
809:
810: /* Mouse target is a plain = or {mouse}. */
811: if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
812: m = &cmdq->item->mouse;
813: switch (type) {
814: case CMD_FIND_PANE:
815: fs.wp = cmd_mouse_pane(m, &fs.s, &fs.wl);
816: if (fs.wp != NULL)
817: fs.w = fs.wl->window;
818: break;
819: case CMD_FIND_WINDOW:
820: case CMD_FIND_SESSION:
821: fs.wl = cmd_mouse_window(m, &fs.s);
822: if (fs.wl != NULL) {
823: fs.w = fs.wl->window;
824: fs.wp = fs.w->active;
825: }
826: break;
827: }
828: if (fs.wp == NULL) {
829: if (~flags & CMD_FIND_QUIET)
830: cmdq_error(cmdq, "no mouse target");
831: goto error;
832: }
833: return (&fs);
834: }
1.8 nicm 835:
836: /* Marked target is a plain ~ or {marked}. */
837: if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) {
838: if (!server_check_marked()) {
839: if (~flags & CMD_FIND_QUIET)
840: cmdq_error(cmdq, "no marked target");
841: goto error;
842: }
843: fs.s = marked_session;
844: fs.wl = marked_winlink;
845: fs.idx = fs.wl->idx;
846: fs.w = fs.wl->window;
847: fs.wp = marked_window_pane;
848: return (&fs);
849: }
1.1 nicm 850:
851: /* Find separators if they exist. */
1.8 nicm 852: copy = xstrdup(target);
1.1 nicm 853: colon = strchr(copy, ':');
854: if (colon != NULL)
855: *colon++ = '\0';
856: if (colon == NULL)
857: period = strchr(copy, '.');
858: else
859: period = strchr(colon, '.');
860: if (period != NULL)
861: *period++ = '\0';
862:
863: /* Set session, window and pane parts. */
864: session = window = pane = NULL;
865: if (colon != NULL && period != NULL) {
866: session = copy;
867: window = colon;
868: pane = period;
869: } else if (colon != NULL && period == NULL) {
870: session = copy;
871: window = colon;
872: } else if (colon == NULL && period != NULL) {
873: window = copy;
874: pane = period;
875: } else {
876: if (*copy == '$')
877: session = copy;
878: else if (*copy == '@')
879: window = copy;
880: else if (*copy == '%')
881: pane = copy;
882: else {
883: switch (type) {
884: case CMD_FIND_SESSION:
885: session = copy;
886: break;
887: case CMD_FIND_WINDOW:
888: window = copy;
889: break;
890: case CMD_FIND_PANE:
891: pane = copy;
892: break;
893: }
894: }
1.9 nicm 895: }
896:
897: /* Set exact match flags. */
898: if (session != NULL && *session == '=') {
899: session++;
900: fs.flags |= CMD_FIND_EXACT_SESSION;
901: }
902: if (window != NULL && *window == '=') {
903: window++;
904: fs.flags |= CMD_FIND_EXACT_WINDOW;
1.1 nicm 905: }
906:
907: /* Empty is the same as NULL. */
908: if (session != NULL && *session == '\0')
909: session = NULL;
910: if (window != NULL && *window == '\0')
911: window = NULL;
912: if (pane != NULL && *pane == '\0')
913: pane = NULL;
914:
915: /* Map though conversion table. */
916: if (session != NULL)
917: session = cmd_find_map_table(cmd_find_session_table, session);
918: if (window != NULL)
919: window = cmd_find_map_table(cmd_find_window_table, window);
920: if (pane != NULL)
921: pane = cmd_find_map_table(cmd_find_pane_table, pane);
922:
923: log_debug("target %s (flags %#x): session=%s, window=%s, pane=%s",
924: target, flags, session == NULL ? "none" : session,
925: window == NULL ? "none" : window, pane == NULL ? "none" : pane);
926:
927: /* No pane is allowed if want an index. */
928: if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) {
929: if (~flags & CMD_FIND_QUIET)
930: cmdq_error(cmdq, "can't specify pane here");
931: goto error;
932: }
933:
934: /* If the session isn't NULL, look it up. */
935: if (session != NULL) {
936: /* This will fill in session. */
937: if (cmd_find_get_session(&fs, session) != 0)
938: goto no_session;
939:
940: /* If window and pane are NULL, use that session's current. */
941: if (window == NULL && pane == NULL) {
942: fs.wl = fs.s->curw;
943: fs.idx = -1;
944: fs.w = fs.wl->window;
945: fs.wp = fs.w->active;
946: goto found;
947: }
948:
949: /* If window is present but pane not, find window in session. */
950: if (window != NULL && pane == NULL) {
951: /* This will fill in winlink and window. */
952: if (cmd_find_get_window_with_session(&fs, window) != 0)
953: goto no_window;
954: if (~flags & CMD_FIND_WINDOW_INDEX)
955: fs.wp = fs.wl->window->active;
956: goto found;
957: }
958:
959: /* If pane is present but window not, find pane. */
960: if (window == NULL && pane != NULL) {
961: /* This will fill in winlink and window and pane. */
962: if (cmd_find_get_pane_with_session(&fs, pane) != 0)
963: goto no_pane;
964: goto found;
965: }
966:
967: /*
968: * If window and pane are present, find both in session. This
969: * will fill in winlink and window.
970: */
971: if (cmd_find_get_window_with_session(&fs, window) != 0)
972: goto no_window;
973: /* This will fill in pane. */
974: if (cmd_find_get_pane_with_window(&fs, pane) != 0)
975: goto no_pane;
976: goto found;
977: }
978:
979: /* No session. If window and pane, try them. */
980: if (window != NULL && pane != NULL) {
981: /* This will fill in session, winlink and window. */
982: if (cmd_find_get_window(&fs, window) != 0)
983: goto no_window;
984: /* This will fill in pane. */
985: if (cmd_find_get_pane_with_window(&fs, pane) != 0)
986: goto no_pane;
987: goto found;
988: }
989:
990: /* If just window is present, try it. */
991: if (window != NULL && pane == NULL) {
992: /* This will fill in session, winlink and window. */
993: if (cmd_find_get_window(&fs, window) != 0)
994: goto no_window;
995: if (~flags & CMD_FIND_WINDOW_INDEX)
996: fs.wp = fs.wl->window->active;
997: goto found;
998: }
999:
1000: /* If just pane is present, try it. */
1001: if (window == NULL && pane != NULL) {
1002: /* This will fill in session, winlink, window and pane. */
1003: if (cmd_find_get_pane(&fs, pane) != 0)
1004: goto no_pane;
1005: goto found;
1006: }
1007:
1008: current:
1009: /* None is the current session. */
1010: free(copy);
1011: if (flags & CMD_FIND_WINDOW_INDEX)
1012: current.idx = -1;
1013: return (¤t);
1014:
1015: error:
1016: free(copy);
1017: return (NULL);
1018:
1019: found:
1020: free(copy);
1021: return (&fs);
1022:
1023: no_session:
1024: if (~flags & CMD_FIND_QUIET)
1025: cmdq_error(cmdq, "can't find session %s", session);
1026: goto error;
1027:
1028: no_window:
1029: if (~flags & CMD_FIND_QUIET)
1030: cmdq_error(cmdq, "can't find window %s", window);
1031: goto error;
1032:
1033: no_pane:
1034: if (~flags & CMD_FIND_QUIET)
1035: cmdq_error(cmdq, "can't find pane %s", pane);
1036: goto error;
1037: }
1038:
1039: /* Log the result. */
1040: void
1041: cmd_find_log_state(const char *f, const char *target, struct cmd_find_state *fs)
1042: {
1043: log_debug("%s: target %s%s", f, target == NULL ? "none" : target,
1044: fs != NULL ? "" : " (failed)");
1045: if (fs == NULL)
1046: return;
1047: if (fs->s != NULL)
1048: log_debug("\ts=$%u", fs->s->id);
1049: else
1050: log_debug("\ts=none");
1051: if (fs->wl != NULL) {
1052: log_debug("\twl=%u %d w=@%u %s", fs->wl->idx,
1053: fs->wl->window == fs->w, fs->w->id, fs->w->name);
1054: } else
1055: log_debug("\twl=none");
1056: if (fs->wp != NULL)
1057: log_debug("\twp=%%%u", fs->wp->id);
1058: else
1059: log_debug("\twp=none");
1060: if (fs->idx != -1)
1061: log_debug("\tidx=%d", fs->idx);
1062: else
1063: log_debug("\tidx=none");
1064: }
1065:
1066: /* Find the current session. */
1067: struct session *
1068: cmd_find_current(struct cmd_q *cmdq)
1069: {
1070: struct cmd_find_state *fs;
1071: int flags = CMD_FIND_QUIET;
1072:
1073: fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags);
1074: cmd_find_log_state(__func__, NULL, fs);
1075: if (fs == NULL)
1076: return (NULL);
1077:
1078: return (fs->s);
1079: }
1080:
1081: /* Find the target session or report an error and return NULL. */
1082: struct session *
1083: cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached)
1084: {
1085: struct cmd_find_state *fs;
1086: int flags = 0;
1087:
1088: if (prefer_unattached)
1089: flags |= CMD_FIND_PREFER_UNATTACHED;
1090:
1091: fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags);
1092: cmd_find_log_state(__func__, target, fs);
1093: if (fs == NULL)
1094: return (NULL);
1095:
1096: return (fs->s);
1097: }
1098:
1099: /* Find the target window or report an error and return NULL. */
1100: struct winlink *
1101: cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp)
1102: {
1103: struct cmd_find_state *fs;
1104:
1105: fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0);
1106: cmd_find_log_state(__func__, target, fs);
1107: if (fs == NULL)
1108: return (NULL);
1109:
1110: if (sp != NULL)
1111: *sp = fs->s;
1112: return (fs->wl);
1113: }
1114:
1.8 nicm 1115: /* Find the target window, defaulting to marked rather than current. */
1116: struct winlink *
1117: cmd_find_window_marked(struct cmd_q *cmdq, const char *target,
1118: struct session **sp)
1119: {
1120: struct cmd_find_state *fs;
1121: int flags = CMD_FIND_DEFAULT_MARKED;
1122:
1123: fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags);
1124: cmd_find_log_state(__func__, target, fs);
1125: if (fs == NULL)
1126: return (NULL);
1127:
1128: if (sp != NULL)
1129: *sp = fs->s;
1130: return (fs->wl);
1131: }
1132:
1.1 nicm 1133: /* Find the target pane and report an error and return NULL. */
1134: struct winlink *
1135: cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp,
1136: struct window_pane **wpp)
1137: {
1138: struct cmd_find_state *fs;
1139:
1140: fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0);
1.8 nicm 1141: cmd_find_log_state(__func__, target, fs);
1142: if (fs == NULL)
1143: return (NULL);
1144:
1145: if (sp != NULL)
1146: *sp = fs->s;
1147: if (wpp != NULL)
1148: *wpp = fs->wp;
1149: return (fs->wl);
1150: }
1151:
1152: /* Find the target pane, defaulting to marked rather than current. */
1153: struct winlink *
1154: cmd_find_pane_marked(struct cmd_q *cmdq, const char *target,
1155: struct session **sp, struct window_pane **wpp)
1156: {
1157: struct cmd_find_state *fs;
1158: int flags = CMD_FIND_DEFAULT_MARKED;
1159:
1160: fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags);
1.1 nicm 1161: cmd_find_log_state(__func__, target, fs);
1162: if (fs == NULL)
1163: return (NULL);
1164:
1165: if (sp != NULL)
1166: *sp = fs->s;
1167: if (wpp != NULL)
1168: *wpp = fs->wp;
1169: return (fs->wl);
1170: }
1171:
1172: /* Find the target client or report an error and return NULL. */
1173: struct client *
1174: cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet)
1175: {
1176: struct client *c;
1177: char *copy;
1178: size_t size;
1179: const char *path;
1180:
1181: /* A NULL argument means the current client. */
1182: if (target == NULL) {
1183: c = cmd_find_current_client(cmdq);
1184: if (c == NULL && !quiet)
1185: cmdq_error(cmdq, "no current client");
1186: return (c);
1187: }
1188: copy = xstrdup(target);
1189:
1190: /* Trim a single trailing colon if any. */
1191: size = strlen(copy);
1192: if (size != 0 && copy[size - 1] == ':')
1193: copy[size - 1] = '\0';
1194:
1195: /* Check path of each client. */
1196: TAILQ_FOREACH(c, &clients, entry) {
1197: if (c->session == NULL || c->tty.path == NULL)
1198: continue;
1199: path = c->tty.path;
1200:
1201: /* Try for exact match. */
1202: if (strcmp(copy, path) == 0)
1203: break;
1204:
1205: /* Try without leading /dev. */
1206: if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
1207: continue;
1208: if (strcmp(copy, path + (sizeof _PATH_DEV) - 1) == 0)
1209: break;
1210: }
1211:
1212: /* If no client found, report an error. */
1213: if (c == NULL && !quiet)
1214: cmdq_error(cmdq, "can't find client %s", copy);
1215:
1216: free(copy);
1217: return (c);
1218: }
1219:
1220: /*
1221: * Find the target session and window index, whether or not it exists in the
1222: * session. Return -2 on error or -1 if no window index is specified. This is
1223: * used when parsing an argument for a window target that may not exist (for
1224: * example if it is going to be created).
1225: */
1226: int
1227: cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp)
1228: {
1229: struct cmd_find_state *fs;
1230: int flags = CMD_FIND_WINDOW_INDEX;
1231:
1232: fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags);
1233: cmd_find_log_state(__func__, target, fs);
1234: if (fs == NULL)
1235: return (-2);
1236:
1237: if (sp != NULL)
1238: *sp = fs->s;
1239: return (fs->idx);
1240: }