mirror of
https://github.com/tmux/tmux.git
synced 2026-06-21 01:35:23 +00:00
Merge master into floating_panes.
This commit is contained in:
@@ -27,13 +27,17 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_pane_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_pane_filter(struct cmdq_item *,
|
||||
struct session *, struct winlink *,
|
||||
struct window_pane *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_pane_entry = {
|
||||
.name = "kill-pane",
|
||||
.alias = "killp",
|
||||
|
||||
.args = { "at:", 0, 0, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||
.args = { "af:t:", 0, 0, NULL },
|
||||
.usage = "[-a] [-f filter] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@@ -46,22 +50,19 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window_pane *loopwp, *tmpwp, *wp = target->wp;
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
server_unzoom_window(wl->window);
|
||||
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
|
||||
if (loopwp == wp)
|
||||
continue;
|
||||
server_client_remove_pane(loopwp);
|
||||
layout_close_pane(loopwp);
|
||||
window_remove_pane(wl->window, loopwp);
|
||||
}
|
||||
server_redraw_window(wl->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (filter != NULL && !args_has(args, 'a')) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'a'))
|
||||
return (cmd_kill_pane_all(item, filter));
|
||||
|
||||
if (wp == NULL) {
|
||||
cmdq_error(item, "no active pane to kill");
|
||||
return (CMD_RETURN_ERROR);
|
||||
@@ -69,3 +70,48 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_kill_pane(wp);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_pane_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window_pane *wp = target->wp;
|
||||
struct window_pane *loopwp, *tmpwp;
|
||||
|
||||
server_unzoom_window(wl->window);
|
||||
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
|
||||
if (loopwp == wp)
|
||||
continue;
|
||||
if (!cmd_kill_pane_filter(item, s, wl, loopwp, filter))
|
||||
continue;
|
||||
server_client_remove_pane(loopwp);
|
||||
layout_close_pane(loopwp);
|
||||
window_remove_pane(wl->window, loopwp);
|
||||
}
|
||||
server_redraw_window(wl->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_pane_filter(struct cmdq_item *item, struct session *s,
|
||||
struct winlink *wl, struct window_pane *wp, const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, wl, wp);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
@@ -28,13 +30,16 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_session_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_session_filter(struct cmdq_item *,
|
||||
struct session *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_session_entry = {
|
||||
.name = "kill-session",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "aCgt:", 0, 0, NULL },
|
||||
.usage = "[-aCg] " CMD_TARGET_SESSION_USAGE,
|
||||
.args = { "aCgf:t:", 0, 0, NULL },
|
||||
.usage = "[-aCg] [-f filter] " CMD_TARGET_SESSION_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, 0 },
|
||||
|
||||
@@ -50,6 +55,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = target->s, *sloop, *stmp;
|
||||
struct session_group *sg;
|
||||
struct winlink *wl;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (filter != NULL && (!args_has(args, 'a') || args_has(args, 'C'))) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
@@ -57,14 +68,9 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
wl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
}
|
||||
server_redraw_session(s);
|
||||
} else if (args_has(args, 'a')) {
|
||||
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
|
||||
if (sloop != s) {
|
||||
server_destroy_session(sloop);
|
||||
session_destroy(sloop, 1, __func__);
|
||||
}
|
||||
}
|
||||
} else if (args_has(args, 'g') &&
|
||||
} else if (args_has(args, 'a'))
|
||||
return (cmd_kill_session_all(item, filter));
|
||||
else if (args_has(args, 'g') &&
|
||||
(sg = session_group_contains(s)) != NULL) {
|
||||
TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) {
|
||||
server_destroy_session(sloop);
|
||||
@@ -76,3 +82,42 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_session_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct session *s = cmdq_get_target(item)->s;
|
||||
struct session *sloop, *stmp;
|
||||
|
||||
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
|
||||
if (sloop == s)
|
||||
continue;
|
||||
if (!cmd_kill_session_filter(item, sloop, filter))
|
||||
continue;
|
||||
server_destroy_session(sloop);
|
||||
session_destroy(sloop, 1, __func__);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_session_filter(struct cmdq_item *item, struct session *s,
|
||||
const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, NULL, NULL);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
@@ -25,13 +27,16 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_window_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_window_filter(struct cmdq_item *,
|
||||
struct session *, struct winlink *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_window_entry = {
|
||||
.name = "kill-window",
|
||||
.alias = "killw",
|
||||
|
||||
.args = { "at:", 0, 0, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_WINDOW_USAGE,
|
||||
.args = { "af:t:", 0, 0, NULL },
|
||||
.usage = "[-a] [-f filter] " CMD_TARGET_WINDOW_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||
|
||||
@@ -57,10 +62,15 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct winlink *wl = target->wl, *loop;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct session *s = target->s;
|
||||
u_int found;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (filter != NULL && !args_has(args, 'a')) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_unlink_window_entry) {
|
||||
if (!args_has(args, 'k') && !session_is_linked(s, w)) {
|
||||
@@ -72,39 +82,76 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
|
||||
RB_NEXT(winlinks, &s->windows, wl) == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
/* Kill all windows except the current one. */
|
||||
do {
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window != wl->window) {
|
||||
server_kill_window(loop->window, 0);
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found != 0);
|
||||
|
||||
/*
|
||||
* If the current window appears in the session more than once,
|
||||
* kill it as well.
|
||||
*/
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window == wl->window)
|
||||
found++;
|
||||
}
|
||||
if (found > 1)
|
||||
server_kill_window(wl->window, 0);
|
||||
|
||||
server_renumber_all();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_has(args, 'a'))
|
||||
return (cmd_kill_window_all(item, filter));
|
||||
|
||||
server_kill_window(wl->window, 1);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_window_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
struct winlink *loop;
|
||||
u_int found, kill_current;
|
||||
|
||||
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
|
||||
RB_NEXT(winlinks, &s->windows, wl) == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
/* Kill all windows except the current one. */
|
||||
do {
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window != wl->window &&
|
||||
cmd_kill_window_filter(item, s, loop, filter)) {
|
||||
server_kill_window(loop->window, 0);
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found != 0);
|
||||
|
||||
/*
|
||||
* If the current window appears in the session more than once, kill it
|
||||
* as well if it matches the filter.
|
||||
*/
|
||||
found = kill_current = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window == wl->window) {
|
||||
found++;
|
||||
if (cmd_kill_window_filter(item, s, loop, filter))
|
||||
kill_current = 1;
|
||||
}
|
||||
}
|
||||
if (kill_current && found > 1)
|
||||
server_kill_window(wl->window, 0);
|
||||
|
||||
server_renumber_all();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_window_filter(struct cmdq_item *item, struct session *s,
|
||||
struct winlink *wl, const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, wl, NULL);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -37,67 +38,71 @@ const struct cmd_entry cmd_server_access_entry = {
|
||||
.name = "server-access",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "adlrw", 0, 1, NULL },
|
||||
.usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]",
|
||||
.args = { "adglrw", 0, 1, NULL },
|
||||
.usage = "[-adglrw] " CMD_TARGET_PANE_USAGE " [user|group]",
|
||||
|
||||
.flags = CMD_CLIENT_CANFAIL,
|
||||
.exec = cmd_server_access_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
|
||||
cmd_server_access_deny(struct cmdq_item *item, id_t id, int flags,
|
||||
const char *type, const char *name)
|
||||
{
|
||||
struct client *loop;
|
||||
struct server_acl_user *user;
|
||||
uid_t uid;
|
||||
|
||||
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
uid = proc_get_peer_uid(loop->peer);
|
||||
if (uid == server_acl_get_uid(user)) {
|
||||
loop->exit_message = xstrdup("access not allowed");
|
||||
loop->flags |= CLIENT_EXIT;
|
||||
}
|
||||
}
|
||||
server_acl_user_deny(pw->pw_uid);
|
||||
|
||||
server_acl_deny(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct client *c = cmdq_get_target_client(item);
|
||||
char *name;
|
||||
struct passwd *pw = NULL;
|
||||
char *arg;
|
||||
const char *name = NULL, *type;
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
id_t id;
|
||||
int flags = 0;
|
||||
|
||||
if (args_has(args, 'l')) {
|
||||
server_acl_display(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_count(args) == 0) {
|
||||
cmdq_error(item, "missing user argument");
|
||||
cmdq_error(item, "missing user or group argument");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
|
||||
if (*name != '\0')
|
||||
pw = getpwnam(name);
|
||||
if (pw == NULL) {
|
||||
cmdq_error(item, "unknown user: %s", name);
|
||||
free(name);
|
||||
arg = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
|
||||
if (args_has(args, 'g')) {
|
||||
type = "group";
|
||||
if ((gr = getgrnam(arg)) != NULL) {
|
||||
id = gr->gr_gid;
|
||||
name = gr->gr_name;
|
||||
flags |= SERVER_ACL_IS_GROUP;
|
||||
}
|
||||
} else {
|
||||
type = "user";
|
||||
if ((pw = getpwnam(arg)) != NULL) {
|
||||
id = pw->pw_uid;
|
||||
name = pw->pw_name;
|
||||
}
|
||||
}
|
||||
if (name == NULL) {
|
||||
cmdq_error(item, "unknown %s: %s", type, arg);
|
||||
free(arg);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(name);
|
||||
free(arg);
|
||||
|
||||
if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
|
||||
if ((~flags & SERVER_ACL_IS_GROUP) && (id == 0 || id == getuid())) {
|
||||
cmdq_error(item, "%s owns the server, can't change access",
|
||||
pw->pw_name);
|
||||
name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
@@ -111,36 +116,35 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
if (args_has(args, 'd'))
|
||||
return (cmd_server_access_deny(item, pw));
|
||||
return (cmd_server_access_deny(item, id, flags, type, name));
|
||||
if (args_has(args, 'a')) {
|
||||
if (server_acl_user_find(pw->pw_uid) != NULL) {
|
||||
cmdq_error(item, "user %s is already added",
|
||||
pw->pw_name);
|
||||
if (server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s is already added", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
server_acl_allow(id, flags);
|
||||
/* Do not return - allow -r or -w with -a. */
|
||||
} else if (args_has(args, 'r') || args_has(args, 'w')) {
|
||||
/* -r or -w implies -a if user does not exist. */
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL)
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
/* -r or -w implies -a if the entry does not exist. */
|
||||
if (!server_acl_find(id, flags))
|
||||
server_acl_allow(id, flags);
|
||||
}
|
||||
|
||||
if (args_has(args, 'w')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow_write(pw->pw_uid);
|
||||
server_acl_allow_write(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'r')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_deny_write(pw->pw_uid);
|
||||
server_acl_deny_write(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const struct cmd_entry cmd_new_pane_entry = {
|
||||
.name = "new-pane",
|
||||
.alias = "newp",
|
||||
|
||||
.args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vx:X:y:Y:Z", 0, -1, NULL },
|
||||
.args = { "bBc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vx:X:y:Y:Z", 0, -1, NULL },
|
||||
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||
"[-s style] [-S active-border-style] "
|
||||
@@ -56,7 +56,7 @@ const struct cmd_entry cmd_split_window_entry = {
|
||||
.name = "split-window",
|
||||
.alias = "splitw",
|
||||
|
||||
.args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vZ", 0, -1, NULL },
|
||||
.args = { "bBc:de:EfF:hIkl:m:p:PR:s:S:t:T:vZ", 0, -1, NULL },
|
||||
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||
"[-s style] [-S active-border-style] "
|
||||
@@ -244,5 +244,15 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
environ_free(sc.environ);
|
||||
if (input)
|
||||
return (CMD_RETURN_WAIT);
|
||||
|
||||
if (args_has(args, 'B')) {
|
||||
/*
|
||||
* With -B, block this command queue item until the pane's
|
||||
* command exits; window_pane_block_finish will be called to
|
||||
* continue it.
|
||||
*/
|
||||
new_wp->block_item = item;
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
12
proc.c
12
proc.c
@@ -57,6 +57,7 @@ struct tmuxpeer {
|
||||
struct imsgbuf ibuf;
|
||||
struct event event;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
int flags;
|
||||
#define PEER_BAD 0x1
|
||||
@@ -301,7 +302,6 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
void (*dispatchcb)(struct imsg *, void *), void *arg)
|
||||
{
|
||||
struct tmuxpeer *peer;
|
||||
gid_t gid;
|
||||
|
||||
peer = xcalloc(1, sizeof *peer);
|
||||
peer->parent = tp;
|
||||
@@ -314,8 +314,10 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
imsgbuf_allow_fdpass(&peer->ibuf);
|
||||
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
|
||||
|
||||
if (getpeereid(fd, &peer->uid, &gid) != 0)
|
||||
if (getpeereid(fd, &peer->uid, &peer->gid) != 0) {
|
||||
peer->uid = (uid_t)-1;
|
||||
peer->gid = (gid_t)-1;
|
||||
}
|
||||
|
||||
log_debug("add peer %p: %d (%p)", peer, fd, arg);
|
||||
TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
|
||||
@@ -384,3 +386,9 @@ proc_get_peer_uid(struct tmuxpeer *peer)
|
||||
{
|
||||
return (peer->uid);
|
||||
}
|
||||
|
||||
gid_t
|
||||
proc_get_peer_gid(struct tmuxpeer *peer)
|
||||
{
|
||||
return (peer->gid);
|
||||
}
|
||||
|
||||
222
server-acl.c
222
server-acl.c
@@ -22,6 +22,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -29,158 +30,203 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct server_acl_user {
|
||||
uid_t uid;
|
||||
struct server_acl_entry {
|
||||
id_t id;
|
||||
|
||||
int flags;
|
||||
#define SERVER_ACL_READONLY 0x1
|
||||
int flags;
|
||||
|
||||
RB_ENTRY(server_acl_user) entry;
|
||||
RB_ENTRY(server_acl_entry) entry;
|
||||
};
|
||||
|
||||
static int
|
||||
server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2)
|
||||
server_acl_cmp(struct server_acl_entry *entry1,
|
||||
struct server_acl_entry *entry2)
|
||||
{
|
||||
if (user1->uid < user2->uid)
|
||||
if ((entry1->flags ^ entry2->flags) & SERVER_ACL_IS_GROUP) {
|
||||
if (entry1->flags & SERVER_ACL_IS_GROUP)
|
||||
return (1);
|
||||
return (-1);
|
||||
return (user1->uid > user2->uid);
|
||||
}
|
||||
|
||||
if (entry1->id < entry2->id)
|
||||
return (-1);
|
||||
return (entry1->id > entry2->id);
|
||||
}
|
||||
|
||||
RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries;
|
||||
RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp);
|
||||
RB_HEAD(server_acl_entries, server_acl_entry) server_acl_entries;
|
||||
RB_GENERATE_STATIC(server_acl_entries, server_acl_entry, entry, server_acl_cmp);
|
||||
|
||||
/* Initialize server_acl tree. */
|
||||
static struct server_acl_entry *
|
||||
server_acl_entry_find(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_entry find = {
|
||||
.id = id,
|
||||
.flags = flags & SERVER_ACL_IS_GROUP
|
||||
};
|
||||
|
||||
return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
|
||||
}
|
||||
|
||||
static struct server_acl_entry *
|
||||
server_acl_check(struct client *c)
|
||||
{
|
||||
struct server_acl_entry *entry;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid == (uid_t)-1)
|
||||
return (NULL);
|
||||
|
||||
entry = server_acl_entry_find(uid, 0);
|
||||
if (entry != NULL)
|
||||
return (entry);
|
||||
|
||||
gid = proc_get_peer_gid(c->peer);
|
||||
if (gid == (gid_t)-1)
|
||||
return (NULL);
|
||||
|
||||
return (server_acl_entry_find(gid, SERVER_ACL_IS_GROUP));
|
||||
}
|
||||
|
||||
static void
|
||||
server_acl_update(void)
|
||||
{
|
||||
struct server_acl_entry *entry;
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
entry = server_acl_check(c);
|
||||
if (entry == NULL) {
|
||||
c->exit_message = xstrdup("access not allowed");
|
||||
c->flags |= CLIENT_EXIT;
|
||||
} else if (entry->flags & SERVER_ACL_READONLY)
|
||||
c->flags |= CLIENT_READONLY;
|
||||
else
|
||||
c->flags &= ~CLIENT_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize ACL tree. */
|
||||
void
|
||||
server_acl_init(void)
|
||||
{
|
||||
RB_INIT(&server_acl_entries);
|
||||
|
||||
if (getuid() != 0)
|
||||
server_acl_user_allow(0);
|
||||
server_acl_user_allow(getuid());
|
||||
server_acl_allow(0, 0);
|
||||
server_acl_allow(getuid(), 0);
|
||||
}
|
||||
|
||||
/* Find user entry. */
|
||||
struct server_acl_user*
|
||||
server_acl_user_find(uid_t uid)
|
||||
/* Check if an ACL entry exists. */
|
||||
int
|
||||
server_acl_find(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_user find = { .uid = uid };
|
||||
|
||||
return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
|
||||
return (server_acl_entry_find(id, flags) != NULL);
|
||||
}
|
||||
|
||||
/* Display the tree. */
|
||||
void
|
||||
server_acl_display(struct cmdq_item *item)
|
||||
{
|
||||
struct server_acl_user *loop;
|
||||
struct server_acl_entry *loop;
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
const char *name;
|
||||
char type;
|
||||
|
||||
RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
|
||||
if (loop->uid == 0)
|
||||
continue;
|
||||
if ((pw = getpwuid(loop->uid)) != NULL)
|
||||
name = pw->pw_name;
|
||||
if (~loop->flags & SERVER_ACL_IS_GROUP) {
|
||||
if (loop->id == 0)
|
||||
continue;
|
||||
if ((pw = getpwuid(loop->id)) != NULL)
|
||||
name = pw->pw_name;
|
||||
else
|
||||
name = "unknown";
|
||||
type = 'U';
|
||||
} else {
|
||||
if ((gr = getgrgid(loop->id)) != NULL)
|
||||
name = gr->gr_name;
|
||||
else
|
||||
name = "unknown";
|
||||
type = 'G';
|
||||
}
|
||||
if (loop->flags & SERVER_ACL_READONLY)
|
||||
cmdq_print(item, "%s (%c,R)", name, type);
|
||||
else
|
||||
name = "unknown";
|
||||
if (loop->flags == SERVER_ACL_READONLY)
|
||||
cmdq_print(item, "%s (R)", name);
|
||||
else
|
||||
cmdq_print(item, "%s (W)", name);
|
||||
cmdq_print(item, "%s (%c,W)", name, type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow a user. */
|
||||
/* Allow an ACL entry. */
|
||||
void
|
||||
server_acl_user_allow(uid_t uid)
|
||||
server_acl_allow(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_user *user;
|
||||
struct server_acl_entry *entry;
|
||||
|
||||
user = server_acl_user_find(uid);
|
||||
if (user == NULL) {
|
||||
user = xcalloc(1, sizeof *user);
|
||||
user->uid = uid;
|
||||
RB_INSERT(server_acl_entries, &server_acl_entries, user);
|
||||
entry = server_acl_entry_find(id, flags);
|
||||
if (entry == NULL) {
|
||||
entry = xcalloc(1, sizeof *entry);
|
||||
entry->id = id;
|
||||
entry->flags = flags & SERVER_ACL_IS_GROUP;
|
||||
RB_INSERT(server_acl_entries, &server_acl_entries, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Deny a user (remove from the tree). */
|
||||
/* Deny an ACL entry (remove it from the tree). */
|
||||
void
|
||||
server_acl_user_deny(uid_t uid)
|
||||
server_acl_deny(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_user *user;
|
||||
struct server_acl_entry *entry;
|
||||
|
||||
user = server_acl_user_find(uid);
|
||||
if (user != NULL) {
|
||||
RB_REMOVE(server_acl_entries, &server_acl_entries, user);
|
||||
free(user);
|
||||
entry = server_acl_entry_find(id, flags);
|
||||
if (entry != NULL) {
|
||||
RB_REMOVE(server_acl_entries, &server_acl_entries, entry);
|
||||
free(entry);
|
||||
server_acl_update();
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow this user write access. */
|
||||
/* Allow this ACL entry write access. */
|
||||
void
|
||||
server_acl_user_allow_write(uid_t uid)
|
||||
server_acl_allow_write(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_user *user;
|
||||
struct client *c;
|
||||
struct server_acl_entry *entry;
|
||||
|
||||
user = server_acl_user_find(uid);
|
||||
if (user == NULL)
|
||||
entry = server_acl_entry_find(id, flags);
|
||||
if (entry == NULL)
|
||||
return;
|
||||
user->flags &= ~SERVER_ACL_READONLY;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid != (uid_t)-1 && uid == user->uid)
|
||||
c->flags &= ~CLIENT_READONLY;
|
||||
}
|
||||
entry->flags &= ~SERVER_ACL_READONLY;
|
||||
server_acl_update();
|
||||
}
|
||||
|
||||
/* Deny this user write access. */
|
||||
/* Deny this ACL entry write access. */
|
||||
void
|
||||
server_acl_user_deny_write(uid_t uid)
|
||||
server_acl_deny_write(id_t id, int flags)
|
||||
{
|
||||
struct server_acl_user *user;
|
||||
struct client *c;
|
||||
struct server_acl_entry *entry;
|
||||
|
||||
user = server_acl_user_find(uid);
|
||||
if (user == NULL)
|
||||
entry = server_acl_entry_find(id, flags);
|
||||
if (entry == NULL)
|
||||
return;
|
||||
user->flags |= SERVER_ACL_READONLY;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid != (uid_t)-1 && uid == user->uid)
|
||||
c->flags |= CLIENT_READONLY;
|
||||
}
|
||||
entry->flags |= SERVER_ACL_READONLY;
|
||||
server_acl_update();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the client's UID exists in the ACL list and if so, set as read only
|
||||
* if needed. Return false if the user does not exist.
|
||||
* Check if the client's UID or GID exists in the ACL list and if so, set as
|
||||
* read only if needed. UID entries take precedence over GID entries. Return
|
||||
* false if no entry exists.
|
||||
*/
|
||||
int
|
||||
server_acl_join(struct client *c)
|
||||
{
|
||||
struct server_acl_user *user;
|
||||
uid_t uid;
|
||||
struct server_acl_entry *entry;
|
||||
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid == (uid_t)-1)
|
||||
entry = server_acl_check(c);
|
||||
if (entry == NULL)
|
||||
return (0);
|
||||
|
||||
user = server_acl_user_find(uid);
|
||||
if (user == NULL)
|
||||
return (0);
|
||||
if (user->flags & SERVER_ACL_READONLY)
|
||||
if (entry->flags & SERVER_ACL_READONLY)
|
||||
c->flags |= CLIENT_READONLY;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get UID for user entry. */
|
||||
uid_t
|
||||
server_acl_get_uid(struct server_acl_user *user)
|
||||
{
|
||||
return (user->uid);
|
||||
}
|
||||
|
||||
@@ -695,12 +695,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
|
||||
bdr_bottom = fwp->yoff + fwp->sy;
|
||||
if (py == bdr_bottom)
|
||||
break;
|
||||
if (window_pane_is_floating(wp)) {
|
||||
/* Floating pane, check top border. */
|
||||
bdr_top = fwp->yoff - 1;
|
||||
if (py == bdr_top)
|
||||
break;
|
||||
}
|
||||
if (py == bdr_top)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fwp != NULL)
|
||||
|
||||
2
server.c
2
server.c
@@ -497,6 +497,8 @@ server_child_exited(pid_t pid, int status)
|
||||
log_debug("%%%u exited", wp->id);
|
||||
wp->flags |= PANE_EXITED;
|
||||
|
||||
window_pane_block_finish(wp);
|
||||
|
||||
if (window_pane_destroy_ready(wp))
|
||||
server_destroy_pane(wp, 1);
|
||||
break;
|
||||
|
||||
76
tmux.1
76
tmux.1
@@ -1207,6 +1207,7 @@ Kill the
|
||||
server and clients and destroy all sessions.
|
||||
.It Xo Ic kill\-session
|
||||
.Op Fl aCg
|
||||
.Op Fl f Ar filter
|
||||
.Op Fl t Ar target\-session
|
||||
.Xc
|
||||
Destroy the given session, closing any windows linked to it and no other
|
||||
@@ -1214,6 +1215,12 @@ sessions, and detaching all clients attached to it.
|
||||
If
|
||||
.Fl a
|
||||
is given, all sessions but the specified one is killed.
|
||||
When
|
||||
.Fl a
|
||||
is given,
|
||||
.Fl f
|
||||
specifies a filter.
|
||||
Only sessions for which the filter is true are killed.
|
||||
If
|
||||
.Fl g
|
||||
is given and the session is in a session group, all sessions in the group are
|
||||
@@ -1578,35 +1585,57 @@ option.
|
||||
Rename the session to
|
||||
.Ar new\-name .
|
||||
.It Xo Ic server\-access
|
||||
.Op Fl adlrw
|
||||
.Op Ar user
|
||||
.Op Fl adglrw
|
||||
.Op Ar user | group
|
||||
.Xc
|
||||
Change the access or read/write permission of
|
||||
.Ar user .
|
||||
.Ar user
|
||||
or
|
||||
.Ar group .
|
||||
The user running the
|
||||
.Nm
|
||||
server (its owner) and the root user cannot be changed and are always
|
||||
permitted access.
|
||||
.Fl g
|
||||
changes a group rather than a user.
|
||||
.Pp
|
||||
.Fl a
|
||||
and
|
||||
.Fl d
|
||||
are used to give or revoke access for the specified user.
|
||||
If the user is already attached, the
|
||||
are used to give or revoke access for the specified user or group.
|
||||
If a client is already attached, the
|
||||
.Fl d
|
||||
flag causes their clients to be detached.
|
||||
flag causes it to be detached unless it is still permitted by another entry.
|
||||
.Pp
|
||||
.Fl r
|
||||
and
|
||||
.Fl w
|
||||
change the permissions for
|
||||
.Ar user :
|
||||
.Ar user
|
||||
or
|
||||
.Ar group :
|
||||
.Fl r
|
||||
makes their clients read-only and
|
||||
makes matching clients read-only and
|
||||
.Fl w
|
||||
writable.
|
||||
.Fl l
|
||||
lists current access permissions.
|
||||
User entries are shown with
|
||||
.Ql U ,
|
||||
group entries with
|
||||
.Ql G ,
|
||||
and read-only or writable entries with
|
||||
.Ql R
|
||||
or
|
||||
.Ql W ,
|
||||
for example
|
||||
.Ql user1 (U,W)
|
||||
or
|
||||
.Ql testgroup (G,R) .
|
||||
If both a user and group entry match a client, the user entry takes
|
||||
precedence.
|
||||
Only the effective group ID of the client is used, not its supplementary
|
||||
groups.
|
||||
.Pp
|
||||
By default, the access list is empty and
|
||||
.Nm
|
||||
@@ -3132,6 +3161,7 @@ the marked pane is used rather than the current pane.
|
||||
.Tg killp
|
||||
.It Xo Ic kill\-pane
|
||||
.Op Fl a
|
||||
.Op Fl f Ar filter
|
||||
.Op Fl t Ar target\-pane
|
||||
.Xc
|
||||
.D1 Pq alias: Ic killp
|
||||
@@ -3139,11 +3169,18 @@ Destroy the given pane.
|
||||
If no panes remain in the containing window, it is also destroyed.
|
||||
The
|
||||
.Fl a
|
||||
option kills all but the pane given with
|
||||
option kills all panes in the window except the pane given with
|
||||
.Fl t .
|
||||
When
|
||||
.Fl a
|
||||
is given,
|
||||
.Fl f
|
||||
specifies a filter.
|
||||
Only panes for which the filter is true are killed.
|
||||
.Tg killw
|
||||
.It Xo Ic kill\-window
|
||||
.Op Fl a
|
||||
.Op Fl f Ar filter
|
||||
.Op Fl t Ar target\-window
|
||||
.Xc
|
||||
.D1 Pq alias: Ic killw
|
||||
@@ -3152,8 +3189,14 @@ Kill the current window or the window at
|
||||
removing it from any sessions to which it is linked.
|
||||
The
|
||||
.Fl a
|
||||
option kills all but the window given with
|
||||
option kills all windows in the session except the window given with
|
||||
.Fl t .
|
||||
When
|
||||
.Fl a
|
||||
is given,
|
||||
.Fl f
|
||||
specifies a filter.
|
||||
Only windows for which the filter is true are killed.
|
||||
.Tg lastp
|
||||
.It Xo Ic last\-pane
|
||||
.Op Fl deZ
|
||||
@@ -3414,7 +3457,7 @@ but a different format may be specified with
|
||||
.Fl F .
|
||||
.Tg newp
|
||||
.It Xo Ic new\-pane
|
||||
.Op Fl bdefhIkPvZ
|
||||
.Op Fl bBdefhIkPvZ
|
||||
.Op Fl c Ar start\-directory
|
||||
.Op Fl e Ar environment
|
||||
.Op Fl F Ar format
|
||||
@@ -3490,6 +3533,17 @@ but also sets the
|
||||
option for this pane to
|
||||
.Ar message .
|
||||
.Pp
|
||||
.Fl B
|
||||
blocks until
|
||||
.Ar shell\-command
|
||||
exits, then returns its exit status.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux new-pane -B 'vi afile'
|
||||
$ echo $?
|
||||
0
|
||||
.Ed
|
||||
.Pp
|
||||
.Fl E ,
|
||||
or an empty
|
||||
.Ar shell\-command ,
|
||||
|
||||
16
tmux.h
16
tmux.h
@@ -1305,6 +1305,7 @@ struct window_pane {
|
||||
char tty[TTY_NAME_MAX];
|
||||
int status;
|
||||
struct timeval dead_time;
|
||||
struct cmdq_item *block_item; /* new-pane -B: waiting for pane exit */
|
||||
|
||||
int fd;
|
||||
struct bufferevent *event;
|
||||
@@ -2436,6 +2437,7 @@ void proc_flush_peer(struct tmuxpeer *);
|
||||
void proc_toggle_log(struct tmuxproc *);
|
||||
pid_t proc_fork_and_daemon(int *);
|
||||
uid_t proc_get_peer_uid(struct tmuxpeer *);
|
||||
gid_t proc_get_peer_gid(struct tmuxpeer *);
|
||||
|
||||
/* cfg.c */
|
||||
extern int cfg_finished;
|
||||
@@ -3433,6 +3435,7 @@ struct window *window_find_by_id(u_int);
|
||||
void window_update_activity(struct window *);
|
||||
struct window *window_create(u_int, u_int, u_int, u_int);
|
||||
void window_pane_set_event(struct window_pane *);
|
||||
void window_pane_block_finish(struct window_pane *);
|
||||
struct window_pane *window_get_active_at(struct window *, u_int, u_int);
|
||||
struct window_pane *window_find_string(struct window *, const char *);
|
||||
int window_has_floating_panes(struct window *);
|
||||
@@ -3875,15 +3878,16 @@ struct screen *sixel_to_screen(struct sixel_image *);
|
||||
#endif
|
||||
|
||||
/* server-acl.c */
|
||||
#define SERVER_ACL_READONLY 0x1
|
||||
#define SERVER_ACL_IS_GROUP 0x2
|
||||
void server_acl_init(void);
|
||||
struct server_acl_user *server_acl_user_find(uid_t);
|
||||
int server_acl_find(id_t, int);
|
||||
void server_acl_display(struct cmdq_item *);
|
||||
void server_acl_user_allow(uid_t);
|
||||
void server_acl_user_deny(uid_t);
|
||||
void server_acl_user_allow_write(uid_t);
|
||||
void server_acl_user_deny_write(uid_t);
|
||||
void server_acl_allow(id_t, int);
|
||||
void server_acl_deny(id_t, int);
|
||||
void server_acl_allow_write(id_t, int);
|
||||
void server_acl_deny_write(id_t, int);
|
||||
int server_acl_join(struct client *);
|
||||
uid_t server_acl_get_uid(struct server_acl_user *);
|
||||
|
||||
/* hyperlink.c */
|
||||
u_int hyperlinks_put(struct hyperlinks *, const char *,
|
||||
|
||||
2
tty.c
2
tty.c
@@ -2051,6 +2051,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
|
||||
|
||||
tty_margin_off(tty);
|
||||
if (ctx->flags & TTY_CTX_CELL_INVALIDATE)
|
||||
tty_invalidate(tty);
|
||||
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
|
||||
|
||||
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
|
||||
|
||||
@@ -82,6 +82,23 @@ utf8_is_hangul_filler(const struct utf8_data *ud)
|
||||
return (memcmp(ud->data, "\343\205\244", 3) == 0);
|
||||
}
|
||||
|
||||
/* Count regional indicator characters. */
|
||||
static u_int
|
||||
utf8_regional_count(const struct utf8_data *ud)
|
||||
{
|
||||
u_int count = 0, i;
|
||||
|
||||
for (i = 0; i + 4 <= ud->size; i++) {
|
||||
if (ud->data[i] == 0xf0 &&
|
||||
ud->data[i + 1] == 0x9f &&
|
||||
ud->data[i + 2] == 0x87 &&
|
||||
ud->data[i + 3] >= 0xa6 &&
|
||||
ud->data[i + 3] <= 0xbf)
|
||||
count++;
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
/* Should these two characters combine? */
|
||||
int
|
||||
utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add)
|
||||
@@ -94,8 +111,13 @@ utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add)
|
||||
return (0);
|
||||
|
||||
/* Regional indicators. */
|
||||
if ((a >= 0x1F1E6 && a <= 0x1F1FF) && (w >= 0x1F1E6 && w <= 0x1F1FF))
|
||||
if ((a >= 0x1F1E6 && a <= 0x1F1FF) && (w >= 0x1F1E6 && w <= 0x1F1FF)) {
|
||||
if (utf8_regional_count(with) != 1)
|
||||
return (0);
|
||||
if (utf8_regional_count(add) != 1)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Emoji skin tone modifiers. */
|
||||
switch (a) {
|
||||
|
||||
@@ -355,6 +355,7 @@ window_tree_build(void *modedata, struct sort_criteria *sort_crit,
|
||||
uint64_t *tag, const char *filter)
|
||||
{
|
||||
struct window_tree_modedata *data = modedata;
|
||||
int squash_groups = data->squash_groups;
|
||||
struct session *s, **l;
|
||||
struct session_group *sg, *current;
|
||||
u_int n, i;
|
||||
@@ -370,15 +371,14 @@ window_tree_build(void *modedata, struct sort_criteria *sort_crit,
|
||||
l = sort_get_sessions(&n, sort_crit);
|
||||
if (n == 0)
|
||||
return;
|
||||
s = l[n - 1];
|
||||
for (i = 0; i < n; i++) {
|
||||
if (data->squash_groups &&
|
||||
(sg = session_group_contains(s)) != NULL) {
|
||||
s = l[i];
|
||||
if (squash_groups && (sg = session_group_contains(s)) != NULL) {
|
||||
if ((sg == current && s != data->fs.s) ||
|
||||
(sg != current && s != TAILQ_FIRST(&sg->sessions)))
|
||||
continue;
|
||||
}
|
||||
window_tree_build_session(l[i], modedata, sort_crit, filter);
|
||||
window_tree_build_session(s, modedata, sort_crit, filter);
|
||||
}
|
||||
|
||||
switch (data->type) {
|
||||
|
||||
58
window.c
58
window.c
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -380,6 +381,13 @@ window_pane_destroy_ready(struct window_pane *wp)
|
||||
|
||||
if (~wp->flags & PANE_EXITED)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* If a command queue item is blocked on this pane, wait for the
|
||||
* child's exit status before destroying it.
|
||||
*/
|
||||
if (wp->block_item != NULL && (~wp->flags & PANE_STATUSREADY))
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
@@ -642,12 +650,32 @@ window_get_active_at(struct window *w, u_int x, u_int y)
|
||||
|
||||
pane_status = options_get_number(w->options, "pane-border-status");
|
||||
|
||||
if (pane_status == PANE_STATUS_TOP) {
|
||||
/*
|
||||
* Prefer a pane's top border status line over the pane above's
|
||||
* bottom border.
|
||||
*/
|
||||
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||
if (!window_pane_visible(wp) || window_pane_is_floating(wp))
|
||||
continue;
|
||||
|
||||
window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
|
||||
if ((int)x < xoff || x > xoff + sx)
|
||||
continue;
|
||||
if ((int)y == yoff - 1)
|
||||
return (wp);
|
||||
}
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||
if (!window_pane_visible(wp))
|
||||
continue;
|
||||
window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
|
||||
if (!window_pane_is_floating(wp)) {
|
||||
/* Tiled - to and including bottom or right border. */
|
||||
/*
|
||||
* Tiled - to and including the right border, excluding
|
||||
* the bottom border.
|
||||
*/
|
||||
if ((int)x < xoff || x > xoff + sx)
|
||||
continue;
|
||||
if (pane_status == PANE_STATUS_TOP) {
|
||||
@@ -1120,12 +1148,38 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
|
||||
return (wp);
|
||||
}
|
||||
|
||||
void
|
||||
window_pane_block_finish(struct window_pane *wp)
|
||||
{
|
||||
struct cmdq_item *item = wp->block_item;
|
||||
struct client *c;
|
||||
int retval = 0;
|
||||
|
||||
if (item == NULL)
|
||||
return;
|
||||
wp->block_item = NULL;
|
||||
|
||||
if (wp->flags & PANE_STATUSREADY) {
|
||||
if (WIFEXITED(wp->status))
|
||||
retval = WEXITSTATUS(wp->status);
|
||||
else if (WIFSIGNALED(wp->status))
|
||||
retval = WTERMSIG(wp->status) + 128;
|
||||
}
|
||||
|
||||
c = cmdq_get_client(item);
|
||||
if (c != NULL && c->session == NULL)
|
||||
c->retval = retval;
|
||||
cmdq_continue(item);
|
||||
}
|
||||
|
||||
static void
|
||||
window_pane_destroy(struct window_pane *wp)
|
||||
{
|
||||
struct window_pane_resize *r;
|
||||
struct window_pane_resize *r1;
|
||||
|
||||
window_pane_block_finish(wp);
|
||||
|
||||
window_pane_reset_mode_all(wp);
|
||||
free(wp->searchstr);
|
||||
|
||||
@@ -1280,7 +1334,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
|
||||
TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
|
||||
wme->screen = wme->mode->init(wme, fs, args);
|
||||
}
|
||||
wme->kill = args_has(args, 'k');
|
||||
wme->kill = args != NULL ? args_has(args, 'k') : 0;
|
||||
wp->screen = wme->screen;
|
||||
|
||||
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
|
||||
|
||||
Reference in New Issue
Block a user