From 203a7732f5fafb5f9b7e8564bd3bd4587e168134 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 7 Apr 2014 21:25:44 +0100 Subject: [PATCH] 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. --- src/client.cc | 16 ++++++++++++++-- src/client.hh | 9 ++++++++- src/client_manager.cc | 4 +++- src/client_manager.hh | 2 +- src/env_vars.cc | 20 ++++++++++++++++++++ src/env_vars.hh | 18 ++++++++++++++++++ src/main.cc | 7 ++++++- src/remote.cc | 32 ++++++++++++++++++++++++++++++++ src/remote.hh | 4 +++- src/shell_manager.hh | 4 +--- 10 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 src/env_vars.cc create mode 100644 src/env_vars.hh diff --git a/src/client.cc b/src/client.cc index 4f8dfbdf..cda0ef5c 100644 --- a/src/client.cc +++ b/src/client.cc @@ -14,10 +14,13 @@ namespace Kakoune Client::Client(std::unique_ptr&& ui, std::unique_ptr&& window, - SelectionList selections, String name) + SelectionList selections, + EnvVarMap env_vars, + String name) : m_ui{std::move(ui)}, m_window{std::move(window)}, 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_window(*m_window); @@ -151,4 +154,13 @@ void Client::check_buffer_fs_timestamp() 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; +} + } diff --git a/src/client.hh b/src/client.hh index ac191542..56b8504b 100644 --- a/src/client.hh +++ b/src/client.hh @@ -5,6 +5,7 @@ #include "utils.hh" #include "display_buffer.hh" #include "input_handler.hh" +#include "env_vars.hh" namespace Kakoune { @@ -17,7 +18,9 @@ class Client : public SafeCountable public: Client(std::unique_ptr&& ui, std::unique_ptr&& window, - SelectionList selections, String name); + SelectionList selections, + EnvVarMap env_vars, + String name); ~Client(); // handle all the keys currently available in the user interface @@ -37,12 +40,16 @@ public: void change_buffer(Buffer& buffer); + const String& get_env_var(const String& name) const; + private: DisplayLine generate_mode_line() const; std::unique_ptr m_ui; std::unique_ptr m_window; + EnvVarMap m_env_vars; + InputHandler m_input_handler; DisplayLine m_status_line; diff --git a/src/client_manager.cc b/src/client_manager.cc index f818e226..9fe18ea7 100644 --- a/src/client_manager.cc +++ b/src/client_manager.cc @@ -25,12 +25,14 @@ String ClientManager::generate_name() const } Client* ClientManager::create_client(std::unique_ptr&& ui, + EnvVarMap env_vars, const String& init_commands) { Buffer& buffer = **BufferManager::instance().begin(); WindowAndSelections ws = get_free_window(buffer); 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); try { diff --git a/src/client_manager.hh b/src/client_manager.hh index cc5c6c0c..b25fe525 100644 --- a/src/client_manager.hh +++ b/src/client_manager.hh @@ -22,7 +22,7 @@ public: ~ClientManager(); Client* create_client(std::unique_ptr&& ui, - const String& init_cmd); + EnvVarMap env_vars, const String& init_cmd); bool empty() const { return m_clients.empty(); } size_t count() const { return m_clients.size(); } diff --git a/src/env_vars.cc b/src/env_vars.cc new file mode 100644 index 00000000..d6c04fa3 --- /dev/null +++ b/src/env_vars.cc @@ -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; +} + +} diff --git a/src/env_vars.hh b/src/env_vars.hh new file mode 100644 index 00000000..087d51a7 --- /dev/null +++ b/src/env_vars.hh @@ -0,0 +1,18 @@ +#ifndef env_vars_hh_INCLUDED +#define env_vars_hh_INCLUDED + +#include "string.hh" + +#include + +namespace Kakoune +{ + +using EnvVarMap = std::unordered_map; + +EnvVarMap get_env_vars(); + +} + +#endif // env_vars_hh_INCLUDED + diff --git a/src/main.cc b/src/main.cc index a026f664..43b9675a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -101,6 +101,10 @@ void register_env_vars() "reg_.+", [](const String& name, const Context& context) -> String { 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", [](const String& name, const Context& context) -> String @@ -189,7 +193,7 @@ void create_local_client(const String& init_command) UserInterface* ui = new LocalNCursesUI{}; static Client* client = ClientManager::instance().create_client( - std::unique_ptr{ui}, init_command); + std::unique_ptr{ui}, get_env_vars(), init_command); signal(SIGHUP, [](int) { if (client) ClientManager::instance().remove_client(*client); @@ -221,6 +225,7 @@ int run_client(const String& session, const String& init_command) EventManager event_manager; auto client = connect_to(session, std::unique_ptr{new NCursesUI{}}, + get_env_vars(), init_command); while (true) event_manager.handle_next_events(); diff --git a/src/remote.cc b/src/remote.cc index 2d71a888..72e3425f 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -74,6 +74,17 @@ public: write(memoryview(vec)); } + template + void write(const std::unordered_map& map) + { + write(map.size()); + for (auto& val : map) + { + write(val.first); + write(val.second); + } + } + void write(Color color) { write(color.color); @@ -210,6 +221,20 @@ DisplayBuffer read(int socket) return db; } +template +std::unordered_map read_map(int socket) +{ + uint32_t size = read(socket); + std::unordered_map res; + while (size--) + { + auto key = read(socket); + auto val = read(socket); + res.insert({std::move(key), std::move(val)}); + } + return res; +} + class RemoteUI : public UserInterface { public: @@ -368,12 +393,15 @@ void RemoteUI::set_input_callback(InputCallback callback) } RemoteClient::RemoteClient(int socket, std::unique_ptr&& ui, + const EnvVarMap& env_vars, const String& init_command) : m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()), m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }} { Message msg(socket); 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) | (int)m_dimensions.column) }; msg.write(key); @@ -446,6 +474,7 @@ void RemoteClient::write_next_key() std::unique_ptr connect_to(const String& session, std::unique_ptr&& ui, + const EnvVarMap& env_vars, const String& init_command) { auto filename = "/tmp/kak-" + session; @@ -459,6 +488,7 @@ std::unique_ptr connect_to(const String& session, throw connection_failed(filename); return std::unique_ptr{new RemoteClient{sock, std::move(ui), + env_vars, init_command}}; } @@ -525,8 +555,10 @@ private: } if (c == 0) // end of initial command stream, go to interactive ui { + EnvVarMap env_vars = read_map(socket); std::unique_ptr ui{new RemoteUI{socket}}; ClientManager::instance().create_client(std::move(ui), + std::move(env_vars), m_buffer); Server::instance().remove_accepter(this); return; diff --git a/src/remote.hh b/src/remote.hh index 22259930..cebd56cf 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -4,6 +4,7 @@ #include "display_buffer.hh" #include "event_manager.hh" #include "user_interface.hh" +#include "env_vars.hh" namespace Kakoune { @@ -23,7 +24,7 @@ class RemoteClient { public: RemoteClient(int socket, std::unique_ptr&& ui, - const String& init_command); + const EnvVarMap& env_vars, const String& init_command); private: void process_next_message(); @@ -35,6 +36,7 @@ private: }; std::unique_ptr connect_to(const String& session, std::unique_ptr&& ui, + const EnvVarMap& env_vars, const String& init_command); void send_command(const String& session, const String& command); diff --git a/src/shell_manager.hh b/src/shell_manager.hh index 0774fdf2..a6fe38fd 100644 --- a/src/shell_manager.hh +++ b/src/shell_manager.hh @@ -3,15 +3,13 @@ #include "string.hh" #include "utils.hh" - -#include +#include "env_vars.hh" namespace Kakoune { class Context; using EnvVarRetriever = std::function; -using EnvVarMap = std::unordered_map; class ShellManager : public Singleton {