Rewrite of the target resolution internals to be simpler and more

consistent but with much less duplication, but keeping the same internal
API. Also adds more readable aliases for some of the special tokens used
in targets (eg "{start}" instead of "^"). Some behaviours may have
changed, for example prefix matches now happen before fnmatch.
This commit is contained in:
nicm 2015-04-27 16:25:57 +00:00
parent a70762c9b5
commit 95195f5258
13 changed files with 1250 additions and 1027 deletions

View File

@ -69,6 +69,7 @@ SRCS= arguments.c \
cmd-unbind-key.c \
cmd-wait-for.c \
cmd.c \
cmd-find.c \
cmd-queue.c \
colour.c \
control.c \

View File

@ -53,7 +53,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
u_int idx;
int utf8flag;
if ((c = cmd_current_client(cmdq)) == NULL) {
if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) {
cmdq_error(cmdq, "no client available");
return (CMD_RETURN_ERROR);
}

View File

@ -61,7 +61,7 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq)
char *action;
u_int idx, cur;
if ((c = cmd_current_client(cmdq)) == NULL) {
if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) {
cmdq_error(cmdq, "no client available");
return (CMD_RETURN_ERROR);
}

View File

@ -87,7 +87,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq)
ses_template = win_template = NULL;
ses_action = win_action = NULL;
if ((c = cmd_current_client(cmdq)) == NULL) {
if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) {
cmdq_error(cmdq, "no client available");
return (CMD_RETURN_ERROR);
}

View File

@ -78,7 +78,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq)
if (c == NULL)
return (CMD_RETURN_ERROR);
} else {
c = cmd_current_client(cmdq);
c = cmd_find_client(cmdq, NULL, 1);
if (c == NULL && !args_has(self->args, 'p')) {
cmdq_error(cmdq, "no client available");
return (CMD_RETURN_ERROR);

View File

@ -143,7 +143,7 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq)
const char *template;
u_int i, match_flags;
if ((c = cmd_current_client(cmdq)) == NULL) {
if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) {
cmdq_error(cmdq, "no client available");
return (CMD_RETURN_ERROR);
}

1114
cmd-find.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
if (c != NULL && c->session == NULL)
cwd = c->cwd;
else if ((s = cmd_current_session(cmdq, 0)) != NULL)
else if ((s = cmd_find_current(cmdq)) != NULL)
cwd = s->cwd;
else
cwd = AT_FDCWD;

View File

@ -137,7 +137,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
cwd = fd;
} else if (c != NULL && c->session == NULL)
cwd = c->cwd;
else if ((c0 = cmd_current_client(cmdq)) != NULL)
else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL)
cwd = c0->session->cwd;
else {
fd = open(".", O_RDONLY);

View File

@ -93,7 +93,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
if (c != NULL && c->session == NULL)
cwd = c->cwd;
else if ((s = cmd_current_session(cmdq, 0)) != NULL)
else if ((s = cmd_find_current(cmdq)) != NULL)
cwd = s->cwd;
else
cwd = AT_FDCWD;

937
cmd.c
View File

@ -116,24 +116,6 @@ const struct cmd_entry *cmd_table[] = {
NULL
};
ARRAY_DECL(client_list, struct client *);
ARRAY_DECL(sessionslist, struct session *);
int cmd_session_better(struct session *, struct session *, int);
struct session *cmd_choose_session_list(struct sessionslist *);
struct session *cmd_choose_session(int);
struct client *cmd_choose_client(struct client_list *);
struct client *cmd_lookup_client(const char *);
struct session *cmd_lookup_session(struct cmd_q *, const char *, int *);
struct winlink *cmd_lookup_window(struct session *, const char *, int *);
int cmd_lookup_index(struct session *, const char *, int *);
struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *);
struct session *cmd_window_session(struct cmd_q *, struct window *,
struct winlink **);
struct winlink *cmd_find_window_offset(const char *, struct session *, int *);
int cmd_find_index_offset(const char *, struct session *, int *);
struct window_pane *cmd_find_pane_offset(const char *, struct winlink *);
int
cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
{
@ -332,183 +314,6 @@ cmd_print(struct cmd *cmd, char *buf, size_t len)
return (off);
}
/*
* Figure out the current session. Use: 1) the current session, if the command
* context has one; 2) the most recently used session containing the pty of the
* calling client, if any; 3) the session specified in the TMUX variable from
* the environment (as passed from the client); 4) the most recently used
* session from all sessions.
*/
struct session *
cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
{
struct client *c = cmdq->client;
struct session *s;
struct sessionslist ss;
struct winlink *wl;
struct window_pane *wp;
const char *path;
int found;
/* Try the queue session. */
if (c != NULL && c->session != NULL)
return (c->session);
/*
* If the name of the calling client's pty is known, build a list of
* the sessions that contain it and if any choose either the first or
* the newest.
*/
path = c == NULL ? NULL : c->tty.path;
if (path != NULL) {
ARRAY_INIT(&ss);
RB_FOREACH(s, sessions, &sessions) {
found = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (strcmp(wp->tty, path) == 0) {
found = 1;
break;
}
}
if (found)
break;
}
if (found)
ARRAY_ADD(&ss, s);
}
s = cmd_choose_session_list(&ss);
ARRAY_FREE(&ss);
if (s != NULL)
return (s);
}
return (cmd_choose_session(prefer_unattached));
}
/* Is this session better? */
int
cmd_session_better(struct session *s, struct session *best,
int prefer_unattached)
{
if (best == NULL)
return (1);
if (prefer_unattached) {
if (!(best->flags & SESSION_UNATTACHED) &&
(s->flags & SESSION_UNATTACHED))
return (1);
else if ((best->flags & SESSION_UNATTACHED) &&
!(s->flags & SESSION_UNATTACHED))
return (0);
}
return (timercmp(&s->activity_time, &best->activity_time, >));
}
/*
* Find the most recently used session, preferring unattached if the flag is
* set.
*/
struct session *
cmd_choose_session(int prefer_unattached)
{
struct session *s, *best;
best = NULL;
RB_FOREACH(s, sessions, &sessions) {
if (cmd_session_better(s, best, prefer_unattached))
best = s;
}
return (best);
}
/* Find the most recently used session from a list. */
struct session *
cmd_choose_session_list(struct sessionslist *ss)
{
struct session *s, *sbest;
struct timeval *tv = NULL;
u_int i;
sbest = NULL;
for (i = 0; i < ARRAY_LENGTH(ss); i++) {
if ((s = ARRAY_ITEM(ss, i)) == NULL)
continue;
if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
sbest = s;
tv = &s->activity_time;
}
}
return (sbest);
}
/*
* Find the current client. First try the current client if set, then pick the
* most recently used of the clients attached to the current session if any,
* then of all clients.
*/
struct client *
cmd_current_client(struct cmd_q *cmdq)
{
struct session *s;
struct client *c;
struct client_list cc;
if (cmdq->client != NULL && cmdq->client->session != NULL)
return (cmdq->client);
/*
* No current client set. Find the current session and return the
* newest of its clients.
*/
s = cmd_current_session(cmdq, 0);
if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
ARRAY_INIT(&cc);
TAILQ_FOREACH(c, &clients, entry) {
if (s == c->session)
ARRAY_ADD(&cc, c);
}
c = cmd_choose_client(&cc);
ARRAY_FREE(&cc);
if (c != NULL)
return (c);
}
ARRAY_INIT(&cc);
TAILQ_FOREACH(c, &clients, entry)
ARRAY_ADD(&cc, c);
c = cmd_choose_client(&cc);
ARRAY_FREE(&cc);
return (c);
}
/* Choose the most recently used client from a list. */
struct client *
cmd_choose_client(struct client_list *cc)
{
struct client *c, *cbest;
struct timeval *tv = NULL;
u_int i;
cbest = NULL;
for (i = 0; i < ARRAY_LENGTH(cc); i++) {
if ((c = ARRAY_ITEM(cc, i)) == NULL)
continue;
if (c->session == NULL)
continue;
if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
cbest = c;
tv = &c->activity_time;
}
}
return (cbest);
}
/* Adjust current mouse position for a pane. */
int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
@ -560,7 +365,8 @@ cmd_mouse_window(struct mouse_event *m, struct session **sp)
/* Get current mouse pane if any. */
struct window_pane *
cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp)
cmd_mouse_pane(struct mouse_event *m, struct session **sp,
struct winlink **wlp)
{
struct winlink *wl;
struct window_pane *wp;
@ -577,745 +383,6 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp)
return (wp);
}
/* Find the target client or report an error and return NULL. */
struct client *
cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet)
{
struct client *c;
char *tmparg;
size_t arglen;
/* A NULL argument means the current client. */
if (arg == NULL) {
c = cmd_current_client(cmdq);
if (c == NULL && !quiet)
cmdq_error(cmdq, "no clients");
return (c);
}
tmparg = xstrdup(arg);
/* Trim a single trailing colon if any. */
arglen = strlen(tmparg);
if (arglen != 0 && tmparg[arglen - 1] == ':')
tmparg[arglen - 1] = '\0';
/* Find the client, if any. */
c = cmd_lookup_client(tmparg);
/* If no client found, report an error. */
if (c == NULL && !quiet)
cmdq_error(cmdq, "client not found: %s", tmparg);
free(tmparg);
return (c);
}
/*
* Lookup a client by device path. Either of a full match and a match without a
* leading _PATH_DEV ("/dev/") is accepted.
*/
struct client *
cmd_lookup_client(const char *name)
{
struct client *c;
const char *path;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || c->tty.path == NULL)
continue;
path = c->tty.path;
/* Check for exact matches. */
if (strcmp(name, path) == 0)
return (c);
/* Check without leading /dev if present. */
if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
continue;
if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
return (c);
}
return (NULL);
}
/* Lookup a session by name. If no session is found, NULL is returned. */
struct session *
cmd_lookup_session(struct cmd_q *cmdq, const char *name, int *ambiguous)
{
struct session *s, *sfound;
struct window *w;
struct window_pane *wp;
*ambiguous = 0;
/* Look for $id first. */
if ((s = session_find_by_id_str(name)) != NULL)
return (s);
/* Try as pane or window id. */
if ((wp = window_pane_find_by_id_str(name)) != NULL)
return (cmd_window_session(cmdq, wp->window, NULL));
if ((w = window_find_by_id_str(name)) != NULL)
return (cmd_window_session(cmdq, w, NULL));
/*
* Look for matches. First look for exact matches - session names must
* be unique so an exact match can't be ambigious and can just be
* returned.
*/
if ((s = session_find(name)) != NULL)
return (s);
/*
* Otherwise look for partial matches, returning early if it is found to
* be ambiguous.
*/
sfound = NULL;
RB_FOREACH(s, sessions, &sessions) {
if (strncmp(name, s->name, strlen(name)) == 0 ||
fnmatch(name, s->name, 0) == 0) {
if (sfound != NULL) {
*ambiguous = 1;
return (NULL);
}
sfound = s;
}
}
return (sfound);
}
/*
* Lookup a window or return -1 if not found or ambigious. First try as an
* index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
* idx if the window index is a valid number but there is no window with that
* index.
*/
struct winlink *
cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
{
struct winlink *wl, *wlfound;
struct window *w;
struct window_pane *wp;
const char *errstr;
u_int idx;
*ambiguous = 0;
/* Try as pane or window id. */
if ((wl = cmd_lookup_winlink_windowid(s, name)) != NULL)
return (wl);
/* Lookup as pane or window id. */
if ((wp = window_pane_find_by_id_str(name)) != NULL) {
wl = winlink_find_by_window(&s->windows, wp->window);
if (wl != NULL)
return (wl);
}
if ((w = window_find_by_id_str(name)) != NULL) {
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL)
return (wl);
}
/* First see if this is a valid window index in this session. */
idx = strtonum(name, 0, INT_MAX, &errstr);
if (errstr == NULL) {
if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
return (wl);
}
/* Look for exact matches, error if more than one. */
wlfound = NULL;
RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(name, wl->window->name) == 0) {
if (wlfound != NULL) {
*ambiguous = 1;
return (NULL);
}
wlfound = wl;
}
}
if (wlfound != NULL)
return (wlfound);
/* Now look for pattern matches, again error if multiple. */
wlfound = NULL;
RB_FOREACH(wl, winlinks, &s->windows) {
if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
fnmatch(name, wl->window->name, 0) == 0) {
if (wlfound != NULL) {
*ambiguous = 1;
return (NULL);
}
wlfound = wl;
}
}
if (wlfound != NULL)
return (wlfound);
return (NULL);
}
/*
* Find a window index - if the window doesn't exist, check if it is a
* potential index and return it anyway.
*/
int
cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
{
struct winlink *wl;
const char *errstr;
u_int idx;
idx = strtonum(name, 0, INT_MAX, &errstr);
if (errstr == NULL)
return (idx);
if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
return (wl->idx);
if (*ambiguous)
return (-1);
return (-1);
}
/* Lookup window id in a session. An initial @ means a window id. */
struct winlink *
cmd_lookup_winlink_windowid(struct session *s, const char *arg)
{
const char *errstr;
u_int windowid;
if (*arg != '@')
return (NULL);
windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (NULL);
return (winlink_find_by_window_id(&s->windows, windowid));
}
/* Find session and winlink for window. */
struct session *
cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp)
{
struct session *s;
struct sessionslist ss;
struct winlink *wl;
/* If this window is in the current session, return that winlink. */
s = cmd_current_session(cmdq, 0);
if (s != NULL) {
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL) {
if (wlp != NULL)
*wlp = wl;
return (s);
}
}
/* Otherwise choose from all sessions with this window. */
ARRAY_INIT(&ss);
RB_FOREACH(s, sessions, &sessions) {
if (winlink_find_by_window(&s->windows, w) != NULL)
ARRAY_ADD(&ss, s);
}
s = cmd_choose_session_list(&ss);
ARRAY_FREE(&ss);
if (wlp != NULL)
*wlp = winlink_find_by_window(&s->windows, w);
return (s);
}
/* Find the target session or report an error and return NULL. */
struct session *
cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached)
{
struct session *s;
struct client *c;
char *tmparg;
size_t arglen;
int ambiguous;
/* A NULL argument means the current session. */
if (arg == NULL) {
if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL)
cmdq_error(cmdq, "can't establish current session");
return (s);
}
/* Trim a single trailing colon if any. */
tmparg = xstrdup(arg);
arglen = strlen(tmparg);
if (arglen != 0 && tmparg[arglen - 1] == ':')
tmparg[arglen - 1] = '\0';
/* An empty session name is the current session. */
if (*tmparg == '\0') {
free(tmparg);
if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL)
cmdq_error(cmdq, "can't establish current session");
return (s);
}
/* Find the session, if any. */
s = cmd_lookup_session(cmdq, tmparg, &ambiguous);
/* If it doesn't, try to match it as a client. */
if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
s = c->session;
/* If no session found, report an error. */
if (s == NULL) {
if (ambiguous)
cmdq_error(cmdq, "more than one session: %s", tmparg);
else
cmdq_error(cmdq, "session not found: %s", tmparg);
}
free(tmparg);
return (s);
}
/* Find the target session and window or report an error and return NULL. */
struct winlink *
cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp)
{
struct session *s;
struct winlink *wl;
const char *winptr;
char *sessptr = NULL;
int ambiguous = 0;
/*
* Find the current session. There must always be a current session, if
* it can't be found, report an error.
*/
if ((s = cmd_current_session(cmdq, 0)) == NULL) {
cmdq_error(cmdq, "can't establish current session");
return (NULL);
}
/* A NULL argument means the current session and window. */
if (arg == NULL) {
if (sp != NULL)
*sp = s;
return (s->curw);
}
/* Time to look at the argument. If it is empty, that is an error. */
if (*arg == '\0')
goto not_found;
/* Find the separating colon and split into window and session. */
winptr = strchr(arg, ':');
if (winptr == NULL)
goto no_colon;
winptr++; /* skip : */
sessptr = xstrdup(arg);
*strchr(sessptr, ':') = '\0';
/* Try to lookup the session if present. */
if (*sessptr != '\0') {
if ((s = cmd_lookup_session(cmdq, sessptr, &ambiguous)) == NULL)
goto no_session;
}
if (sp != NULL)
*sp = s;
/*
* Then work out the window. An empty string is the current window,
* otherwise try special cases then to look it up in the session.
*/
if (*winptr == '\0')
wl = s->curw;
else if (winptr[0] == '!' && winptr[1] == '\0')
wl = TAILQ_FIRST(&s->lastw);
else if (winptr[0] == '^' && winptr[1] == '\0')
wl = RB_MIN(winlinks, &s->windows);
else if (winptr[0] == '$' && winptr[1] == '\0')
wl = RB_MAX(winlinks, &s->windows);
else if (winptr[0] == '+' || winptr[0] == '-')
wl = cmd_find_window_offset(winptr, s, &ambiguous);
else
wl = cmd_lookup_window(s, winptr, &ambiguous);
if (wl == NULL)
goto not_found;
if (sessptr != NULL)
free(sessptr);
return (wl);
no_colon:
/*
* No colon in the string, first try special cases, then as a window
* and lastly as a session.
*/
if (arg[0] == '=' && arg[1] == '\0') {
if ((wl = cmd_mouse_window(&cmdq->item->mouse, &s)) == NULL) {
cmdq_error(cmdq, "no mouse target");
goto error;
}
} else if (arg[0] == '!' && arg[1] == '\0') {
if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
goto not_found;
} else if (arg[0] == '+' || arg[0] == '-') {
if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
goto lookup_session;
} else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
goto lookup_session;
if (sp != NULL)
*sp = s;
return (wl);
lookup_session:
if (ambiguous)
goto not_found;
if (*arg != '\0' &&
(s = cmd_lookup_session(cmdq, arg, &ambiguous)) == NULL)
goto no_session;
if (sp != NULL)
*sp = s;
return (s->curw);
no_session:
if (ambiguous)
cmdq_error(cmdq, "multiple sessions: %s", arg);
else
cmdq_error(cmdq, "session not found: %s", arg);
goto error;
not_found:
if (ambiguous)
cmdq_error(cmdq, "multiple windows: %s", arg);
else
cmdq_error(cmdq, "window not found: %s", arg);
goto error;
error:
free(sessptr);
return (NULL);
}
struct winlink *
cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
{
struct winlink *wl;
int offset = 1;
if (winptr[1] != '\0')
offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
if (offset == 0)
wl = cmd_lookup_window(s, winptr, ambiguous);
else {
if (winptr[0] == '+')
wl = winlink_next_by_number(s->curw, s, offset);
else
wl = winlink_previous_by_number(s->curw, s, offset);
}
return (wl);
}
/*
* Find the target session and window index, whether or not it exists in the
* session. Return -2 on error or -1 if no window index is specified. This is
* used when parsing an argument for a window target that may not exist (for
* example if it is going to be created).
*/
int
cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp)
{
struct session *s;
struct winlink *wl;
const char *winptr;
char *sessptr = NULL;
int idx, ambiguous = 0;
/*
* Find the current session. There must always be a current session, if
* it can't be found, report an error.
*/
if ((s = cmd_current_session(cmdq, 0)) == NULL) {
cmdq_error(cmdq, "can't establish current session");
return (-2);
}
/* A NULL argument means the current session and "no window" (-1). */
if (arg == NULL) {
if (sp != NULL)
*sp = s;
return (-1);
}
/* Time to look at the argument. If it is empty, that is an error. */
if (*arg == '\0')
goto not_found;
/* Find the separating colon. If none, assume the current session. */
winptr = strchr(arg, ':');
if (winptr == NULL)
goto no_colon;
winptr++; /* skip : */
sessptr = xstrdup(arg);
*strchr(sessptr, ':') = '\0';
/* Try to lookup the session if present. */
if (sessptr != NULL && *sessptr != '\0') {
if ((s = cmd_lookup_session(cmdq, sessptr, &ambiguous)) == NULL)
goto no_session;
}
if (sp != NULL)
*sp = s;
/*
* Then work out the window. An empty string is a new window otherwise
* try to look it up in the session.
*/
if (*winptr == '\0')
idx = -1;
else if (winptr[0] == '!' && winptr[1] == '\0') {
if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
goto not_found;
idx = wl->idx;
} else if (winptr[0] == '+' || winptr[0] == '-') {
if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
goto invalid_index;
} else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
goto invalid_index;
free(sessptr);
return (idx);
no_colon:
/*
* No colon in the string, first try special cases, then as a window
* and lastly as a session.
*/
if (arg[0] == '!' && arg[1] == '\0') {
if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
goto not_found;
idx = wl->idx;
} else if (arg[0] == '+' || arg[0] == '-') {
if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
goto lookup_session;
} else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
goto lookup_session;
if (sp != NULL)
*sp = s;
return (idx);
lookup_session:
if (ambiguous)
goto not_found;
if (*arg != '\0' &&
(s = cmd_lookup_session(cmdq, arg, &ambiguous)) == NULL)
goto no_session;
if (sp != NULL)
*sp = s;
return (-1);
no_session:
if (ambiguous)
cmdq_error(cmdq, "multiple sessions: %s", arg);
else
cmdq_error(cmdq, "session not found: %s", arg);
free(sessptr);
return (-2);
invalid_index:
if (ambiguous)
goto not_found;
cmdq_error(cmdq, "invalid index: %s", arg);
free(sessptr);
return (-2);
not_found:
if (ambiguous)
cmdq_error(cmdq, "multiple windows: %s", arg);
else
cmdq_error(cmdq, "window not found: %s", arg);
free(sessptr);
return (-2);
}
int
cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
{
int idx, offset = 1;
if (winptr[1] != '\0')
offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
if (offset == 0)
idx = cmd_lookup_index(s, winptr, ambiguous);
else {
if (winptr[0] == '+') {
if (s->curw->idx == INT_MAX)
idx = cmd_lookup_index(s, winptr, ambiguous);
else
idx = s->curw->idx + offset;
} else {
if (s->curw->idx == 0)
idx = cmd_lookup_index(s, winptr, ambiguous);
else
idx = s->curw->idx - offset;
}
}
return (idx);
}
/*
* Find the target session, window and pane number or report an error and
* return NULL. The pane number is separated from the session:window by a .,
* such as mysession:mywindow.0.
*/
struct winlink *
cmd_find_pane(struct cmd_q *cmdq,
const char *arg, struct session **sp, struct window_pane **wpp)
{
struct session *s;
struct winlink *wl;
const char *period, *errstr;
char *winptr, *paneptr;
u_int idx;
/* Get the current session. */
if ((s = cmd_current_session(cmdq, 0)) == NULL) {
cmdq_error(cmdq, "can't establish current session");
return (NULL);
}
if (sp != NULL)
*sp = s;
/* A NULL argument means the current session, window and pane. */
if (arg == NULL) {
*wpp = s->curw->window->active;
return (s->curw);
}
/* Lookup as pane id. */
if ((*wpp = window_pane_find_by_id_str(arg)) != NULL) {
s = cmd_window_session(cmdq, (*wpp)->window, &wl);
if (sp != NULL)
*sp = s;
return (wl);
}
/* Look for a separating period. */
if ((period = strrchr(arg, '.')) == NULL)
goto no_period;
/* Pull out the window part and parse it. */
winptr = xstrdup(arg);
winptr[period - arg] = '\0';
if (*winptr == '\0')
wl = s->curw;
else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL)
goto error;
/* Find the pane section and look it up. */
paneptr = winptr + (period - arg) + 1;
if (*paneptr == '\0')
*wpp = wl->window->active;
else if (paneptr[0] == '+' || paneptr[0] == '-')
*wpp = cmd_find_pane_offset(paneptr, wl);
else if (paneptr[0] == '!' && paneptr[1] == '\0') {
if (wl->window->last == NULL) {
cmdq_error(cmdq, "no last pane");
goto error;
}
*wpp = wl->window->last;
} else {
idx = strtonum(paneptr, 0, INT_MAX, &errstr);
if (errstr != NULL)
goto lookup_string;
*wpp = window_pane_at_index(wl->window, idx);
if (*wpp == NULL)
goto lookup_string;
}
free(winptr);
return (wl);
lookup_string:
/* Try pane string description. */
if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) {
cmdq_error(cmdq, "can't find pane: %s", paneptr);
goto error;
}
free(winptr);
return (wl);
no_period:
/* Check mouse event. */
if (arg[0] == '=' && arg[1] == '\0') {
*wpp = cmd_mouse_pane(&cmdq->item->mouse, &s, &wl);
if (*wpp == NULL) {
cmdq_error(cmdq, "no mouse target");
return (NULL);
}
if (sp != NULL)
*sp = s;
return (wl);
}
/* Try as a pane number alone. */
idx = strtonum(arg, 0, INT_MAX, &errstr);
if (errstr != NULL)
goto lookup_window;
/* Try index in the current session and window. */
if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
goto lookup_window;
return (s->curw);
lookup_window:
/* Try pane string description. */
if ((*wpp = window_find_string(s->curw->window, arg)) != NULL)
return (s->curw);
/* Try as a window and use the active pane. */
if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL)
*wpp = wl->window->active;
return (wl);
error:
free(winptr);
return (NULL);
}
struct window_pane *
cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
{
struct window *w = wl->window;
struct window_pane *wp = w->active;
u_int offset = 1;
if (paneptr[1] != '\0')
offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
if (offset > 0) {
if (paneptr[0] == '+')
wp = window_pane_next_by_number(w, wp, offset);
else
wp = window_pane_previous_by_number(w, wp, offset);
}
return (wp);
}
/* Replace the first %% or %idx in template by s. */
char *
cmd_template_replace(const char *template, const char *s, int idx)

184
tmux.1
View File

@ -358,8 +358,9 @@ argument with one of
or
.Ar target-pane .
These specify the client, session, window or pane which a command should affect.
.Pp
.Ar target-client
is the name of the
should be the name of the
.Xr pty 4
file to which the client is connected, for example either of
.Pa /dev/ttyp1
@ -367,27 +368,35 @@ or
.Pa ttyp1
for the client attached to
.Pa /dev/ttyp1 .
If no client is specified, the current client is chosen, if possible, or an
error is reported.
If no client is specified,
.Nm
attempts to work out the client currently in use; if that fails, an error is
reported.
Clients may be listed with the
.Ic list-clients
command.
.Pp
.Ar target-session
is the session id prefixed with a $, the name of a session (as listed by the
is tried as, in order:
.Bl -enum -offset Ds
.It
A session ID prefixed with a $.
.It
An exact name of a session (as listed by the
.Ic list-sessions
command), or the name of a client with the same syntax as
.Ar target-client ,
in which case the session attached to the client is used.
When looking for the session name,
.Nm
initially searches for an exact match; if none is found, the session names
are checked for any for which
.Ar target-session
is a prefix or for which it matches as an
command).
.It
The start of a session name, for example
.Ql mysess
would match a session named
.Ql mysession .
.It
An
.Xr fnmatch 3
pattern.
If a single match is found, it is used as the target session; multiple matches
pattern which is matched against the session name.
.El
.Pp
If a single session is found, it is used as the target session; multiple matches
produce an error.
If a session is omitted, the current session is used if available; if no
current session is available, the most recently used is chosen.
@ -400,12 +409,29 @@ follows the same rules as for
.Ar target-session ,
and
.Em window
is looked for in order: as a window index, for example mysession:1;
as a window ID, such as @1;
as an exact window name, such as mysession:mywindow; then as an
is looked for in order as:
.Bl -enum -offset Ds
.It
A special token, listed below.
.It
A window index, for example
.Ql mysession:1
is window 1 in session
.Ql mysession .
.It
A window ID, such as @1.
.It
An exact window name, such as
.Ql mysession:mywindow .
.It
The start of a window name, such as
.Ql mysession:mywin .
.It
As an
.Xr fnmatch 3
pattern or the start of a window name, such as mysession:mywin* or
mysession:mywin.
pattern matched against the window name.
.El
.Pp
An empty window name specifies the next unused index if appropriate (for
example the
.Ic new-window
@ -415,53 +441,50 @@ commands)
otherwise the current window in
.Em session
is chosen.
The special character
.Ql \&!
uses the last (previously current) window,
.Ql ^
selects the highest numbered window,
.Ql $
selects the lowest numbered window, and
.Ql +
and
.Ql -
select the next window or the previous window by number.
When the argument does not contain a colon,
.Nm
first attempts to parse it as window; if that fails, an attempt is made to
match a session.
.Pp
The following special tokens are available to indicate particular windows. Each
has a single-character alternative form.
.Bl -column "XXXXXXXXXX" "X"
.It Sy "Token" Ta Sy "" Ta Sy "Meaning"
.It Li "{start}" Ta "^" Ta "The lowest-numbered window"
.It Li "{end}" Ta "$" Ta "The highest-numbered window"
.It Li "{last}" Ta "!" Ta "The last (previously current) window"
.It Li "{next}" Ta "+" Ta "The next window by number"
.It Li "{previous}" Ta "-" Ta "The previous window by number"
.It Li "{mouse}" Ta "=" Ta "The window where the mouse event happened"
.El
.Pp
.Ar target-pane
takes a similar form to
may be a
pane ID or takes a similar form to
.Ar target-window
but with the optional addition of a period followed by a pane index, for
example: mysession:mywindow.1.
but with the optional addition of a period followed by a pane index or pane ID,
for example:
.Ql mysession:mywindow.1 .
If the pane index is omitted, the currently active pane in the specified
window is used.
If neither a colon nor period appears,
.Nm
first attempts to use the argument as a pane index; if that fails, it is looked
up as for
.Ar target-window .
A
.Ql + ,
.Ql -
or
.Ql \&!
indicate the next, previous or last pane.
One of the strings
.Em top ,
.Em bottom ,
.Em left ,
.Em right ,
.Em top-left ,
.Em top-right ,
.Em bottom-left
or
.Em bottom-right
may be used instead of a pane index.
The following special tokens are available for the pane index:
.Bl -column "XXXXXXXXXXXXXX" "X"
.It Sy "Token" Ta Sy "" Ta Sy "Meaning"
.It Li "{last}" Ta "!" Ta "The last (previously active) pane"
.It Li "{next}" Ta "+" Ta "The next pane by number"
.It Li "{previous}" Ta "-" Ta "The previous pane by number"
.It Li "{top}" Ta "" Ta "The top pane"
.It Li "{bottom}" Ta "" Ta "The bottom pane"
.It Li "{left}" Ta "" Ta "The leftmost pane"
.It Li "{right}" Ta "" Ta "The rightmost pane"
.It Li "{top-left}" Ta "" Ta "The top-left pane"
.It Li "{top-right}" Ta "" Ta "The top-right pane"
.It Li "{bottom-left}" Ta "" Ta "The bottom-left pane"
.It Li "{bottom-right}" Ta "" Ta "The bottom-right pane"
.It Li "{up}" Ta "" Ta "The pane above the active pane"
.It Li "{down}" Ta "" Ta "The pane below the active pane"
.It Li "{left}" Ta "" Ta "The pane to the left of the active pane"
.It Li "{right}" Ta "" Ta "The pane to the right of the active pane"
.It Li "{mouse}" Ta "=" Ta "The pane where the mouse event happened"
.El
.Pp
The special characters
The tokens
.Ql +
and
.Ql -
@ -470,19 +493,34 @@ may be followed by an offset, for example:
select-window -t:+2
.Ed
.Pp
When dealing with a session that doesn't contain sequential window indexes,
they will be correctly skipped.
.Pp
Sessions, window and panes are each numbered with a unique ID; session IDs are
prefixed with a
.Ql $ ,
windows with a
.Ql @ ,
and panes with a
.Ql % .
These are unique and are unchanged for the life of the session, window or pane
in the
.Nm
also gives each pane created in a server an identifier consisting of a
.Ql %
and a number, starting from zero.
A pane's identifier is unique for the life of the
.Nm
server and is passed to the child process of the pane in the
server.
The pane ID is passed to the child process of the pane in the
.Ev TMUX_PANE
environment variable.
It may be used alone to target a pane or the window containing it.
IDs may be displayed using the
.Ql session_id ,
.Ql window_id ,
or
.Ql pane_id
formats (see the
.Sx FORMATS
section) and the
.Ic display-message ,
.Ic list-sessions ,
.Ic list-windows
or
.Ic list-panes
commands.
.Pp
.Ar shell-command
arguments are
@ -3144,7 +3182,9 @@ The following mouse events are available:
Each should be suffixed with a location, for example
.Ql MouseDown1Status .
.Pp
The special character
The special token
.Ql {mouse}
or
.Ql =
may be used as
.Ar target-window

25
tmux.h
View File

@ -1740,8 +1740,19 @@ size_t args_print(struct args *, char *, size_t);
int args_has(struct args *, u_char);
void args_set(struct args *, u_char, const char *);
const char *args_get(struct args *, u_char);
long long args_strtonum(
struct args *, u_char, long long, long long, char **);
long long args_strtonum(struct args *, u_char, long long, long long,
char **);
/* cmd-find.c */
struct session *cmd_find_current(struct cmd_q *);
struct session *cmd_find_session(struct cmd_q *, const char *, int);
struct winlink *cmd_find_window(struct cmd_q *, const char *,
struct session **);
struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **,
struct window_pane **);
struct client *cmd_find_client(struct cmd_q *, const char *, int);
int cmd_find_index(struct cmd_q *, const char *,
struct session **);
/* cmd.c */
int cmd_pack_argv(int, char **, char *, size_t);
@ -1756,16 +1767,6 @@ int cmd_mouse_at(struct window_pane *, struct mouse_event *,
struct winlink *cmd_mouse_window(struct mouse_event *, struct session **);
struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **,
struct winlink **);
struct session *cmd_current_session(struct cmd_q *, int);
struct client *cmd_current_client(struct cmd_q *);
struct client *cmd_find_client(struct cmd_q *, const char *, int);
struct session *cmd_find_session(struct cmd_q *, const char *, int);
struct winlink *cmd_find_window(struct cmd_q *, const char *,
struct session **);
int cmd_find_index(struct cmd_q *, const char *,
struct session **);
struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **,
struct window_pane **);
char *cmd_template_replace(const char *, const char *, int);
extern const struct cmd_entry *cmd_table[];
extern const struct cmd_entry cmd_attach_session_entry;