Add support for querying client environement variables

At connection, a remote client sends all its environement to the
server, which then provides access to client env through
kak_client_env_VAR_NAME variables in the shell.
This commit is contained in:
Maxime Coste 2014-04-07 21:25:44 +01:00
parent 33de42610d
commit 203a7732f5
10 changed files with 106 additions and 10 deletions

View File

@ -14,10 +14,13 @@ namespace Kakoune
Client::Client(std::unique_ptr<UserInterface>&& ui, Client::Client(std::unique_ptr<UserInterface>&& ui,
std::unique_ptr<Window>&& window, std::unique_ptr<Window>&& window,
SelectionList selections, String name) SelectionList selections,
EnvVarMap env_vars,
String name)
: m_ui{std::move(ui)}, m_window{std::move(window)}, : m_ui{std::move(ui)}, m_window{std::move(window)},
m_input_handler{m_window->buffer(), std::move(selections), m_input_handler{m_window->buffer(), std::move(selections),
std::move(name)} std::move(name)},
m_env_vars(env_vars)
{ {
context().set_client(*this); context().set_client(*this);
context().set_window(*m_window); context().set_window(*m_window);
@ -151,4 +154,13 @@ void Client::check_buffer_fs_timestamp()
reload_buffer(context(), filename); reload_buffer(context(), filename);
} }
const String& Client::get_env_var(const String& name) const
{
auto it = m_env_vars.find(name);
static String empty{};
if (it == m_env_vars.end())
return empty;
return it->second;
}
} }

View File

@ -5,6 +5,7 @@
#include "utils.hh" #include "utils.hh"
#include "display_buffer.hh" #include "display_buffer.hh"
#include "input_handler.hh" #include "input_handler.hh"
#include "env_vars.hh"
namespace Kakoune namespace Kakoune
{ {
@ -17,7 +18,9 @@ class Client : public SafeCountable
public: public:
Client(std::unique_ptr<UserInterface>&& ui, Client(std::unique_ptr<UserInterface>&& ui,
std::unique_ptr<Window>&& window, std::unique_ptr<Window>&& window,
SelectionList selections, String name); SelectionList selections,
EnvVarMap env_vars,
String name);
~Client(); ~Client();
// handle all the keys currently available in the user interface // handle all the keys currently available in the user interface
@ -37,12 +40,16 @@ public:
void change_buffer(Buffer& buffer); void change_buffer(Buffer& buffer);
const String& get_env_var(const String& name) const;
private: private:
DisplayLine generate_mode_line() const; DisplayLine generate_mode_line() const;
std::unique_ptr<UserInterface> m_ui; std::unique_ptr<UserInterface> m_ui;
std::unique_ptr<Window> m_window; std::unique_ptr<Window> m_window;
EnvVarMap m_env_vars;
InputHandler m_input_handler; InputHandler m_input_handler;
DisplayLine m_status_line; DisplayLine m_status_line;

View File

@ -25,12 +25,14 @@ String ClientManager::generate_name() const
} }
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
EnvVarMap env_vars,
const String& init_commands) const String& init_commands)
{ {
Buffer& buffer = **BufferManager::instance().begin(); Buffer& buffer = **BufferManager::instance().begin();
WindowAndSelections ws = get_free_window(buffer); WindowAndSelections ws = get_free_window(buffer);
Client* client = new Client{std::move(ui), std::move(ws.window), Client* client = new Client{std::move(ui), std::move(ws.window),
std::move(ws.selections), generate_name()}; std::move(ws.selections), std::move(env_vars),
generate_name()};
m_clients.emplace_back(client); m_clients.emplace_back(client);
try try
{ {

View File

@ -22,7 +22,7 @@ public:
~ClientManager(); ~ClientManager();
Client* create_client(std::unique_ptr<UserInterface>&& ui, Client* create_client(std::unique_ptr<UserInterface>&& ui,
const String& init_cmd); EnvVarMap env_vars, const String& init_cmd);
bool empty() const { return m_clients.empty(); } bool empty() const { return m_clients.empty(); }
size_t count() const { return m_clients.size(); } size_t count() const { return m_clients.size(); }

20
src/env_vars.cc Normal file
View File

@ -0,0 +1,20 @@
#include "env_vars.hh"
namespace Kakoune
{
EnvVarMap get_env_vars()
{
EnvVarMap env_vars;
for (char** it = environ; *it; ++it)
{
const char* name = *it;
const char* value = name;
while (*value != 0 and *value != '=')
++value;
env_vars[String{name, value}] = (*value == '=') ? value+1 : value;
}
return env_vars;
}
}

18
src/env_vars.hh Normal file
View File

@ -0,0 +1,18 @@
#ifndef env_vars_hh_INCLUDED
#define env_vars_hh_INCLUDED
#include "string.hh"
#include <unordered_map>
namespace Kakoune
{
using EnvVarMap = std::unordered_map<String, String>;
EnvVarMap get_env_vars();
}
#endif // env_vars_hh_INCLUDED

View File

@ -101,6 +101,10 @@ void register_env_vars()
"reg_.+", "reg_.+",
[](const String& name, const Context& context) -> String [](const String& name, const Context& context) -> String
{ return RegisterManager::instance()[name[4]].values(context)[0]; } { return RegisterManager::instance()[name[4]].values(context)[0]; }
}, {
"client_env_.+",
[](const String& name, const Context& context) -> String
{ return context.client().get_env_var(name.substr(11_byte)); }
}, { }, {
"session", "session",
[](const String& name, const Context& context) -> String [](const String& name, const Context& context) -> String
@ -189,7 +193,7 @@ void create_local_client(const String& init_command)
UserInterface* ui = new LocalNCursesUI{}; UserInterface* ui = new LocalNCursesUI{};
static Client* client = ClientManager::instance().create_client( static Client* client = ClientManager::instance().create_client(
std::unique_ptr<UserInterface>{ui}, init_command); std::unique_ptr<UserInterface>{ui}, get_env_vars(), init_command);
signal(SIGHUP, [](int) { signal(SIGHUP, [](int) {
if (client) if (client)
ClientManager::instance().remove_client(*client); ClientManager::instance().remove_client(*client);
@ -221,6 +225,7 @@ int run_client(const String& session, const String& init_command)
EventManager event_manager; EventManager event_manager;
auto client = connect_to(session, auto client = connect_to(session,
std::unique_ptr<UserInterface>{new NCursesUI{}}, std::unique_ptr<UserInterface>{new NCursesUI{}},
get_env_vars(),
init_command); init_command);
while (true) while (true)
event_manager.handle_next_events(); event_manager.handle_next_events();

View File

@ -74,6 +74,17 @@ public:
write(memoryview<T>(vec)); write(memoryview<T>(vec));
} }
template<typename Key, typename Val>
void write(const std::unordered_map<Key, Val>& map)
{
write<uint32_t>(map.size());
for (auto& val : map)
{
write(val.first);
write(val.second);
}
}
void write(Color color) void write(Color color)
{ {
write(color.color); write(color.color);
@ -210,6 +221,20 @@ DisplayBuffer read<DisplayBuffer>(int socket)
return db; return db;
} }
template<typename Key, typename Val>
std::unordered_map<Key, Val> read_map(int socket)
{
uint32_t size = read<uint32_t>(socket);
std::unordered_map<Key, Val> res;
while (size--)
{
auto key = read<Key>(socket);
auto val = read<Val>(socket);
res.insert({std::move(key), std::move(val)});
}
return res;
}
class RemoteUI : public UserInterface class RemoteUI : public UserInterface
{ {
public: public:
@ -368,12 +393,15 @@ void RemoteUI::set_input_callback(InputCallback callback)
} }
RemoteClient::RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui, RemoteClient::RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
const EnvVarMap& env_vars,
const String& init_command) const String& init_command)
: m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()), : m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()),
m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }} m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }}
{ {
Message msg(socket); Message msg(socket);
msg.write(init_command.c_str(), (int)init_command.length()+1); msg.write(init_command.c_str(), (int)init_command.length()+1);
msg.write(env_vars);
Key key{ resize_modifier, Codepoint(((int)m_dimensions.line << 16) | Key key{ resize_modifier, Codepoint(((int)m_dimensions.line << 16) |
(int)m_dimensions.column) }; (int)m_dimensions.column) };
msg.write(key); msg.write(key);
@ -446,6 +474,7 @@ void RemoteClient::write_next_key()
std::unique_ptr<RemoteClient> connect_to(const String& session, std::unique_ptr<RemoteClient> connect_to(const String& session,
std::unique_ptr<UserInterface>&& ui, std::unique_ptr<UserInterface>&& ui,
const EnvVarMap& env_vars,
const String& init_command) const String& init_command)
{ {
auto filename = "/tmp/kak-" + session; auto filename = "/tmp/kak-" + session;
@ -459,6 +488,7 @@ std::unique_ptr<RemoteClient> connect_to(const String& session,
throw connection_failed(filename); throw connection_failed(filename);
return std::unique_ptr<RemoteClient>{new RemoteClient{sock, std::move(ui), return std::unique_ptr<RemoteClient>{new RemoteClient{sock, std::move(ui),
env_vars,
init_command}}; init_command}};
} }
@ -525,8 +555,10 @@ private:
} }
if (c == 0) // end of initial command stream, go to interactive ui if (c == 0) // end of initial command stream, go to interactive ui
{ {
EnvVarMap env_vars = read_map<String, String>(socket);
std::unique_ptr<UserInterface> ui{new RemoteUI{socket}}; std::unique_ptr<UserInterface> ui{new RemoteUI{socket}};
ClientManager::instance().create_client(std::move(ui), ClientManager::instance().create_client(std::move(ui),
std::move(env_vars),
m_buffer); m_buffer);
Server::instance().remove_accepter(this); Server::instance().remove_accepter(this);
return; return;

View File

@ -4,6 +4,7 @@
#include "display_buffer.hh" #include "display_buffer.hh"
#include "event_manager.hh" #include "event_manager.hh"
#include "user_interface.hh" #include "user_interface.hh"
#include "env_vars.hh"
namespace Kakoune namespace Kakoune
{ {
@ -23,7 +24,7 @@ class RemoteClient
{ {
public: public:
RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui, RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
const String& init_command); const EnvVarMap& env_vars, const String& init_command);
private: private:
void process_next_message(); void process_next_message();
@ -35,6 +36,7 @@ private:
}; };
std::unique_ptr<RemoteClient> connect_to(const String& session, std::unique_ptr<RemoteClient> connect_to(const String& session,
std::unique_ptr<UserInterface>&& ui, std::unique_ptr<UserInterface>&& ui,
const EnvVarMap& env_vars,
const String& init_command); const String& init_command);
void send_command(const String& session, const String& command); void send_command(const String& session, const String& command);

View File

@ -3,15 +3,13 @@
#include "string.hh" #include "string.hh"
#include "utils.hh" #include "utils.hh"
#include "env_vars.hh"
#include <unordered_map>
namespace Kakoune namespace Kakoune
{ {
class Context; class Context;
using EnvVarRetriever = std::function<String (const String& name, const Context&)>; using EnvVarRetriever = std::function<String (const String& name, const Context&)>;
using EnvVarMap = std::unordered_map<String, String>;
class ShellManager : public Singleton<ShellManager> class ShellManager : public Singleton<ShellManager>
{ {