version 1.47, 2017/04/21 17:22:20 |
version 1.48, 2017/04/21 20:26:34 |
|
|
|
|
#include "tmux.h" |
#include "tmux.h" |
|
|
static struct session *cmd_find_try_TMUX(struct client *, struct window *); |
|
static int cmd_find_client_better(struct client *, struct client *); |
|
static struct client *cmd_find_best_client(struct client **, u_int); |
|
static int cmd_find_session_better(struct session *, struct session *, |
static int cmd_find_session_better(struct session *, struct session *, |
int); |
int); |
static struct session *cmd_find_best_session(struct session **, u_int, int); |
static struct session *cmd_find_best_session(struct session **, u_int, int); |
static int cmd_find_best_session_with_window(struct cmd_find_state *); |
static int cmd_find_best_session_with_window(struct cmd_find_state *); |
static int cmd_find_best_winlink_with_window(struct cmd_find_state *); |
static int cmd_find_best_winlink_with_window(struct cmd_find_state *); |
|
|
static int cmd_find_current_session_with_client(struct cmd_find_state *); |
|
static int cmd_find_current_session(struct cmd_find_state *); |
|
static struct client *cmd_find_current_client(struct cmdq_item *); |
|
|
|
static const char *cmd_find_map_table(const char *[][2], const char *); |
static const char *cmd_find_map_table(const char *[][2], const char *); |
|
|
static int cmd_find_get_session(struct cmd_find_state *, const char *); |
static int cmd_find_get_session(struct cmd_find_state *, const char *); |
|
|
|
|
/* Get session from TMUX if present. */ |
/* Get session from TMUX if present. */ |
static struct session * |
static struct session * |
cmd_find_try_TMUX(struct client *c, struct window *w) |
cmd_find_try_TMUX(struct client *c) |
{ |
{ |
struct environ_entry *envent; |
struct environ_entry *envent; |
char tmp[256]; |
char tmp[256]; |
long long pid; |
long long pid; |
u_int session; |
u_int session; |
struct session *s; |
|
|
|
envent = environ_find(c->environ, "TMUX"); |
envent = environ_find(c->environ, "TMUX"); |
if (envent == NULL) |
if (envent == NULL) |
|
|
return (NULL); |
return (NULL); |
if (pid != getpid()) |
if (pid != getpid()) |
return (NULL); |
return (NULL); |
log_debug("client %p TMUX is %s (session @%u)", c, envent->value, |
log_debug("client %p TMUX %s (session @%u)", c, envent->value, session); |
session); |
return (session_find_by_id(session)); |
|
|
s = session_find_by_id(session); |
|
if (s == NULL || (w != NULL && !session_has(s, w))) |
|
return (NULL); |
|
return (s); |
|
} |
} |
|
|
/* Is this client better? */ |
/* Is this client better? */ |
|
|
return (timercmp(&c->activity_time, &than->activity_time, >)); |
return (timercmp(&c->activity_time, &than->activity_time, >)); |
} |
} |
|
|
/* Find best client from a list, or all if list is NULL. */ |
/* Find best client for session. */ |
static struct client * |
static struct client * |
cmd_find_best_client(struct client **clist, u_int csize) |
cmd_find_best_client(struct session *s) |
{ |
{ |
struct client *c_loop, *c; |
struct client *c_loop, *c; |
u_int i; |
|
|
|
|
if (s->flags & SESSION_UNATTACHED) |
|
s = NULL; |
|
|
c = NULL; |
c = NULL; |
if (clist != NULL) { |
TAILQ_FOREACH(c_loop, &clients, entry) { |
for (i = 0; i < csize; i++) { |
if (c_loop->session == NULL) |
if (clist[i]->session == NULL) |
continue; |
continue; |
if (s != NULL && c_loop->session != s) |
if (cmd_find_client_better(clist[i], c)) |
continue; |
c = clist[i]; |
if (cmd_find_client_better(c_loop, c)) |
} |
c = c_loop; |
} else { |
|
TAILQ_FOREACH(c_loop, &clients, entry) { |
|
if (c_loop->session == NULL) |
|
continue; |
|
if (cmd_find_client_better(c_loop, c)) |
|
c = c_loop; |
|
} |
|
} |
} |
return (c); |
return (c); |
} |
} |
|
|
u_int ssize; |
u_int ssize; |
struct session *s; |
struct session *s; |
|
|
if (fs->item != NULL && fs->item->client != NULL) { |
|
fs->s = cmd_find_try_TMUX(fs->item->client, fs->w); |
|
if (fs->s != NULL) |
|
return (cmd_find_best_winlink_with_window(fs)); |
|
} |
|
|
|
ssize = 0; |
ssize = 0; |
RB_FOREACH(s, sessions, &sessions) { |
RB_FOREACH(s, sessions, &sessions) { |
if (!session_has(s, fs->w)) |
if (!session_has(s, fs->w)) |
|
|
return (0); |
return (0); |
} |
} |
|
|
/* Find current session when we have an unattached client. */ |
|
static int |
|
cmd_find_current_session_with_client(struct cmd_find_state *fs) |
|
{ |
|
struct window_pane *wp; |
|
|
|
/* |
|
* If this is running in a pane, we can use that to limit the list of |
|
* sessions to those containing that pane (we still use the current |
|
* window in the best session). |
|
*/ |
|
if (fs->item != NULL) { |
|
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { |
|
if (strcmp(wp->tty, fs->item->client->ttyname) == 0) |
|
break; |
|
} |
|
} else |
|
wp = NULL; |
|
|
|
/* Not running in a pane. We know nothing. Find the best session. */ |
|
if (wp == NULL) |
|
goto unknown_pane; |
|
|
|
/* Find the best session and winlink containing this pane. */ |
|
fs->w = wp->window; |
|
if (cmd_find_best_session_with_window(fs) != 0) { |
|
if (wp != NULL) { |
|
/* |
|
* The window may have been destroyed but the pane |
|
* still on all_window_panes due to something else |
|
* holding a reference. |
|
*/ |
|
goto unknown_pane; |
|
} |
|
return (-1); |
|
} |
|
|
|
/* Use the current window and pane from this session. */ |
|
fs->wl = fs->s->curw; |
|
fs->idx = fs->wl->idx; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; |
|
|
|
return (0); |
|
|
|
unknown_pane: |
|
fs->s = NULL; |
|
if (fs->item != NULL) |
|
fs->s = cmd_find_try_TMUX(fs->item->client, NULL); |
|
if (fs->s == NULL) |
|
fs->s = cmd_find_best_session(NULL, 0, fs->flags); |
|
if (fs->s == NULL) |
|
return (-1); |
|
fs->wl = fs->s->curw; |
|
fs->idx = fs->wl->idx; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; |
|
|
|
return (0); |
|
} |
|
|
|
/* |
|
* Work out the best current state. If this function succeeds, the state is |
|
* guaranteed to be completely filled in. |
|
*/ |
|
static int |
|
cmd_find_current_session(struct cmd_find_state *fs) |
|
{ |
|
/* If we know the current client, use it. */ |
|
if (fs->item != NULL && fs->item->client != NULL) { |
|
log_debug("%s: have client %p%s", __func__, fs->item->client, |
|
fs->item->client->session == NULL ? "" : " (with session)"); |
|
if (fs->item->client->session == NULL) |
|
return (cmd_find_current_session_with_client(fs)); |
|
fs->s = fs->item->client->session; |
|
fs->wl = fs->s->curw; |
|
fs->idx = fs->wl->idx; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; |
|
return (0); |
|
} |
|
|
|
/* We know nothing, find the best session and client. */ |
|
fs->s = cmd_find_best_session(NULL, 0, fs->flags); |
|
if (fs->s == NULL) |
|
return (-1); |
|
fs->wl = fs->s->curw; |
|
fs->idx = fs->wl->idx; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; |
|
|
|
return (0); |
|
} |
|
|
|
/* Work out the best current client. */ |
|
static struct client * |
|
cmd_find_current_client(struct cmdq_item *item) |
|
{ |
|
struct cmd_find_state current; |
|
struct session *s; |
|
struct client *c, **clist = NULL; |
|
u_int csize; |
|
|
|
/* If the queue client has a session, use it. */ |
|
if (item->client != NULL && item->client->session != NULL) { |
|
log_debug("%s: using item %p client %p", __func__, item, |
|
item->client); |
|
return (item->client); |
|
} |
|
|
|
/* Otherwise find the current session. */ |
|
cmd_find_clear_state(¤t, item, 0); |
|
if (cmd_find_current_session(¤t) != 0) |
|
return (NULL); |
|
|
|
/* If it is attached, find the best of it's clients. */ |
|
s = current.s; |
|
log_debug("%s: current session $%u %s", __func__, s->id, s->name); |
|
if (~s->flags & SESSION_UNATTACHED) { |
|
csize = 0; |
|
TAILQ_FOREACH(c, &clients, entry) { |
|
if (c->session != s) |
|
continue; |
|
clist = xreallocarray(clist, csize + 1, sizeof *clist); |
|
clist[csize++] = c; |
|
} |
|
if (csize != 0) { |
|
c = cmd_find_best_client(clist, csize); |
|
if (c != NULL) { |
|
free(clist); |
|
return (c); |
|
} |
|
} |
|
free(clist); |
|
} |
|
|
|
/* Otherwise pick best of all clients. */ |
|
return (cmd_find_best_client(NULL, 0)); |
|
} |
|
|
|
/* Maps string in table. */ |
/* Maps string in table. */ |
static const char * |
static const char * |
cmd_find_map_table(const char *table[][2], const char *s) |
cmd_find_map_table(const char *table[][2], const char *s) |
|
|
|
|
/* Clear state. */ |
/* Clear state. */ |
void |
void |
cmd_find_clear_state(struct cmd_find_state *fs, struct cmdq_item *item, |
cmd_find_clear_state(struct cmd_find_state *fs, int flags) |
int flags) |
|
{ |
{ |
memset(fs, 0, sizeof *fs); |
memset(fs, 0, sizeof *fs); |
|
|
fs->item = item; |
|
fs->flags = flags; |
fs->flags = flags; |
|
|
fs->idx = -1; |
fs->idx = -1; |
} |
} |
|
|
/* Check if state is empty/ */ |
/* Check if state is empty. */ |
int |
int |
cmd_find_empty_state(struct cmd_find_state *fs) |
cmd_find_empty_state(struct cmd_find_state *fs) |
{ |
{ |
|
|
void |
void |
cmd_find_from_session(struct cmd_find_state *fs, struct session *s) |
cmd_find_from_session(struct cmd_find_state *fs, struct session *s) |
{ |
{ |
cmd_find_clear_state(fs, NULL, 0); |
cmd_find_clear_state(fs, 0); |
|
|
fs->s = s; |
fs->s = s; |
fs->wl = fs->s->curw; |
fs->wl = fs->s->curw; |
|
|
void |
void |
cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl) |
cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl) |
{ |
{ |
cmd_find_clear_state(fs, NULL, 0); |
cmd_find_clear_state(fs, 0); |
|
|
fs->s = wl->session; |
fs->s = wl->session; |
fs->wl = wl; |
fs->wl = wl; |
|
|
cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s, |
cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s, |
struct window *w) |
struct window *w) |
{ |
{ |
cmd_find_clear_state(fs, NULL, 0); |
cmd_find_clear_state(fs, 0); |
|
|
fs->s = s; |
fs->s = s; |
fs->w = w; |
fs->w = w; |
|
|
int |
int |
cmd_find_from_window(struct cmd_find_state *fs, struct window *w) |
cmd_find_from_window(struct cmd_find_state *fs, struct window *w) |
{ |
{ |
cmd_find_clear_state(fs, NULL, 0); |
cmd_find_clear_state(fs, 0); |
|
|
fs->w = w; |
fs->w = w; |
if (cmd_find_best_session_with_window(fs) != 0) |
if (cmd_find_best_session_with_window(fs) != 0) |
|
|
cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl, |
cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl, |
struct window_pane *wp) |
struct window_pane *wp) |
{ |
{ |
cmd_find_clear_state(fs, NULL, 0); |
cmd_find_clear_state(fs, 0); |
|
|
fs->s = wl->session; |
fs->s = wl->session; |
fs->wl = wl; |
fs->wl = wl; |
|
|
return (0); |
return (0); |
} |
} |
|
|
/* Find current state. */ |
/* Find state from nothing. */ |
int |
int |
cmd_find_current(struct cmd_find_state *fs, struct cmdq_item *item, int flags) |
cmd_find_from_nothing(struct cmd_find_state *fs) |
{ |
{ |
cmd_find_clear_state(fs, item, flags); |
cmd_find_clear_state(fs, 0); |
if (cmd_find_current_session(fs) != 0) { |
|
if (~flags & CMD_FIND_QUIET) |
fs->s = cmd_find_best_session(NULL, 0, fs->flags); |
cmdq_error(item, "no current session"); |
if (fs->s == NULL) |
return (-1); |
return (-1); |
|
fs->wl = fs->s->curw; |
|
fs->idx = fs->wl->idx; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; |
|
|
|
cmd_find_log_state(__func__, fs); |
|
return (0); |
|
} |
|
|
|
/* Find state from mouse. */ |
|
int |
|
cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m) |
|
{ |
|
cmd_find_clear_state(fs, 0); |
|
|
|
if (!m->valid) |
|
return (-1); |
|
|
|
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); |
|
if (fs->wp == NULL) |
|
return (-1); |
|
fs->w = fs->wl->window; |
|
|
|
cmd_find_log_state(__func__, fs); |
|
return (0); |
|
} |
|
|
|
/* Find state from client. */ |
|
int |
|
cmd_find_from_client(struct cmd_find_state *fs, struct client *c) |
|
{ |
|
struct session *s; |
|
struct winlink *wl; |
|
struct window_pane *wp; |
|
|
|
/* If no client, treat as from nothing. */ |
|
if (c == NULL) |
|
return (cmd_find_from_nothing(fs)); |
|
|
|
/* If this is an attached client, all done. */ |
|
if (c->session != NULL) { |
|
cmd_find_from_session(fs, c->session); |
|
return (0); |
} |
} |
|
cmd_find_clear_state(fs, 0); |
|
|
|
/* |
|
* If this is an unattached client running in a pane, we can use that |
|
* to limit the list of sessions to those containing that pane. |
|
*/ |
|
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { |
|
if (strcmp(wp->tty, c->ttyname) == 0) |
|
break; |
|
} |
|
if (wp == NULL) |
|
goto unknown_pane; |
|
|
|
/* If we have a session in TMUX, see if it has this pane. */ |
|
s = cmd_find_try_TMUX(c); |
|
if (s != NULL) { |
|
RB_FOREACH(wl, winlinks, &s->windows) { |
|
if (window_has_pane(wl->window, wp)) |
|
break; |
|
} |
|
if (wl != NULL) { |
|
fs->s = s; |
|
fs->wl = s->curw; /* use active session */ |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; /* use active pane */ |
|
|
|
cmd_find_log_state(__func__, fs); |
|
return (0); |
|
} |
|
} |
|
|
|
/* |
|
* Don't have a session, or it doesn't have this pane. Try all |
|
* sessions. |
|
*/ |
|
fs->w = wp->window; |
|
if (cmd_find_best_session_with_window(fs) != 0) { |
|
if (wp != NULL) { |
|
/* |
|
* The window may have been destroyed but the pane |
|
* still on all_window_panes due to something else |
|
* holding a reference. |
|
*/ |
|
goto unknown_pane; |
|
} |
|
return (-1); |
|
} |
|
fs->wl = fs->s->curw; |
|
fs->w = fs->wl->window; |
|
fs->wp = fs->w->active; /* use active pane */ |
|
|
|
cmd_find_log_state(__func__, fs); |
return (0); |
return (0); |
|
|
|
unknown_pane: |
|
/* |
|
* We're not running in a known pane, but maybe this client has TMUX |
|
* in the environment. That'd give us a session. |
|
*/ |
|
s = cmd_find_try_TMUX(c); |
|
if (s != NULL) { |
|
cmd_find_from_session(fs, s); |
|
return (0); |
|
} |
|
|
|
/* Otherwise we need to guess. */ |
|
return (cmd_find_from_nothing(fs)); |
} |
} |
|
|
/* |
/* |
|
|
* state. Returns 0 on success or -1 on error. |
* state. Returns 0 on success or -1 on error. |
*/ |
*/ |
int |
int |
cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current, |
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, |
struct cmdq_item *item, const char *target, enum cmd_find_type type, |
const char *target, enum cmd_find_type type, int flags) |
int flags) |
|
{ |
{ |
struct mouse_event *m; |
struct mouse_event *m; |
|
struct cmd_find_state current; |
char *colon, *period, *copy = NULL; |
char *colon, *period, *copy = NULL; |
const char *session, *window, *pane; |
const char *session, *window, *pane; |
int window_only = 0, pane_only = 0; |
int window_only = 0, pane_only = 0; |
|
|
log_debug("%s: item %p, flags %#x", __func__, item, flags); |
log_debug("%s: item %p, flags %#x", __func__, item, flags); |
|
|
/* Clear new state. */ |
/* Clear new state. */ |
cmd_find_clear_state(fs, item, flags); |
cmd_find_clear_state(fs, flags); |
|
|
/* Find current state. */ |
/* Find current state. */ |
if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { |
if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { |
|
|
} else if (cmd_find_valid_state(&item->shared->current)) { |
} else if (cmd_find_valid_state(&item->shared->current)) { |
fs->current = &item->shared->current; |
fs->current = &item->shared->current; |
log_debug("%s: current is from queue", __func__); |
log_debug("%s: current is from queue", __func__); |
} else { |
} else if (cmd_find_from_client(¤t, item->client) == 0) { |
fs->current = current; |
fs->current = ¤t; |
log_debug("%s: current is from argument", __func__); |
log_debug("%s: current is from client", __func__); |
} |
} else |
|
return (-1); |
if (!cmd_find_empty_state(fs->current) && |
if (!cmd_find_empty_state(fs->current) && |
!cmd_find_valid_state(fs->current)) |
!cmd_find_valid_state(fs->current)) |
fatalx("invalid current find state"); |
fatalx("invalid current find state"); |
|
|
cmd_find_client(struct cmdq_item *item, const char *target, int quiet) |
cmd_find_client(struct cmdq_item *item, const char *target, int quiet) |
{ |
{ |
struct client *c; |
struct client *c; |
|
struct session *s; |
char *copy; |
char *copy; |
size_t size; |
size_t size; |
|
|
/* A NULL argument means the current client. */ |
/* A NULL argument means the current client. */ |
if (item != NULL && target == NULL) { |
if (target == NULL) { |
c = cmd_find_current_client(item); |
c = NULL; |
|
if (item->client != NULL && item->client->session != NULL) |
|
c = item->client; |
|
else { |
|
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET); |
|
if (s != NULL) |
|
c = cmd_find_best_client(s); |
|
} |
if (c == NULL && !quiet) |
if (c == NULL && !quiet) |
cmdq_error(item, "no current client"); |
cmdq_error(item, "no current client"); |
log_debug("%s: no target, return %p", __func__, c); |
log_debug("%s: no target, return %p", __func__, c); |