[BACK]Return to cmd-find.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/cmd-find.c, Revision 1.1

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